C.W.K.
Stream
Lesson 01 of 05 · published

blame Without Blame

~18 min · blame, history

Level 0Untracked Rookie
0 XP0/47 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

blame is forensics, not accusation

The unfortunate name implies finger-pointing. The actual use case is forensics: which commit introduced this line, in what context, with what message, and what other lines came in with it? git blame answers all of these. Used well, it is the fastest way to understand why a piece of code looks the way it does — and the second-fastest way to find the conversation that justifies it.

The output of git blame file.js is one line per source line: commit hash (short), author, date, line number, and the line content. Each line tells you the most recent commit that touched it. From there, git show <hash> opens the full commit — message, full diff, and crucially the explanation of why those changes happened together. The blame line is the entry point; the commit message and surrounding diff are the evidence.

Two flags solve the most common annoyance. git blame -w ignores whitespace-only changes when assigning blame. git blame -M follows code that was moved within the same file (also -M -M for cross-file moves). Without these, a refactor that only reformatted or moved a function makes the entire region's blame point at the refactor commit, hiding the original intent. With them, blame walks past mechanical churn to the actual logic-introducing commit.

The pro move: git log -L :functionName:file.js. This shows the entire history of a single function across the project — every commit that ever changed it, in chronological order, with diffs. You see the function evolve. For tracking down "when did this method's behavior change?" or "why is this validation here?" it is dramatically faster than reading git log file.js and then opening each commit to see whether it touched the function. Add it to muscle memory.

Code

Blame basics with sane flags·bash
# Default blame (annoying for refactor-heavy files):
git blame src/auth.js

# Useful — ignore whitespace + follow within-file moves:
git blame -w -M src/auth.js

# Cross-file moves and copies (slower but thorough):
git blame -w -C -C src/auth.js

# Just lines 40-60:
git blame -L 40,60 src/auth.js

# Limit to commits before a specific date:
git blame --before="2026-04-01" src/auth.js
Function-history with git log -L·bash
# Track the entire history of authenticateUser() in src/auth.js:
git log -L :authenticateUser:src/auth.js

# Track lines 40-60 of src/auth.js across history:
git log -L 40,60:src/auth.js

# Open the most recent commit that touched a specific blame line:
git show $(git blame -L 42,42 src/auth.js | awk '{print $1}')

External links

Exercise

Pick a file in any active project that has been around for at least a year. Run git blame with no flags, then with -w -M, and compare the noise level. For one specific line, follow the hash to git show and read the commit message — write down whether the message actually explained the line. Then try git log -L on a function in the same file and note what it revealed.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.