Concept · Merging & history

git bisect

Binary-search history for the commit that broke something: 1,000 commits take about 10 tests.

3 minbisectbinary searchregression hunting

Something worked in last month's release and is broken now, with hundreds of commits in between. git bisect does a binary search: you mark one commit good and one bad, Git checks out the commit halfway between, you test it, and each answer halves the range. 1,000 commits take about 10 steps (log₂ 1000 ≈ 10).

bash
git bisect start
git bisect bad                 # current commit is broken
git bisect good v2.3.0         # this release was fine
# Git checks out a commit in the middle: test it, then
git bisect good                # or: git bisect bad
# … repeat until:
# 4e1a9c2 is the first bad commit
git bisect reset               # return to where you started

If a commit can't be tested (it doesn't build, or it's unrelated), run git bisect skip and Git picks a neighbour.

Automate it

If a command can tell good from bad, Git can run the whole search for you:

bash
git bisect start HEAD v2.3.0
git bisect run go test ./billing -run TestInvoiceTotals

The command's exit code decides each step: 0 means good, 1–127 (except 125) means bad, and 125 means skip. A small script that builds, runs a reproduction and exits with the right code can find the culprit unattended while you do something else. It's often faster than reading code.

bash
#!/bin/sh
make build || exit 125                     # can't build → skip this commit
./bin/app check-invoice fixtures/42.json   # exits non-zero when the bug is present

Making history bisectable

Bisect is only as good as your commits:

  • Every commit on main should build and pass tests. A trail of "WIP" or "fix typo" commits that don't compile means lots of skip, and sometimes an inconclusive result.
  • Small commits pin the bug down tightly. Finding a 2,000-line "refactor everything" commit tells you little.
  • Merge commits complicate it. If the bug came from combining two branches, bisect may point at the merge commit. git bisect start --first-parent searches only along main's own line, to find which merged PR introduced the problem.

That's one reason teams squash or rebase messy branch histories before merging. The power tools lesson covers bisect alongside stash, cherry-pick and the reflog.

Learn it in depth