Understanding branches in Git

In this article we will take a deep dive into Git branches. We’ll see how simple yet powerful the concept of branching in Git is. This article is part of a series of articles that will also be part of our book Rebase which you can and should follow it’s progress on Leanpup.

In our last Git article we took a detailed look on the anatomy of a commit. Now that we know how commits work in Git let’s expand on our knowledge and understand branches. It’s important to first grok the concept of branching in Git before we go into merging.

Starting with a simple scenario

Let’s start by looking at a simple commit history.

A simple commit history

Let’s assume our entire repository history is made up only from those three commits. There’s a pointer called master pointing at commit a5c3eb. That’s all what branches are: they are movable pointers. Let’s create a branch called feature.

git branch feature

Creating another branch

Seen that? We just created another pointer called feature pointing at the exact same commit. Basically, all Git does it creates a file called feature with the contents being a 40 char string, the SHA-1 revision name of the commit.

But wait! Now that we have two different branches pointing to the same commit. How does Git know which branch is currently checked out? This is where the HEAD pointer comes into play!

Understanding HEAD

The HEAD is a special pointer that simply points to the currently checked out branch or commit. And again, it’s a simple file inside the .git folder called HEAD which in our case currently contains the string master.

Ok then, what happens if we switch to our feature branch?

git checkout feature

Switching branches

Exactly, all what happened is that HEAD is now pointing to feature instead of master. Switching between master and feature at this point boils down to Git replacing the string master with feature in the HEAD file. Super cheap!

But what happens if we now create or modify some files and make another commit? Let’s find out.

vim file.txt
git add file.txt
git commit -m "yay, that's fun!"

Adding a new commit to feature

We created a new commit c57e22 and the feature pointer moved on to it. That’s because branches are simply movable pointers to commits. But why did feature move and master did not? Because feature is the currently checked out branch. And how does Git know? Because HEAD points to feature! Simple isn’t it?

Let’s switch back to master.

git checkout master

Switching back to master

What happened now is that the HEAD pointer changed to master and the state of the entire working directory was changed to what it was at a5c3eb.

Let’s modify a file and create another commit.

vim anotherFile.txt
git add anotherFile.txt
git commit -m "yay, more fun!"

Adding a new commit to master

A new commit 3aa2ff appeared and master moved on to it. At this point our history has diverged. And this is the whole point of branches. They enable us to have parallel evolution of a code base.

At this point you may be wondering how to merge both ends back together. This is exactly what we will look at in our next Git episode. Stay tuned!

Detached HEAD

Ever came across a detached HEAD? Now that we know how Git handles branches, it’s the perfect time to demystify the detached HEAD. You’ll be suprised how simple it is. In fact, you may have spotted a hint in the text above.

The HEAD is a special pointer that simply points to the currently checked out branch or commit

Not only can we check out branches, we can also checkout any commit revision explicitly.

git checkout 339aa3

Checking out commits explicitly

You see? What happened now is that HEAD points to 339aa3 explicitly and the entire working directory was changed to what it looked like at that commit.

When HEAD points to a commit hash explicitly (rather than to a branch) it’s in a detached state.

What happens if we create new commits from here? Let’s find out!

vim someFile.txt
git add someFile.txt
git commit -m "detached HEAD fun"

Adding commits to a detached HEAD

Let’s add one more!

vim someFile.txt
git add someFile.txt
git commit -m "more detached HEAD fun"

Adding even more

We can go on like that. Does that mean, we don’t actually need branches? Yes and no. What we just did is kinda like having a branch without a branch name. It works but there are two problems:

  • How do you remember the SHA-1 key without a simple branch name?
  • Commits that are not reachable by any branch or tag will be garbage collected and removed from the repository after 30 days.

No need to worry though. We can simply create a new branch right here!

git checkout -b bugfix

Giving it a name again

Bottom line: If in doubt, just branch. Branches are too lightweight to even think twice about them.

Written by  Author

Christoph Burgdorf