Git bisect is like CPR or a fire extinguisher: You shouldn’t need it very often, but when you, it might just save a life.
In case you are not familiar with its splendor, git bisect is a tool for pinpointing the commit that caused a change. Usually, the change you’re interested in is the introduction of a bug. Bisect is handy when, for instance, you discover a regression and you don’t know what caused it or exactly how long it’s been around. Bisect isolates the offending commit with logarithmic efficiency. It accomplishes this by performing a binary search of the commits on your branch, dividing and conquering until only one commit remains.
The basic version of the process is simple. You provide git with a good commit (one you know is after the change) and a bad commit (one you know is before the change). Git then asks you a series of yes-or-no questions of the form: How about this other commit here—is it good? Approximately log-base-2 of the number of questions in your good-bad interval later, you have your culprit.
Here are the commands for navigating through this process:
1 2 3 4 5 6 7 8 9
At this point, Git will proceed to checkout commits for you. Your job is to give the thumbs up or thumbs down, Roman-emperor style.
1 2 3 4
At each step you’ll need to do whatever it is you need to do to check which side of the change the commit is on.
If you’re working on a Rails app, this might involve running
rake db:migrate and
bundle install, and then checking whether a particular test passes or a particular behavior of your application is present.
When git zeroes in on its target, it will spit out a message telling you what it found, e.g.
1 2 3 4 5 6
At this point, you’ll probably want to get out of bisect mode and back onto your working branch. To do this, run
That’s all you really need to know to start using git bisect.
In researching this post, though, I came across some additional commands that are worth learning.
First, if during the bisect process, git lands you on a particularly awkward commit—e.g. one for which the needed environment is hard to replicate from your present position—you can run
git bisect skip and git will offer you a different commit to test.
Second—and way cooler—you can automate your yes/no decisions by feeding a custom script to
git bisect run.
Git will run the script at every decision point, and will take an exit code of 0 to mean good, and anything else (except 125) to mean bad (details here).
Test frameworks like RSpec exit with 0 only when all tests pass (or none are run), so if you’re trying to identify the commit that caused a test failure, this is your tool.