Bug Hunting With Git


Popular version control software Git is a more versatile tool than one might expect. In addition to the basic version control functionality, Git also offers a command called bisect. Combined with automatic tests or unit tests, bisect is an efficient and quick way to find the correct moment from the version history, when a bug was introduced to the source code.

Bisect command is used to analyze a commit history with binary search. The idea behind the binary search is to take a set of elements and split them in half and check if the selected subset contains what we want. By repeating this process yields quickly to the match.

In the following example we are using the following Git commands:

1. git bisect start
2. git bisect bad
3. git bisect good
4. git bisect reset

Let’s assume that we have a version history with a working version tagged as “v1.0”. After this point, there has been 17 new commits and somewhere down the line a bug was introduced. Unfortunately, there has been so many changes that pinpointing the exact time or place is impossible.

Let’s start the bug hunt by commanding git bisect start. This command activates the Git binary search mode.

Next, we will tell Git that the currect state of the code is bad: git bisect bad.

Until now, Git has been silent. No indication has been given that anything special is happening.

Finally, we tell what is the last known good version of the source code, in our case a tag called “v1.0”: git bisect good v1.0.

Now Git tells us an estimate, how many steps it might take to find the faulty commit:

Bisecting: 8 revisions left to test after this (roughly 3 steps)

Additionally, Git tells us, what commit has been activated as a current version under work. This allows us to compile and run tests against the selected commit and check if the bug is there or not.

Let’s assume that in this case the selected commit contains the bug: git bisect bad.

Git tells us the updated situation:

Bisecting: 3 revisions left to test after this (roughly 2 steps)

Also, Git reports the version hash that has been lifted under the test.

After a quick testing we see that this version has nothing wrong, so we tell Git that: git bisect good.

Git also gives a status update:

Bisecting: 1 revision left to test after this (roughly 1 step)

Let’s test again and decide that this version is also good: git bisect good.

Bisecting: 0 revisions left to test after this (roughly 0 steps)

The given statistic shows that we are about to hit the nail in the head. A quick test and we’ll see that this version is buggy: git bisect bad.

Now Git finally speaks up. It tells us, what the commit introducing the bug is, and gives detailed information about the SHA hash, author, date and commit message. Also, Git lists all the files that have been changed in this version.

We have found the exact commit that introduced the bug in to the product!

Finally, now that the bug has been pinpointed and hopefully we have understanding how to fix it, we have to tell Git that the hunt is over and we can go back to normal: git bisect reset. Git will restore the version control to the state we were in before we called git bisect start command.

As we can see from the previous example, Git bisect is handy way in situations, where we know that there is a bug somewhere, but it’s not immediately clear, where the problem is because of the long commit history. The example had 17 separate commits. Instead of going through them one by one, we only needed four separate test rounds to find the bug from the seventh commit.

If this small introduction piqued your interest to the bisect command, you can seek more information for example by reading about git bisect run command. Bisect run makes it possible to automate the bug checking from the code and opens interesting possibilities for test automation.

More reading:

– [Wikipedia: Binary Search Algorithm](https://en.wikipedia.org/wiki/Binary_search_algorithm)
– [Git Documentation: Bisect](https://git-scm.com/docs/git-bisect)

Writer is Matti Kärki, a senior developer from Ouro