<aside> 🔀
Module 13 — Searching and Debugging History (git blame, bisect, grep). A repository's history is a crime-scene database, and this module is the forensics kit. git blame tells you who last touched a line and why; git bisect binary-searches thousands of commits to find which one introduced a bug; the pickaxe (log -S/-G) hunts for when a string appeared or vanished; git grep searches the code itself, at any point in time. These are the tools that turn "when did this break?" from an afternoon into a minute.
🧠 concept → 🧩 analogy → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (hidden)
</aside>
<aside> 🚦
Before you start. You need Module 4 (log, revision syntax, ranges), Module 5 (detached HEAD — bisect uses it), and Module 3 (commits as snapshots). The exercises build a small repo with a planted bug — a converter whose constant gets silently changed — so every tool has a real culprit to find. bc is used in one example; any Linux/macOS shell has it.
</aside>
<aside> 📖
Official docs: git-blame manual · Git Book §7.10 — Debugging with Git
</aside>
git blame <file> annotates every line with the commit that last changed it, plus that commit's author and date. It answers "who wrote this line, and in which commit?" — the starting move of almost every debugging session, because the commit it names is where you go to learn why the line is the way it is (its message, its diff, its author to ask). git blame -L <start>,<end> <file> restricts the output to a line range — essential on large files where you only care about the function that's misbehaving.
The exercises use a converter script with a bug planted in its history: someone changed a density constant from 120 to 100, breaking every conversion. Blame is how you find the hand on the knife.
🧪 Exercise 13.1 — set up the scene, then blame the file
mkdir -p ~/git-course && cd ~/git-course
git init -q kitchen && cd kitchen
cat > convert.sh <<'EOF'
#!/bin/bash
# convert grams to cups (flour)
grams_to_cups() {
echo "scale=2; $1 / 120" | bc
}
EOF
git add convert.sh && git commit -q -m "Add grams_to_cups converter" --author="Ben <[email protected]>"
echo "# added usage note" >> convert.sh && git commit -qa -m "Document converter usage"
echo "# tweak comment" >> convert.sh && git commit -qa -m "Clarify comment"
sed -i 's|/ 120|/ 100|' convert.sh && git commit -qa -m "Adjust flour density constant" # THE BUG
echo "# more docs" >> convert.sh && git commit -qa -m "Expand docs"
echo 'extra() { echo hi; }' >> convert.sh && git commit -qa -m "Add helper"
git blame -L 3,5 convert.sh # blame just the function's lines
<aside> 🔍
Real-world analogy — the margin notes in a shared textbook
git blame is a textbook where every sentence has a tiny margin note: "revised by Aisha, March, see revision #47." You don't read the whole book to find who rewrote the definition on page 30 — you glance at the margin of that one line. The note points you to the full revision, where the reasoning lives.
Where the analogy stops working. Margin notes credit whoever touched the line, and so does blame — which is exactly its blind spot. If someone reformatted the whole file (re-indented, moved a block), blame credits them for every line, burying the person who wrote the actual logic. The bug's real author hides behind a later cosmetic change. That's why "blame" is a misleading name — it finds the last editor, not the responsible author — and why the pros reach for blame -w (ignore whitespace) and, when that's not enough, the pickaxe (Part B), which follows the content rather than the line.
</aside>
<aside> 📖
Official docs: git-log manual (pickaxe) · Git Book §7.10 — Debugging with Git
</aside>
Blame finds the last change to a line; the pickaxe finds every commit that changed how many times a string appears anywhere in history. git log -S "<string>" lists commits where the count of that string went up or down — i.e. where it was added or removed. This is the tool for "when did this config key first appear?", "which commit deleted that function?", or "who introduced this magic number?" — questions blame can't answer because the code may have been added, deleted, and re-added across many lines. git log -G "<regex>" is the broader cousin: it matches commits whose diff contains a line matching the regex (any change touching such a line), where -S only fires when the number of occurrences changes. Add -- <path> to scope to a file, and -p to see the actual diffs.
🧪 Exercise 13.2 — pickaxe the density constant
cd ~/git-course/kitchen
git log -S "120" --oneline -- convert.sh # commits that added/removed "120"
git log -S "/ 100" --oneline -- convert.sh # commits that added/removed the bad value
git log -G "/ 1[0-9]0" --oneline -- convert.sh # regex: any change to a "/ 1x0" line