Skip to main content
Technology & EngineeringGit Workflow152 lines

Git Bisect Debug

Debugging with git bisect and advanced git log techniques to pinpoint regressions

Quick Summary11 lines
You are an expert in using git bisect and advanced git log techniques to pinpoint regressions and understand code history.

## Key Points

- Write a reproducible test script for `git bisect run` so the search is fully automated and does not require manual testing at each step
- Use `.git-blame-ignore-revs` to filter out bulk formatting commits from blame output, keeping blame useful after large-scale style changes
- Combine `git log -S` (pickaxe) with `--follow` to trace when a specific function or variable was introduced, renamed, or removed
- Forgetting to run `git bisect reset` after finishing, which leaves HEAD in a detached state at an arbitrary commit
- Marking a commit as good or bad incorrectly during manual bisect, which sends the binary search down the wrong path and produces a wrong result
skilldb get git-workflow-skills/Git Bisect DebugFull skill: 152 lines
Paste into your CLAUDE.md or agent config

Git Bisect and Debugging — Git Workflows

You are an expert in using git bisect and advanced git log techniques to pinpoint regressions and understand code history.

Overview

When a bug appears and you do not know which commit introduced it, git bisect performs a binary search through commit history to find the exact culprit. Combined with advanced git log and git blame usage, these tools form a powerful debugging toolkit built into Git itself.

Core Concepts

git bisect — binary search for a bad commit:

# Start bisecting
git bisect start

# Mark the current commit as bad (has the bug)
git bisect bad

# Mark a known good commit (before the bug existed)
git bisect good v1.8.0

# Git checks out a midpoint commit. Test it, then:
git bisect good   # if the bug is NOT present
git bisect bad    # if the bug IS present

# Repeat until Git identifies the first bad commit
# When done, reset to your original HEAD
git bisect reset

Automated bisect with a test script:

# Git runs the script at each step; exit 0 = good, exit 1 = bad
git bisect start HEAD v1.8.0
git bisect run npm test

# Or with a custom script
git bisect run bash -c 'node -e "require(\"./src/math\").add(2,2) === 4 || process.exit(1)"'

Implementation Patterns

Advanced git log for investigation:

# Search commits by message keyword
git log --oneline --grep="payments"

# Search commits that changed a specific string in code (pickaxe)
git log -S "calculateTotal" --oneline

# Search commits that changed a regex pattern
git log -G "def (calculate|compute)" --oneline

# Show commits that touched a specific file
git log --oneline --follow -- src/billing/invoice.ts

# Show commits between two tags
git log v1.8.0..v1.9.0 --oneline --no-merges

# Show a graph of branch topology
git log --oneline --graph --all --decorate

git blame for line-level history:

# Who last changed each line
git blame src/billing/invoice.ts

# Blame a specific line range
git blame -L 40,60 src/billing/invoice.ts

# Ignore whitespace-only changes
git blame -w src/billing/invoice.ts

# Show the commit before the current blame (dig deeper)
git blame --ignore-rev <formatting-commit-hash> src/billing/invoice.ts

# Ignore bulk formatting commits (store hashes in a file)
echo "<hash>" >> .git-blame-ignore-revs
git config blame.ignoreRevsFile .git-blame-ignore-revs

git diff for targeted inspection:

# Diff between two commits for a specific file
git diff v1.8.0..v1.9.0 -- src/billing/invoice.ts

# Show only function-level changes (requires language diff driver)
git diff --function-context HEAD~5..HEAD -- src/billing/invoice.ts

# Word-level diff for prose or config files
git diff --word-diff HEAD~1

Combining bisect with blame:

# 1. Bisect to find the bad commit
git bisect start HEAD v1.8.0
git bisect run npm test
# Result: abc1234 is the first bad commit

# 2. Inspect the commit
git show abc1234 --stat
git show abc1234 -- src/billing/invoice.ts

# 3. Check surrounding history
git log --oneline abc1234~5..abc1234

Core Philosophy

Git bisect transforms the question "which of 500 commits broke this?" from an hours-long manual investigation into a logarithmic binary search that finds the answer in nine steps. The key insight is that debugging regressions is fundamentally a search problem, and binary search is optimal for ordered sequences. Your commit history is an ordered sequence where some prefix of commits is "good" and some suffix is "bad." Bisect exploits this structure to find the boundary in O(log n) time. On a repository with 1,000 commits between the known-good and known-bad points, bisect needs at most 10 checks instead of 1,000.

The power of git bisect run — automated bisecting with a test script — cannot be overstated. Manual bisecting requires a human to test each midpoint commit, which is slow, error-prone, and impossible outside business hours. An automated bisect script runs the entire search unattended and produces the exact commit that introduced the regression. Writing the test script is the hard part, but it forces you to define the regression precisely: not "the page looks wrong" but "this function returns 5 instead of 4." That precision is itself a debugging aid because it often clarifies exactly what broke and why.

Git's history investigation tools (bisect, log -S, blame, diff) form a complete forensic toolkit when combined. Bisect finds which commit introduced a change. git log -S (the pickaxe) finds commits that added or removed a specific string. git blame shows who last modified each line and why. git log --follow tracks a file through renames. Each tool answers a different question, and real debugging often chains them: bisect to find the commit, show to see the diff, blame to understand the surrounding context, log -S to find related changes elsewhere.

Anti-Patterns

  • Manual bisecting without a script. Manually checking out each midpoint, running tests, and marking good/bad is slow and error-prone. A single incorrect marking sends the binary search down the wrong path and produces a wrong result. Invest the time to write a git bisect run script that automates the entire search.

  • Forgetting git bisect reset. Finishing a bisect investigation but not running git bisect reset leaves HEAD in a detached state at an arbitrary commit. Any subsequent work happens on a detached HEAD, which is confusing and risks losing commits. Always reset when done.

  • Bisecting across non-buildable commits. If some commits in the range fail to build (broken intermediate states, merge conflicts), bisect marks them as bad even though the bug is not present. Use git bisect skip for commits that cannot be tested, or write a run script that exits with code 125 (skip) for build failures.

  • Using blame without .git-blame-ignore-revs. After a large-scale formatting change (Prettier, Black, gofmt), every line shows the formatting commit as the last author, rendering blame useless. Configure .git-blame-ignore-revs with bulk formatting commit hashes to see through to the meaningful changes.

  • Searching commit messages instead of diffs. Using git log --grep searches commit messages, which only works if the author described the change well. Using git log -S "functionName" searches the actual diff content, finding commits that added or removed the string regardless of the commit message quality.

Best Practices

  • Write a reproducible test script for git bisect run so the search is fully automated and does not require manual testing at each step
  • Use .git-blame-ignore-revs to filter out bulk formatting commits from blame output, keeping blame useful after large-scale style changes
  • Combine git log -S (pickaxe) with --follow to trace when a specific function or variable was introduced, renamed, or removed

Common Pitfalls

  • Forgetting to run git bisect reset after finishing, which leaves HEAD in a detached state at an arbitrary commit
  • Marking a commit as good or bad incorrectly during manual bisect, which sends the binary search down the wrong path and produces a wrong result

Install this skill directly: skilldb add git-workflow-skills

Get CLI access →