Git Blame: The Complete Guide for 2026
git blame answers the question: “Who last changed this line?” It annotates a file line-by-line with the commit and author responsible for the current contents.
Used well, blame turns debugging from guesswork into a repeatable workflow: you identify the commit that introduced a behavior, inspect the context, and then jump to the right fix (or the right person to ask).
Table of Contents
- Quick start: 10 useful git blame commands
- How to read git blame output
- Blame a specific line range with -L
- Blaming at a revision (or before a change)
- Follow renames and moved code (--follow, -M, -C)
- Ignore whitespace and formatting commits (-w, ignore-revs)
- Porcelain output for scripting
- A practical investigation workflow
- Troubleshooting and performance tips
- FAQ
1. Quick start: 10 useful git blame commands
These are the most common “recipes” you’ll use day-to-day.
| Goal | Command |
|---|---|
| Blame the whole file | git blame -- path/to/file |
| Blame lines 120–160 only | git blame -L 120,160 -- path/to/file |
| Blame 20 lines starting at 120 | git blame -L 120,+20 -- path/to/file |
| Ignore whitespace changes | git blame -w -- path/to/file |
| Follow file renames | git blame --follow -- path/to/file |
| Detect moved lines within the file | git blame -M -- path/to/file |
| Detect copied/moved lines from other files | git blame -C -C -- path/to/file |
| Blame the file as of a specific revision | git blame v1.2.3 -- path/to/file |
| Ignore known formatting commits | git blame --ignore-revs-file .git-blame-ignore-revs -- path/to/file |
| Machine-readable output for scripts | git blame --line-porcelain -- path/to/file |
-- before the path to separate revisions/options from filenames. This avoids confusion when a file name looks like an option.
2. How to read git blame output
A typical blame line looks like this:
e1d2c3f4 (Alice Example 2026-02-10 14:32:11 +0000 128) return parse_config(value)
Breakdown:
e1d2c3f4— the commit that last changed this line.Alice Example— author name (use-eto show email).- Date/time — format is configurable (see
--date=short/--date=relative). 128— the line number in the final output.- Line content — the current contents of the file at that line.
Once you have the commit hash, your next move is usually:
# Inspect the commit
git show e1d2c3f4
# Or only the changes that touched this file
git show e1d2c3f4 -- path/to/file
git blame shows who last changed the line as it exists today. If the code was later refactored, moved, or formatted, the blame “owner” may be a refactor/format commit rather than the original author.
3. Blame a specific line range with -L
Blaming an entire file is often noisy. The -L option lets you focus on one function or block.
By explicit line numbers
# Lines 120 through 160
git blame -L 120,160 -- src/server/auth.ts
By start + count
# 40 lines starting at line 120
git blame -L 120,+40 -- src/server/auth.ts
By regex (advanced)
Git also supports regex-based ranges in some workflows. For example, you can find a function with a pattern and blame around it.
# Blame from the first match of "function login" to the next "}"
# (Exact support can vary; line-number ranges are the most portable.)
git blame -L '/function login/',+80 -- src/server/auth.ts
git blame -L with git log -L for “how did this function evolve?” history. See our git log guide for commit-range and filtering patterns.
4. Blaming at a revision (or before a change)
You can blame a file as it existed at a particular revision:
# Blame as of a tag
git blame v2.0.0 -- src/server/auth.ts
# Blame as of a commit
git blame 1a2b3c4d -- src/server/auth.ts
This is useful when a line changed recently and you want to see “who owned it” before the refactor.
Compare two points in time
A practical approach is:
- Blame the current line(s) to find the most recent touching commit.
- Use
git showto understand what changed. - If it’s a refactor/format change, blame an earlier revision.
5. Follow renames and moved code (--follow, -M, -C)
Out of the box, blame can “stop” at refactors. Git provides flags to track history across renames and code movement.
Follow file renames with --follow
# If src/auth.ts used to be src/login.ts, --follow can track the rename
git blame --follow -- src/auth.ts
Detect moved lines within the same file with -M
# Useful after a big refactor that re-ordered functions
git blame -M -- src/auth.ts
Detect copied/moved lines across files with -C
# Try once (light)
git blame -C -- src/auth.ts
# Try harder (more expensive)
git blame -C -C -- src/auth.ts
# Try even harder (slow on large repos)
git blame -C -C -C -- src/auth.ts
-C options can be expensive because Git searches for matching code in other files. Start with -M or a single -C, and only escalate if needed.
6. Ignore whitespace and formatting commits (-w, ignore-revs)
Blame becomes dramatically more useful when you remove noise from “mechanical” changes like formatting or bulk renames.
Ignore whitespace changes with -w
# Ignore indentation-only and whitespace-only changes
git blame -w -- src/auth.ts
Ignore known formatting commits with .git-blame-ignore-revs
Many teams keep a file named .git-blame-ignore-revs in the repo root. It contains commit SHAs (one per line) that should be ignored in blame output.
# Create the file (example)
cat > .git-blame-ignore-revs <<'EOF'
# Prettier reformat
3f8c2b1a4d5e6f7890abc1234567890abcdef123
# Go fmt across repo
8a7b6c5d4e3f2a1901234567890abcdef12345678
EOF
Then:
git blame --ignore-revs-file .git-blame-ignore-revs -- src/auth.ts
You can also set a global or repo config so you don’t have to type the flag every time:
# In the repo
git config blame.ignoreRevsFile .git-blame-ignore-revs
# Or globally
git config --global blame.ignoreRevsFile ~/.config/git/ignore-revs
.git-blame-ignore-revs in the same PR. Future-you will thank you.
7. Porcelain output for scripting
For tooling, use porcelain mode. It prints stable, parseable fields, including commit metadata.
# Line-porcelain includes per-line metadata
git blame --line-porcelain -- src/auth.ts | head -n 40
This is helpful for:
- Building “ownership” reports (top authors per file).
- Finding suspect commits programmatically.
- Integrating with editors or internal dashboards.
8. A practical investigation workflow
Here’s a workflow that works well for real bugs and confusing code paths.
Step 1: blame the smallest surface area possible
# Blame a function or suspicious block
git blame -L 120,160 -- src/auth.ts
Step 2: open the commit and review what changed
git show <sha>
# If the commit is large, focus on one file
git show <sha> -- src/auth.ts
Step 3: inspect surrounding history
# Show commits that touched the file
git log --oneline -- src/auth.ts
# Show patches
git log -p -- src/auth.ts
If you want to compare two points (for example, your branch vs main), use git diff:
git fetch origin
git diff origin/main...HEAD
Step 4: confirm a regression with git bisect (when needed)
Blame gives you a suspect. git bisect gives you a proof by testing history. If the bug is critical or the history is messy, bisect is the right tool.
# See: /blog/git-bisect-complete-guide
# (Mark a good commit and a bad commit, then iterate.)
9. Troubleshooting and performance tips
- Blame points to a formatting commit: use
-wand/or--ignore-revs-file. - Blame stops at a refactor: try
-M(moved lines) and then-C(copied from other files). - File was renamed: use
--follow. - Too slow: blame a smaller range (
-L) and avoid multiple-Cunless you truly need it. - Need function history: use
git log -Lin addition to blame.
FAQ
Is git blame the same as git log?
No. git blame annotates the current file line-by-line. git log shows a sequence of commits. Use blame to find the commit for a line, then use log to explore how and why it changed over time.
How do I ignore a large reformat commit in blame?
Create a .git-blame-ignore-revs file listing the commit SHA and use git blame --ignore-revs-file .git-blame-ignore-revs. For convenience, set blame.ignoreRevsFile in your git config.
Why does blame show me, even though I didn’t “write” the feature?
Blame shows the last commit that touched the line. If you ran a formatter, moved code, or refactored a file, your commit might be the most recent touch even if the original logic came from earlier commits. Use -M/-C and ignore-revs to reduce noise.
How do I find the original author when code was moved between files?
Try git blame -M -C -C -- file. This asks Git to detect moved and copied code. If that’s too slow, narrow the scope with -L or blame an earlier revision first.