Mastering Git Bisect: How to Easily Track Down Bugs in Your Codebase

Discover how git uses the power of binary search to help you debug your code faster.

Lucyivatt
The Tech Collective
6 min readJul 18, 2024

--

Image by Yancy Min on Unsplash

Have you ever found yourself in a frustrating situation where a bug mysteriously appears in your project, and you can’t pinpoint when or how it got in? 🐛 Tracking down the specific commit or code change that introduced the bug can be challenging and time-consuming, especially when working in large codebases.

This is where git bisect comes in really handy! In this guide, we’ll explore how to use git bisect to quickly and efficiently identify the commit that introduced the problem. 👩‍💻

What exactly is `git bisect`?

git bisect is a powerful Git command that helps you find the commit that introduced a bug using a binary search algorithm 🔎. By marking commits as "good" or "bad," we can use git bisect to narrow down the range of problematic commits until we find the one who introduced the issue.

Check out the official git documentation for more detailed information 📑.

Getting Started

Before using git bisect, you need to:

  1. Identify how to reproduce the bug: For each commit, you should have a clear method to determine if it behaves correctly (“good”) or exhibits the bug (“bad”). This could involve navigating to a specific webpage, interacting with elements, running specific functions that trigger the behaviour etc.
  2. Determine the initial commit range: Find a commit where the code was working correctly and a commit where the bug is present (this can be the current commit by default).

Step-by-step Guide

I’ve created a simple calculator Python script as an example. If you want to follow along, feel free to clone the repository by using the commands below.

git clone git@github.com:LucyIvatt/git-bisect-demo.git
cd git-bisect-demo

In the script calculator.py, I’ve included 5 different functions: add, multiply, subtract, divide and power — each with an example printed to the console.

print("5 + 3 =", add(5, 3))
print("4 * 6 =", multiply(4, 6))
print("10 - 2 =", subtract(10, 2))
print("21 / 3 =", divide(21, 3))
print("2^2 =", power(2, 2))

But wait! When I run the script this is the output I’m getting, but 5 + 3 isn’t 125 👀

> python3 calculator.py
5 + 3 = 125
4 * 6 = 24
10 - 2 = 8
21 / 3 = 7
2^2 = 4

I know it was working when I created the addition function 🤔. So I’ll first find out which commit that was using git log or viewing my commits on GitHub.

I can see that this was ‘f5413b3 added add function’.

> git log --oneline
ac10d38 added power function
e06c764 added divide function
a997f5d added subtract function
707160c added multiply function
f5413b3 added add function
be163ec added calculator.py
113121f Initial commit

Now I’m going to start the bisection with git bisect start.

I know the commit f5413b3 was working okay, so I’ll mark this as a “good” commit using git bisect good [commit hash].

I’ll then leave the “bad” commit blank which will use the current commit (ac10d38) by default.

> git bisect start
status: waiting for both good and bad commits

> git bisect good f5413b3
status: waiting for bad commit, 1 good commit known

> git bisect bad
Bisecting: 1 revision left to test after this (roughly 1 step)
[a997f5d6af36165e248727e0d007a606f2167bb2] added subtract function

Now you can see that git has selected the commit in the middle of the range to check if it’s good or bad, which happens to be the commit where I added the subtract function.

BAD   ac10d38 added power function
e06c764 added divide function
--> a997f5d added subtract function
707160c added multiply function
GOOD f5413b3 added add function

Running the program again, I can still see that the addition calculation is incorrect, so I will mark that commit as bad by using git bisect bad.

> python3 calculator.py
5 + 3 = 125
4 * 6 = 24
10 - 2 = 8

> git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[707160c4b73320a279af822dc740a111315edf00] added multiply function

We can now assume that the ‘e06c764 added divide function’ commit is also affected by the bug, but is not the commit at fault, because it’s located between two “bad” commits.

Now git selects the commit in the middle of the new smaller search range, which happens to be multiply, the only commit remaining.

BAD   ac10d38 added power function
--- e06c764 added divide function
BAD a997f5d added subtract function
--> 707160c added multiply function
GOOD f5413b3 added add function

Rerunning the application, we can see the bug is gone! So we can mark this commit as good.

> python3 calculator.py
5 + 3 = 8
4 * 6 = 24

> git bisect good
a997f5d6af36165e248727e0d007a606f2167bb2 is the first bad commit
Author: Lucy Ivatt <lucy.ivatt@createfuture.com>
Date: Fri Jul 12 21:48:14 2024 +0100

added subtract function

calculator.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

Git has identified that the subtract commit we previously checked must have been the first time the bug appeared.

We can now use this information to go and investigate that commit and see what happened. Going onto GitHub and looking at the commit (or if you want to use git show [commit hash]), we can see that in this commit, the return value for add() got accidentally changed from a + to ** which must be the issue!

Screenshot showing the additions a git commit made to a file. It added a subtract function, as well as accidentally changing the add function to return the exponent.
Screenshot of subtract commit on GitHub

Finally, all you need to do is run git bisect reset to get back to the current commit and get to fixing that bug!

> git bisect reset
Previous HEAD position was 707160c added multiply function
Switched to branch 'main'
Your branch is up to date with 'origin/main'.

Additional Features

Here are some slightly more complex things you can do with git bisect, make sure to check out the git documentation for more information!

  • Skipping commits: ⏩ During the bisecting process, you may encounter commits that you know cannot be the cause of the bug. Instead of wasting time testing them manually, you can skip these commits or skip a range:
> git bisect skip // single commit

> git bisect skip v2.5..v2.6 // skip a range
  • Custom Test Script: 📝 If your bug requires complex conditions to reproduce or test manually, you can write a custom test script. The script should exit with code 0 if the commit is “good”, and exit with a code between 1 and 127 if the commit is “bad”— except 125 which is reserved for commits which should be skipped. You can run this after starting the bisection and setting the initial range using the following command:
> git bisect run ./script_path.sh [arguments]

I’ve included an example of this for the calculator.py scenario in the GitHub repo!

  • Alternate Terms: Don’t like “good” or “bad”? You can also use “old” and “new” or even create your own using the following command when you start bisecting:
git bisect start --term-old <term-old> --term-new <term-new>

# e.g. if you are looking for a change which caused performance issues
git bisect start --term-old fast --term-new slow

Conclusion

So, the next time you encounter an elusive bug in your project, remember the power of git bisect💪 .

By partially automating the search for the exact commit that introduced the issue, git bisect transforms the daunting task of debugging into a much more systematic and manageable process.

--

--