TechLead
Lesson 9 of 9
5 min read
Advanced Git

Git Bisect for Debugging

Use binary search to efficiently find the commit that introduced a bug.

Git Bisect

Git bisect uses binary search to find the commit that introduced a bug. It's incredibly efficient, testing log₂(n) commits instead of n.

Basic Bisect Workflow

# Start bisect
git bisect start

# Mark current (buggy) commit as bad
git bisect bad

# Mark a known good commit
git bisect good v1.0.0
# Or: git bisect good abc1234

# Git checks out middle commit
# Test the code, then mark:
git bisect good  # If bug not present
git bisect bad   # If bug is present

# Repeat until found
# Bisecting: 3 revisions left to test
# [abc1234] First bad commit

# End bisect
git bisect reset

Bisect with Commit Range

# Start with known range
git bisect start HEAD v1.0.0

# Or specify bad and good in start
git bisect start bad-commit good-commit

# View current bisect state
git bisect log

Automated Bisect

# Run a script to test each commit
git bisect start HEAD v1.0.0
git bisect run npm test

# Script should exit:
# 0 = good (test passes)
# 1-124, 126-127 = bad (test fails)
# 125 = skip (can't test this commit)

# Custom test script
git bisect run ./test-for-bug.sh

Test Script Example

#!/bin/bash
# test-for-bug.sh

# Build the project
npm run build || exit 125  # Skip if build fails

# Run specific test
npm test -- --grep "login feature" || exit 1

# Or check for specific behavior
if grep -r "buggy_pattern" src/; then
  exit 1  # Bad
fi

exit 0  # Good

Skipping Commits

# Skip current commit (can't test)
git bisect skip

# Skip a range
git bisect skip v1.0..v1.1

# Skip commits matching pattern
git bisect skip $(git rev-list --grep="WIP" HEAD)

Bisect with Terms

# Use custom terms instead of good/bad
git bisect start --term-new=fixed --term-old=broken

# Mark commits with custom terms
git bisect fixed
git bisect broken

# Useful for finding fix instead of bug:
git bisect start --term-new=fixed --term-old=unfixed

Viewing Bisect Progress

# Show bisect log
git bisect log

# Save log for replay
git bisect log > bisect.log

# Replay bisect session
git bisect replay bisect.log

# Visualize bisect
git bisect visualize
# Or: gitk (GUI)

Bisect with Paths

# Only bisect commits touching specific paths
git bisect start -- src/auth/

# Limit to specific file
git bisect start -- src/components/Login.jsx

Complex Bisect Example

# Find when performance degraded
git bisect start HEAD v1.0.0

# Test script for performance
cat > perf-test.sh << 'EOF'
#!/bin/bash
npm run build 2>/dev/null || exit 125

# Run benchmark
result=$(npm run benchmark --silent | grep "ops/sec" | awk '{print $1}')

# Bad if performance below threshold
if (( $(echo "$result < 1000" | bc -l) )); then
  exit 1
fi
exit 0
EOF

chmod +x perf-test.sh
git bisect run ./perf-test.sh

Bisect for Regression

# Find when feature stopped working
git bisect start

# Current: feature broken
git bisect bad HEAD

# Last known working
git bisect good v2.0.0

# Automated test
git bisect run sh -c '
  npm install --silent 2>/dev/null
  npm run build --silent 2>/dev/null || exit 125
  npm test -- --grep "specific feature" --silent
'

After Finding the Bug

# Bisect found: abc1234 is first bad commit

# View the problematic commit
git show abc1234

# See what changed
git diff abc1234^..abc1234

# View blame for specific file
git blame -L 10,20 src/file.js abc1234

# End bisect and return to branch
git bisect reset

# Create fix
git checkout -b fix-regression
# Make changes...
git commit -m "Fix regression introduced in abc1234"

Tips for Effective Bisect

  • Have a reliable, automated test
  • Use skip for broken/untestable commits
  • Narrow down with path limiting
  • Save bisect logs for documentation
  • Consider bisect run for complex tests
  • Remember to bisect reset when done

Efficiency

# Commits to test with bisect:
100 commits  → ~7 tests (log₂100)
1000 commits → ~10 tests
10000 commits → ~14 tests

# Much faster than linear search!

Continue Learning