GIT Deep Dive – This is what HEAD really is

Search for information on Git HEAD and the chances are you come away with a vague idea and possibly a good idea, but confused. It need not be that way if you look at the essence of why we have a HEAD.

I am going to explain in two sentences exactly what it is. Then I will explain what uses and what changes HEAD but remember; don’t lose sight of what the essence of HEAD is.

  • HEAD points to a single COMMIT record within the git history.
  • When we run git commit, It needs to know what the last COMMIT was so it can add it as the parent.

That is it. It is no more complicated than that. So what makes it confusing when you read articles? It is because they tend to explain HEAD based on something that uses it or something that changes it.

What is Inside HEAD

The value for HEAD is held in the file .git\HEAD. Let’s take a look inside.

You are likely to see something like ref: refs/heads/master. This says, “I am currently pointing at the master branch and the last commit ID for master is in file refs/heads/master“.

Take a look inside .git\refs\heads\master. Notice on windows the file shows forward slashes but we need to reference the file using back slashes. A reference to the fact that git was engineered for Linux first.

You will see a 40 byte SHA-1 hash which is unique for each object. Let’s check what type of object this points to.

No surprise, it is a commit object. Git has 3 other object types, tree, blob and tag.

Let’s look at the contents of the commit.

This shows our commit object which includes a parent. This was the hash value which HEAD pointed to before this commit was created. Now this commit has been created, the HEAD references this new commit.


What happens when you switch to a different branch

You will notice above that head points to the master reference file and that file stores the last commit that took place on that branch. When we run git log, by default it uses head as its starting point.

Let’s switch branch to B1 and run git log.

We can see that HEAD is now referencing B1 and the commit history is shown in relation to B1.

Detaching the Head

I mentioned right at the beginning that HEAD is just a reference to a commit. In most cases it references a branch that references a commit. We can however; get head to reference any commit that is not referenced by anything else.

I have ran a git log for branch B1 and output the full commit hash value.

Let’s checkout the previous commit.

Let’s view the log.

You can see that HEAD now points at a commit which has no other references. Let’s view the .git\HEAD file.

When the HEAD file contains a commit hash we call this “Detached Head State“. I am not going to get into why you might want to detach the head however; if you decide to commit changes with a detached head, make sure you either create a branch or tag at the head or merge the changes into another branch. If you don’t the git garbage collector will think they are no longer needed and remove them.

While we are here lets create a tag at this commit.

I will now checkout B1 and then look at the log.

HEAD is now back to referencing a branch and notice the creation of the tag v1.0 on the second commit.

What Other Commands Use the Head Reference

Any command that take a <commit>, <object>, or <tree-ish> as an argument can use head in its place.

HEAD Latest commit. Same as HEAD@{0}
HEAD^ Previous commit. Same as HEAD@{1}
HEAD^^ Two commits before. Same as HEAD@{2}
HEAD~6 Six commits before. Same as HEAD@{6}

One of the most important commands that use HEAD is git log. It constructs the log by looking at HEAD and then following the parent references.

That is probably not everything but definitely enough to keep aHEAD of the game. J