422 lines
10 KiB
Markdown
422 lines
10 KiB
Markdown
---
|
|
name: git:bisect
|
|
description: Interactive git bisect workflow to find commits that introduced bugs using binary search
|
|
---
|
|
|
|
# Git Bisect - Binary Search for Bug-Introducing Commits
|
|
|
|
Interactive git bisect workflow to find commits that introduced bugs using binary search.
|
|
|
|
## Command
|
|
|
|
`/git:bisect [mode] [commit-ref]`
|
|
|
|
## Arguments
|
|
|
|
- `$1`: mode - `start|good|bad|skip|reset` (optional, interactive if not provided)
|
|
- `$2`: commit-ref (optional, depends on mode)
|
|
|
|
## Description
|
|
|
|
Git bisect uses binary search to efficiently find the commit that introduced a bug. Instead of checking every commit, it splits the commit range in half repeatedly, asking you to test each midpoint. This can find a bug in log(n) checks instead of n checks.
|
|
|
|
## Workflow
|
|
|
|
### Starting a Bisect Session
|
|
|
|
1. **Pre-flight checks:**
|
|
- Check if bisect is already in progress: `git bisect log`
|
|
- Check for uncommitted changes: `git status --porcelain`
|
|
- If changes exist, warn user and suggest stashing
|
|
|
|
2. **Gather information:**
|
|
- Ask for the "good" commit (where bug didn't exist)
|
|
- Default: last tag or commit from 1 week ago
|
|
- User can specify: commit hash, tag, branch name
|
|
- Ask for the "bad" commit (where bug exists)
|
|
- Default: HEAD (current commit)
|
|
- User can specify: commit hash, tag, branch name
|
|
- Calculate and show number of commits between good and bad
|
|
- Estimate number of steps needed: ~log2(n)
|
|
|
|
3. **Start bisect:**
|
|
```bash
|
|
git bisect start
|
|
git bisect bad [bad-commit]
|
|
git bisect good [good-commit]
|
|
```
|
|
|
|
4. **Show first commit to test:**
|
|
- Display commit hash, date, author, message
|
|
- Show which files changed: `git show --stat <commit>`
|
|
- Provide clear instructions on what to test
|
|
|
|
### During Bisect Session
|
|
|
|
5. **Test current commit:**
|
|
- Ask user to test whether the bug exists
|
|
- Provide clear instructions:
|
|
- "Run your tests"
|
|
- "Manually test the feature"
|
|
- "Check the logs"
|
|
- Wait for user feedback
|
|
|
|
6. **Mark commit based on test result:**
|
|
- If bug exists: `git bisect bad`
|
|
- If bug doesn't exist: `git bisect good`
|
|
- If commit is untestable (won't build, etc): `git bisect skip`
|
|
- Option to abort: `git bisect reset`
|
|
|
|
7. **Show progress:**
|
|
- Display remaining commits to test
|
|
- Show estimated steps left
|
|
- Display bisect log: `git bisect log`
|
|
|
|
8. **Repeat steps 5-7** until the first bad commit is found
|
|
|
|
### Completion
|
|
|
|
9. **When bug commit identified:**
|
|
- Display full commit details:
|
|
```bash
|
|
git show <commit-hash>
|
|
```
|
|
- Show commit message, author, date
|
|
- Show full diff
|
|
- Highlight which files were changed
|
|
|
|
10. **Offer actions:**
|
|
- Create a branch from this commit for investigation
|
|
- Create a branch from parent commit for fix
|
|
- Copy commit hash to clipboard
|
|
- View commit in GitHub/GitLab (if remote exists)
|
|
- Reset bisect and return to original HEAD
|
|
|
|
11. **Reset bisect:**
|
|
```bash
|
|
git bisect reset
|
|
```
|
|
- Return to original branch/commit
|
|
- Confirm user is back to starting state
|
|
|
|
### Abort/Reset Anytime
|
|
|
|
At any point during the bisect, user can:
|
|
- Type 'abort' to run `git bisect reset`
|
|
- Type 'skip' to skip current commit
|
|
- Type 'log' to see bisect progress
|
|
- Type 'visualize' to see bisect state graphically
|
|
|
|
## Safety Checks
|
|
|
|
### Before Starting
|
|
|
|
- **Uncommitted changes:**
|
|
```bash
|
|
if [ -n "$(git status --porcelain)" ]; then
|
|
echo "Warning: You have uncommitted changes."
|
|
echo "Options:"
|
|
echo " 1. Stash changes: git stash save 'Pre-bisect stash'"
|
|
echo " 2. Commit changes"
|
|
echo " 3. Discard changes (dangerous)"
|
|
echo " 4. Cancel bisect"
|
|
# Wait for user choice
|
|
fi
|
|
```
|
|
|
|
- **Already in bisect:**
|
|
```bash
|
|
if [ -f .git/BISECT_LOG ]; then
|
|
echo "Bisect already in progress!"
|
|
echo "Options:"
|
|
echo " 1. Continue current bisect"
|
|
echo " 2. Reset and start new bisect"
|
|
echo " 3. Cancel"
|
|
# Wait for user choice
|
|
fi
|
|
```
|
|
|
|
- **Detached HEAD warning:**
|
|
- Explain that bisect will put repo in detached HEAD state
|
|
- Assure user that `git bisect reset` will return to original state
|
|
|
|
### During Bisect
|
|
|
|
- **Clear instructions at each step:**
|
|
- Show current commit details
|
|
- Explain what user needs to test
|
|
- Show available commands (good/bad/skip/reset)
|
|
- Display progress and remaining steps
|
|
|
|
- **Skip commit handling:**
|
|
- Explain when to skip (build failures, unrelated changes)
|
|
- Warn that excessive skipping reduces effectiveness
|
|
- Show how many commits have been skipped
|
|
|
|
### After Bisect
|
|
|
|
- **Confirmation before reset:**
|
|
- Ask if user wants to save any notes
|
|
- Offer to create branch at bug commit
|
|
- Confirm reset operation
|
|
|
|
- **Verify return state:**
|
|
- Check user is back on original branch
|
|
- Verify working directory is clean
|
|
- Confirm bisect refs are removed
|
|
|
|
## Error Handling
|
|
|
|
### Invalid Commit References
|
|
|
|
```bash
|
|
if ! git rev-parse --verify "$commit_ref" >/dev/null 2>&1; then
|
|
echo "Error: '$commit_ref' is not a valid commit reference"
|
|
echo "Valid formats:"
|
|
echo " - Commit hash: abc1234 or abc1234567890abcdef"
|
|
echo " - Branch name: main, feature-branch"
|
|
echo " - Tag: v1.0.0"
|
|
echo " - Relative: HEAD~5, HEAD^^^"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
### Good Commit is Newer Than Bad Commit
|
|
|
|
```bash
|
|
if [ $(git rev-list --count $good_commit..$bad_commit) -eq 0 ]; then
|
|
echo "Error: Good commit ($good_commit) is not an ancestor of bad commit ($bad_commit)"
|
|
echo "Make sure:"
|
|
echo " - Good commit is older (bug didn't exist yet)"
|
|
echo " - Bad commit is newer (bug exists)"
|
|
echo " - Both commits are on the same branch history"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
### Build Failures
|
|
|
|
```bash
|
|
# When user reports commit won't build
|
|
echo "This commit won't build/test. Options:"
|
|
echo " 1. Skip this commit: git bisect skip"
|
|
echo " 2. Skip a range: git bisect skip v1.0..v1.5"
|
|
echo " 3. Reset and try different good/bad commits"
|
|
# Wait for user choice
|
|
```
|
|
|
|
### Conflicts or Issues
|
|
|
|
```bash
|
|
# If checkout fails during bisect
|
|
if [ $? -ne 0 ]; then
|
|
echo "Error: Could not checkout commit"
|
|
echo "This might indicate:"
|
|
echo " - Local modifications conflict with commit"
|
|
echo " - Repository corruption"
|
|
echo "Run 'git bisect reset' to abort and investigate"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Example 1: Basic Interactive Bisect
|
|
|
|
```bash
|
|
# Start interactive bisect
|
|
/git:bisect
|
|
|
|
# Claude will prompt:
|
|
# "When did you last see the code working correctly?"
|
|
# User: "On the v1.2.0 tag"
|
|
#
|
|
# "When did you first notice the bug?"
|
|
# User: "In the current commit (HEAD)"
|
|
#
|
|
# Starting bisect with 47 commits between v1.2.0 and HEAD
|
|
# This will take approximately 6 steps
|
|
#
|
|
# Now at: commit abc1234
|
|
# Author: John Doe
|
|
# Date: 2025-10-15
|
|
# Message: Refactor authentication module
|
|
#
|
|
# Files changed:
|
|
# src/auth.js | 45 +++++++++++++++++++++
|
|
# tests/auth.test.js | 23 +++++++++++
|
|
#
|
|
# Please test if the bug exists in this commit.
|
|
# Reply with: good, bad, skip, or abort
|
|
```
|
|
|
|
### Example 2: Direct Mode Commands
|
|
|
|
```bash
|
|
# Start bisect directly
|
|
/git:bisect start
|
|
|
|
# Mark HEAD as bad
|
|
/git:bisect bad
|
|
|
|
# Mark a specific commit as good
|
|
/git:bisect good v1.2.0
|
|
|
|
# Skip untestable commit
|
|
/git:bisect skip
|
|
|
|
# View bisect log
|
|
git bisect log
|
|
|
|
# Reset and return to original state
|
|
/git:bisect reset
|
|
```
|
|
|
|
### Example 3: Automated Testing
|
|
|
|
```bash
|
|
# For repositories with automated tests
|
|
/git:bisect
|
|
|
|
# At each commit, run:
|
|
npm test # or: pytest, cargo test, go test, etc.
|
|
|
|
# If tests pass:
|
|
/git:bisect good
|
|
|
|
# If tests fail:
|
|
/git:bisect bad
|
|
|
|
# Continue until bug commit found
|
|
```
|
|
|
|
### Example 4: Binary Search Range
|
|
|
|
```bash
|
|
# Search within specific range
|
|
/git:bisect start HEAD v1.0.0
|
|
|
|
# Or use commit hashes
|
|
/git:bisect start abc1234 def5678
|
|
|
|
# Or use date-based references
|
|
/git:bisect start HEAD HEAD@{2.weeks.ago}
|
|
```
|
|
|
|
## Advanced Usage
|
|
|
|
### Bisect with Script
|
|
|
|
For fully automated bisecting when you have a test that can determine good/bad:
|
|
|
|
```bash
|
|
# Create a test script that exits 0 for good, 1 for bad
|
|
cat > test-bug.sh << 'EOF'
|
|
#!/bin/bash
|
|
npm test -- --testNamePattern="specific bug test"
|
|
EOF
|
|
chmod +x test-bug.sh
|
|
|
|
# Run bisect with script
|
|
git bisect start HEAD v1.0.0
|
|
git bisect run ./test-bug.sh
|
|
```
|
|
|
|
### Visualize Bisect State
|
|
|
|
```bash
|
|
# Text-based visualization
|
|
git bisect visualize --oneline
|
|
|
|
# Or with gitk (if available)
|
|
git bisect visualize
|
|
```
|
|
|
|
### Skip a Range of Commits
|
|
|
|
```bash
|
|
# If you know commits in a range are all untestable
|
|
git bisect skip v1.1.0..v1.2.0
|
|
```
|
|
|
|
### Bisect Log Analysis
|
|
|
|
```bash
|
|
# View complete bisect log
|
|
git bisect log
|
|
|
|
# Replay bisect from log
|
|
git bisect replay path/to/bisect-log.txt
|
|
```
|
|
|
|
## Tips for Effective Bisecting
|
|
|
|
1. **Choose good starting points:**
|
|
- Good commit: Use a tagged release where you know code worked
|
|
- Bad commit: Use the commit where you first noticed the bug
|
|
- Wider range = more accurate, but more steps
|
|
|
|
2. **Have a clear test:**
|
|
- Know exactly what behavior indicates the bug
|
|
- Have a reproducible test case
|
|
- Ideally have an automated test
|
|
|
|
3. **Handle build failures:**
|
|
- Skip commits that won't build
|
|
- If too many skip, widen the initial range
|
|
|
|
4. **Track progress:**
|
|
- Note down commit hashes you've tested
|
|
- Keep test results consistent
|
|
- Don't change test criteria mid-bisect
|
|
|
|
5. **When bug commit found:**
|
|
- Verify it's truly the first bad commit
|
|
- Check if bug is in the commit itself or its merge
|
|
- Look at what changed in that commit
|
|
|
|
## Output Format
|
|
|
|
During bisect, Claude will display structured output:
|
|
|
|
```
|
|
========================================
|
|
Git Bisect Status
|
|
========================================
|
|
Range: v1.2.0 (good) → HEAD (bad)
|
|
Total commits: 47
|
|
Estimated steps: 6 remaining
|
|
|
|
Current Commit:
|
|
----------------------------------------
|
|
Hash: abc1234567890abcdef
|
|
Author: Jane Smith <jane@example.com>
|
|
Date: Mon Oct 16 14:32:10 2025 -0700
|
|
Message: Update user authentication flow
|
|
|
|
Files Changed (5):
|
|
src/auth/login.js | 34 +++++++++++++++---
|
|
src/auth/session.js | 12 +++++--
|
|
tests/auth/login.test.js| 45 ++++++++++++++++++++++++
|
|
...
|
|
|
|
========================================
|
|
Action Required:
|
|
----------------------------------------
|
|
1. Test if bug exists in this commit
|
|
2. Reply with result:
|
|
- 'good' - bug does NOT exist
|
|
- 'bad' - bug DOES exist
|
|
- 'skip' - cannot test this commit
|
|
- 'abort' - stop bisect
|
|
|
|
What's your result?
|
|
```
|
|
|
|
## Related Commands
|
|
|
|
- `/git:reflog-recover` - Recover from bisect mistakes
|
|
- `/git:cherry-pick-helper` - Cherry-pick the bug fix
|
|
- `/git:worktree` - Test multiple commits simultaneously
|
|
- `/git:branch-cleanup` - Clean up bisect test branches
|