Here, I’ll just collect a list of my commonly used commands in git for later reference. I followed along using this excellent branching tutorial.

Other cheat sheets

Misc other helpful resources:

Create a repository

Create a local repository called sandbox:

mkdir sandbox
cd sandbox
git init .

Commits

git add 99bottles.py
git commit -m "Created a draft of 99bottles.py"

Log

# This shows you the diffs/patches along with the commits:
git log -p
# Super fancy:
git log --graph --abbrev-commit --stat --summary -C --decorate --date=local

Diff

# to see what you've changed but not yet staged:
git diff
# to see what you've staged that will go into your next commit:
git diff --staged

Branching

We now want to change the code to use variables instead of repeat the “bottles of beer” strings. For that we open up a branch:

git branch variable-names
git checkout variable-names
# Or, shorter: git checkout -b variable-names

emacs 99bottles.py
# ...Make your modifications...

# Shorter version than 'git add' followed by 'git commit':
git commit -a -m "Change the strings to variables, making the for-loop shorter"

# Send your changes in the 'master' branch to the remote called 'origin' (GitHub):
git push origin variable-names  # *when* you do this has no effect on how the repo's history will look
# Or: git push --all origin  # this pushes all branches. Only use on your own repo!

Advanced branching

  • Set the master branch three commits back (-f: even if it exists already)
git branch -f master HEAD~3

Merging

Now, as soon as others review and sign off your code, you can merge variable-names into master:

git checkout master
git merge variable-names

This creates a merge commit.

Watch out for the order of the branch names: here, you merge variable-names into master, so that master contains the commits from variable-names. You could in theory now checkout bugfix and merge master; then, both branches would point to the same merge commit.

This website offers helpful additional info on updating your local repo.

Rebasing

  • Rebasing transforms branches into a nice linear sequence of commits
  • In short, it changes the base commit of the branch it’s run from
  • The danger is that it rewrites history, so you shouldn’t use it if others already pulled the commits you want to rebase

To apply the changes that happened in the master branch to the bugFix branch, you checkout bugFix and then rebase master into bugFix:

git checkout -b bugFix
git commit -a -m "Fix some bug"
git rebase master

In plain words, git rebase master means rebase the current HEAD on top of master. After this rebase, bugFix is right on top of master (one commit ahead).

To get master up to date with bugFix, you’d have to checkout and rebase master, too.

If you specify two branches, you will rebase master into bugFix as well. This way, you don’t have to be on bugFix when you issue the rebase command:

git rebase master bugFix
# This is equivalent to:
# git checkout bugFix
# git rebase master

Interactive rebasing

When you don’t know what commits of your bugFix branch you want to keep, you can review them before you rebase. Again, you check out the bugFix branch, then rebase master into it, but this time, interactively:

git checkout bugFix
git rebase -i master

If your master branch is up-to-date with the newest commit, you can reorder the last 4 commits via

git rebase -i HEAD~4
git rebase -i myBranch  # if myBranch is a direct ancestor of master/HEAD

Moving around in the commit history

  • HEAD points to the most recent commit your working is based on
  • If you git checkout a commit instead of a branch, git detaches the HEAD i.e. it’s not HEAD => master => afbe194 anymore, but HEAD => afbe194
  • Instead of a SHA1 hash, you can move upwards with the shortcuts ^ and ~<num>.
    • You can checkout master^^ or HEAD~2 to go to the grandparent of master or HEAD, respectively
    • For the difference between ^ and ~, see here and here. In brief, ^1 (equal to ^) and ^2 check out the “mother” and “father” of a commit, whereas ~2 checks out the grandparent.
  • You can use the @{<num>} syntax along with git reflog to see where your HEAD last pointed to.

Reversing changes

An important distinction:

  • git reset HEAD~1 moves a branch reference backwards; it “rewrites history”.
    • This leaves your working files at the state of the latest commit (they are now modified), but resets the source tree to the specified commit
    • The argument --hard resets your local files to the specified commit
    • The argument --soft keeps the modified files in the staging area
  • git revert HEAD adds a new “automatic” commit that reverts the changes of the specified commit. It’s a reverse cherry-pick, kind of. Note that you specify the actual commit, not the state before the desired commit. Something like git revert HEAD~3 reverts only that one commit, and not everything that happened after it!
  • You can also git rebase -i HEAD~4 and interactively delete the commits you don’t want anymore

Moving work around: cherry-picking

If you have three commits across, say, three different branches, and want to copy these changes into your main branch:

git cherry-pick 862ff59 02659a3 54e8e5d

This creates three new commits under master.

Understand that cherry-pick makes only sense if the picked commit is not a direct ancestor of HEAD.

Tags

Branches can be moved around easily, they should not be used to permanently mark specific points in history.

