Git Blame: The Complete Guide for 2026

Published February 15, 2026 · 20 min read

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.

⚙ Not in a repo? Use our Git Diff Viewer to paste a patch, preview changes, and share a clean diff with teammates.

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).

⚙ Quick reference: Keep the Git Commands Cheat Sheet open while you investigate.
⚙ Related: After you find a suspect commit, review it with git diff (or use the Git Diff Viewer), and explore surrounding history with git log.

Table of Contents

  1. Quick start: 10 useful git blame commands
  2. How to read git blame output
  3. Blame a specific line range with -L
  4. Blaming at a revision (or before a change)
  5. Follow renames and moved code (--follow, -M, -C)
  6. Ignore whitespace and formatting commits (-w, ignore-revs)
  7. Porcelain output for scripting
  8. A practical investigation workflow
  9. Troubleshooting and performance tips
  10. 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
Tip: Use -- 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:

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
Important: 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
Tip: Combine 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:

  1. Blame the current line(s) to find the most recent touching commit.
  2. Use git show to understand what changed.
  3. 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
Performance note: -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
Tip: When you add a formatting-only PR, update .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:

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

Team culture: “blame” is a command name, not a philosophy. Use it to understand history, not to shame teammates. Many “ownership” signals are artifacts of refactors and formatting changes.

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.

Related Resources

Git: The Complete Guide
A practical Git primer and mental model
Git Log Complete Guide
Ranges, filters, searching, and formatting views
Git Diff Complete Guide
Compare commits, branches, and working tree changes
Git Bisect Complete Guide
Prove regressions with a binary search through history
Git Undo (Reset vs Revert)
Safely undo changes locally or on shared branches
Git Commands Cheat Sheet
One-page quick reference for daily Git workflows