Git bisect is by far my favourite git tool. It’s not that I get to use it that often because it is only used for finding bugs and the like. Yet, when you need it it is extremely valuable.

Git bisect lets you do a binary search (a bisecting method) on the git commit history.

The most common case is that you find yourself with a bug. Something used to work and now it doesn’t. To use the command you need three things.

  1. A test,
  2. a commit where the test passes and
  3. a commit where it failed.

The test can be a formal, software defined test, but it is much easier to start with a test where you run something and then determine whether the result is good or bad. An example would be that you have written a GUI program and you just noticed that when you maximise and then minimise the window the program crashes. You know that at some point in the past this problem didn’t exist. So your test is: “maximise then minimise, does it crash?” You know that the test fails for the current commit. You next have to find a commit where everything was fine. Let’s say that it was fine three weeks ago.
Do this on your command line:
git bisect start
git bisect bad
git checkout @{21.days.ago}
# Run your test to make sure it passes.
git bisect good

What happens next is magic. If you have a background in Computer Science, you will know what binary search is. If not, no reason to fret about it. It is very straight forward to understand. Git bisect knows one bad commit and one good commit and that the first bad commit must lie somewhere between the two. It will therefore give you the commit in the middle of the two and ask you to check it. You can repeat this process until the good and the bad commit are next to each other. Then you know the first bad commit.

Each step will halve the number of commits that could have introduced the bug. This means it will take at most log2(n) steps to find the bug. One thousand commits will take ten steps to find the bug, a million commits would only require twenty steps! That is why binary search is such a great tool. And git bisect is not just something cool in theory. I have used it numerous times; Linux kernel developers will often ask you to run it if you want to report a bug, and for many developers it is a tool they use daily.

Continuing on the command line, telling bisect what the state of the commit is on every step, you will see this:

Bisecting: 4 revisions left to test after this (roughly 2 steps)
 [3320804482953259aa8dae55c24ba6b3f9514ebb] Some commit message
$ git bisect bad
Bisecting: 2 revisions left to test after this (roughly 1 step)
 [4731fa93db9604ff2d085585a805c80fce668e05] Another commit message.
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 0 steps)
 [81952396f65a4415751fa209b8d13b6901b86166] Added two more tests
git bisect good
4731fa93db9604ff2d085585a805c80fce668e05 is the first bad commit
 commit 4731fa93db9604ff2d085585a805c80fce668e05
 Author: Christoph Siedentop
 Date: Wed Nov 24 15:09:25 2010 +0100
Introduced a bug for show.

:040000 040000 bfbcc10667d2b06ade4853683638d6e575473f08 fa6a4ac56e11df30f75c037482c124ee04570bd0 M test

You can try git bisect visualize to show you what you just did.

As a last step, look at the changed lines the first bad commit touched. If you have small commits you benefit from almost immediately seeing what the problem is.
Once you know the problem, git bisect reset will reset everything for you.

Skipping

One problem that happens quite often is this. Along the way of searching for the buggy commit, git bisect presents you with a commit where you can’t test for the bug. Mostly this is because the program doesn’t build or a different bug prevents you from running the test. In this case, you just say git bisect skip and you will get a different commit to test.

As a side note: you can tell git bisect to skip complete ranges of commits (like a development branch). The command is like this: git bisect skip $( git rev-list ... ). Look at the docs for git rev-list for more options.

If you have too many skipped commits, git bisect will end with a message saying, “can’t bisect any further” the bug could be in any of these commits and then give you a list of commits. What you can do is then either apply a patch to each commit and test that, or create a new branch where you apply the patch first and then rebase the rest of the original branch. Let’s say you have commits A, B, C, D, E. Where you can’t test A, B, C because of a different issue. D fixes that. Do an interactive rebase of A to D and move to D commit immediately after the A commit and then squash it. That means that A gets the fix from D and appears as a single new commit. But this is advanced stuff.

Scripting

The last trick up the sleeve of git bisect is that you can use a script instead of manual testing. I personally don’t use it much because to check 1000 commits I just need 10 checks and those I can definitely do manually. It is really simple as well, though. Write a script that git bisect can run. The script should return exit codes which determine whether something was good, bad or needs to be skipped.

Start again with
git bisect start
git bisect bad
git bisect good HEAD~9 # Or where a good commit is.

Now run the script like that:
git bisect run test_script
The script should return 0 if the commit is good. It should respond with 1 to 127 if the commit is bad, leaving out 125. The 125 is reserved for skipping the tested commit. Anything above 127 will result in the bisecting being aborted. Many automatic testing tools do this already, for example Python’s unittest, Ruby’s rake or the make command. But you have a continuous integration server for that? Right?

Git bisect is a very powerful tool, and I hope you find it useful someday.

Leave a Comment