Any semi-experienced developer would have used Git branches as part of their development workflow. They are very important for effective collaboration and isolating under-development code from production code. Developers often use branches to try out new ideas, tools, etc and then delete the branch once done. You would agree with me when I say that working with branches in Git is a breeze, so much so that most of us take this feature for granted and never stop to think about the underlying implementation or appreciate its lightweightness.
Most of us, who have never worked with any other version control system, will not be able to appreciate the ease of working with branches in Git. Older version control systems like Subversion usually took seconds or minutes (depending on the size of the repository) to create a new branch. On the other hand, creating a new branch in Git is almost instantaneous.
In this post, I am going to explore the inner implementation of branching in Git and help you figure out: How are branches in Git so lightweight?
Branches as pointers
In the previous post, we learned that commits in Git are simple objects with a reference to the snapshot of the code at the time of commit and a reference to the parent (previous commit).
Similarly, branches in Git are simple movable pointers that point to one of the commits. When you make a commit, the current branch's pointer automatically moves to the new commit.
There are no special properties associated with the
main
branch. It is just another branch that is created by default by Git for you to start working on.
Creating a new branch
When you create a new branch, a new pointer is created that points to the last commit made in the current branch. The following command creates a new branch called featureX
:
git branch featureX
About HEAD
To know which branch you are currently on, Git maintains a special pointer called HEAD
. It points to the current branch. In the figures above, you can see that HEAD
is pointing to the main
branch.
The command git branch <branchName>
only creates a new branch but does not switch to it. To switch to the new branch, in this case featureX
, you need to run:
git checkout featureX
As you can see in the figure below, switching to featureX
moved the HEAD
pointer.
You can create and switch to a new branch in a single command:
git checkout -b featureX
What happens when you make a commit now?
A new commit object is created and the featureX
branch pointer moves to the new commit, along with the HEAD
pointer.
Moving back to main
If you move back to the main
branch now, the HEAD
pointer will again point to the main
branch, as shown below. Also, your files will be reverted to the snapshot that main
points to.
If you make a new commit now, your project history will diverge. A new commit will be created which will be isolated from the commit you made in the featureX
branch. The figure below should help you get a better understanding.
Commits in both branches are isolated from each other. Switching to a branch will revert your files to the snapshot that the branch points to.
As already mentioned in the post about customizing git logs, you can view the divergent history of your repository using the --graph
flag.
From Git version 2.23, Git has introduced the
switch
subcommand which can be used to switch between branches.
To switch to a branch, run:
git switch <branchName>
.To create and switch to a new branch:
git switch --create <branchName>
.To return to the previously checked-out branch:
git switch -
.
Conclusion
A branch in Git is a simple file that contains the 40-character SHA-1 checksum of the commit it points to, making them extremely cheap and easy to create and destroy. Since we are recording the parent of commit, as mentioned in the previous post, Git can easily find the common merge base of two branches, making branches easy to use and merge.
Wondering how the git branch file is 41 bytes in size? 40 bytes for the 40-character checksum and 1 byte for the newline.
Top comments (0)