Quinn's Blog

Fundamentals of git

January 06, 2014

I’m going to document here some basics about git. I think a lot of very skilled developers who use git everyday haven’t yet “grocked” the fundamentals of how it works. For starters, I am going to define a glossary of terms relating to git (more are available by running “git help glossary”).

working tree (or working copy)

The working tree is the name given to the files and folders you are able to edit and are being managed by git.

HEAD

The HEAD ref represents where you currently * are * in the git history. For example, if you are on the master branch, your head is associated with the most recent commit on the master branch.

clean (working tree)

The working tree is considered clean (or pristine) if there is no difference between the code contained in the working tree and the state of the commit referenced by HEAD.

sha

the sha is the canonical reference to a commit. All commits have a sha which is a (practically) unique identifier of the commit.

ref

A ref is any reference to a commit. a ref can be a branch or tag name, sha, HEAD or it can also be part of git’s special syntax for referencing commits.

Different ways to reference commits

git provides many different ways to reference commits beyond a branch or tag name. The two I am going to be using the most for this article are the relative references. For example, to reference a commit’s ancestor you would use ^. Add more ^‘s to move further back in time. Also, you can use ~number to have the same affect. Some examples

HEAD^  # => one commit behind HEAD
HEAD~1 #

HEAD^^^^^^^^^^ # => ten commits behind HEAD
HEAD~10        #

Diffs, the working tree and HEAD

Any time a diff exists (seen by running “git diff”) this is because of a difference between HEAD and the working tree. If either of these change (the HEAD changes, or the working tree changes) it changes the diff.

reset

the git reset command moves the current location of HEAD. When adding --hard to git reset it will also modify the working tree to match the new commit that HEAD is associated with after being moved. HEAD is implied if no ref is given. Some examples:

git reset --hard
# is the same as
git reset --hard HEAD

This command moves HEAD to where it already is and modifies the working tree to match. This has the effect of removing any diff on the current working tree.

checkout

It is possible to associate a branch with HEAD so that it remains associated with HEAD as commits are created (rather than remaining associated with a specific commit). This is achieved through “checking out” a branch. git checkout can also be used to checkout any ref. git checkout differs from git reset --hard in that it attemps to safely move HEAD, i.e. it will throw an error if there is a diff that would be overwritten by the checkout.

rebase (non-interactive)

git rebase, when used non-interactively, is a way of moving groups of commits around within a project. git rebase is able to determine the common ancestor of the current branch as well as the target, and move only the commits that are different. For example:

      -----------* feature-001
    /
----*-----*
          ▲
          | master

git checkout feature-001
git rebase master

    | this commit used to be the
    | place where master and feature-001
    | diverged. It is no longer significant
    ▼
----*-----*-----------* feature-001
          ▲
          | master

try it out!

Make a new branch on a project and see if you can complete some of these git related tasks with only using reset.

  1. Squash the last 5 commits on your current branch into one commit.
  2. Split the most recent commit you made into two commits.

Try doing this using checkout, reset, and rebase:

  1. split the 5th newest (~5) commit into two commits.

rebase (interactive)

git rebase -i is a toolchain to help comlete some of the tasks described above. It allows you to squash, split, and delete commits easily. For example, git rebase -i HEAD~5 will allow you to reorganize and edit the last 5 commits on your project. Try to complete the tasks above using git rebase -i.

I hoped this helped you understand git’s core features a little bit more. It’s easy to use git without fully understanding what is going on, but it’s important to have a deep understanding of the working of git to trully take advantage of it’s power.