Image of What is Git HEAD?

ADVERTISEMENT

Introduction

If you have used Git to work on a project before, you have likely noticed the term HEAD appear in response to commits, checkouts, and pulls. You may have even run into issues with a detached HEAD a few times. However, this seemingly complex feature is actually simple and helpful. In this article, we will answer the question What is Git HEAD? Learning about Git HEAD will add to your knowledge and understanding of Git's version control capabilities and increase your confidence as a software developer.

Git Refs and Heads

Before jumping right into our main question, it will be useful to provide some background information on two Git concepts, refs and heads.

In Git, a ref is a human readable name that references a commit ID. A ref is essentially a pointer to a commit. Examples of refs are branch names such as master, dev, etc, and tags such as v0.1, v0.2, etc. You can think of each of these as a variable name that points to a commit ID. The commit ID that a ref points to is dynamic so it can change over time.

When representing a branch name, a ref such as master represents the tip (most recent commit ID) on that branch. Refs are stored in a special location in your repository at the path .git/refs/.

In Git, a head is a ref that points to the tip (latest commit) of a branch. You can view your repository’s heads in the path .git/refs/heads/. In this path you will find one file for each branch, and the content in each file will be the commit ID of the tip (most recent commit) of that branch. For example, there is a file called master in that path that contains the commit ID of the tip of the master branch. When you make a new commit on a branch or pull commits from a remote, the head file for that branch is always updated to reflect the commit ID of the tip of the branch.

A general understanding of refs and heads is important because they tell us what branch names and tags really are in Git. They are not complicated or special. They are simply pointers to commits, in the form of text files where the file name represents the name of the ref/head and the content is the commit ID that the ref points to. I strongly recommend you open up terminal, browse to the root of your Git repository, and use your favorite text editor to peek at some of the files in the .git/refs/heads/ directory and their content.

Note: Refs can actually point to other refs, which will resolve to the commit ID of the destination ref. For example, if you create a tag called v2.0 pointing to the master branch, the v2.0 tag will resolve to the commit ID of the tip of the master branch at that point in time. If you are interested in looking at the tags in your Git repository, find them in .git/refs/tags/ directory.

What is Git HEAD?

Now that we know the basics of refs and heads, let talk about the more specific concept of Git HEAD.

HEAD is a special ref that points to the commit you are currently working on - the currently checked out commit. You can think of it as a global variable that can change depending on which commit you've checked out in your working directory. It is stored in a file called .git/HEAD, which I recommend you peek at in your text editor. HEAD usually points to the tip/head of the currently active branch, which is represented in the .git/HEAD file as follows:

ref: refs/heads/master

This enables Git to know that the user's working directory currently corresponds to the tip of the master branch. When you use the git checkout command, HEAD is changed to point to the head of the newly checked out branch. So if you run the command git checkout dev, the HEAD file will be updated as:

ref: refs/heads/dev

This enables Git to understand that you are currently working on the tip of the dev branch.

However, HEAD can also point to specific commit IDs, just like any other ref. The git checkout command will accept any reference and store it in .git/HEAD, as seen below:

What is Git HEAD?

In this screenshot, we used git reflog to view our commit history. This shows each commit reference ID in the left-hand column. We can then use git checkout <commit ID> to move to a previous commit. This places Git into a state called detached HEAD, which means that HEAD is not currently pointing to a branch head (tip). In this state, you can view and edit any files in the repository, exactly as they were in that commit.

There are two ways to return HEAD to a normal state:

  1. If you want to delete any changes associated with the detached HEAD, you can simply checkout an existing branch (e.g. git checkout master). This will fully revert your repository to that branch's state and set HEAD to point to that branch's head.

  2. If you want to keep your changes, you can simply create a new branch (e.g. git branch tmp). This will store your changes in the tmp branch and move HEAD to the tip of that branch. After doing that, you can checkout the original branch and merge your new branch into it (e.g. git checkout master followed by git merge tmp).

When should I use Git HEAD?

The ability to return to a previous commit is immensely useful during development. For example, if a commit introduces a breaking change to a file, you can use git log -p <file> to view all of the commits that have affected that file. In this interface, you can use the arrow keys to scroll up/down and press Q to exit.

What is Git HEAD?

Once you find the commit in question, you can revert the full branch state to it using git checkout <commit ID>. This detaches HEAD back to that commit so that you can view that file's old version in context (e.g. to run and test your application). Alternatively, if you only want to revert that specific file, you can run git checkout <commit ID> <file> to restore it.

Conclusion

In this article, we learned about what Git refs and heads are. We also discussed what Git HEAD is and how to manipulate it to view previous commits. Overall, a basic understanding what Git HEAD is will clarify some of the Git tasks that you work with every day. It will also help you better troubleshoot issues and feel more confident about your skills as a software developer.

If you're interested in learning more about how Git works under the hood, check out our Baby Git Guidebook for Developers, which dives into Git's code in an accessible way. We wrote it for curious developers to learn how Git works at the code level. To do this we documented the first version of Git's code and discuss it in detail.

We hope you enjoyed this post! Feel free to shoot me an email at jacob@initialcommit.io with any questions or comments.