Git Bisect is Awesome


As the sole developer on most of the projects I use git with, I generally know who breaks my code: me. However, when attempting to maintain Rails plugins that do some pretty intrusive stuff in core Rails functionality, I find myself waking up some mornings, doing a git pull in my vendor/rails submodule, and finding a bunch of failing tests. Then, I go get some coffee, and spend a bit of time scratching my head over what just caused the breakage. It comes with the territory. Still, better to track 3-0-stable as changes come along than scramble to figure this stuff out all at once and release a new version of MetaWhere every time a new Rails gem is released, right? Thus began my love affair with git bisect.

What’s git bisect?

I’m glad you asked.

GIT-BISECT(1)                     Git Manual                     GIT-BISECT(1)

       git-bisect - Find by binary search the change that introduced a bug

Well, that sounds helpful. And “binary search” sounds very technical and impressive, indeed.

That’s great. Now: what’s git bisect?

OK, so let’s put it another way. You know that guessing game where someone picks a number, another person guesses a number, and is then told whether they are high or low? It’s kind of like that. Except instead of numbers, you’re guessing code revisions, and neither you nor the computer actually knows what the magic number is.

So, how do I use it?

Well, in my case, I knew the version of Rails tagged “v3.0.0” worked properly. I knew my currently checked out HEAD didn’t work. The bug sits somewhere in the middle. So here’s the lay of the land.

Let’s tell git a little bit about what we know.

    Neo:meta_where emiller$ cd vendor/rails
    Neo:rails emiller$ git bisect start
    Neo:rails emiller$ git bisect bad
    Neo:rails emiller$ git bisect good v3.0.0
    Bisecting: 51 revisions left to test after this (roughly 6 steps)
    [6a60387dce327b038ab16e1950d723e7b02a063d] Add two integration tests for
    GeneratedAttribute missing type error. [#5461 state:resolved]

What I’ve done is to tell git to start bisecting, tell it my currently checked out revision is bad, and tell it that the revision tagged v3.0.0 is good. So, git does what any computer trying to win a guessing game would do, and does the most logical thing, splits the known revisions down the middle, then checks the middle one out.

I then go back and run my tests:

    Neo:rails emiller$ cd ../..
    Neo:meta_where emiller$ rake test
    (in /Users/emiller/Documents/Projects/meta_where)
    vendor/arel/lib" "/Users/emiller/.rvm/gems/ruby-1.9.2-p0/gems/rake-
    0.8.7/lib/rake/rake_test_loader.rb" "test/test_base.rb" "test/test_relations.rb" 
    (... some testing fun ...)

…and I still find that the test fails.

So, back we go:

    Neo:meta_where emiller$ cd vendor/rails/
    Neo:rails emiller$ git bisect bad
    Bisecting: 25 revisions left to test after this (roughly 5 steps)
    [c2a87b45b8dab6e0b928333beca627f607690625] Change relation
    merging to always append select, group and order values

Git now knows that any revisions after the one we just tested are bad…

…so it splits the revisions before the one it had just guessed in two (binary search, get it?) and tries again.

This time, I find my tests pass, so I go back into my vendor/rails directory and say git bisect good. Git’s view of the world now looks like this:

We continue the game, telling git whether each revision it “guesses” is good or bad, by our silly, arbitrary standards of “tests pass.” :)

And thus, we find the change that introduced the bug, and the bug gets squished.

A quick git bisect reset sends us back to our previous HEAD revision, and we write a blog post extolling the awesomeness of git bisect.

comments powered by Disqus