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) NAME 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?
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) /Users/emiller/.rvm/rubies/ruby-1.9.2-p0/bin/ruby -I"lib:lib:test:vendor/rails/activerecord/lib:vendor/rails/activesupport/lib: 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…
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:
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