Falling in love with bisecting


Hunting down the source of a bug can be tricky. Often I find myself relying less on my actual knowledge and tools and more on my intuition. There is, of course, a better way and recently I've embraced it wholeheartedly. And that better way is built right into git!

The Concept

Don't get me wrong: intuition has great value. In my experience, even a few months of study can help to build a mental model of a system such that I can guess where bugs originate before touching the code. Even in cases with high familiarity, however, we are working the realm of guesswork and hypothesis. If my first guess is wrong, I find it easy to chase my tail as I track down false lead after false lead, and can miss what's right in front of me. When I feel this happening, I turn to git bisect.

bisect, in the style of all things git, is slightly arcane and clunky but incredibly powerful. All you have to do* is tell git a commit known to be good and a commit known to be bad, and it will guide you through an efficient exploration of your repository's history. At each step of the process, bisect presents you with a commit and asks "is this commit good, or bad?" A quick test of your system and you can produce the answer (failing tests, incorrect behavior, broken build, what have you) and inform git; it can then continue with a binary search to find the offending change.

The Interface

Git, of course, is a viper's nest full of nightmares. Even with a decent understanding of what's happening under the hood, I am terrified of git's interface. git bisect can be a little tricky, but in my experience mastery is rewarded. You can initiate bisect with

git bisect start

At any time, you can bail out of this operation with

git bisect reset

which is good to keep in mind if you find yourself tangled. After you've started the bisect, provide a commit with the "new" state of your repository (where the bug is present, when the performance improved, etc) and any commit that you know to be the "old" state (where the bug is absent, where the performance had not yet improved). These commands used to be called "good" and "bad" but have been generalized; when bug-hunting I often use "good" and "bad" because I find them clearer.

git bisect new my-good-commit and git bisect old my-bad-commit (or git bisect good my-good-commit and git bisect bad my-bad-commit )

After you've specified your commits, git will check out the midpoint and ask you to evaluate it. Do whatever you need (run tests, inspect an app, check benchmarks) and then inform git that this commit is using the new or old behavior (or that it is good or bad) with a simple git bisect new/old/good/bad as desired. The process will repeat a few times until you find what you're looking for!

If you'd like to learn more, the git bisect docs have more detail and even explore the topic of automatic bisecting. Happy hacking!