Git Bisect

Learn how to use the git-bisect command to make life easier.

Git bisect mode

The git bisect method is the kind of tool we’ll use about once every six months, but when we use it, we’ll be totally thrilled that it exists.

Isolation commits for debugging

The git bisect is indicated when something has gone wrong in our code, and we believe it to be the result of a code change but we cannot isolate which change resulted in the problem. The git bisect's goal is to isolate the commit where the change occurred.

Note: Use git bisect to track down mysterious failures in our code when we have no idea how they were inserted.

We start using git bisect with two commands:

$ git bisect start
$ git bisect bad

The first command puts Git into what we’ll call bisect mode, and the second command says that the current Git snapshot is bad, meaning it contains the behavior we’re trying to fix.

We then switch the Git snapshot to the secure hash algorithm of a previous commit that we believe is good because it doesn’t have the behavior. And we tell Git that branch is good. For example, we’d use this if we’ve determined that SHA 34ace43 is good:

$ git checkout 34ace43
$ git bisect good

We can combine those into one command, git bisect good 34ace43.

Git advantages

Git now does something really neat. It derives a straight line of commits between the good one and the bad one, picks the middle of that line, and checks itself into that commit. We’ll see some commentary on the console explaining how many commits are in the line and roughly how many steps Git expects the bisect to take.

Our job is to do whatever we need to in the newly entered commit to determine if it is “good” or “bad” based on whether the incorrect behavior exists. Based on that, we enter either git bisect good or git bisect bad. (If we really can’t tell, we can do git bisect skip).

Git now has enough information to know that the bad change was in one-half of the commits. If we said the commit was bad, then the change was in the first half of the commits; if we said it was good, then the change had to come after. Git splits the narrowed-down list of commits in half and checks out the middle commit.

We repeat our checks until eventually, we can isolate a change where the beginning state is good and the end state is bad. Ideally, inspecting the commit list of the commit with that change will give us a hint as to what’s causing the behavior, since one of those changes is likely the cause. This process can save us hours.

Encapsulation with git bisect

Furthermore, if we can encapsulate whatever test we’re running against our codebase in a script that follows the Unix convention of returning a nonzero value on failure, we can pass that script to git bisect and Git will automatically do the good/bad thing for we based on the result of the script. The syntax is of the following form:

$ git bisect run my_script

We note in passing that whichever tool we’re using to run our tests has this behavior, so git bisect run rspec should work, though it’ll probably be a little on the slow side.

For the full git bisect effect, a few things need to be true:

  • The problem needs to have been caused by a code change, not a change in our environment.

  • We need to be able to reliably trigger the problem.

  • It helps if our commits are relatively small and if the system is in a loadable and executable state after each one.

That said, when this works, it can work in big ways.

Get hands-on with 1200+ tech skills courses.