Skip to content
Home
Git Bisect: Finding the Commit That Introduced a Bug

Git Bisect: Finding the Commit That Introduced a Bug

Git Git 7 min read 1465 words Beginner ExcellentWiki Editorial Team

git bisect performs a binary search through your commit history to find the exact commit that introduced a bug. Instead of manually checking commits one by one, bisect automates the search in O(log n) time — finding a bug among 1024 commits requires only 10 steps. This makes it one of the most efficient debugging tools in a developer’s toolkit.

How Bisect Works

Binary search assumes a linear history where each commit is either “good” (bug-free) or “bad” (buggy), and the bug appeared exactly once. Bisect splits the range in half, checks the midpoint, and narrows the range until it finds the single offending commit.

G — G — G — G — ? — ? — ? — ? — B — B — B — B
├──────────────────────┤
good                 bad
     └── bisect picks midpoint → test → repeat

The efficiency of bisect becomes apparent at scale. For a repository with 10,000 commits between good and bad, bisect finds the culprit in at most 14 steps. Manual searching would require checking hundreds of commits on average.

Basic Usage

Start a bisect session by marking a known good commit and a known bad commit:

git bisect start
git bisect bad HEAD          # current commit has the bug
git bisect good v1.0.0       # last release was clean

Git checks out a commit halfway between good and bad. Test the commit, then tell git the result:

git bisect good               # this commit is clean, bug is later
# or
git bisect bad                # this commit has the bug, bug is earlier

Repeat until git reports the first bad commit:

Bisecting: 0 revisions left to test after this (roughly 10 steps)
[abc1234] Fix typo in login validation

When the session ends, inspect the culprit:

git show abc1234
git log abc1234^..abc1234  # see what changed in that commit

Always end the session when done:

git bisect reset

Failing to run git bisect reset leaves your repository in a detached HEAD state, which can be confusing when you switch to other tasks. If you ever find yourself in a detached HEAD unexpectedly, check whether a bisect session is still active with git bisect log.

Automating Bisect

For bugs that can be detected by a script, automate the entire bisect with git bisect run:

git bisect start HEAD v1.0.0
git bisect run npm test          # run the test suite at each step

The script must exit with code 0 (good) or non-zero (bad). Common automation scripts:

# bisect.sh — run specific tests
#!/bin/bash
npm run build 2>/dev/null || exit 125  # skip if build fails
npx jest tests/login.test.js           # exit code 0 = good, non-zero = bad

Exit code 125 tells git to skip that commit (e.g., it doesn’t compile). Git will try a nearby commit instead.

Example: Finding a Performance Regression

#!/bin/bash
# perf-bisect.sh — detect if a benchmark is 20% slower than baseline
BENCH_RESULT=$(npm run bench | grep "render time" | awk '{print $3}')
BASELINE=100  # milliseconds
if (( $(echo "$BENCH_RESULT > 120" | bc -l) )); then
    exit 1  # bad — too slow
else
    exit 0  # good — acceptable performance
fi

Run it:

git bisect start HEAD v2.0.0
git bisect run bash perf-bisect.sh

Automated bisect is especially powerful for regression testing. Many teams set up nightly bisect runs that automatically detect when a test starts failing and identify the exact commit responsible, sending the result directly to the developer who caused the regression.

Advanced Bisect Techniques

Bisecting with Merge Commits

By default, bisect follows first-parent history, ignoring merge commits. Use --first-parent to avoid checking merge commits:

git bisect start --first-parent HEAD v1.0.0

This is useful when merges are frequent and you want to traverse the mainline only.

Skipping Commits

Some commits can’t be tested — broken builds, WIP, or environment-specific failures. Skip them:

git bisect skip
# or skip a range
git bisect skip abc1234..def5678

When you skip a commit, Git picks a nearby commit instead. If the bug is intermittent, skipping commits that produce ambiguous results prevents bisect from converging on a wrong answer.

Bisect with Multiple Bad Commits

If the bug might have been introduced in multiple steps, use git bisect terms to customize:

git bisect start --term-new fast --term-old slow
git bisect fast HEAD
git bisect slow v1.0.0

