Stage 2 · Tools
Remote Collaboration
blame & bisect
Find who changed what and binary-search for the commit that broke something.
git blame
git blame annotates each line of a file with the last commit that modified it. This shows who made the change, when, and the commit message. It is the first tool to reach for when you need to understand the history of a specific piece of code.
git blame src/auth/middleware.tsPrints every line of middleware.ts with the commit hash, author, timestamp, and line content. You can trace any line back to its origin.
git blame -L 10,20 src/auth/middleware.tsRestricts blame output to lines 10 through 20. Use this when you only care about a specific function or block, not the entire file.
Use git blame -M to detect when a line was moved from another file. Without -M, blame reports the commit that last touched the line in its current file, even if the content was copied from elsewhere.
Blame Options
Blame has several options that change what information it reports. The most useful ones help you ignore irrelevant changes and focus on meaningful modifications.
git blame -w src/auth/middleware.tsReports the commit that introduced the meaningful content, ignoring whitespace-only changes. A line reformatted by a linter will still show the original author.
git blame -C src/auth/middleware.tsTracks lines across commits to find the original commit that introduced them, even if they were moved by a later commit. Useful for squashed commits where the blame commit is the squash, not the original work.
git blame --porcelain src/auth/middleware.ts | head -20Outputs machine-readable blame data. Each commit is followed by key-value pairs: author, time, summary. Useful for scripting or building custom blame tools.
git blame -L 15,15 src/auth/middleware.tsBlame a single line, then use the commit hash to view the full commit: git show <hash>. This takes you from a line of code to the exact change that introduced it.
git blame -w -L 42,42 file.ts is a powerful one-liner: find who wrote line 42, ignoring formatting changes. This is the fastest way to find the person who understands a piece of code.
git bisect
git bisect performs a binary search through commit history to find the exact commit that introduced a bug. If you know something works at commit A and is broken at commit B, bisect finds the breaking commit in O(log n) steps.
git bisect start
git bisect bad # current commit is broken
git bisect good v1.2.0 # this tag was workingStarts the bisect session. Git checks out a commit halfway between good and bad. You test the code and mark each checkout as good or bad until Git identifies the exact breaking commit.
# Test the current checkout
npm test
# If tests pass:
git bisect good
# If tests fail:
git bisect bad
# Git checks out the next commit to testEach good/bad answer halves the search space. After ~10 steps, you've searched 1000 commits. After ~20 steps, you've searched a million. Git reports the exact commit that introduced the regression.
# When bisect finishes, it prints the bad commit
git bisect resetResets HEAD back to the branch you were on before starting bisect. Always run this when you're done, whether bisect completed or you need to abort.
Bisect checks out arbitrary commits. If you have uncommitted changes, bisect may fail or give incorrect results. Commit or stash everything before starting.
Automated Bisect
Instead of manually testing each commit, you can give bisect a script that exits 0 for good, 1 for bad (or 125 for skip). Git runs the script at each step and automatically narrows down the breaking commit.
git bisect start HEAD v1.0.0
git bisect run npm testRuns npm test at each commit. If the test passes (exit 0), that commit is marked good. If it fails (exit 1), it's marked bad. Git drives the entire process — you just wait for the result.
#!/bin/bash
# test-build.sh - returns 0 if build succeeds
npm run build 2>/dev/null
if [ $? -eq 0 ]; then
exit 0 # good
else
exit 1 # bad
fi
git bisect start HEAD v2.0.0
git bisect run ./test-build.shA custom script can test anything: build success, runtime behavior, performance thresholds. The script must exit with 0 (good), 1 (bad), or 125 (skip) for bisect to work automatically.
git bisect run sh -c "npm install && npm test"Wraps the test in a shell command that installs dependencies first. If npm install fails (exit non-zero, non-1), bisect skips that commit. Useful when some commits have broken lockfiles or missing dependencies.
Exit code 125 tells bisect to skip a commit entirely (e.g., code doesn't compile at that point). This is different from exit 1 (bad). Skipping lets bisect continue past commits that can't be tested.
Real-World Use Cases
Both blame and bisect are practical debugging tools that shine in real production scenarios.
blame use cases:
- A test started failing — blame the failing line to find who changed the logic
- A performance regression — blame the hot path to see what changed
- Security audit — blame sensitive files to review the chain of custody
- Legacy code — find the original author for context on complex logic
bisect use cases:
- A feature stopped working — bisect with a manual smoke test
- CI started failing — bisect with
npm testas the automated test - Performance dropped — bisect with a benchmark script
- A dependency broke — bisect to find when the lockfile changed
git log -S "calculateDiscount" --onelineSearches commit history for changes that add or remove the string calculateDiscount. Combined with blame, this gives you the full story of when and why a function appeared.
Use git blame to find the author and commit that last modified a line. Use git bisect to find the commit that introduced a regression. Together, they answer the two fundamental debugging questions: who wrote this, and when did it break?
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.