A bug report says: "it worked last week." Somewhere between then and now, a commit broke it — and there are two hundred commits in between. git bisect finds the culprit with binary search: you mark one known-good and one known-bad commit, and git walks you through ~8 testable steps to the guilty one.
The basic session #
$ git bisect start
$ git bisect bad # current state is broken
$ git bisect good v1.4.0 # this tag was fine
Bisecting: 100 revisions left to test after this (roughly 7 steps)
$ # test the checked-out commit...
$ git bisect bad # still broken
$ git bisect good # this one works
...
f3e2d1c is the first bad commit
$ git bisect reset # return to where you were
Log₂(200) ≈ 8 — eight decisions instead of two hundred builds. Git narrows the range each time you classify the commit it checks out, and the final report names the first bad commit with its full log context.
Automating the test #
When a script can judge the commit, bisect runs itself:
$ git bisect start HEAD v1.4.0
$ git bisect run ./test-regression.sh
running '...' ...
f3e2d1c is the first bad commit
bisect run executes the command at each step; exit 0 means good, non-zero means bad. The test script can be anything — a build plus assertion, a curl against a deployed build, a one-off shell script:
#!/usr/bin/env bash
set -e
npm ci --silent
npm run build --silent
grep -q 'max-age=31536000' dist/_headers
The flags that matter #
| Need | Flag |
|---|---|
| Skip a commit that will not build | git bisect skip |
| Test a range of commits | git bisect start HEAD v1.4.0 -- path/ (narrow by path) |
| View current status/terms | git bisect log |
| Reuse/save the session | git bisect log > bisect.log |
| End the session | git bisect reset |
-- path/ narrows the search to commits touching a directory — when the symptom is clearly in one area, the range halves before you start.
After the verdict #
The report names the first bad commit — then the real work starts: read that commit's diff, decide whether to revert it or fix forward, and note in the fix commit that bisect found it. The bisect session itself ends with reset — forgetting it leaves you on a detached HEAD in the middle of history.
The recovery tools around it are reflog and pickaxe log search; the Git series continues.
