IAnswerable

Find the Commit That Broke It with git bisect

16 Mar 2026

Something broke and you don’t know which of the last two hundred commits did it. Don’t read the diff history. Binary search it with git bisect and you’ll have the answer in six or seven steps, no matter how many commits are in play.

Start it

git bisect start
git bisect bad                  # current commit is broken
git bisect good v1.4.0          # this tag/commit was known working

Git checks out the midpoint commit between good and bad. Test it, tell git the result, repeat:

git bisect good   # or
git bisect bad

Each answer halves the search space. Two hundred commits takes about eight steps, not two hundred. The loop looks like this:

flowchart TD
    A["bisect start"] --> B["mark known bad/good"]
    B --> C["git checks out the midpoint commit"]
    C --> D["test it"]
    D -->|"still broken"| E["bisect bad"]
    D -->|"works fine"| F["bisect good"]
    E --> G{"commits left to narrow?"}
    F --> G
    G -->|"yes"| C
    G -->|"no"| H["first bad commit found"]
    H --> I["bisect reset"]
  

Automate the test

If the bug is a failing test, a crash, or anything you can check with a script or exit code, don’t do it by hand. Let git bisect run drive the whole thing:

git bisect start
git bisect bad HEAD
git bisect good v1.4.0
git bisect run npm test -- --grep "checkout flow"

Git checks out each candidate commit, runs the command, and reads the exit code: 0 means good, nonzero means bad. It walks the whole bisection unattended and prints the first bad commit at the end. For a compiled language, the run script usually needs to build first — that’s fine, just make the script do build && test.

The gotchas

Exit code 125 skips a commit. If a candidate commit doesn’t build, or the bug is simply untestable at that point (a dependency didn’t exist yet, the test file itself didn’t exist), have your script exit 125. Git marks it untestable and picks a different midpoint instead of corrupting the result.

#!/bin/bash
npm install || exit 125
npm run build || exit 125
npm test -- --grep "checkout flow"

Flaky tests break bisect. If the test only fails sometimes, git bisect run will confidently walk you to the wrong commit, because a “good” result on a flaky test doesn’t mean the commit was actually good. Run the test a few times in the script and only report bad if it fails consistently, or fix the flakiness first.

Non-code changes confuse it. Bisecting a bug that depends on data migrations or environment state can give you a false “bad” commit if the environment wasn’t reset between checkouts. If your bug depends on database state, reset it in the run script before each test.

When you’re done

git bisect reset

This is the step people forget. Without it, you’re stuck on a detached HEAD in the middle of your history. git bisect reset puts you back on the branch you started from.

After you find it

git bisect gives you the first bad commit, not the full explanation. Read that commit’s diff — it’s usually small, since bisect found the exact point behavior flipped — and you’ll generally see the bug immediately. If the commit is a big merge, git bisect can optionally walk first-parent history only with --first-parent, which is worth using if your main branch is full of merge commits from feature branches.

The whole workflow beats git log -p scrolling because it turns “read N commits” into “read log₂(N) commits,” and it turns “eyeball the diff and guess” into “run the actual test and know.”