Jake Worth

Find the Breaking Git Commit

Published: May 17, 2022 3 min read

  • git

Something is broken, you can’t figure out why, and you’re wondering: how and when did this break?

Maybe there are a lot of changes to consider, and isolating that breaking change is hard.

Or, maybe you’re curious how things break in your organization so you can improve.

The command-line tool for this situation is git bisect. Git bisect is a binary search through your Git commit history. Let’s walk through it.

‘Start’

You’re on master, at HEAD, and something is broken. A white screen. An inexplicable error.

Indicate this as your starting point with start:

$ git bisect start
 (git)-[master|bisect]-$

Now we’re at master in a bisect, per the prompt.

‘Bad’

Note that this commit is broken with bad:

$ git bisect bad
 (git)-[master|bisect]-$

You can verify that it did something with git show, or any command that shows refs:

$ git show
commit 8bec317a (HEAD -> master, refs/bisect/bad)

Note the bad ref at the end.

Find a ‘Good’ Commit

Now, we need to find a commit in the past where things worked. I usually jump back twenty commits to start. When in doubt, jump farther.

$ git checkout HEAD~20
Note: switching to 'HEAD~20'.

Test your code again. Still broken? If so, jump back again.

Once you find a place where it isn’t broken, that’s a ‘good’ place. Make a note:

$ git bisect good
Bisecting: 22 revisions left to test after this (roughly 5 steps)
[69d80fa4acjad4bd38e0b802ab87d4a6a3279c40] Merge pull request #8 from repo/branch

As the prompt explains, we are roughly five steps from an answer!

Bisecting

Time to bisect. Test your code again. Still broken? If so, make a note:

$ git bisect bad
Bisecting: 11 revisions left to test after this (roughly 4 steps)
[56988ajc43ab972baf43984a18f4580971cc2450] Remove deprecation warning

Or, if it isn’t broken, swap good for bad.

Keep this up until there are no more commits to test. You’ll see a conclusion like this:

(git)-[v0.2.21~1|bisect]-$ git bisect bad
c66d2c2daja91def4aaaf107da9136c43861296f is the first bad commit
commit c66d2c2daja91def4aaaf107da9136c43861296f 
Author: Some Dev <dev@example.com>
Date:   Tue Aug 28 14:57:00 2018 -0700

    Quickpush to fix all the bugs 😅

 example.gemspec | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

show your bad commit to learn more.

$ git show c66d2c2daja91def4aaaf107da9136c43861296f

Lastly, exit with reset:

$ git bisect reset
Switched to branch 'master'

To summarize:

# Start
$ git bisect start

# Test your feature and find out it's broken
$ git bisect bad

# Travel backwards
$ git checkout HEAD~<n>

# Test your feature and find out it works
$ git bisect good

# The bisect continues...

Going Further

Got a program that returns 0 for success or 1 for error, like a unit test? Use it to automate your bisect.

$ git bisect start
$ git bisect bad
$ git checkout <good-ref>
$ git bisect good
$ git bisect run <rspec spec/demonstrates_the_feature_spec.rb>

For bisect to be useful, you must be committing atomically. If your practice is to squash-merge PR’s, or commit swaths of unrelated code with each commit, bisect is only going to tell you that something broke when you merged in a ton of changes. That isn’t typically useful information.

Why Bother?

Why bother with this technique? Why not just fix the issue and move on?

My advice is to try it a few times when you’re stuck debugging and see. When I have used it, I’ve felt like a forensic scientist, able to glean insights about a tough problem. Insights that would have taken me much longer to arrive at another way.

Let’s Chat

Do you bisect? How has it helped you? Let me know in the comments.

✉️ Get better at programming by learning with me. Subscribe to Jake Worth's Newsletter for bi-weekly ideas, creations, and curated resources from across the world of programming. Join me today!


Blog of Jake Worth, software engineer in Maine.

© 2022 Jake Worth.