By admin , 29 April 2026

Why blame

Blame answers "who wrote this line and why?" - the gateway to digging through history when something seems wrong, suspicious, or surprisingly clever. The name is misleading; the goal is rarely to assign fault, more often to find context.

The basics

git blame src/checkout.js
git blame -L 40,60 src/checkout.js     # specific line range

Each line shows the SHA, author, date, and content.

Following moved code

Code often moves between files. Default blame stops at the last move. To follow further:

git blame -M src/checkout.js     # follow within-file moves
git blame -C src/checkout.js     # follow across-file moves
git blame -CCC src/checkout.js   # aggressive cross-file detection

Each C increases sensitivity. Three Cs catches even small fragments moved between files - useful, slow.

Ignoring noise commits

Mass-format commits (prettier, black, lint autofix) bury the real history. .git-blame-ignore-revs lists SHAs to skip:

# .git-blame-ignore-revs
abc1234567890   # Apply prettier across codebase
def4567890123   # Apply black to all Python
git blame --ignore-revs-file=.git-blame-ignore-revs src/checkout.js
# Or set as default:
git config blame.ignoreRevsFile .git-blame-ignore-revs

GitHub honours this file natively.

Blaming by content

To find when a particular string was introduced:

git log -S 'EMAIL_REGEX' -- src/
git log -G 'email.*regex' --pickaxe-regex -- src/

-S finds commits that change the count of a string. -G finds commits that touch a regex match. Both are blame-shaped tools at the commit level.

Hunting a regression

Combine blame with bisect for full archaeology:

git blame -L 40,60 src/checkout.js
# find suspicious SHA
git show <sha>
git log --follow -p src/checkout.js
git bisect start
git bisect bad HEAD
git bisect good <earlier-sha>

Annotated views in editors

VS Code's GitLens, JetBrains' built-in annotate, and Vim's fugitive.vim offer interactive blame. Click a line, see the commit, navigate back through history.

Reverse blame

git log --reverse traces forward in time:

git log --reverse --follow -p -L 40,60:src/checkout.js

This shows each change to lines 40-60 chronologically - useful when you want to see the evolution of a function from inception.

Blame without history

Sometimes you just want to know who last modified a line, without metadata noise:

git blame --line-porcelain src/checkout.js | \
  sed -n 's/^author //p' | sort | uniq -c | sort -rn

This counts authors of every line - a quick "who owns this file?" report.

Etiquette

Blame is for understanding, not finger-pointing. The author of a line might have written it five years and three jobs ago. Use blame to ask about the change ("I see you added this regex - was email validation always this strict?"), not to assign fault.

Long-term maintenance

Maintain .git-blame-ignore-revs as you go. Whenever you do a mass reformat, add the SHA. Future archaeologists will thank you.