git-bisect
verifiede7e7efca-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 file
# 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.