How to Show Git Diff with Line Blame Information in the Terminal
When reviewing code changes in a terminal, running git diff tells you what changed, while git blame tells you who last touched a specific line and when. However, standard Git commands don't combine these two views natively. If you want to see a diff where each deleted or context line displays its associated commit hash or author metadata, you need a specialized tool or script.
In this guide, we will explore several modern solutions to get inline blame context directly within your terminal diff output.
Method 1: Using Modern Terminal Diff Viewers (Recommended)
The easiest way to get enhanced diff output with built-in context in the terminal is by using dedicated CLI tools designed for Git visualization.
1. Delta (git-delta)
Delta is a syntax-highlighting pager for Git that dramatically improves terminal output. While Delta focuses primarily on side-by-side and syntax-highlighted diffs, it can be integrated with Git aliases or run alongside interactive blame viewers.
2. Tig (Text-mode Interface for Git)
If you need an interactive terminal session where diffs and blames live together seamlessly, Tig is the standard solution.
# Install tig (macOS / Linux)
brew install tig # macOS
sudo apt install tig # Ubuntu/Debian
# Open tig blame view for a file
tig blame path/to/file.pyInside tig blame, pressing d on any line opens the main diff for that specific commit in a split window, giving you immediate contextual diff and blame history.
Method 2: Custom Python Script to Annotate Git Diff with Blame
If you want a non-interactive CLI output that directly prints the standard diff hunk decorated with the commit hash from git blame, you can parse the diff output using a lightweight Python script.
Save the following script as git-diff-blame.py in your PATH:
#!/usr/bin/env python3
import re
import subprocess
import sys
def run_cmd(cmd):
return subprocess.check_output(cmd, shell=True, text=True)
def diff_with_blame():
# Get standard diff
try:
diff_output = run_cmd("git diff HEAD")
except subprocess.CalledProcessError:
return
current_file = None
hunk_re = re.compile(r"^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@")
for line in diff_output.splitlines():
if line.startswith("--- a/"):
current_file = line[6:]
print(line)
continue
elif line.startswith("+++"):
print(line)
continue
match = hunk_re.match(line)
if match:
old_start = int(match.group(1))
print(line)
current_old_line = old_start
continue
# Process removed or context lines
if (line.startswith("-") or line.startswith(" ")) and current_file:
try:
blame = run_cmd(f"git blame -L {current_old_line},{current_old_line} --porcelain HEAD -- {current_file}")
commit = blame.splitlines()[0].split()[0][:8]
except Exception:
commit = "00000000"
print(f"[{commit}] {line}")
if line.startswith("-") or line.startswith(" "):
current_old_line += 1
else:
print(line)
if __name__ == "__main__":
diff_with_blame()
Make the script executable and add a Git alias for convenient usage:
chmod +x git-diff-blame.py
git config --global alias.diff-blame "!python3 /path/to/git-diff-blame.py"Now running git diff-blame will prefix changed lines with the 8-character commit hash responsible for that line prior to your modifications.
Method 3: Native Line-Level History (`git log -L`)
If you are trying to understand how a specific block of code evolved alongside its diffs, Git provides a powerful built-in command without requiring extra tools:
git log -L <start_line>,<end_line>:<path/to/file>For example, to trace lines 15 to 30 of app.py along with the commit diffs that affected those lines:
git log -L 15,30:app.pyThis outputs the exact sequence of commits, authors, commit messages, and the corresponding diff for those specific lines over time.
Summary
- Use Tig for a fast, interactive terminal UI combining diff and blame.
- Use a custom Python script if you require raw terminal stdout containing line hashes embedded inside diff hunks.
- Use
git log -Lto inspect historical diffs for specific line ranges without third-party dependencies.