Git Diff: The Complete Guide for 2026

Published February 15, 2026 · 22 min read

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.

⚙ Quick reference: Bookmark the Git Commands Cheat Sheet for a one-page list of essential commands and flags.
⚙ Not in a repo? Use our Diff Checker to compare text, patches, config files, and docs side-by-side.

Table of Contents

  1. Quick start: 8 useful git diff commands
  2. The 3 states Git compares (working tree, index, HEAD)
  3. Unstaged vs staged diffs
  4. Diffing commits (and one commit)
  5. Diffing branches: two dots vs three dots
  6. Limiting diff to files, folders, and patterns
  7. Summary views: --stat, --name-only, --numstat
  8. Whitespace, context, and quality checks
  9. Word-level diff for prose and config
  10. Diff algorithms (histogram, patience)
  11. Rename/copy detection and binary diffs
  12. Patch workflows: git apply and friends
  13. Interactive patch mode: add -p, restore -p
  14. Using git difftool (Meld, VS Code, vimdiff)
  15. Useful aliases for daily work
  16. Troubleshooting and best practices
  17. 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
Tip: 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):

           (commit)
             HEAD
              |
              |  git diff --staged
              v
            Index
              |
              |  git diff
              v
        Working tree

And that’s why:

Gotcha: If you ran 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
Tip: If you’re unsure, start with 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
Tip for teams: For consistent whitespace and line endings across platforms, consider a .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 .
Tip: Combine 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

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.

Related Resources

Git: The Complete Guide
Everything from branching to recovery and workflows
Git Log Complete Guide
Commit history, ranges, searching, and formatting views
Git Blame Complete Guide
Find who changed a line (ranges, renames, moved code, ignore-revs)
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
Diff Checker Tool
Compare text changes side-by-side (patches, config, docs)
Git Merge Conflicts Guide
Resolve conflicts safely and understand what happened