Great work!

XP to next level

BugEater

git blame: Line-by-Line Authorship

Learning Objectives

By the end of this lesson you will be able to:

  • Read every column of git blame output
  • Narrow blame to the exact lines you care about
  • Follow a blame result through to the full commit
  • Use blame as a question-finding tool rather than a fault-finding one

The Question It Answers

Your login test fails. The selector #login-btn finds nothing. The button is clearly on the page, so someone changed something.

git blame src/views/LoginView.js
8f3c2a1d (Marta Kovalenko 2026-07-14 11:23:07 +0300  41) const LOGIN_BUTTON_ID = 'login-btn-v2';
1a2b3c4d (Ivan Petrenko   2026-03-02 09:41:15 +0200  42) const FORM_SELECTOR   = '.auth-form';
8f3c2a1d (Marta Kovalenko 2026-07-14 11:23:07 +0300  43) const SUBMIT_TIMEOUT  = 5000;

Line 41 changed on 14 July, in commit 8f3c2a1d, authored by Marta Kovalenko. The ID became login-btn-v2. Your test looks for login-btn. That is your bug report, found in one command.

Reading the Columns

Column Content
8f3c2a1d Abbreviated commit ID — the last commit to touch this line
Marta Kovalenko Author of that commit
2026-07-14 11:23:07 +0300 When that commit was authored
41) Current line number in the file
the rest The line's content

The crucial word is "last". Blame shows the most recent commit that modified each line, not the commit that originally introduced it. A line rewritten five times shows only the fifth. Lesson 5.3's pickaxe search is how you find the first.

Narrowing the Output

A 900-line file produces 900 lines of output. Don't read them.

git blame -L 38,45 src/views/LoginView.js

Lines 38 to 45 only. This is the flag you will use every time.

git blame -L 41,+5 src/views/LoginView.js     # line 41 and the next 5
git blame -L '/LOGIN_BUTTON_ID/',+3 src/views/LoginView.js   # from a regex match

That last form is genuinely useful when you know the symbol but not the line number.

Two more flags worth knowing:

git blame -w src/views/LoginView.js

Ignores whitespace-only changes, so a reformat doesn't claim every line in the file. Almost always what you want.

git blame --date=short src/views/LoginView.js

2026-07-14 instead of the full timestamp. Much easier to scan.

A combination worth aliasing:

git config --global alias.who "blame -w --date=short"

From a Line to the Whole Story

Blame gives you a commit ID. That is the start, not the end.

git show 8f3c2a1d

The full commit: message, author, date, and every file it changed. Very often the message answers your question outright — "Rename login button ID for the new auth flow (PAY-4102)" tells you this was deliberate and gives you a ticket to read.

git log -1 --format="%H%n%an <%ae>%n%ad%n%n%B" 8f3c2a1d

Just the metadata and full message, without the diff.

git branch --contains 8f3c2a1d
git tag --contains 8f3c2a1d

Which branches and releases have this change — that is, whether it is already in production or still only on a feature branch. For a tester triaging a report from a customer, that is often the most important fact of all.

On the Word "Blame"

The command is badly named. What it produces is a name to ask, not a name to accuse.

The change on line 41 was almost certainly intentional and correct — a deliberate rename as part of a new auth flow. The defect is that the test was not updated with it. That is a process finding, not a personal one, and it is worth writing up that way.

The best outcome of a blame investigation is usually not a bug report at all. It is a two-minute conversation:

"Hey Marta — I see login-btn became login-btn-v2 in 8f3c2a1. Is the old ID gone for good, or is it still used anywhere? I need to update the auth suite."

Thirty seconds to write. Saves an afternoon of guessing.

Pro Tip: Most IDEs and GitHub's file view have blame built in, usually as "Annotate" or a "Blame" button. They show the same data with clickable commits. Use them for exploring; use the command line when you need -L, -w, or a result you can paste into a ticket.

Key Takeaways

  • git blame <file> shows the last commit to touch each line, with author and date
  • -L 38,45 narrows the output to the lines you actually care about
  • -w ignores whitespace-only changes so reformats don't mask the real author
  • git show <commit> turns a blame result into the full story, including the message
  • git branch --contains / git tag --contains tell you whether the change is live yet
  • Blame produces someone to ask, not someone to blame — the two-minute conversation is the point

Quiz

What does the commit ID in git blame output tell you about a line?

You only care about lines 38 to 45 of a 900-line file. Which flag?

Someone reformatted the whole file last week. Why add -w to your blame?

Blame points at Marta's commit that renamed a button ID. What is the most productive next step?