- Install Git distributed version control software.
- Set up your default credentials (or display without a value to set):
$ git config --global user.name "John Doe"
$ git config --global user.email [email protected]
or if he's a GitHub user, so you see his icon: [email protected]
For example on MacOS:
~/Documents/code/test❯ git config --list
credential.helper=osxkeychain
init.defaultbranch=main
user.name=Cees Timmerman
user.email=c.timmerman@company
filter.lfs.clean=git-lfs clean -- %f
filter.lfs.smudge=git-lfs smudge -- %f
filter.lfs.process=git-lfs filter-process
filter.lfs.required=true
core.excludesfile=/Users/ct/.gitignore_global
core.autocrlf=input
difftool.sourcetree.cmd=opendiff "$LOCAL" "$REMOTE"
difftool.sourcetree.path=
mergetool.sourcetree.cmd=/Applications/Sourcetree.app/Contents/Resources/opendiff-w.sh "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED"
mergetool.sourcetree.trustexitcode=true
commit.template=/Users/ct/.stCommitMsg
core.sshcommand=ssh -i ~/.ssh/myed25519key
~/Documents/code/test❯ cat ~/.gitconfig
[user]
name = Cees Timmerman
email = c.timmerman@company
[filter "lfs"]
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %f
process = git-lfs filter-process
required = true
[core]
excludesfile = /Users/ct/.gitignore_global
autocrlf = input
[difftool "sourcetree"]
cmd = opendiff \"$LOCAL\" \"$REMOTE\"
path =
[mergetool "sourcetree"]
cmd = /Applications/Sourcetree.app/Contents/Resources/opendiff-w.sh \"$LOCAL\" \"$REMOTE\" -ancestor \"$BASE\" -merge \"$MERGED\"
trustExitCode = true
[commit]
template = /Users/ct/.stCommitMsg
[core]
sshCommand = "ssh -i ~/.ssh/myed25519key"
Generate a key with ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/myed25519key -C "For Git from MacOS."
and add the ~/.ssh/myed25519key.pub
content to your Bitbucket server keys for example.
You can ignore your editor's config files globally with for example git config --global core.excludesFile '~/.gitignore'
and nano ~/.gitignore
.vscode/
Ctrl+S, Ctrl+X.
Main or master is the (preferably working) development branch in most git branching models. (GitHub Flow, GitLab Flow, etc.; not Git Flow.) If you want to stabilize for a release, create a release branch and only merge bug fixes from master to it by cherry picking. When a branch works in your local environment (e.g. VirtualBox), rebase and push it to the remote. Ideally, merge requests should get reviewed and the remote production branch should get tested and deployed after it passes.
Check out new remote branch:
$ git fetch
$ git checkout -t origin/TICKET2 # Or `git switch TICKET2` since 2020.
Branch 'TICKET2' set up to track remote branch 'TICKET2' from 'origin'.
Switched to a new branch 'TICKET2'
And delete the old branch locally:
git branch -d TICKET1
Or if that complains:
git branch --delete --force TICKET1
Or create a new local branch and push it to remote:
$ git checkout -b TICKET2
Switched to a new branch 'TICKET2'
$ git add . # Stage all changes. `git add -u .` to ignore untracked files.
$ git reset DEBUG_FILE # Unstage this file.
$ git status # Verify changes.
$ git commit -m "MESSAGE"
$ git push -u origin TICKET2 # Note the lack of a slash.
Publish and track a new branch without getting a src refspec error:
git push -u origin HEAD:NEW_BRANCH_NAME
That -u
sets to track the new upstream branch just like this:
git branch -u origin/NEW_BRANCH_NAME
List all branches:
git branch -a
Rename local branch:
git branch -m NEW_BRANCH_NAME
Delete remote branch:
git push origin -d REMOTE_BRANCH_NAME
Remove remotely deleted branches from local remotes:
git fetch --prune
If you used https and are tired of entering your password, switch to git with a private key (after adding ssh-add ~/.ssh/id_ed25519
to your shell init):
git remote remove origin
git remote add origin [email protected]:project/components/subproject.git
git branch -u origin/TICKET TICKET
Alternatively on GitHub you can use a more fine-grained personal access token: git remote set-url origin https://<token>@github.com/<user>/<repo>.git
Get origin with git remote -v
. You could also add one for no apparent reason.
Merge the main branch before pushing to it:
git merge origin/master
Or if you want to rewrite history which nobody else depends on, rebase:
git rebase origin/master
git push --force-with-lease
Conflicting files should be edited and staged as usual before committing the merge.
If git pull
(with git config pull.rebase true
to avoid littering your local branch with merge commits) complains, autostash:
$ git pull
error: cannot pull with rebase: You have unstaged changes.
error: please commit or stash them.
$ git pull --rebase --autostash
Updating 48a1590..660f95a
Created autostash: ac9d78e
Fast-forward
ide.html | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 ide.html
Applied autostash.
Auto-autostash with git config rebase.autoStash true
If you messed up and nobody is basing work on that yet, reset to a good state (with --hard
to drop the changes; without to redo your commits to split or squash them for example, which is easier using git rebase -i HEAD~2
if you want to keep/amend the last 2 commit messages):
git reset --hard LAST_GOOD_HASH
git push --force-with-lease --force-if-includes
--force-if-includes
ensures you're including the latest commit from remote. --force-with-lease
only ensures you've fetched that. If the remote is messed up, you can use git push --force
to overwrite it.
If git randomly (seems to happen while rebasing) complains about changing CRLF to LF and vice versa like in this project, ensure core.autocrlf
is not false
. You'd think false
leaves random txt files alone but apprently not. false
with default core.eol
being native converting both ways at once seems to contradict this. true
seems to work better but input
should be the safest afaik.
If your repo is FUBAR, rename it and clone again. You can compare file versions using commit hashes from git log --graph
: git diff OLD_HASH NEW_HASH [FILE]
To gatekeep commits, use .git\hooks\pre-commit
with your favorite linter, e.g.
#!/bin/sh
pylint *.py
or similar but more bloated versions generated by a Git hooks package manager such as the confusingly-named pre-commit.