GitIntermediate

Git Bisect: Finding the Commit That Broke Something

When a bug appeared somewhere in the last 200 commits and you have no idea which one, git bisect finds it in log2(n) steps instead of manually checking each one.

DevFieldGuideJune 16, 2026 (updated July 11, 2026)6 min read
Share:

git bisect is one of the most underused commands in Git — a binary search over your commit history that finds the exact commit that introduced a bug, often in under ten steps even across hundreds of commits.

Prerequisites
  • A Git repository with a bug that used to work at some known-good point in history.
  • Ideally, a way to check "is the bug present" that can either be done by hand at each step, or scripted for full automation.

Step 1: Start a bisect session

bash
git bisect start
git bisect bad                 # the current commit has the bug
git bisect good v1.4.0          # this earlier tag/commit was known-good

Git checks out a commit roughly halfway between the known-good and known-bad points, and waits for you to test it.

git bisect startMark current commit bad, an earlier one good
Git checks out the midpointHalfway between good and bad
You test itgit bisect good or git bisect bad
RepeatRange halves each time — log2(n) steps total
Git reports the exact commitThe first bad one

Step 2: Test each checkout and report the result

bash
# Git has checked out a commit halfway through the range
npm test
# If it fails:
git bisect bad
# If it passes:
git bisect good

Each answer halves the remaining range — this is why bisect finds the culprit in roughly log2(n) steps: bisecting 500 commits takes about 9 steps, not 500.

Step 3: Git reports the first bad commit

a1b2c3d is the first bad commit commit a1b2c3d4e5f6... Author: ... Date: ... Refactor payment validation logic

This is the exact commit that introduced the regression — from here, git show a1b2c3d shows exactly what changed, usually making the actual bug obvious.

Step 4: End the session

bash
git bisect reset

This returns your working directory to the branch/commit you were on before starting — easy to forget, and worth doing immediately after finding the culprit so you're not left in a detached HEAD state.

Automating it entirely with git bisect run

If the check can be expressed as a script that exits 0 for good and non-zero for bad, the entire process runs unattended:

bash
git bisect start
git bisect bad HEAD
git bisect good v1.4.0
git bisect run npm test

Git checks out each midpoint, runs npm test, reads the exit code, and continues automatically until it reports the exact bad commit — no manual checking required at each step.

Bisecting with a custom script for anything more specific

For a bug that isn't a clean test failure (a specific function returning the wrong value, for instance), write a small script that checks exactly that condition and exits accordingly:

bash
#!/bin/sh
# check-bug.sh
result=$(node -e "console.log(require('./calc').total([1,2,3]))")
if [ "$result" = "6" ]; then
  exit 0   # good
else
  exit 1   # bad
fi
bash
git bisect run ./check-bug.sh

This generalizes bisect to any bug that can be checked programmatically, not just "does the test suite pass."

Handling commits that can't be tested

Sometimes a commit in the range genuinely can't be built or tested (a broken intermediate state, a WIP commit) — mark it as untestable rather than guessing:

bash
git bisect skip

Git picks a different commit to test instead and continues the search, excluding the unskippable one from consideration.

Bisecting a specific file or path only

For a large repository, restricting bisect's consideration to commits touching a specific path can speed up the process and avoid testing irrelevant commits entirely:

bash
git bisect start -- src/payments/

This limits bisect's candidate range to commits that actually modified something under src/payments/, which is worth doing whenever you already know roughly where the bug lives — it doesn't change the binary-search logic, just narrows the pool of commits being searched.

Viewing the bisect log and replaying it

Every good/bad decision is recorded, and can be reviewed or replayed — useful for sharing a bisect session with a teammate, or resuming one after being interrupted:

bash
git bisect log > bisect-log.txt
 
# later, or on another machine:
git bisect replay bisect-log.txt

git bisect replay re-runs the exact sequence of decisions from a saved log, arriving at the same state without needing to manually re-test every commit — genuinely useful for handing off a half-finished bisect session to someone else, or resuming one across a break.

Visualizing where you are mid-bisect

git bisect visualize (or git bisect view) opens a graphical view of the remaining candidate commits — useful for getting a sense of how much range is left, or spotting a commit whose message already hints at the likely cause before finishing the full search:

bash
git bisect visualize

This doesn't change the search logic at all — it's purely a way to see the current state of the remaining range, which can be reassuring (or occasionally reveal the answer early) partway through a long bisect session.

Common mistakes

Common mistakes
  • Forgetting git bisect reset after finding the culprit, leaving the repository in a detached HEAD state that causes confusion on the next commit or checkout.
  • Using git bisect run with a flaky test script. An intermittently-failing test can make bisect converge on the wrong commit — for anything flaky, either fix the flakiness first or run the check multiple times per commit inside the script.
  • Testing a commit inconsistently by hand (different browser state, different environment) across manual bisect steps, introducing noise into what should be a deterministic good/bad signal.
  • Assuming the "first bad commit" bisect reports is always the actual root cause. It's the first commit where the symptom appears — for a bug surfaced indirectly by an unrelated earlier change, the real fix might belong in a different, earlier commit than the one bisect points to.

Frequently Asked Questions

DevFieldGuide
DevFieldGuide

Editorial Team

Practical tutorials and developer tools, written and maintained by the DevFieldGuide team.

Enjoyed this article?

Get the next one straight to your inbox, along with the best of what we publish each week.

Related Articles

More in Git

View all