Blog
3 min read

git bisect: Find the Commit That Broke Your App

Something worked last week and doesn't now. git bisect binary-searches your history to find the exact commit that introduced a bug in a handful of steps. Manual bisecting, automating it with git bisect run and a test script, skipping untestable commits, and using it with AI agents.

"It worked on Monday." Since then there are 120 commits, some yours, some a teammate's, some from an AI agent. Which one broke it?

git bisect finds out with a binary search: it checks out the commit halfway between "known good" and "known bad", you say whether it works, and it halves the range again. 120 commits takes about 7 steps; 1,000 takes about 10.

Manual bisect

git bisect start
git bisect bad                 # the current commit is broken
git bisect good v1.4.0         # this tag (or commit hash) worked

Git checks out a commit in the middle:

Bisecting: 60 revisions left to test after this (roughly 6 steps)
[a1b2c3d] Refactor invoice totals

Test it — run the app, run a test, reproduce the bug — then tell Git:

git bisect good    # this one works
# or
git bisect bad     # this one is broken

Repeat. Eventually:

e4f5a6b is the first bad commit
commit e4f5a6b
    Round totals before applying discount

That's your culprit. Finish:

git bisect reset   # back to where you started

Now git show e4f5a6b tells you exactly what changed. (How to read a diff)

Finding a "good" commit

If you don't know one, try a tag, a release, or something from last week:

git log --oneline --since="2 weeks ago" | tail -1

Automating it: git bisect run

If you can write a command that exits 0 when the code is good and non-zero when bad, Git can do the whole search unattended:

git bisect start HEAD v1.4.0
git bisect run npm test -- invoice.test.ts

Or with a small script:

#!/usr/bin/env bash
# check.sh
npm ci --silent || exit 125            # 125 = can't test this commit, skip it
npm run build --silent || exit 125
node scripts/check-invoice-total.js     # exits 1 if the bug is present
git bisect run ./check.sh

Exit code 125 tells bisect "this commit can't be tested" (it doesn't build, say) — it skips it.

A great pattern: first write a test that reproduces the bug (it fails now), then bisect run with that test. You find the cause and end up with a regression test. (TDD with Claude Code)

Useful extras

git bisect skip          # can't test this one manually
git bisect log           # what you've marked so far
git bisect visualize     # remaining commits (or: git bisect view --oneline)
git bisect terms --term-old=fast --term-new=slow   # for things that aren't "bugs", e.g. a performance regression

Custom terms are handy for "when did this get slow?" — mark commits fast or slow.

Tips

  • Clean working tree — commit or stash changes first. (git stash explained)
  • Reinstall dependencies at each step if package.json changes in the range, or results lie. (node_modules explained)
  • Database migrations in the range can make old commits incompatible with your current database; test against a scratch database.
  • Merge commits and huge commits make the answer less precise — another reason for small commits. (How to squash commits)

With an AI agent

git bisect run is ideal to hand to a coding agent: "Write a script that reproduces the invoice rounding bug, then use git bisect run between v1.4.0 and HEAD to find the commit that introduced it, then explain the change." It's mechanical, verifiable, and the result is a specific diff to reason about. (Using Git with Claude Code)


EasySpawn keeps your repository, dependencies and a scratch database on a persistent server, so Claude Code can run a full automated bisect while you get on with something else. See how it works or join the waitlist.

Related: How to Undo Almost Anything in Git · Debugging for Beginners · git reset vs git revert · Using Git With Claude Code

Keep reading