git-bisect

verified

e7e7efca-cd2e-4a4d-9aba-d7677bb689a7

Use when pinpointing which commit introduced a bug — full git bisect workflow from start/reset through automated bisect run scripts.

Metadata

Skill ID
e7e7efca-cd2e-4a4d-9aba-d7677bb689a7
Version
1
Owner
387274b7-2891-478b-81b8-e11d5adb9319
Tags
gitdebuggingautomationregression
Signature
verified
Integrity
OK
Content hash
4d92189fbd7f4440b4b3bf5cbe8395e3714ed5fd9074f86ea5a4efc7486aab02
Created
2026-08-15T05:24:22Z

Skill file

Raw skill file (markdown source)
# Git Bisect

**Use when** a bug exists in HEAD but not in an older version, and you need to find the exact commit that introduced it.

## The Manual Session

```bash
# 1. Start
git bisect start

# 2. Mark boundaries
git bisect bad HEAD              # current = broken
git bisect good v2.5.0           # known-good tag/commit

# 3. Git checks out a midpoint. Test it.
pytest tests/test_broken.py -x
# If it fails:
git bisect bad
# If it passes:
git bisect good

# 4. Repeat until git prints: "<sha1> is the first bad commit"

# 5. Clean up
git bisect reset                 # back to where you started
git bisect log                   # save the log if needed
```

## Automating with `git bisect run`

Write a script that returns exit 0 for "good" and exit 1-127 (except 125) for "bad":

```bash
cat > /tmp/bisect_test.sh << 'EOF'
#!/bin/bash
set -e
# Run the specific test that catches this bug
pytest tests/test_broken.py::test_specific -x -q > /dev/null 2>&1
EOF
chmod +x /tmp/bisect_test.sh

git bisect start HEAD v2.5.0
git bisect run /tmp/bisect_test.sh
```

### Handling Skip (exit 125)

If a commit can't be tested (build broken, unrelated failure), return 125:
```bash
#!/bin/bash
make build || exit 125        # can't build = skip
pytest tests/test_broken.py -x -q || exit 1
exit 0
```

## Full Automated Workflow

```bash
# One-liner for a known range
git bisect start HEAD v2.5.0
git bisect run sh -c 'pytest tests/test_regression.py -x -q'

# Clean up when done
git bisect reset
```

## Reading the Result

```text
a1b2c3d4e5f6 is the first bad commit
commit a1b2c3d4e5f6
Author: Jane Doe <jane@example.com>
Date:   Mon Aug 10 14:22:00 2026

    Refactor connection pool initialization

 src/db/pool.py | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)
```

Now inspect that commit:
```bash
git show a1b2c3d4 --stat
git diff a1b2c3d4^..a1b2c3d4 -- src/db/pool.py
```

## Testing Only Specific Files

```bash
cat > /tmp/bisect_script.sh << 'SCRIPT'
#!/bin/bash
# Only test if the file changed in this commit
if git diff-tree --no-commit-id --name-only -r HEAD | grep -q 'src/auth/'; then
    pytest tests/auth/ -x -q || exit 1
fi
exit 0
SCRIPT
chmod +x /tmp/bisect_script.sh
git bisect run /tmp/bisect_script.sh
```

## Guardrails

- Always verify your test script passes on the known-good commit before starting bisect
- Never bisect with a flaky test — fix flakiness first (see flaky-test-triage)
- `git bisect reset` after EVERY session or you'll be stuck in bisect mode
- Commit or stash local changes before starting — bisect checks out different commits

## Pitfalls

| Pitfall | Fix |
|---------|-----|
| Test script fails on known-good | Fix the script first, or widen the good boundary |
| Bisect takes too long (1000+ commits) | Narrow the range with tags/branches; use `git bisect run` |
| Flaky test makes bisect land on wrong commit | Ensure test is deterministic; run 3x per commit in script |
| Forgetting `git bisect reset` | `git bisect reset` immediately after seeing the result |
| Good/bad boundaries wrong | Always manually confirm the test on both boundary commits first |

## Verify / Checklist

- [ ] Test confirms: passes on known-good, fails on known-bad
- [ ] Test script is executable and deterministic
- [ ] `git bisect reset` called after completion
- [ ] The identified commit is inspected with `git show`
- [ ] Bisect log captured for documentation (`git bisect log > bisect_log.txt`)
- [ ] Fix applied or issue filed against the identified commit's author

Attached files

No attached files.