Git Diff Explained for Beginners: Every Variant You Actually Need

git diff answers the most important question in version control: "what exactly changed?" But the command behaves differently depending on what you compare — working tree, staging area, commits, or branches — and that trips up almost every beginner. This guide walks through each variant with a short, real example, then shows you how to read the output git prints.

The Three Places Your Code Lives

To understand which diff you are looking at, remember that git tracks three snapshots of your project at all times:

  • Working tree — the files on disk you are editing right now.
  • Staging area (index) — what you have marked with git add to go into the next commit.
  • Last commit (HEAD) — the most recent saved snapshot.

Every git diff variant is just a choice of which two snapshots to compare.

Working Tree vs Staged: git diff and git diff --staged

# Unstaged changes: working tree vs staging area
git diff

# Staged changes: staging area vs last commit
git diff --staged        # (--cached is the same thing)

# Everything since the last commit: working tree vs HEAD
git diff HEAD

The classic beginner confusion: you edit a file, run git add, then run git diff — and see nothing. That is correct behaviour. Plain git diff shows only unstaged changes; your edit is now staged, so it appears under git diff --staged instead. A practical workflow:

  • Run git diff while working, to see what you have touched.
  • Run git diff --staged right before committing — this is exactly what will go into the commit.

Between Commits and Branches

# Between two specific commits (older first reads naturally)
git diff 4f2a91c..8de77b0

# Between two branches (what feature adds on top of main)
git diff main..feature-login

# Between your last two commits
git diff HEAD~1..HEAD

# Against the remote after a fetch
git fetch
git diff main..origin/main

Read a..b as "what changed going from a to b": lines added in b show as plus lines. Swap the order and every plus becomes a minus. (For diffs, git diff a..b and git diff a b are the same; the two-dot syntax means something different for git log, which is a story for another day.)

Narrowing to a Single File and Summarising with --stat

# Only one file (the -- separates paths from revisions)
git diff -- src/utils/auth.js
git diff main..feature -- src/utils/auth.js

# Per-file summary instead of full content
git diff --stat main..feature

 src/utils/auth.js    | 41 ++++++++++++++++-------
 src/pages/login.js   | 12 ++++--
 package.json         |  2 +-
 3 files changed, 38 insertions(+), 17 deletions(-)

--stat is the right first move on any big diff: see which files changed and how much, then drill into the interesting ones individually.

Two Flags That Save Real Time: --word-diff and -w

--word-diff highlights changes within lines instead of whole lines — ideal for prose, documentation, and long config lines:

git diff --word-diff README.md

The API rate limit is [-100-]{+500+} requests per minute.

-w ignores whitespace differences entirely — essential after re-indenting or reformatting, when the normal diff shows hundreds of changed lines but nothing meaningful:

# Show only changes that are not pure whitespace
git diff -w

# Bonus: diff any two files, no repository required
git diff --no-index old-config.env new-config.env

Reading the Output

Every variant prints the same unified format:

diff --git a/src/app.js b/src/app.js
--- a/src/app.js
+++ b/src/app.js
@@ -8,4 +8,5 @@ function startServer() {
   const app = express();
-  const port = 3000;
+  const port = process.env.PORT || 3000;
+  app.use(express.json());
   app.listen(port);
  • a/ and b/ are the old and new versions of the file.
  • The @@ -8,4 +8,5 @@ header means: 4 lines starting at line 8 in the old file became 5 lines starting at line 8 in the new one.
  • Minus lines were removed, plus lines were added, space-prefixed lines are unchanged context.
  • A minus line directly followed by a similar plus line is a modified line — here, the port became configurable.

For a deeper dive into this format and side-by-side rendering, see unified vs split view. And when you just have two blobs of text rather than commits, paste them into the Dev Brains AI Diff Checker for an instant in-browser comparison.

Frequently Asked Questions

Why does git diff show nothing after I run git add?

Plain git diff compares your working tree against the staging area (index). Once you stage changes with git add, they move into the index, so plain git diff sees no difference. Use git diff --staged to see what is staged and about to be committed.

How do I see the difference between two branches in git?

Use git diff main..feature to see the full difference between the tips of the two branches. Add --stat for a per-file summary instead of full content, or append a path (git diff main..feature -- src/app.js) to limit the diff to one file.

How can I compare two files that are not in a git repository?

Use git diff --no-index file1 file2, which works on any two files anywhere on disk. If you would rather not use a terminal, the free Dev Brains AI Diff Checker compares two pasted texts line by line entirely in your browser.

Try the Free Diff Checker

Compare two texts line by line in your browser — no repository, no terminal, no signup required.

Related articles