Archives for: August 2007, 09

08/09/07

An introduction to the git bisect feature

Recently git is becoming a very popular revision control system for several projects. I tend to use it regularly too, both for my own projects, because it's used by a projects I hack on, or because I can get all powerful git features when working on a project which uses SVN as RCS (which is a good thing in some environments) through git-svn.

One of the very nice features of git is "bisect", which allows you to pin down the commit which broke code (or functionality) pretty easily. I used it eg. some days ago to figure out which commit in the "avivo" driver caused the driver to break on my system.

How does it work? Basicly, you clone a git repository, you start a bisect session, say "this commit did work", "this one doesnt work", and that's it. git will checkout a commit "in the middle" of the good and bad revisions, you compile/run/test the result, and tell git whether this version did work as expected or not. If it did work, git will checkout the revision between the current one (which was already at the middle between good and bad) and bad for you to test. If it did not work you'll get the revision in the middle of the "good" revision and the current one.
Think about binary search, it's similar (actually, bisect *is* a binary search).

This way you're able to figure out which commit broke the app in a very short time (unless it crashes your system and it takes 3minutes to boot ;-)).

There is even more: if your application got a nice test suite consisting of unit test and alike, you're able to automate the whole process. The only thing you need is some script/tool/... which runs the test suites and returns a 0 exit code on success, or something else on failure. If you got this, git will do the whole bisect automagicly and tell you without any intervention what broke your application.

To demonstrate this I wrote a simple script. You can find it here using gitweb, or git-clone http://git.nicolast.be/git-bisect-sample.git.

Once you got the script on your system, you can test it. It takes 2 arguments: the number of commits to make, and which commit should break the application. The script will create a branch, then start creating a simple bash script. All this script does is assign 0 to i, increment i and decrement i. Every revision one increment and one decrement will be added. At the "bad" revision, one more increment will be added though. At the end i is used as return value.
As you can see, before the bad revision the script will return 0, after it it will return 1, so it's a test suite on its own ;-)

Here's a sample run:

$ sh createrepo.sh 20 16
Switched to a new branch "git_bisect_test"
Doing commit number 1
Created commit 9ce7f50f937b4e0e82c9c7fe143bb0483eb8e308
 1 files changed, 5 insertions(+), 0 deletions(-)
 create mode 100755 testcase.sh
Creating good_tag
Doing commit number 2
.....

Doing commit number 20
Created commit 0298fc50cb61eca005053a1c948f8651b2a346eb
 1 files changed, 2 insertions(+), 0 deletions(-)
Creating bad_tag

So, it created a branch called "git_bisect_test" (just to keep our repository clean), did a first commit, tagged this as "good_tag" (we assume our first commit is a working application here. The good and bad revisions shouldn't be tagged, but this makes it easier for us further on). Then the script is created/updated and committed 20 times, breaking at the 16th commit. At the end, "bad_tag" is created. Once again, this is not necessary at all.

Now we found out our application is broken, so we'll bisect to figure out where it broke.

$ git-bisect start
$ git-bisect good good_tag
$ git-bisect bad bad_tag 
Bisecting: 10 revisions left to test after this
[632c9c04b97578d674a2980648ee4ab748a8b147] commit number 11

We're at "revision #11".

No the magic can start:

$ git-bisect run ./testcase.sh
running ./testcase.sh
Bisecting: 5 revisions left to test after this
[d6514e8d9fe55827fe78d650548d948a4647fc50] commit number 16
running ./testcase.sh
Bisecting: 3 revisions left to test after this
[79e4ce719557e53c203789e1991f1ec00be823f7] commit number 14
running ./testcase.sh
Bisecting: 1 revisions left to test after this
[09f09d9a4e322a49b40de3f9d595f5132198c9b3] commit number 15
running ./testcase.sh
d6514e8d9fe55827fe78d650548d948a4647fc50 is first bad commit
commit d6514e8d9fe55827fe78d650548d948a4647fc50
Author: Nicolas Trangez < >
Date:   Thu Aug 9 23:43:48 2007 +0200

    commit number 16

:100755 100755 bd66b47cf24d9d3d00ac289395851d436b404774 ba01220423f64ea8b92a9df75c36a1a7ddda89ac M      testcase.sh
bisect run success

Exactly, like it should, it figured out commit 16 (well, git got no commit numbers, this is just the commit message to keep things understandable easily) broke our application.

All we got to do now is stop the bisect process

$ git-bisect reset

and fix our application by looking up a diff of the bad revision.

In this sample case we should also clear the cruft the createrepo.sh script created:

$ git-tag -d good_tag
$ git-tag -d bad_tag
$ git-checkout master
$ git-branch -D git_bisect_test

That's it. More information can be found in the git manual and in the git bisect manpage.

Permalink . Ikke . 11:28:32 pm . 897 Words . Coding Corner . . 2269 views . 2 comments