By admin , 29 April 2026

What you will achieve

You will use git bisect with an automated test script to identify the exact commit that introduced a regression. The technique scales from "the bug appeared sometime this month" to thousand-commit windows in minutes.

Set up a sandbox with a planted bug

mkdir bisect-tutorial && cd bisect-tutorial
git init

cat > calc.js <<'EOF'
function add(a, b) { return a + b; }
module.exports = { add };
EOF
git add calc.js && git commit -m "Initial calc"

for i in 1 2 3 4 5 6 7 8 9 10; do
  echo "// touched: $i" >> calc.js
  git commit -am "Tweak $i"
done

# Plant a bug at commit 5 (counting from 1)
git checkout HEAD~5
sed -i.bak 's/a + b/a - b/' calc.js && rm calc.js.bak
git commit -am "Refactor add (oops)"
git checkout main
git rebase HEAD~5

(In real projects you do not plant bugs - this is for the demo.)

Write a reproducer

cat > test.js <<'EOF'
const { add } = require('./calc');
if (add(2, 3) !== 5) {
  console.error('FAIL: add(2,3) should be 5');
  process.exit(1);
}
EOF

Run it - it fails. We know HEAD is bad. Find a known-good baseline:

git checkout HEAD~12
node test.js     # passes
git checkout main

Manual bisect

git bisect start
git bisect bad HEAD
git bisect good HEAD~12
# Git checks out the midpoint

For each checkout, run the test:

node test.js
# if pass:
git bisect good
# if fail:
git bisect bad

After a few steps, Git prints:

abc1234 is the first bad commit
Author: ...
Date: ...
    Refactor add (oops)

Reset

git bisect reset

Automated bisect

Even better, let bisect run unattended:

git bisect start HEAD HEAD~12
git bisect run node test.js

Bisect runs your script at each candidate. Exit 0 = good; non-zero = bad; 125 = skip. Within seconds, Git prints the offending SHA.

Skipping untestable commits

If a checkout cannot be evaluated (broken build, unrelated dependency missing), skip:

git bisect skip
# or in a script, exit 125

Customising terms

For a regression where the issue is not "broken" but "slow":

git bisect start --term-old=fast --term-new=slow
git bisect slow HEAD
git bisect fast v2.4.0

What makes bisect work well

  • Atomic commits. Each commit builds and tests pass; bisect can evaluate any commit.
  • Reproducible tests. Flaky tests poison bisect.
  • Deterministic bug. Intermittent bugs require scripted retries.

Real-world example

# Production bug appeared between v3.0.0 and HEAD
git bisect start HEAD v3.0.0
git bisect run ./scripts/reproduce-issue.sh

Twenty minutes later, you have a 50-line commit to investigate instead of 50,000.

The mental model

Bisect performs binary search through commits. log2(N) checks identify the first bad commit. For 1024 commits, ten checks. For 16k commits, fourteen.

Tips

  • Wrap the reproducer in a script - even a one-liner. Reproducibility matters.
  • Test the reproducer on known-good and known-bad commits before bisect run.
  • For first-parent only history, use --first-parent to skip merged feature branch interiors.

Bisect turns "I have no idea where this regression came from" into a 20-minute mechanical exercise. Use it the next time a bug bites; it will earn its place in your toolkit immediately.