Skip to main content

git bisect โ€” Finding the Broken Commit

What It Doesโ€‹

git bisect performs a binary search through commit history to efficiently find the exact commit that introduced a bug. Instead of checking commits one by one (which could take hours on a large repo), bisect narrows the search logarithmically โ€” finding the culprit in logโ‚‚(N) steps.

For 1000 commits, bisect finds the bad commit in at most 10 steps.


Basic Manual Bisectโ€‹

Step 1 โ€” Start bisect and mark the boundariesโ€‹

git bisect start

# Mark the current state as "bad" (the bug is present here)
git bisect bad HEAD

# Mark a known good commit (the bug was not present here)
git bisect good v1.1.0
# or by SHA:
git bisect good a3f9bc2

Git checks out the commit at the midpoint of the range.

Step 2 โ€” Test and mark each commitโ€‹

After each checkout, test whether the bug is present:

# If the bug IS present at this commit:
git bisect bad

# If the bug is NOT present at this commit:
git bisect good

Git narrows the range with each answer and checks out the next midpoint.

Step 3 โ€” Git identifies the culpritโ€‹

After enough answers, Git outputs:

b7e2a1f is the first bad commit
commit b7e2a1f
Author: Bob Johnson <[email protected]>
Date: Mon Jan 15 14:22:10 2024

refactor(transactions): extract date range logic to utility class

src/main/java/com/example/util/DateRangeUtils.java | 42 +++++
1 file changed, 42 insertions(+)

Step 4 โ€” End bisect and return to your branchโ€‹

git bisect reset
# HEAD is now back on your original branch

Automated Bisect with a Scriptโ€‹

The real power of bisect is automation. Provide a script that returns exit code 0 for "good" and non-zero for "bad" โ€” Git will run the entire search unattended.

Example: bisect using a Maven testโ€‹

git bisect start
git bisect bad HEAD
git bisect good v1.1.0

# Run a specific test โ€” pass/fail determines good/bad automatically
git bisect run ./mvnw -q test \
-Dtest="TransactionServiceTest#findTransactions_dateRangeFilter_returnsCorrectResults"

Git runs your test at each midpoint commit, automatically marking good/bad based on the exit code, and delivers the result without any manual input.

Example: bisect using a shell scriptโ€‹

#!/bin/bash
# scripts/bisect-check.sh

# Build silently
./mvnw -q package -DskipTests 2>/dev/null || exit 125
# Exit 125 = "skip this commit" (e.g., build is broken โ€” not the bug we're hunting)

# Run the specific test
./mvnw -q test -Dtest="TransactionServiceTest#findTransactions_emptyResult"
git bisect run bash scripts/bisect-check.sh
Exit Code 125

If a commit can't be tested (e.g., compile error unrelated to the bug), exit with 125. Git will skip that commit and move to the next one.


Bisect with Terms (Custom Good/Bad Labels)โ€‹

For regressions that are not clearly "good/bad" (e.g., performance changes):

git bisect start --term-old fast --term-new slow
git bisect slow HEAD # current is slow
git bisect fast v1.1.0 # v1.1.0 was fast
# Git now prompts "is this commit fast or slow?"
git bisect fast # this commit is fast
git bisect slow # this commit is slow

Bisect Log and Replayโ€‹

Save the bisect session to replay later:

git bisect log > bisect-session.log

# Replay a saved session
git bisect reset
git bisect replay bisect-session.log

Tips for Effective Bisectโ€‹

TipDetail
Write a reproducible test firstAutomate with bisect run for speed and accuracy
Use tags as boundariesgit bisect good v1.0.0 โ€” release tags are reliable known-good points
Skip broken buildsExit 125 from your script to skip non-compilable commits
Check reflog aftergit reflog shows all commits visited during bisect
Once found, git showgit show <bad-commit> shows the exact diff that introduced the bug

Bisect Saves Hours

On a repo with 500 commits between the last known-good release and HEAD, manually checking each commit would take all day. git bisect run with an automated test finds the exact culprit in under 10 steps โ€” usually in a few minutes. Write the test first.

Interview Questions (Senior Level)โ€‹

  1. How do you prepare deterministic bisect scripts for flaky test environments?
  2. When should commits be skipped with exit code 125, and why?
  3. How do you bisect effectively across refactors and intermittent build failures?
  4. What post-bisect steps ensure the root cause is fully validated?

Short answer guide:

  • Use stable repro tests and isolate nondeterministic dependencies.
  • Skip only untestable commits to preserve binary-search integrity.
  • Combine manual checkpoints with automated bisect runs.
  • Confirm fix with forward/backward validation and regression tests.
Interview Focus

Show how git bisect run shortens mean-time-to-root-cause during regressions.

Interview Trap

Starting bisect without a deterministic reproduction or clear good/bad boundary.