Skip to content
Back to skills

Bisect Regression

ASecurity

Use when a test or behavior that previously worked is now broken and the commit that introduced the regression is unknown — to use `git bisect` to binary-search the commit history and identify the exact commit that caused it.

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 8, 2026
ai-agentspythongoshellbashtestingdebugginggitdocumentation

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add jeffreytse/grimoire-core --skill bisect-regression --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Bisect Regression?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Bisect Regression
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jeffreytse-bisect-regression/badge)](https://www.skillsdirectory.com/skills/jeffreytse-bisect-regression)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: bisect-regression
description: Use when a test or behavior that previously worked is now broken and the commit that introduced the regression is unknown — to use `git bisect` to binary-search the commit history and identify the exact commit that caused it.
source: Git documentation "git-bisect" (git-scm.com/docs/git-bisect); Chacon & Straub "Pro Git" Ch. 7 "Debugging with Git" (git-scm.com/book); Google Engineering Practices internal tooling documentation
tags: [git-bisect, regression, debugging, git, binary-search, commit-history, root-cause]
---

# Bisect Regression

Use `git bisect` to binary-search commit history and identify the exact commit that introduced a regression — reducing a search across thousands of commits to O(log n) steps.

## Why This Is Best Practice

**Adopted by:** `git bisect` is a built-in Git command used by the Linux kernel team (Torvalds designed it for this purpose), Google, Mozilla, and any team using Git for version control; Chacon & Straub's "Pro Git" (the canonical Git reference at git-scm.com) dedicates a section to `git bisect` as the primary tool for locating regressions; the `git bisect run` subcommand enables full automation, making it suitable for CI regression detection
**Impact:** Binary search finds the culprit commit in ⌈log₂(n)⌉ steps — 10 commits to check in a 1,000-commit history, 17 in a 100,000-commit history; without bisect, engineers manually read commit history, which takes O(n) time; `git bisect run` with an automated test script reduces a multi-hour manual investigation to under 5 minutes for most repositories; the Linux kernel team uses automated bisect to find regressions across tens of thousands of commits
**Why best:** Reading commit messages and diffs to find a regression is O(n) and error-prone — commits are often grouped by feature, not by the system component that regressed; `git bisect` bypasses reading and directly identifies the offending commit by testing behavior; the alternative (blaming based on recent changes) misses regressions introduced by seemingly unrelated commits

Sources: Chacon & Straub "Pro Git" 2nd ed. Ch. 7 "Debugging with Git" (git-scm.com/book/en/v2); Git documentation "git-bisect" (git-scm.com/docs/git-bisect); Torvalds on bisect design (lkml.org)

## Steps

### 1. Identify a known-bad and known-good commit

Before starting bisect, establish two reference points:

```bash
# Current HEAD is broken → this is "bad"
# Find a known-good commit: last release tag, a recent passing CI build, or "it worked last week"

git log --oneline -20          # scan recent commits
git tag --sort=-creatordate    # list tags newest-first; recent release tags are good candidates
```

You need:
- **Bad commit:** the current broken state (usually `HEAD`)
- **Good commit:** any commit where the behavior worked (a tag, a SHA, a branch)

The range between good and bad is what bisect will search. Wider range = more steps, but bisect is still fast: 100 commits = 7 steps; 10,000 commits = 14 steps.

### 2. Start bisect

```bash
git bisect start
git bisect bad                          # mark HEAD (current) as bad
git bisect good v2.3.1                  # mark a known-good tag or SHA
```

Git immediately checks out the midpoint commit between good and bad:

```
Bisecting: 47 revisions left to test after this (roughly 6 steps)
[abc1234] Some commit message
```

### 3. Test the checked-out commit

At each midpoint, test whether the behavior is broken:

```bash
# Run your test, build, or manual check
npm test -- --grep "the failing behavior"
# OR
./run-specific-test.sh
# OR manually test the feature
```

Then tell bisect the result:

```bash
git bisect good    # this commit does NOT have the bug
git bisect bad     # this commit DOES have the bug
```

Git checks out the next midpoint. Repeat until bisect outputs:

```
abc1234 is the first bad commit
commit abc1234
Author: Someone <someone@example.com>
Date:   Mon Jan 1 12:00:00 2024

    feat: add new cache invalidation logic
```

### 4. Automate with `git bisect run` (preferred)

If you have an automated test that reliably reproduces the bug, automate the entire process:

```bash
git bisect start
git bisect bad HEAD
git bisect good v2.3.1
git bisect run npm test -- --grep "the failing behavior"
```

`git bisect run` rules:
- Exit code `0` = good (test passes = no bug)
- Exit code `1`–`127` (except `125`) = bad (test fails = bug present)
- Exit code `125` = skip this commit (cannot test — e.g., won't compile)

```bash
# Example: run a single test file
git bisect run python -m pytest tests/test_cache.py::test_invalidation -x

# Example: shell script that builds and tests
git bisect run ./scripts/bisect-test.sh
```

For repositories that sometimes fail to compile at intermediate commits, use `125` skip:

```bash
#!/bin/bash
# bisect-test.sh
make || exit 125          # skip if build fails
./run-test.sh             # 0 = good, non-zero = bad
```

### 5. Inspect the bad commit

Once bisect identifies the first bad commit:

```bash
git show abc1234           # full diff of the offending commit
git log --stat abc1234     # files changed
```

Read the diff with the bug in mind. The regression is in this commit. Common patterns:

- An off-by-one introduced in a refactor
- A condition that was inadvertently inverted
- A caching layer added that broke a side effect the caller depended on
- A dependency version bump that changed behavior

### 6. End the bisect session

```bash
git bisect reset           # returns HEAD to the original branch/commit
```

Always run `git bisect reset` when done — it restores the working tree to its pre-bisect state. Forgetting leaves you on a detached HEAD at the last-tested commit.

### 7. Apply findings

With the offending commit identified:

1. Read the commit message and PR/issue it references
2. Understand the intended change and why it caused the regression
3. Write a regression test that reproduces the bug (`write-regression-test`)
4. Fix the bug — either revert the offending commit or make a targeted correction
5. Reference the bisect-identified commit SHA in the fix commit message: "Fix regression introduced in abc1234"

## Rules

- Always run `git bisect reset` at the end — never leave the repo in detached HEAD state
- The test used in `git bisect run` must be deterministic — flaky tests produce wrong good/bad assignments and lead bisect to wrong commits
- Exit code `125` to skip commits that can't be tested — never force a bad/good verdict on an untestable commit
- The good commit must predate the bad commit — bisect searches forward in time, not backward

## Common Mistakes

- **Forgetting `git bisect reset`**: leaves repository in detached HEAD state; `git status` will show "HEAD detached at abc1234"; fix by running `git bisect reset` or `git checkout main`
- **Using a flaky test**: if the test sometimes passes and sometimes fails independently of the commit, bisect will misclassify commits and converge on the wrong commit; fix or isolate the flakiness before bisecting
- **Good commit too recent**: choosing a good commit that's only 5 commits before bad when the actual regression was introduced 200 commits ago; if bisect finds a "first bad commit" that looks correct and unrelated to the bug, widen the good commit range
- **Not using `git bisect run`**: manually testing 15 commits one by one when an automated test exists; `git bisect run` reduces a 30-minute process to 2 minutes

## When NOT to Use

- When the regression was introduced in the last 1–3 commits — reading the diff directly is faster than the bisect setup overhead; bisect pays off at 5+ commits to search
- When no automated test or reliable manual check exists to determine good/bad — bisect requires a deterministic test; without one, you cannot assign good/bad reliably; write a reproducing test first (`write-regression-test`), then bisect

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…