This is useful for non-bug scenarios like performance regression hunting. Instead of “good” and “bad”, you use “fast” and “slow” to find where performance degraded.

Visualizing the Bisect Progress

git bisect visualize
# opens gitk showing the remaining range

When Bisect Fails

The Bug Appears and Disappears

If a bug is intermittent, bisect may report the wrong commit. Possible solutions:

  • Use git bisect skip on commits where the test result is ambiguous
  • Run the test multiple times and only report after consistent results
  • Narrow the range manually before starting bisect

The Bug Existed Before the Good Commit

If the “good” commit also has the bug, pick an older good commit. Bisect can’t find a bug that was always present.

Merge Commits Confuse Bisect

When a merge introduces the bug but the true cause is in one parent, bisect may blame the merge itself. Use git bisect --no-checkout to bisect without checking out:

git bisect start --no-checkout HEAD v1.0.0
git bisect run my_test_script.sh

Bisect on Feature Branches

For bugs found on a feature branch:

# Find where this branch diverged from main
MERGE_BASE=$(git merge-base feature/main)
git bisect start HEAD $MERGE_BASE

This checks only commits on the feature branch, not the entire mainline history.

Real-World Bisect Example

Imagine you are on a team of 20 developers and a regression is discovered in production: users cannot complete checkout when using Safari. The bug was not present in last week’s release (v2.3.0) but exists in today’s (v2.4.0). There have been 340 commits between the two releases.

Without bisect, you would manually scan through commits looking for something that could affect Safari rendering. With bisect, you run:

git bisect start v2.4.0 v2.3.0
git bisect run npm run test:e2e -- --browser=safari

After 9 automated steps, bisect reports: abc8901 Fix CSS reset order in checkout form. You open that commit, see the problem immediately — a CSS property that Safari does not support — and revert it. Total time: 15 minutes instead of hours.

Bisect in CI Pipelines

Some teams integrate bisect into their CI pipeline. When a test suite fails on the latest commit, the CI system automatically starts a bisect session to identify the culprit. The result is posted to the pull request that introduced the regression, often before the developer who caused it has even seen the failure notification.

Bisect Best Practices

  • Start with a clear reproduction script — the more reliable your test, the faster bisect converges
  • Use the smallest possible range — Bisect is faster when you provide a tight good/bad range. If you know the bug appeared this week, use commits from Monday and Friday rather than the entire project history
  • Document the bisect sessiongit bisect log saves the session so you can share it with teammates or replay it later
  • Combine with git blame — Once bisect identifies the commit, use git blame on the relevant file to focus on the exact lines that changed

Summary

git bisect turns a tedious manual search into a fast, systematic process. With git bisect run and a reproduction script, you can automatically pinpoint bug-introducing commits across hundreds of changes in minutes. It’s one of the most powerful tools in a developer’s debugging arsenal — easy to learn, hard to live without. Mastering bisect will save you hours of manual investigation and help your team resolve regressions before they reach production.


Related: Git Revert and Reset | Git Advanced Commands

FAQ

Q: How many steps does bisect take for N commits? A: Bisect takes O(log₂ N) steps. For 1000 commits, that is about 10 steps. For 1 million commits, about 20 steps.

Q: What does exit code 125 mean in git bisect run? A: It tells Git to skip that commit entirely (e.g., because the build is broken). Git will test a nearby commit instead.

Q: Can bisect work with non-linear history? A: Yes, but merge commits can confuse it. Use --first-parent to follow the mainline only, or --no-checkout for complex merge-heavy histories.

Q: What happens if I forget to run git bisect reset? A: Your repository stays in detached HEAD state. Run git bisect reset to return to your original branch.

Q: Can I bisect for things other than bugs? A: Yes. Use --term-new and --term-old to rename the search criteria — for example, “fast” and “slow” for performance regression hunting.

Q: How do I save and share a bisect session? A: Use git bisect log > bisect-log.txt to save the session. Share the log with your team or replay it later with git bisect replay bisect-log.txt.

Section: Git 1465 words 7 min read Beginner 756 articles in section Report inaccuracy Back to top