Many developers use Git in their daily life. Among all the operations, branch commands might be one of the most important part. But do you know what “branch” is, and what are the secrets behind branches? This blog post will share some Git internals with you.

Preparation

Before going further, let’s prepare a new Git repository with a dummy commit:

$ git init
$ echo "Hello world" > README.md
$ git add README.md
$ git commit -m "Initial commit"
$ git log --pretty=oneline
56f0a2324c87464be433b0aa34c0faa4c4eb761f (HEAD -> master) Initial commit

As you can see, there’re only one commit in the repository, the initial one. Please remember this value because we’ll use it later:

56f0a2324c87464be433b0aa34c0faa4c4eb761f

Branch Listing

Now let’s do a basic branch command git branch, which will list all the local branches in the repository:

$ git branch
* master

Branches are stored under a subdirectory of Git internal directory .git/refs/, where local branches are stored in .git/refs/heads/ and remote branches are stored in .git/refs/remotes/. In our example, you can find master branch in the directory of local branches:

$ find .git/refs/heads -type f
.git/refs/heads/master

The asterisk symbol (*) means this branch is our HEAD, a reference to the currently checked-out branch.

$ cat .git/HEAD
ref: refs/heads/master

Finding Commit From Branch

We can see a list of branch names using command git branch, but what if we want to see the information about the last commit in this branch? Well, we can still use the git branch command, with the verbose option:

$ git branch --verbose
* master 56f0a23 Initial commit

Why it knows my last commit? Because a branch in Git is simply a lightweight movable pointer to one of the commits. We can prove it by printing the content of the branch file:

$ cat .git/refs/heads/master
56f0a2324c87464be433b0aa34c0faa4c4eb761f

As you can see, it is a SHA1 value, the hash of the commit I mentioned in the “preparation” section. Thanks to this commit hash, Git is able to find the type and content of this commit-object:

$ git cat-file -t 56f0a2324c87464be433b0aa34c0faa4c4eb761f
commit

$ git cat-file -p 56f0a2324c87464be433b0aa34c0faa4c4eb761f
tree 6fab39d62bb85966ad2c061936d74059adcdbe74
author Mincong HUANG <mincong.h@gmail.com> 1507126168 +0200
committer Mincong HUANG <mincong.h@gmail.com> 1507126168 +0200

Initial commit

Adding New Commits

What happens if you create a new commit in the current branch? The SHA1 value in the branch changes automatically—pointing to the last commit you made. Branch, as a lightweight movable pointer, it moves forward automatically.

$ echo 'Hello Git.' >> README.md
$ git commit -am 'Update README.md'
$ cat .git/refs/heads/master
e5309a4608abbe0ae34fa87ee83fbd4032caf454

$ git cat-file -p e5309a4608abbe0ae34fa87ee83fbd4032caf454
tree 443318058f7d5f9db56a9303afe7440e46e5707a
parent 56f0a2324c87464be433b0aa34c0faa4c4eb761f
author Mincong HUANG <mincong.h@gmail.com> 1507144455 +0200
committer Mincong HUANG <mincong.h@gmail.com> 1507144455 +0200

Update README.md

Creating A Branch

What happens if you create a new branch? Well, Git will create a new pointer for you to move around. Let’s say you create a new issue-branch for bugfix, called issue1. You can do this with the git branch command:

$ git branch issue1

This creates a new pointer at the same commit you’re currently on:

$ git log --oneline --decorate
e5309a4 (HEAD -> master, issue1) Update README.md
56f0a23 Initial commit

And a new file called issue1 is created under directory .git/refs/heads/:

$ find .git/refs/heads -type f
.git/refs/heads/issue1
.git/refs/heads/master

However, your current branch is still master, because the HEAD doesn’t change—it is another lightweight movable pointer on top of branches:

$ cat .git/HEAD
ref: refs/heads/master

Switching branches can be done using the checkout command:

$ git checkout issue1
$ git branch
* issue1
  master

Conclusion

From this blog post, we dug into the Git internals to understand what is HEAD, branches. We saw about how a branch interacts with commits. At the end, we compared the difference between two pointers: HEAD and branch. The key point of this post is to understand:

A branch in Git is simply a lightweight movable pointer to one of the commits.

Hope your enjoy this one. See you the next time!

References