Git Bisect Debug
Debugging with git bisect and advanced git log techniques to pinpoint regressions
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 linesGit 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 runscript that automates the entire search. -
Forgetting
git bisect reset. Finishing a bisect investigation but not runninggit bisect resetleaves 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 skipfor 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-revswith bulk formatting commit hashes to see through to the meaningful changes. -
Searching commit messages instead of diffs. Using
git log --grepsearches commit messages, which only works if the author described the change well. Usinggit 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 runso the search is fully automated and does not require manual testing at each step - Use
.git-blame-ignore-revsto filter out bulk formatting commits from blame output, keeping blame useful after large-scale style changes - Combine
git log -S(pickaxe) with--followto trace when a specific function or variable was introduced, renamed, or removed
Common Pitfalls
- Forgetting to run
git bisect resetafter 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
Related Skills
Code Review
Code review best practices for constructive, efficient pull request reviews
Conventional Commits
Conventional Commits specification for structured, machine-readable commit messages
Git Hooks
Git hooks automation with Husky and lint-staged for pre-commit quality gates
Gitflow
Gitflow branching model for structured release-oriented development workflows
Monorepo Management
Monorepo strategies with Nx and Turborepo for scalable multi-project repositories
Release Management
Release management and tagging strategies for predictable, automated software releases