What is git bisect — Finding the Culprit Commit with Binary Search
"A feature that worked yesterday is broken today. And there have been 200 commits in between." Instead of walking through commits one by one, git bisect uses binary search to quickly find the commit that first introduced the bug.
The idea is simple. Tell Git a "good" (working) commit and a "bad" (broken) commit, and Git checks out the midpoint between them. You test and answer good or bad, and Git moves to the midpoint of the remaining half. Even 1,000 commits take only about 10 steps (log₂1000 ≈ 10).
Knowing the exact commit that introduced a regression makes root-cause analysis dramatically easier. You pinpoint "the commit that changed this one line" instead of a 200-line merge commit.
Manual bisect — Basic Flow
# 1. Start the session
git bisect start
# 2. Mark the current commit as broken
git bisect bad
# 3. Mark a commit you know was healthy (tag, commit hash, or relative ref all work)
git bisect good v1.4.0At this point Git automatically checks out the midpoint commit and tells you how many candidates remain.
Bisecting: 63 revisions left to test after this (roughly 6 steps)
[a1b2c3d...] Refactor payment handlerNow build and test this commit, then report the result.
# If the bug reproduces here
git bisect bad
# If this commit is healthy
git bisect goodGit narrows the range by half again and moves to the next commit. Repeat until it names the culprit.
b9f0e1a2c3... is the first bad commit
commit b9f0e1a2c3...
Cache user session in memoryEnding the Session — reset Is Required
When the search is done (or if you need to abort), you must return to the original branch.
git bisect reset # return to the original HEAD
git bisect reset <commit> # end the session and return to a specific commitDuring a search, git bisect hops between commits and leaves you in a detached HEAD state. If you skip reset, you'll keep working on some midpoint commit, so make it a habit to run git bisect reset when you're done.
Automation — git bisect run
This is the most powerful feature. Pass a command or script that decides good/bad, and Git runs the entire search unattended.
The verdict is determined by the exit code.
| Exit code | Meaning |
|---|---|
0 | good (working) |
1~124, 126, 127 | bad (broken) |
125 | skip (this commit cannot be tested) |
128+ | abort the search |
git bisect start
git bisect bad
git bisect good v1.4.0
# exit 0 (good) if the test passes, exit 1 (bad) if it fails
git bisect run npm test -- payment.spec.jsGit checks out each candidate, runs the command, prints the first bad commit, and stops. Grab a coffee and it'll be done when you get back.
Custom Test Scripts
If you need a build step or a custom check, write a script. Just follow the exit-code rules.
#!/usr/bin/env bash
# bisect-test.sh
# Skip commits that don't even compile
make build || exit 125
# Judge good/bad by whether a specific symptom reproduces
if ./run-app --check-feature; then
exit 0 # good
else
exit 1 # bad
fichmod +x bisect-test.sh
git bisect run ./bisect-test.shexit 125 (skip) means "this commit cannot be judged good or bad" (for example, it doesn't build), so Git skips to a nearby commit.
skip and Logs
You can skip commits by hand or save a log of the session.
git bisect skip # current commit cannot be tested → skip it
git bisect skip <commit1> <commit2>
git bisect log > bisect.log # save the good/bad record so far
git bisect replay bisect.log # replay that record as-isYou can also inspect the remaining search range with bisect visualize (or view) in gitk or the log.
Custom Terms — Instead of good/bad
When good/bad feels awkward — for example with a performance regression — you can rename the terms.
git bisect start --term-old=fast --term-new=slow
git bisect slow # current commit is slow
git bisect fast v1.4.0
git bisect run ./perf-check.shPractical Tips
- Automate the repro first. If you have a one-liner to pass to
bisect run, the search is many times faster. - Pick a generous good point. Mark an older tag you know was healthy as good. If your "good" commit was already broken, the result will be wrong.
- Isolate tests: If each commit needs a fresh install or cache, put that in the script (
npm ci, etc.). Otherwise leftovers from a previous commit can give a false result. - Watch merge commits: If the first bad commit is a merge, you can bisect inside that branch to narrow it to a specific commit.
When you don't know when a bug landed, scanning the log by eye is a linear search. Automate the verdict with git bisect run and you can pinpoint the culprit among hundreds of commits in logarithmic time.
AI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us
Questions & answers
Ask a question about this guide. We'll review and reply.