Learning Objectives
By the end of this lesson you will be able to:
- Recognise when blame is reporting a cosmetic change rather than a real one
- Follow a line through a file rename or a code move
- Configure your repository to ignore bulk-reformatting commits permanently
- Know when to stop trusting blame and switch tools
When Blame Lies to You
Blame reports the last commit that touched each line. That is precise, and it is frequently useless, because a great many commits touch lines without changing anything that matters:
- A Prettier or Black run reformats 4,000 lines
- Tabs become spaces across the whole repository
- A file moves from
src/views/tosrc/components/views/ - A function is cut from one file and pasted into another
- Someone adds a licence header to every source file
After any of these, blame reports one commit and one author for enormous swathes of code. Everything genuinely interesting is hidden behind it.
Seeing Past Whitespace
git blame -w src/views/LoginView.js
Ignores changes that are whitespace-only. This handles indentation changes, trailing-space cleanups and tabs-to-spaces conversions.
It does not handle a reformat that also rewraps lines or reorders arguments — those are real content changes as far as Git is concerned.
Following Code That Moved
Two flags, increasingly aggressive:
git blame -C src/components/views/LoginView.js
-C detects lines copied or moved from other files in the same commit. If a function was cut from AuthForm.js and pasted here, -C follows it back and reports its original author instead of the person who did the move.
git blame -C -C -C src/components/views/LoginView.js
Repeating -C widens the search: two look at files modified in the same commit, three search the entire repository at each commit. It is markedly slower — on a large repository this can take a minute — but for "this code definitely came from somewhere, where?" it is the tool.
For file renames, Git handles them automatically. A pure git mv is detected and blame follows through it with no flags at all. You only need -C when the content moved between files rather than the file itself moving.
The Permanent Fix: .git-blame-ignore-revs
If your project has had a big reformatting commit, you should not have to remember -w forever. Git supports an ignore list.
1. Create a file at the repository root listing the full commit IDs to ignore:
# .git-blame-ignore-revs
# Bulk reformats — not meaningful authorship.
# Prettier across the whole codebase (2026-05-11)
d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3
# Tabs to spaces (2026-02-03)
a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0
Use full 40-character IDs — abbreviated ones are rejected.
2. Tell Git to use it:
git config blame.ignoreRevsFile .git-blame-ignore-revs
3. Commit the file so everyone benefits.
From then on, plain git blame skips past those commits to the real previous author. GitHub, GitLab and most IDEs read this file too, so the web blame view improves as well.
This is one of the highest-value fifteen-minute contributions a tester can make to a codebase they investigate often.
Following a Single Line Through Time
When you need the whole story of one line rather than its last change:
git log -L 41,41:src/views/LoginView.js
This prints every commit that ever changed line 41, newest first, each with its diff. It follows the line as it moves up and down the file, and through renames.
git log -L '/LOGIN_BUTTON_ID/',+1:src/views/LoginView.js
The same, anchored to a symbol rather than a line number — much more robust, since line numbers change constantly.
This is the command that turns "who last touched this?" into "what has this line's entire life been?" and it is worth remembering when blame's single answer isn't enough.
When to Stop Using Blame
Blame answers "who changed this line?". Some questions look similar but need different tools:
| Question | Tool |
|---|---|
| Who last changed this line? | git blame |
| What is this line's full history? | git log -L |
| When did this string first appear anywhere? | git log -S (next lesson) |
| Which commit made this test start failing? | git bisect |
git bisect deserves a mention even though it is beyond this course: it binary-searches history by running a command at each step, and it will find the exact breaking commit in about ten steps across a thousand commits. When blame can't help because the failure isn't tied to one visible line, that is where to go next.
Pro Tip: Before investigating any unfamiliar repository, check whether
.git-blame-ignore-revsexists. If it doesn't and the project has had a big reformat, creating one is a small, permanent improvement to everyone's ability to investigate.
Key Takeaways
- Reformats, moves and mass edits make blame report a cosmetic commit instead of a real one
-wignores whitespace-only changes; it does not survive rewrapping or reordering-Cfollows lines moved or copied from other files; repeat it to widen the search- File renames are followed automatically —
-Cis for content moving between files .git-blame-ignore-revsplusblame.ignoreRevsFilefixes it permanently, for everyonegit log -Lgives one line's complete history;git bisectfinds a breaking commit blame can't