This post originally appeared on Continuity’s engineering blog. I’ve cross-posted it here for posterity.
Git ships with an awesome, underused utility called
git-bisect. I had a bug to track down today that already had a spec, so it was a perfect fit. Normally our continuous integration (CI) service would have alerted us earlier, but unfortunately the failure was masked by another problem.
- 1 executable to test a commit
- 1 known bad commit (often
- 1 known good commit
Prepare the test executable
In this case, I’ve called it
private/git-bisect.sh and filled it with this:
# Don't forget to `chmod +x` this file. # You can add more steps here if necessary, e.g. installing dependencies. rspec spec/services/my_service_spec.rb
Find the bad commit
I’m going to assume
HEAD is a bad commit (meaning that the test executable fails).
Find a good commit
Go back a reasonable amount of time (e.g. make an educated guess, like 1 month) and find a commit that doesn’t fail the test executable.
After you have your good commit, just run a set of commands and
git bisect will track down the source of the problem for you:
bad_commit=HEAD good_commit=fbb3823 git bisect start $bad_commit $good_commit git bisect run private/git-bisect.sh
Eventually, it will have bisected back to the source of the problem, producing output like this:
3f23680fefb5302c780ccc68b5d3006e9f37dd92 is the first bad commit commit 3f23680fefb5302c780ccc68b5d3006e9f37dd92 Author: He Who Shall Not Be Named <email@example.com> Date: Wed Apr 23 11:40:48 2014 -0400 just change something small, no big deal... honest! :040000 040000 088559324ff27ec7be6967e8c50934a9837b8f55 e7f89bede815904bb79d5b01807e4e01c8378f14 M app bisect run success
That first line identifies SHA
3f23680fefb5302c780ccc68b5d3006e9f37dd92 as the source of the problem, which was right in my case. Yay for automation!
Now that I’m all done, I can:
git bisect reset
Git cleans up, and puts me back where I started.
Normally just running
git show $first_bad_commit will reveal something useful. Tracking down the problem depends on the situation, of course. (Keep in mind that the “first bad commit” might not be the one you’re looking for.)
- Git bisect saves the day
For manual testing, this Ruby script can help you bisect without writing an automated test:
#!/usr/bin/env ruby response = nil begin while !%w[y n].include?(response) do print "y/n? " response = gets.chomp end if response == "y" exit 0 else exit 1 end rescue Interrupt exit 2 end