git bisect: Find the Commit That Broke Everything
Binary search your commit history to find exactly which commit introduced a bug — even across hundreds of commits, in minutes.
A bug exists in the current codebase that did not exist three weeks ago. You have no idea which of the 200 commits in between caused it. Checking each one manually would take hours. git bisect does a binary search — it finds the culprit in at most eight steps regardless of how many commits there are.
How it works
You mark a known-good commit and a known-bad commit. Git checks out the midpoint. You test and tell Git whether that commit is good or bad. Git halves the search space and repeats. After a handful of rounds, it pinpoints the first bad commit.
Start a bisect session
git bisect start
git bisect bad # current commit is bad
git bisect good v1.4.0 # this tag (or hash) was working
Git checks out a commit halfway between the two. Test the code — does the bug exist?
git bisect good # bug is NOT present in this commit
git bisect bad # bug IS present in this commit
Repeat until Git announces the culprit:
b2c3d4e5 is the first bad commit
commit b2c3d4e5
Author: ...
Date: ...
feat: add pagination to user list
End the session
git bisect reset
This returns you to the branch you started from. Always run this when you are done — leaving a bisect session open causes confusing behaviour with other Git commands.
Automate with a test script
If you have a script that exits 0 when the code is good and non-zero when it is bad, you can automate the entire process:
git bisect start
git bisect bad HEAD
git bisect good v1.4.0
git bisect run ./scripts/test-feature.sh
Git runs the script at each step and marks the result automatically. The session finishes without any input from you, often in under a minute.
The script needs to be reliable — flaky tests will mislead the binary search. It also needs to be compatible with the range of commits being tested, which is sometimes a problem if the test itself depends on a file that did not exist in older commits.
Find when a file changed significantly
If you know which file is involved but not which commit touched it:
git log --oneline path/to/file.js
This narrows the suspect list. If the list is short enough, reviewing the diffs directly is faster than a bisect. For a long list, start bisect with the earliest commit that touches that file as the known-good point.
When bisect is hard to use
The bug is not reproducible with a simple script. Manual bisect still works — you just test by hand at each step. Even manually, binary search on 100 commits takes at most 7 rounds.
The build is broken on many commits. Tell Git to skip commits where you cannot test:
git bisect skip
Git moves to a nearby commit and continues. The final result will be a range of commits rather than a single one if the true culprit is adjacent to a skipped commit.
The bug is a performance regression. Benchmark scripts work fine as the test command — exit 0 if performance is acceptable, exit 1 if not. The threshold needs to be reliable enough that the result does not flip due to system noise.
The result is a starting point, not the full answer
git bisect finds the commit that first exhibited the bug — but the commit that caused it might be earlier. The commit git identifies might be an innocent victim: it calls a function that was broken by an earlier change, or it exercises a code path that already had a latent bug.
Once you have the commit, read the diff:
git show b2c3d4e5
Understand what it changed. Then look at the commits immediately before it if the change itself looks harmless. The bug is usually either in the identified commit or in a nearby one that set up the conditions.
SysEmperor