Skip to content

Bisect

One-liner

Git bisect performs binary search on your commit history to find exactly which commit introduced a bug.

Why it matters

When a regression appears in a codebase with hundreds or thousands of commits since the last known good state, manually inspecting each commit is impractical. Bisect finds the culprit in O(log₂ N) steps — 11 steps for 2000 commits. It turns a day of debugging into five minutes.

Core concept

Imagine you have a dictionary and you know a word is somewhere between page 1 and page 2000. You open the middle (page 1000), check if the word comes before or after, then repeat on the remaining half. Three-year-olds play this as "higher or lower." That's exactly what bisect does with commits.

You tell git: "this commit is good (no bug), that commit is bad (has the bug)." Git checks out the midpoint. You test. You report good or bad. Git halves the range. Repeat until one commit remains — the first bad one.

How it works

  1. Start — git bisect start enters bisect mode.
  2. Mark boundaries — git bisect bad (current has the bug), git bisect good <sha> (this one didn't).
  3. Test midpoint — git checks out the middle commit. You verify whether the bug is present.
  4. Report — git bisect good or git bisect bad narrows the range by half.
  5. Repeat — steps 3-4 until git announces the first bad commit. Then git bisect reset returns you to your branch.

Key terms

  • Good — a commit where the bug is NOT present
  • Bad — a commit where the bug IS present
  • Skip — exit code 125 or git bisect skip tells bisect this commit can't be tested (e.g., doesn't build); git tries adjacent commits instead
  • git bisect run — automated bisect using a test script that returns 0 (good) or non-zero (bad)
  • First bad commit — the earliest commit in history where the bug appears; the one that introduced it

Common misconceptions

  • "Bisect only works for code bugs" — Any binary property works: performance degradation, binary size increase, broken documentation links — anything expressible as pass/fail.
  • "You need a perfect test for every commit" — Use skip (exit 125) for commits that don't build or can't be tested. Bisect handles gaps gracefully.
  • "Bisect is useless with merge commits" — It works fine with non-linear history. The binary search follows the DAG topology, not a linear line.

When to use / not use

✓ Use when: you have a known good state and a known bad state, the property can be tested (manually or via script), and there are many commits between them.

✗ Don't use when: the bug is intermittent (bisect needs deterministic pass/fail), you can already narrow it to 2-3 commits by reading the log, or the regression spans a single large commit (nothing to bisect within it).

Example

# Automated bisect with a test script
git bisect start
git bisect bad HEAD
git bisect good v2.1.0
git bisect run pytest tests/test_auth.py -x
# Git announces the first bad commit, then:
git bisect reset

Next steps

  1. Write a reusable bisect test script for your project's most common regression type (e.g., performance threshold check)
  2. Learn git bisect log and git bisect replay to document and share your debugging sessions
  3. Combine bisect with git stash to quickly test commits that need local config changes