正在加载,请稍候…

Git Diff Isn't Just About Changes: Know What You're Comparing

Clarifies the different modes of git diff (working tree vs index, between commits, etc.) and how to interpret diff output effectively. Includes a worked

Introduction

git diff is one of the most frequently used Git commands, yet it's also one of the most misunderstood. Many developers assume it simply shows "what changed," but that misses the crucial point: git diff compares two specific objects. If you don't know which two objects are being compared, the output can be misleading.

This article clarifies the six most common comparison modes, explains how to interpret diff output, and provides practical tips for everyday development. By the end, you'll know exactly which variant to use and why.

developer looking at git diff output on terminal

The Core Concept: It's All About the Baseline

The official git-diff documentation states that git diff can compare:

  • Working tree vs index
  • Index vs a tree (usually HEAD)
  • Working tree vs a commit
  • Two arbitrary commits
  • Merge results
  • Two blob objects
  • Two files on disk

Before running any git diff command, ask yourself: "What two things am I comparing?" The answer determines which variant to use and ensures you get the expected output.

The Six Most Common Comparison Modes

1. Default git diff: Working Tree vs Index

This is the most common misconception. Many think it compares working tree to HEAD, but it actually shows differences between the working tree and the staging area (index).

git diff

This shows changes you haven't staged yet. If you've already staged some changes with git add, those won't appear here.

When to use: To review unstaged changes before deciding what to stage next.

2. git diff --cached: Index vs HEAD

This compares the staging area to the last commit (HEAD by default). It shows exactly what would be committed if you ran git commit right now.

git diff --cached
# or
git diff --staged

When to use: To review the exact patch that will be committed.

3. git diff HEAD: Working Tree + Index vs HEAD

This combines both unstaged and staged changes, showing the total difference since the last commit.

git diff HEAD

When to use: To get a complete overview of all changes since your last commit.

4. git diff A B or git diff A..B: Comparing Two Commits

Both forms compare the states of two arbitrary commits. The .. syntax here is not the same as in git log; it's just a separator.

git diff main feature
git diff main..feature  # same as above

When to use: To see the difference between two branches or tags.

5. git diff A...B: Merge-Base Comparison

This compares the merge base of A and B with B. It shows what B introduced since it diverged from A.

git diff main...feature

This is equivalent to:

git diff $(git merge-base main feature) feature

When to use: During code review to see only the changes made on a feature branch, ignoring changes that happened on main in the meantime.

6. git diff --merge-base: Explicit Merge-Base

For clarity, you can use the --merge-base flag:

git diff --merge-base A B

This is useful in scripts and CI pipelines where explicitness improves readability.

Worked Example: Comparing Two Branches

Let's walk through a real scenario. You have a main branch and a feature branch. You want to see what feature introduced.

Setup

# Create two branches
git init
echo "Initial content" > file.txt
git add file.txt && git commit -m "Initial commit"

# Create feature branch
git checkout -b feature
echo "Feature work" >> file.txt
git add file.txt && git commit -m "Feature commit"

# Go back to main and add a commit
git checkout main
echo "Main work" >> file.txt
git add file.txt && git commit -m "Main commit"

Wrong Way

git diff main feature

This shows the difference between the tips of both branches. It includes both the feature work and the main work, making it hard to isolate what the feature branch actually added.

Right Way

git diff main...feature

This shows only the changes made on feature since it branched off from main. The output is clean and focused.

Output Modes: Choosing the Right Format

git diff offers several output formats, each suited for different needs.

Mode Command Use Case
Patch (default) git diff Detailed review of changes
Stat git diff --stat Quick overview of which files changed and how many lines
Name-only git diff --name-only List of changed files (machine-friendly)
Name-status git diff --name-status List of files with change type (M, A, D, R)
Numstat git diff --numstat Machine-friendly stats (additions/deletions per file)
Word-diff git diff --word-diff Highlight individual word changes (great for docs, configs)

Example: --word-diff

git diff --word-diff

This is especially useful for reviewing changes in documentation, configuration files, or long strings where line-level diffs are too coarse.

Common Pitfalls

  • Assuming default git diff compares to HEAD: It compares working tree to index. Use git diff HEAD for total changes.
  • Using git diff A..B thinking it works like git log A..B: In git diff, .. is just a separator; the result is the same as git diff A B.
  • Misunderstanding git diff A...B: It compares the merge base, not the direct tip-to-tip difference.
  • Ignoring timezone when predicting time-based tokens: Always use the server's timezone, not local assumptions.
  • Parsing --name-only output naively: Use -z to handle filenames with spaces or special characters.

FAQ

What's the difference between git diff and git diff HEAD?

git diff compares working tree to index (staging area). git diff HEAD compares the combined working tree + index to the last commit. The former shows unstaged changes; the latter shows all changes since the last commit.

When should I use git diff --cached vs git diff --staged?

They are synonyms. Use whichever you find clearer. --staged is more intuitive for many developers.

How do I compare two files outside a Git repository?

Use git diff --no-index file1 file2. This works even if the files are not in a Git repo.

Why does git diff show nothing when I know I made changes?

You probably already staged those changes with git add. Use git diff --cached to see staged changes, or git diff HEAD to see everything.

How can I see only the list of changed files?

Use git diff --name-only. For a machine-safe list, add -z to use NUL as delimiter.

Conclusion

git diff is not a single command — it's a family of comparison tools. The key is knowing what you're comparing against. Once you master the six modes described here, you'll avoid common pitfalls and use git diff with confidence.

Try it in our text diff tool to visualize differences between two text snippets interactively.