How to hunt down bugs with git bisect

8/21/2021
4 minute read

How to hunt down bugs with git bisect

Just imagine you're on the hunt of finding a bad commit. You could just randomly checkout some commits and check whether or not they are broken. If not you'll go on with the next one. This process is painfully slow and takes much time.

There is a better way of handling this: It's called git bisect.

The idea behind git bisect is that you it asks you whether or not a commit is broken and narrows down the window until you find your commit in question. bisect operates on a binary search. So you only need log(n) steps when your window is n commits large.

bisect consists out of the following steps:

  1. You start git bisect
  2. You tell git which commits it should inspect. You do it in that way that you tell git
    1. What is a known good commit?
    2. What is a known bad commit?
  3. Go in the middle between good and bad and ask the user if the commit is good or bad
  4. If between good and bad are still unchecked commits go to 3.
  5. If there are no commits we found our bad commit
    1. If this commit is good then the last checked one was the first broken commit
    2. If this commit is broken the this one was the first broken commit

Here a more graphical way:

bisect in action

Let's have a look at the following history:

git log --oneline
f135f6a (HEAD -> master) Refactored to defensiv input
525fb09 Added performance and of course no bugs
271310f Refactor to own function
06f2561 Multiply by 2 console application
af61874 Init project
ad2e654 Add gitigore file
a4f0974 Initial empty commit

In this example the commit with the hash 525fb09 introduced the bug but we don't know yet. So we will check all commits. With check I mean for example that you run your application, click through until you encounter your bug or not ... very explorative.

Let's get started:

  1. git bisect start - This will start the whole process. It does nothing visible.
  2. git bisect good a4f0974 - We know the initial commit has not the bug in question
  3. git bisect bad f135f6a - We know the latest commit has the bug. But we are not sure where it was introduced.

3.1 Also git tells you the following: Bisecting: 2 revisions left to test after this (roughly 2 steps)

3.2 If you check now via git log --oneline you see something like that:

06f2561 (HEAD) Multiply by 2 console application
af61874 Init project
ad2e654 Add gitigore file
a4f0974 (refs/bisect/good-a4f09747c40aca77fc459051ff5cd0d2c422947c) Initial empty commit
  1. We are in the middle of our commits. Now we have to tell git whether or not this commit is broken. In our case it is not, so: git bisect good. We get the following output:
Bisecting: 0 revisions left to test after this (roughly 1 step)
[525fb09c073a80bcd5dda3b6959520118da44214] Added performance and of course no bugs
  1. We checked the commit and it is a bad one, so: git bisect bad with the output:
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[271310f061cff11a9190fe2fd7c1da19b74d8163] Refactor to own function

We narrowed down our to the last commit. If this commit is good, then we know it is the commit with the hash 525fb09, if not it is the current one (271310f).

  1. We checked and the commit with the has 271310f is good so: git bisect good and now git tells you all the information it has about the commit:
525fb09c073a80bcd5dda3b6959520118da44214 is the first bad commit
commit 525fb09c073a80bcd5dda3b6959520118da44214
Author: Steven Giesel <me@steven-giesel.com>
Date:   Thu Apr 16 11:04:34 2020 +0200

    Added performance and of course no bugs

 bisect/Program.cs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
  1. At the end you should call git bisect reset to go back from where you started. Otherwise you have a detached had at the last checked commit.

Limitations

Here is a small list of limitations or what not to expect from git bisect

  • git bisect is binary - it only operates on either good or bad commits. It can't tell you specific errors
  • That said, if you have very very big commits it doesn't help that much as you still have to go through hunderds of files
  • It works best with a linear tree. If you have a lot of merges it will make it a bit harder
  • If you are not sure whether or not a commit is bad or good you can use: git bisect skip to skip the current one. This is especially helpful when you can't build a commit

Advanced Use-Cases

You can use a bash script or any kind of command line tool to automate the process.

With git bisect run my_script args you can automate to whole thing. Your script should return 0 if the return is good and 1 to 127 (including), but not 125 if the commit is bad. 125 is used for skip.

Conclusion

git bisect is a nice tool to find a bad commit in your code base. Especially if it is a bit bigger and you don't know where to start. If you want to know more checkout the documentation on git

How does git work?

In this blog post I will show you how git works. Or better we will see how git works on the example of some of the commands.

Are you confused about origin, remote, rebase or merge? You see stuff like origin/main and origin main and just question marks appear? Say no more!

git - useful aliases

In this article I'll show you some of my most used git aliases which make my everyday work easier and more productive.

Getting git version information in your C# code

Did you ever need git-specific information like the latest tag or the current commit inside your C# code? Or even the semantic version number of your current build=

Well, there is an easy solution involving source generators.

An error has occurred. This application may no longer respond until reloaded. Reload x