“What? I already fixed this bug! Why is it still happening?” If you don’t have a good test suite or you don’t run it regularly, finding these sorts of regressions can be difficult. Because git makes it really easy to jump to other commits (branches or history), it is very easy to narrow down what commit(s) caused the regression. git bisect helps you do that.

I’m going to go through an example of git bisect to show the basics.

Let’s say we have history that looks something like this (from the official git repo):

8419d2e... git-format-patch --in-reply-to: accept  with angle brackets
767c98a... git-add -u: do not barf on type changes
f5de799... Remove duplicate note about removing commits with git-filter-branch
f28dd47... git-clone: improve error message if curl program is missing or not executable
568d2cd... git.el: Allow the add and remove commands to be applied to ignored files.
98acc3f... git.el: Allow selecting whether to display uptodate/unknown/ignored files.
1b65504... git.el: Keep the status buffer sorted by filename.
c32da69... hooks--update: Explicitly check for all zeros for a deleted ref.
80bffaf... git-commit: Allow partial commit of file removal.

All we know is that 8419d2e has the bug and 80bffaf works, so we run git bisect start 8419d2e 80bffaf. Git’s output looks like this:

$ git bisect start 8419d2e 80bffaf
Bisecting: 2 revisions left to test after this
[f28dd4774d4723e8251817e910dbdc5507282113] git-clone: improve error message if
curl program is missing or not executable

Now we’re right in the middle of these commits. We find that the bug existed at this point too, so we run git bisect bad:

$ git bisect bad
Bisecting: 0 revisions left to test after this
[c32da692de332d3c9a0b283066e3786af00f4931] hooks--update: Explicitly check for all
zeros for a deleted ref.

This commit doesn’t have the bug, so we run git bisect good:

$ git bisect good
f28dd4774d4723e8251817e910dbdc5507282113 is first bad commit
commit f28dd4774d4723e8251817e910dbdc5507282113
Author: Gerrit Pape 
Date:   Thu Sep 13 11:36:22 2007 +0000

    git-clone: improve error message if curl program is missing or not executable

…snip…

    
    Signed-off-by: Gerrit Pape 
    Signed-off-by: Junio C Hamano 

:100755 100755 18003ab4b39ad0a7848cc20db2f6ad55e1291264
5e582fe247892fa7dffc44556c939863c36edc35 M      git-clone.sh

Now we know the exact commit that caused the regression.

Sure, you could have done that with a temporary branch and git reset --hard, but things get a lot more complicated when merges are involved. git bisect doesn’t have problems walking down branches and keeping track of which sections are good and which sections are bad.

There are a few other options that git bisect has that can help a lot:

  • git bisect visualize
  • git bisect log
  • git bisect replay
  • git bisect run
  • git bisect skip

git bisect visualize opens up the range you’re analyzing in gitk which lets you see which sections you’ve discounted as good or bad.

git bisect log shows you history that can be replayed by git bisect replay. Our example’s log looks like this:

# bad: [8419d2ee9ba8b375186a5c1019df8dfbce610aba] git-format-patch --in-reply-to:
accept  with angle brackets
# good: [80bffaf7fbe09ef62ecb9a6ffea70ac0171b456c] git-commit: Allow partial commit
of file removal.
git-bisect start '8419d2e' '80bffaf'
# bad: [f28dd4774d4723e8251817e910dbdc5507282113] git-clone: improve error
message if curl program is missing or not executable
git-bisect bad f28dd4774d4723e8251817e910dbdc5507282113
# good: [c32da692de332d3c9a0b283066e3786af00f4931] hooks--update: Explicitly
check for all zeros for a deleted ref.
git-bisect good c32da692de332d3c9a0b283066e3786af00f4931

If you screw up bisecting, you can use git bisect log, git bisect reset, and git bisect replay to go back and fix your mistakes.

If you can write a script to automatically test your regression, you can use git bisect run. Your script has to do its test and exit with a status code. If the status code is 0, the revision is good; if the status is between 1 and 127 (but not 125), the revision is bad; if the status is anything else (exit -1 is common), the automatic bisect process will stop; if the status is 125, it means skip that revision.

git bisect skip is used to tell git that you can’t test this revision. There can be cases where git won’t know which commit caused the problem if a skipped commit makes the bisect ambiguous.

Most newer version control systems (especially distributed ones) support bisect features. Subversion is the notable exception.

References:

Advertisements