Git Diff: The Complete Guide for 2026
git diff is Git’s “what changed?” command. It’s the fastest way to review your work before committing, compare your branch to main, and understand exactly what a merge or rebase will introduce.
The confusing part is that Git has multiple states (working tree, staging area, commits), and git diff compares different pairs depending on the options you use. This guide teaches the practical recipes you’ll use daily, with copy‑paste commands and a mental model you can keep forever.
Table of Contents
- Quick start: 8 useful git diff commands
- The 3 states Git compares (working tree, index, HEAD)
- Unstaged vs staged diffs
- Diffing commits (and one commit)
- Diffing branches: two dots vs three dots
- Limiting diff to files, folders, and patterns
- Summary views: --stat, --name-only, --numstat
- Whitespace, context, and quality checks
- Word-level diff for prose and config
- Diff algorithms (histogram, patience)
- Rename/copy detection and binary diffs
- Patch workflows: git apply and friends
- Interactive patch mode: add -p, restore -p
- Using git difftool (Meld, VS Code, vimdiff)
- Useful aliases for daily work
- Troubleshooting and best practices
- FAQ
1. Quick start: 8 useful git diff commands
If you remember nothing else, remember these. They cover 95% of daily workflows.
| Goal | Command |
|---|---|
| See unstaged changes (working tree) | git diff |
| See staged changes (what will be committed) | git diff --staged (or --cached) |
| See everything you changed vs last commit | git diff HEAD |
| Compare your branch to main (merge-base diff) | git diff origin/main...HEAD |
| Show just the filenames | git diff --name-only |
| Show per-file summary (lines added/removed) | git diff --stat |
| Ignore whitespace noise | git diff -w |
| Diff one file / folder | git diff -- path/to/file |
git diff output opens in a pager (usually less). Press q to quit. To disable paging: git --no-pager diff ...
2. The 3 states Git compares (working tree, index, HEAD)
Most confusion around git diff disappears once you learn Git’s three “trees” (states):
- Working tree: your actual files on disk
- Index (staging area): what will go into the next commit
- HEAD: your last commit (the snapshot you currently have checked out)
(commit)
HEAD
|
| git diff --staged
v
Index
|
| git diff
v
Working tree
And that’s why:
git diffcompares working tree ↔ indexgit diff --stagedcompares index ↔ HEADgit diff HEADcompares working tree ↔ HEAD (includes staged + unstaged)
git add, your changes moved to the index, so git diff might show nothing. Use git diff --staged to see what you staged.
3. Unstaged vs staged diffs
After editing files, you usually do:
# 1) Review what changed but is NOT staged
git diff
# 2) Stage some or all changes
git add .
# 3) Review what will be committed
git diff --staged
# 4) Commit
git commit -m "My change"
Staged diff for a single file
git diff --staged -- path/to/file
Show staged/unstaged filenames only
# Unstaged only
git diff --name-only
# Staged only
git diff --staged --name-only
4. Diffing commits (and one commit)
You can compare any two commits by passing them as arguments:
# Compare two commits
git diff <commitA> <commitB>
# Common shorthand: commit ranges
git diff <commitA>..<commitB>
Diff a single commit
To see what changed in just one commit, this pattern is handy:
# Diff the commit against its parent
git diff <commit>^!
For most people, git show <commit> is even more convenient (it includes metadata). If you want to learn commit history workflows, see our git log complete guide.
5. Diffing branches: two dots vs three dots
This is a frequent source of mistakes. Git supports two common “range” notations when comparing branches:
| Notation | What it means | Example |
|---|---|---|
A..B |
Changes between tips A and B (roughly “B minus A”) | git diff main..feature |
A...B |
Changes on B since the merge base of A and B (what your PR introduces) | git diff main...feature |
Compare your branch to main (recommended)
In a pull request workflow, you usually want the “merge base” comparison:
git fetch origin
git diff origin/main...HEAD
This answers: “What changes does my branch introduce compared to where it split from main?”
Compare main to feature in either direction
# What changed on feature since main diverged?
git diff main..feature
# What changed on main that feature doesn't have?
git diff feature..main
git diff main...HEAD for review (PR-style), and use two dots when you intentionally want “tip to tip” comparisons.
6. Limiting diff to files, folders, and patterns
Use -- to separate commits/options from paths. This avoids ambiguity when a branch or tag has the same name as a file.
# Diff only a folder
git diff -- src/
# Diff only one file
git diff -- path/to/file
# Compare branches for a specific folder
git diff main...feature -- src/
Path patterns (globs)
# Quote globs so your shell doesn't expand them
git diff -- '*.py'
git diff -- 'src/**/*.ts'
Exclude paths
Pathspec magic lets you exclude paths:
# Exclude vendor/ and lockfiles
git diff -- . ':(exclude)vendor/' ':(exclude)*.lock'
7. Summary views: --stat, --name-only, --numstat
For large changes, start with a summary view before diving into hunks.
Show affected files only
git diff --name-only
git diff --staged --name-only
Short summary per file
git diff --stat
git diff --staged --stat
Machine-friendly stats
--numstat outputs tab-separated numbers (adds, deletes, file), useful for scripts:
git diff --numstat main...HEAD
Show rename and mode changes
git diff --name-status
Status codes include M (modified), A (added), D (deleted), R (renamed), and more.
8. Whitespace, context, and quality checks
Diffs often include formatting noise. Git has many options to reduce it.
Ignore whitespace
# Ignore ALL whitespace changes (most aggressive)
git diff -w
# More targeted variants
git diff --ignore-space-change
git diff --ignore-all-space
git diff --ignore-space-at-eol
Change context lines
# Show 10 lines of context around each hunk
git diff -U10
# Show minimal context (can be hard to read)
git diff -U0
Check for whitespace errors
--check highlights trailing whitespace and other whitespace errors in the patch:
git diff --check
git diff --staged --check
.gitattributes file and an editorconfig. It prevents “diff noise” and makes reviews faster.
9. Word-level diff for prose and config
For Markdown, docs, and config files, word-level diffs are much easier than line hunks.
# Show word-level changes
git diff --word-diff
# Colored words (no +/- markers)
git diff --color-words
To customize what counts as a “word” (useful for JSON/YAML/INI), use:
git diff --word-diff-regex='[^[:space:]]+'
10. Diff algorithms (histogram, patience)
Sometimes the default diff output looks messy (especially after moving blocks of code). Different algorithms can produce a cleaner, more stable diff:
# Often better for code refactors
git diff --histogram
# Great for long moved blocks and reordering
git diff --patience
# Reduce the number of changed lines (can be slower)
git diff --minimal
You can also set a default globally:
git config --global diff.algorithm histogram
11. Rename/copy detection and binary diffs
Git detects renames heuristically. In large refactors, it helps to be explicit:
# Enable rename detection more aggressively
git diff -M
# Also detect copies
git diff -C
# View rename status in summary
git diff -M --name-status main...HEAD
Binary patches
By default, Git shows “Binary files differ” for images and other binary files. If you need a patch that includes binary content (rare, but sometimes useful):
git diff --binary > changes.patch
12. Patch workflows: git apply and friends
Diffs are also patch files. You can export, email, share, and apply them.
Create a patch from your working tree
git diff > changes.patch
Create a patch from staged changes only
git diff --staged > staged.patch
Check and apply a patch
# Validate first
git apply --check changes.patch
# Apply
git apply changes.patch
When the patch doesn’t apply cleanly
# Try a 3-way apply (uses blob hashes, similar to merge)
git apply -3 changes.patch
# Keep rejects so you can fix them manually
git apply --reject changes.patch
If you want to exchange changes as commits rather than raw diffs, use git format-patch and git am (covered briefly in our git log guide).
13. Interactive patch mode: add -p, restore -p
Interactive patch mode lets you stage (or undo) specific hunks. It’s one of the most powerful Git workflows.
# Stage selected hunks
git add -p
# Unstage selected hunks
git reset -p
# Discard selected hunks from working tree
git restore -p .
git add -p with git diff --staged to ensure each commit is focused and reviewable.
14. Using git difftool (Meld, VS Code, vimdiff)
If you prefer a GUI or a split-pane view, Git can launch an external diff tool.
Run a difftool ad-hoc
# Launch configured difftool
git difftool
# For staged changes
git difftool --staged
Example: configure VS Code as your difftool
git config --global diff.tool vscode
git config --global difftool.vscode.cmd 'code --wait --diff "$LOCAL" "$REMOTE"'
# Then:
git difftool
Other common tools include meld, vimdiff, kdiff3, and beyondcompare.
15. Useful aliases for daily work
A few aliases can make diffing muscle memory.
git config --global alias.d "diff"
git config --global alias.ds "diff --staged"
git config --global alias.dn "diff --name-only"
git config --global alias.dw "diff -w"
git config --global alias.dmain "diff origin/main...HEAD"
Or in ~/.gitconfig:
[alias]
d = diff
ds = diff --staged
dn = diff --name-only
dw = diff -w
dmain = diff origin/main...HEAD
16. Troubleshooting and best practices
- Nothing shows up: If you staged changes, use
git diff --staged. If you already committed, compare commits:git diff HEAD~1..HEAD. - Diff is huge: Start with
--stator--name-only, then limit by path:git diff -- src/. - Whitespace noise: Use
-wor--ignore-space-change. Consider adding.gitattributesto normalize line endings. - Moved blocks look like deletions/additions: Try
--histogramor--patience. - Want to see history + diffs together: Use
git log -p(see our git log guide).
FAQ
Why does git diff show nothing even though files changed?
Most commonly: you staged the changes. git diff shows unstaged changes only. Run git diff --staged to view staged changes. Also check git status to see which state your changes are in.
What’s the difference between git diff HEAD and git diff --staged?
git diff --staged compares index ↔ HEAD (staged changes only). git diff HEAD compares working tree ↔ HEAD (staged + unstaged together).
How do I compare my local branch to the remote main?
Run git fetch origin, then use git diff origin/main...HEAD. This is the best review view for pull requests because it uses the merge base.
How do I export a diff and apply it on another machine?
Export with git diff > changes.patch (or git diff --staged > changes.patch), copy the patch, then apply with git apply changes.patch. Use git apply --check first to validate.