Using Git Bisect to Crush Your Enemies

Using Git Bisect

…to crush your enemies and/or bugs

Or, how to save countless hours and find out where things broke

Git bisect is the most awesome, and most poorly publicized feature of git. It allows git to walk through your branch and quickly find out which commit broke things.

The usage is simple. You point git to a bad commit ( usually the most recent one ) and you point it to a good commit (the most recent one you know of when things were working). So, if, for example, things were working on Tuesday morning, you bring up git log and scroll until you find one from Tuesday morning or maybe late Monday and copy its hash.

$ git bisect start
$ git bisect bad
    # that tells it that the current commit is bad
    # but you could say git bisect bad 48476f7b15022526393e6c4f44f610f552736fdc
$ git bisect good 71f45f2a0302b5fb2331c268ee2bfb3cfde452ab
    # that's the hash of some commit you know to have been good

git will then find the commit mid-way through and wait for you to tell it if it’s good or bad (git bisect good or git bisect bad). If it’s bad it knows the offending commit was earlier. If it’s good it knows the offending commit was later. It continues on in that vein, bisecting the commits with each step until it eventually says something like 54cb86bd35c226dc0df83dc2e2d4c8702fcf2c04 is the first bad commit

When you’re done you run the following to reset your branch to its original state

$ git bisect reset

Full Automation

But it’s even better than that. You can fully automate git bisect. Just kick it off and have it test each branch. All you have to do is write a script that can be executed from the shell and exits with 0 if the commit is good, and a number from 1-124 if it is bad.

You only have to do one thing differently. Once you’ve told it a good and a bad commit it will bisect the commits as usual and give you a system prompt and wait for you to tell it if it is a good or bad commit, but instead of telling it if it’s good or bad, you tell it what it needs to run to test it.

$ git bisect run my_test_script.sh

For example: I’m working on a rails project at the moment and just used this simple rake task which would blow up if the .fixtures method wasn’t loaded correctly:

# lib/tasks/location_test.rake
namespace :location_test do

    desc "tests location.fixtures classload issue"
    task :run => :environment do
        begin 
                Location.first.fixtures
                $stderr.puts "it worked"
        rescue Exception=>e
                $stderr.puts "it died: #{e}"
                exit(1)
        end
        exit(0)
    end
end

So, my run looked like this:

$ git bisect start 
$ git bisect good bebcc378d16d8fe74736c763c144753b2a382933
$ git bisect bad
$ git bisect run bundle exec rake location_test:run 

And git bisect proceeded about its merry way until it found the offending commit. Then I ran git bisect reset and went about fixing the bug.

Pro-Tip

The Git Book adds this great note:

Note that the version which git-bisect checks out for you at each point is just a suggestion, and you’re free to try a different version if you think it would be a good idea. For example, occasionally you may land on a commit that broke something unrelated; run

$ git bisect visualize

which will run gitk and label the commit it chose with a marker that says “bisect”. Choose a safe-looking commit nearby, note its commit id, and check it out with:

$ git reset --hard fb47ddb2db...

then test, run “bisect good” or “bisect bad” as appropriate, and continue.

Obviously this won’t work if you’re running a fully automated bisect, and it points to the one gotcha of git bisect. If there are multiple bugs that inhibit your ability to test it ( either manually or via a more automated means) it can leave you with no obvious candidate. Most of the time, this isn’t a problem, especially when you have done your best to chose the most recent “good” commit that you are confident in.