Topic 6.2
Bisect: Finding the Commit That Broke Things
In one line
git bisect performs an automated binary search across your commit history, narrowing down the exact commit that introduced a bug in a logarithmic number of steps instead of checking every commit one by one.
Think of it like this
Finding a specific broken page in a 1,000-page book by checking the middle page, deciding whether the problem is in the first or second half, then repeating on that half — instead of reading page 1, page 2, page 3 in order. Bisect does exactly this binary-search process across commits instead of pages.
Key ideas
- 01
git bisect startbegins a bisect session.git bisect badmarks your CURRENT commit as known-broken, andgit bisect good <sha>marks a specific earlier commit as known-working — Git then automatically checks out a commit roughly HALFWAY between the two, for you to test. - 02
For each commit Git checks out during the bisect, you test whether the bug is present, then tell Git the result:
git bisect good(this commit is fine, the bug was introduced later) orgit bisect bad(this commit already has the bug, it was introduced earlier or here) — Git narrows the range by half again each time, converging on the exact culprit commit in roughly log₂(N) steps for N candidate commits. - 03
This means even a genuinely large range of candidate commits — say, 1,000 — takes at most about 10 test cycles to narrow down to the exact one that introduced the bug, dramatically faster than manually checking commits one at a time in order, especially once the range spans weeks or months of history.
- 04
git bisect run <script>fully AUTOMATES the process, given a script or command that exits with code 0 for 'good' and non-zero for 'bad' (a specific failing test, for instance) — Git then runs the entire bisect search unattended, checking out each candidate commit, running your script, and interpreting its exit code automatically, reporting the exact culprit commit at the end with zero manual testing needed per step. - 05
git bisect resetends the bisect session and returns you to whatever branch/commit you were on before starting — always run this once you've found the answer (or given up), since bisect leaves you in a detached-HEAD state (checked out directly on a commit, not a branch) until you explicitly end the session.
Code & diagrams
Each test cycle eliminates roughly half of the remaining candidate commits.
Both manual and fully automated versions of the same search.
cd ~/scratch/my-first-repo
# Manual bisect — you test each commit yourself
git bisect start
git bisect bad HEAD # current commit has the bug
git bisect good v1.2.0 # this old tagged commit was fine
# Git checks out a commit roughly halfway between them
# ... manually test the checked-out commit ...
git bisect good # if the bug is NOT present here
# or:
git bisect bad # if the bug IS present here
# Git checks out the next midpoint automatically — repeat until found
git bisect reset # ALWAYS run this when done — returns you to your branch
# --- Fully automated version ---
git bisect start
git bisect bad HEAD
git bisect good v1.2.0
git bisect run npm test -- --grep "checkout total calculation"
# Git runs the test against each candidate commit automatically and
# reports the exact commit that introduced the failure, with no manual steps
git bisect resetExplain it without notes
Why does bisect only need roughly log₂(N) test cycles to find the culprit among N candidate commits, rather than needing to check every single one?
Why is git bisect run so much more valuable when you have a specific automated test that reproduces the bug, versus needing to test manually each time?
Practice
In a repo with enough history, deliberately introduce a 'bug' in an early commit (a simple, findable change), then use manual bisect (marking good/bad by inspection) to find the exact commit that introduced it.
If you have a project with a real test suite, try git bisect run against a specific failing test, pointing it at a known-good and known-bad commit, and let it find the culprit automatically.
Trade-offs
- ↔
Bisect is extremely fast at narrowing down WHICH commit introduced a problem, but it says nothing about WHY — once it identifies the culprit commit,
git showon that SHA (Topic 1.2) is still needed to actually understand and fix the underlying issue; bisect finds the needle, but reading the needle is still up to you.
Done when you can
I understand why bisect's binary search is so much faster than checking commits one at a time.
I can run a manual bisect session, marking commits good/bad, to find a specific culprit.
I know git bisect run exists and can automate the entire search given a test script.