Use tags instead for releases and big merges:

git tag my-tag my-SHA1
git checkout my-tag

Tags can be checked out, but not moved around. If you check out a tag and commit on it, you will go into detached head mode.

Remotes

On GitHub, create a repository called sandbox that is empty but contains an automatic README file. Then, get it on your local machine:

git clone https://github.com/AlexEngelhardt/sandbox.git

Remote branches

It’s crucial to understand the difference between local work and public work.

Remote branches have names such as origin/issue143 or upstream/master. They reflect the state of remote repositories since you last talked to them (e.g. via git fetch).

When you check them out, you are automatically in detached HEAD mode, because you can’t work on these branches directly.

Fetching

git fetch synchronizes your local representation of the remote repository with the current state of the remote repository. It only updates the remote branches (e.g. origin/master), not your local branches (e.g. master).

Again: it does not change any local files! This allows you to inspect the changes and merge them later.

git fetch 

Without any arguments, git fetch just gets all commits from the remote onto all the remote branches.

git fetch origin doot

This command updates your local state of the remote branch, i.e. origin/doot is now up to date.

In order to pull the remote branch theirBranch, to your local branch myBranch, use a colon refspec:

git pull origin theirBranch:myBranch
# more fancy stuff is possible too:
git push origin theirBranch~2:myBranch

This behavior, although possible, is not recommended!

Pulling (fetching & merging)

To update your local state with new information from the remote, you could:

  • cherry-pick from origin/master
  • rebase from origin/master
  • merge origin/master

Merging is the most common choice, and the command git pull is (almost) just a shorthand for fetching and subsequently merging. You can also use git pull --rebase to do a rebase instead of a merge. Note: A git pull origin myBranch merges with your currently checked out branch, not necessarily with your myBranch!

git pull is only almost equivalent to git fetch; git merge, because pull is merging from remote only and ignores your local commits in your local branch which is tracking the remote branch being pulled from (Source).

Fancy pulling commands are very similar to the respective fetch & merge commands. A few examples of equivalent commands:

git pull origin foo
git fetch origin foo; git merge origin/foo

Note: The above pull command means that if you are currently not on your local foo branch but instead on someFeature, you will merge the remote foo branch into your someFeature branch instead!

git pull origin bar~1:bugFix
git fetch origin bar~1:bugFix; git merge bugFix

Pushing

  • Note: Only push to bare repositories, i.e. repos that have no working directory. This ensures you don’t mess up another developer’s workdir.

To push the branch bugFix to your remote origin, you need to have write access, and nobody else should have pushed in the meantime (otherwise, pull first):

git push origin bugFix

This says: Go to the branch named “bugFix” in my repository, grab all the commits, and then go to the branch “bugFix” on the remote named “origin.” Place whatever commits are missing on that branch and then tell me when you’re done.

Without any arguments, git push just pushes the currently checked out branch to the remote that it’s set to track:

git push

If you’re not on a branch (but instead, e.g., in detached HEAD mode), this command will fail.

In order to push your local branch myBranch to the remote branch theirBranch, use a colon refspec:

git push origin myBranch:theirBranch
# more fancy stuff is possible too:
git push origin myBranch~2:theirBranch

Diverged history

When your local representation of the remote is out-of-date, your git push might fail because it is now a very ambiguous situation. You first have to update your local repository.

The easiest fix is to rebase your work:

git fetch
git rebase origin/master  # or: git pull --rebase
git push

Alternatively, you can merge, which doesn’t move your work, but creates a merge commit instead:

git fetch
git merge origin/master  # or: git pull
git push

Now, both your local and the remote repo have a merge commit with two ancestors, i.e. the remote changes and your local changes.

The rebase vs. merge discussion is a hot topic among developers. It boils down to “not rewriting history” (merging) vs. “having a clean commit tree” (rebasing).

Remote-tracking branches

The command git clone usually does the following things:

  • Download all remote branches (called origin/*)
  • Create only one local branch called master
  • Set master to track the origin/master branch

You can edit this default behavior. If you want a local branch called doot instead of master, but doot should track master, issue

git checkout -b doot origin/master
git pull

The git pull now updates your local branch doot instead of master!

Another way to set the tracking, if your branch doot already exists, and you want doot to track origin/master, is

git branch -u origin/master doot

Stashing

  • git stash goes back to the HEAD commit and stores all other (staged or unstaged) changes in a temporary stash to be reapplied later

When things go wrong: Merge conflicts

see here for a deeper introduction and here for a short summary.

Your options are basically:

  • Resolve manually
  • Cancel merge
  • “omg just accept their stuff”

Misc lessons learned

  • git reset --hard HEAD does not remove untracked files. If you git add them and then reset, they will be lost.