Git bisect: The unsung hero for hunting down bugs
The best solution to quickly find that annoying bug among hundreds of commits
The year is 2010, a wild bug appears in a code base, but this is no common bug, this is one that likes to play hide and seek among the thousands of lines some feature introduced into the branch you are working on… The year is now 2020 and sadly, this wild bug still visits you from time to time, coming in many shapes and forms, but always using the same technique, hiding between large batches of commits.
We have all lived (and survived) stories like this, where a seamlessly commit introduces a bug that is hard to track down, and though we should deliver small batches of code, we don’t always live in that greenfield.
Back in 2010, I was introduced to one of the git “unsung heroes”: git bisect Since then, it has saved me lots of time hunting down bugs. This is not a tool that you will be using every day (or at least I hope you don’t have to), but when you do, you will really appreciate how a simple tool can accomplish so much.
What does it do
Plain and simple, git bisect allows you to interactively search through a batch of commits until you find the commit that introduced the bug or changes you are looking for. In order to do this, git bisect keeps jumping between commits, while asking you whether the commit you are currently on is working or not. Based on your response, git bisect decides what commit to analyze next, and this process goes on until it finds the commit you are looking for.
How does it work
How does that help me search a batch of 100 commits? The easiest way to answer that question, is to show you the tool in action.
Let’s start by creating an empty repository with 100 commits with each commit message being the commit number from 1 (oldest) to 100 (newest):
mkdir /tmp/bisect-test
cd /tmp/bisect-test
git init
# Create 100 fake commits. Commit messages will be its ordering number (greatest is newest)
for i in `seq 1 100`; do
git commit --allow-empty -m "$i"
done
If now we perform a git log
we should see something similar to:
In a real scenario the only thing that we’d know at this point is that our HEAD
contains a bug that wasn’t there in the past. Our first mission is to find a commit where the bug we are searching for doesn’t exist.
To find that commit we can either go back in history to a specific tag where we know things were working, or we can try to go back in steps of 50 commits with git reset --hard HEAD~50
until we find one commit where the bug is not present.
In our scenario, let’s pick commit number 7 whose hash is 5f310beb96815
as the commit that we know things were working.
We kick off bisect with git bisect start
and then we inform it about one good and one bad commit:
From the image above we can see that I informed bisect that our good commit is 5f310beb96815
and our bad commit is HEAD
which is the current commit.
With that information, bisect performed a git checkout
to a commit between those two (100 and 7). In this case, it is commit 53.
At this point, we have literally rolled back our commit history, and now we just have to check if this commit is exhibiting the bug we are looking for. For our scenario let’s imagine that it is. We need to tell bisect that the commit is bad, using git bisect bad
Since commit 7 was good and 53 was bad, it’s safe to assume that we are looking for a commit between commits 7 and 53, clearing all commits between 53 and 100. That’s exactly what bisect does, so it will move the history to a commit in between 7 and 53. In this case, commit 30.
On commit 30 we test again our code to see if the bug is there. Assuming that it’s not, we inform bisect about that with git bisect good
Having informed bisect that commit 53 is broken but 30 was not, bisect will once again checkout to a commit between these two. In this case, commit 41!
We would keep issuing git bisect good
and git bisect bad
commands until we find the culprit, in which case we get something similar to this:
I found you, Pikachu!
We found out that commit 42 is our culprit (but that should be no surprise :-), so now we just need to inspect the code it introduced and our quest should be much easier.
The year is 2020, and you no longer fear large chunks of commits coming into your branch. With git bisect as your secret weapon, you know that finding those commits won’t take you much time and energy, and you will be able to use them to solve more interesting riddles.