git bisect: turn which commit broke it into one command

The hard part of regression hunting is not the binary search, it is defining what counts as broken. Write a script that answers that, and bisect compresses n builds into log n.

The real gate on git bisect is not the search. It is that you must supply a check that answers broken or not by itself. Without one, bisect means a human re-running the app and typing good and bad, which saves almost nothing.

Write the check first

Define broken as the exit code of one command: 0 means good, anything else means bad.

#!/usr/bin/env bash
# scripts/bisect-repro.sh
set -euo pipefail
npm run build
node scripts/check-the-thing.mjs

As long as it fails on bad commits and passes on good ones, bisect runs unattended:

git bisect start
git bisect bad HEAD
git bisect good v1.4.0
git bisect run ./scripts/bisect-repro.sh

git bisect run checks out each candidate, runs the script, marks it by exit code, and prints the first bad commit.

The good commit has to actually be good

The most common failure is a starting point that is too recent. Pick the last release as good and, if the bug already existed there, the whole interval is wrong and you will land on an unrelated commit.

The test is crude but works: run the check on the candidate good commit first. If it fails there, keep walking back.

Exit code 125

Exit code 125 means the commit cannot be judged, and bisect skips it. That matters for commits that do not build:

if ! npm run build > /dev/null 2>&1; then
  exit 125   # does not build here, skip
fi

Without that guard, one broken build in the middle invalidates the whole run.

Log it instead of remembering it

git bisect log > /tmp/bisect.log    # checkpoint mid-run
git bisect replay /tmp/bisect.log   # reproduce later

Run git bisect reset before anything else, or you keep working on a detached HEAD. Keep the log and paste the command into the issue once the fix lands. The next person does not have to rediscover it.

What bisect really saves is not the search steps. It is that writing the check forces you to pin down the boundary of the bug.

← Back to all posts

Comments

…