Git Bisect — Binary-Search for the Bug
A practical git bisect cheatsheet showing how to binary-search commit history to find the exact change that introduced a bug.
Code
Git Bisect Cheatsheet
git bisect performs a binary search over your commit history to find the commit
that introduced a bug. You tell Git one bad commit and one good commit;
Git checks out the midpoint and you mark it good or bad. Each answer halves the
remaining range, so even thousands of commits resolve in a handful of steps.
Core commands
git bisect start # begin a bisect session
git bisect bad # mark the CURRENT commit as bad (bug present)
git bisect good <commit> # mark a known-good commit (bug absent)
git bisect good # mark the CURRENT checked-out commit as good
git bisect bad <commit> # mark a specific commit as bad
git bisect skip # can't test this commit — skip it
git bisect reset # end the session, return to original HEAD
Basic manual flow
# 1. Start and tell Git the boundaries.
git bisect start
git bisect bad # HEAD is broken
git bisect good v1.4.0 # this tag/commit worked
# 2. Git checks out a midpoint commit. Test it, then mark it:
git bisect good # if the bug is NOT present here
# ...or...
git bisect bad # if the bug IS present here
# 3. Repeat until Git prints the first bad commit, e.g.:
# a1b2c3d is the first bad commit
# 4. Clean up — go back to where you started.
git bisect reset
You can also pass both boundaries on the start line:
git bisect start <bad> <good>
# example:
git bisect start HEAD v1.4.0
Automate with a test script (git bisect run)
git bisect run runs a command at each step and reads its exit code:
- exit
0→ commit is good - exit
1–124,126,127→ commit is bad - exit
125→ commit should be skipped (cannot be tested) - any exit
>= 128aborts the bisect
git bisect start
git bisect bad HEAD
git bisect good v1.4.0
git bisect run ./test.sh # runs at every step, fully unattended
git bisect reset # after it reports the first bad commit
Run an existing test suite directly:
git bisect run npm test
git bisect run pytest -x tests/test_login.py
git bisect run make check
Run a single command and treat a non-zero exit as "bad":
git bisect run sh -c 'grep -q "expected output" <(./build-and-run.sh)'
Example test script
#!/usr/bin/env bash
# test.sh — exit 0 = good, 1 = bad, 125 = skip (untestable build)
# Build; if the build itself is broken, skip this commit instead of blaming it.
make build || exit 125
# Run the focused check that reproduces the bug.
./run-check || exit 1
exit 0
chmod +x test.sh # make sure it is executable before `git bisect run`
Skipping untestable commits
Use skip when a commit cannot be judged (won't build, missing data, etc.):
git bisect skip # skip the current commit
git bisect skip <commit> # skip a specific commit
git bisect skip v2.0..v2.1 # skip a whole range
When skips prevent an exact answer, Git reports the smallest range still containing the first bad commit.
Inspecting and resetting
git bisect log # show the bisect session so far
git bisect log > bisect.log # save it to replay later
git bisect replay bisect.log # re-run a saved/edited session
git bisect visualize # open gitk on the remaining range
git bisect view # alias for visualize
git bisect reset # finish: restore the original HEAD
git bisect reset <commit> # finish AND check out a specific commit
Common workflows
1. Find a regression between two tags, manually
git bisect start
git bisect bad v3.0.0
git bisect good v2.9.0
# Git checks out a midpoint each round — test the app and answer:
git bisect good # or: git bisect bad
# ...repeat until Git names the first bad commit...
git bisect reset
2. Fully automated with a unit test
git bisect start HEAD v2.9.0 # bad good in one line
git bisect run npm test -- login.spec.js
# Git prints "<sha> is the first bad commit"
git show <sha> # inspect the offending change
git bisect reset
3. Bisect a flaky/expensive build with skips
git bisect start
git bisect bad
git bisect good HEAD~200
git bisect run ./test.sh # test.sh exits 125 when build fails -> auto-skip
git bisect reset
4. Save, abort, and resume later
git bisect start
git bisect bad
git bisect good v1.0
git bisect good # a few rounds in...
git bisect log > bisect.log # checkpoint
git bisect reset # stop for now
# later — pick up exactly where you left off:
git bisect replay bisect.log
Gotchas / tips
good/badare just labels. For non-regression searches (e.g. "when did this get fixed?") use custom terms:git bisect start --term-old=fixed --term-new=broken, then mark withgit bisect fixed/git bisect broken.- Always
git bisect resetwhen done — otherwise you stay on a detached midpoint commit and your working tree looks "wrong". - Commit or stash local changes first. Bisect checks out different commits; a dirty working tree blocks the checkout.
- Exit code 125 means skip, not bad. Use it in scripts for commits that won't build so a broken build is never mistaken for the bug.
- Keep the test fast and deterministic.
git bisect runcalls it once per step; flaky tests give wrong verdicts. Reproduce the bug with the tightest possible check. - The "good" commit must actually be good. If you pick a good commit that is already broken, bisect will point at the wrong place.
- Rebuild side effects can leak. If artifacts persist between checkouts, add
a clean step (e.g.
make clean,rm -rf node_modules/.cache) inside the script to avoid false results.
Git Bisect — Binary-Search for the Bug
git bisect walks your commit history with a binary search to pinpoint the exact
commit that introduced a regression. You mark a known-bad commit and a known-good
commit, then Git checks out the midpoint and asks you to judge it — halving the
search space each step until the culprit is isolated.
When to use it
- A test or feature worked at some point in the past but is broken now.
- You have a reliable way to tell “good” from “bad” (a manual check or a script).
- The offending change is hidden somewhere in dozens or thousands of commits.
Why it is fast
Instead of checking every commit, bisect needs only log2(N) steps. A range of
1000 commits is narrowed down in about 10 checks — and with git bisect run it
can do all of them unattended.
Full cheatsheet
The complete copy-pasteable reference — start/good/bad, automation with a test
script, skip, reset, and end-to-end workflows — lives in the snippet:
snippets/markdown.md.