XClose
Menu

Debugging With Git Bisect

You can use

git bisect

to find out which commit caused a bug.

An example repository

In a nice open source example, I found an arbitrary exemplar on github

In [1]:
import os
top_dir = os.getcwd()
git_dir = os.path.join(top_dir, 'learning_git')
os.chdir(git_dir)
In [2]:
%%bash
rm -rf bisectdemo
git clone https://github.com/shawnsi/bisectdemo.git
Cloning into 'bisectdemo'...
In [3]:
bisect_dir=os.path.join(git_dir,'bisectdemo')
os.chdir(bisect_dir)
In [4]:
%%bash
python squares.py 2 # 4
4

This has been set up to break itself at a random commit, and leave you to use bisect to work out where it has broken:

In [5]:
%%bash 
./breakme.sh > break_output
error: branch 'buggy' not found.
Switched to a new branch 'buggy'

Which will make a bunch of commits, of which one is broken, and leave you in the broken final state

In [6]:
python squares.py 2 # Error message
  File "<ipython-input-6-8e2377cd54bf>", line 1
    python squares.py 2 # Error message
                 ^
SyntaxError: invalid syntax

Bisecting manually

In [7]:
%%bash
git bisect start
git bisect bad # We know the current state is broken
git checkout master
git bisect good # We know the master branch state is OK

Bisect needs one known good and one known bad commit to get started

Solving Manually

python squares.py 2 # 4
git bisect good
python squares.py 2 # 4
git bisect good
python squares.py 2 # 4
git bisect good
python squares.py 2 # Crash
git bisect bad
python squares.py 2 # Crash
git bisect bad
python squares.py 2 # Crash
git bisect bad
python squares.py 2 #Crash
git bisect bad
python squares.py 2 # 4
git bisect good
python squares.py 2 # 4
git bisect good
python squares.py 2 # 4
git bisect good

And eventually:

git bisect good
    Bisecting: 0 revisions left to test after this (roughly 0 steps)

python squares.py 2
    4

git bisect good
2777975a2334c2396ccb9faf98ab149824ec465b is the first bad commit
commit 2777975a2334c2396ccb9faf98ab149824ec465b
Author: Shawn Siefkas <shawn.siefkas@meredith.com>
Date:   Thu Nov 14 09:23:55 2013 -0600

    Breaking argument type
git bisect end

Solving automatically

If we have an appropriate unit test, we can do all this automatically:

In [7]:
%%bash
git bisect start
git bisect bad HEAD # We know the current state is broken
git bisect good master # We know master is good
git bisect run python squares.py 2
Bisecting: 500 revisions left to test after this (roughly 9 steps)
[b169c7082723bccb3e617deb203fc9964f6afdc0] Comment 500
running python squares.py 2
4
Bisecting: 250 revisions left to test after this (roughly 8 steps)
[4a8885a2796a3d007c196f57f72e934430fb686d] Comment 750
running python squares.py 2
4
Bisecting: 125 revisions left to test after this (roughly 7 steps)
[f434d22cacaf5a80b14e9de9f87cb3ad07e76382] Comment 875
running python squares.py 2
4
Bisecting: 62 revisions left to test after this (roughly 6 steps)
[97434f52f2d40d910946eacc2f0f2374b40fa4f6] Comment 937
running python squares.py 2
Bisecting: 31 revisions left to test after this (roughly 5 steps)
[70b469370a5aaa3bb339bfde069ee2c4052b114f] Comment 905
running python squares.py 2
Bisecting: 15 revisions left to test after this (roughly 4 steps)
[cf1abb255bb70df6b919458781b67c7a30187b82] Comment 889
running python squares.py 2
Bisecting: 7 revisions left to test after this (roughly 3 steps)
[81e93a99db64e63558ceec772e50d99758ffd02a] Comment 881
running python squares.py 2
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[e97367749f9892736b78841e5891cddc8abbeab1] Comment 877
running python squares.py 2
Bisecting: 0 revisions left to test after this (roughly 1 step)
[b803946c94f09165796299065147acffc5f0a774] Breaking argument type
running python squares.py 2
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[b1274e723ed32a5742cd463781f11c15e0c26a3d] Comment 876
running python squares.py 2
4
b803946c94f09165796299065147acffc5f0a774 is the first bad commit
commit b803946c94f09165796299065147acffc5f0a774
Author: Shawn Siefkas <shawn.siefkas@meredith.com>
Date:   Thu Nov 14 09:23:55 2013 -0600

    Breaking argument type

:100644 100644 3eb1287e59ddbdfa96e34d09d13e8775edb6e476 64c3699607ac061f33f9de84230db701ba31dcef M	squares.py
bisect run success
Traceback (most recent call last):
  File "squares.py", line 9, in <module>
    print(integer**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
Traceback (most recent call last):
  File "squares.py", line 9, in <module>
    print(integer**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
Traceback (most recent call last):
  File "squares.py", line 9, in <module>
    print(integer**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
Traceback (most recent call last):
  File "squares.py", line 9, in <module>
    print(integer**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
Traceback (most recent call last):
  File "squares.py", line 9, in <module>
    print(integer**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
Traceback (most recent call last):
  File "squares.py", line 9, in <module>
    print(integer**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

Boom!