Git Diff
Compare unstaged, staged, and committed states so you can preview exactly what the next commit will record.
Every diff has two sides
Return to the three-state model
The working tree is the editable checkout on disk. The index is Git’s proposed next snapshot. HEAD normally resolves to the current branch’s latest commit. A diff does not mean “show every change”; it means “compare these two named states.”
What exists on disk now
Your current tracked file content, including edits made after the latest staging operation.
What is staged next
The proposed snapshot assembled by git add, which may differ from both disk and HEAD.
The three core comparisons
# Working tree compared with the index
git diff
# Index compared with HEAD
git diff --cached
# Working tree compared with HEAD
git diff HEADgit diff
Working tree versus index. Read this as “what tracked edits have I not staged?”
git diff --cached
Index versus HEAD. Read this as “what patch is staged for the next ordinary commit?”
git diff HEAD
Working tree versus HEAD. Read this as “what tracked content differs from the current commit, staged or not?”
git status --short
A state summary, not a patch. Use it beside diff to find untracked paths and staged/unstaged combinations.
Test what you learned
Type the command that shows the patch currently staged for the next commit.
One file can have staged and unstaged changes
Staging is not a permanent label attached to a filename. Git records file content in the index. If you stage a file and edit it again, the staged version remains in the index while the newer working copy contains an additional change.
EDITChange line Agit diffshows line A because the working tree differs from the index.STAGERun git addLine A moves into the index; cached diff now shows it.
EDIT AGAINChange line BPlain diff shows line B while cached diff still shows line A.
REVIEWChoose deliberatelyStage line B only if it belongs in the same coherent snapshot.
Run all three core comparisons when status displays both staged and unstaged changes for one path. No single default command tells the whole story.
Read a unified patch
diff --git a/src/greeting.js b/src/greeting.js
index 42f10ab..67d9a11 100644
--- a/src/greeting.js
+++ b/src/greeting.js
@@ -1,4 +1,5 @@
export function greeting(name) {
- return "Hello " + name;
+ const safeName = name.trim();
+ return `Hello ${safeName}`;
}--- a/ and +++ b/
Labels for the old and new sides of the file comparison. They are not arithmetic signs.
@@ -1,4 +1,5 @@
A hunk header describing the old and new line ranges represented in this patch section.
Space prefix
Unchanged context that helps Git and reviewers locate the surrounding code.
Minus and plus prefixes
Content removed from the old side and added to the new side. A modification is represented as removal plus addition.
Understand untracked and newly staged files
Plain git diff does not print a brand-new untracked file because that path is not represented in the index side of the comparison. git status --short reports it as ??. Once you intentionally stage it, git diff --cached shows the complete new-file patch.
git status --short
# ?? src/new-helper.js
git add src/new-helper.js
git diff --cached -- src/new-helper.jsCompare commits, branches, and tags
# Changes needed to move from the older commit to the newer commit
git diff 2a71b4c 8f42d91
# Commits can also be branches, tags, or other revision expressions
git diff main feature/search
# Restrict the comparison to one path
git diff main feature/search -- src/search.jsWith two revisions, Git shows the changes needed to transform the first snapshot into the second. The command does not move HEAD, modify either branch, or imply which side is correct. Direction matters: reversing the revisions reverses additions and deletions.
git diff A..B
For diff, this is effectively the same endpoint comparison as git diff A B: snapshot A against snapshot B.
git diff A...B
Compare B with the merge base of A and B. This often answers “what did B introduce since the histories diverged?”
Test what you learned
Type the command that compares feature/search with its merge base against main.
Narrow and format the review
git diff --stat
git diff --name-status
git diff --word-diff
git diff --check
# Show more surrounding lines when context matters
git diff --unified=8
# Ignore whitespace-only changes only when that matches the review question
git diff --ignore-all-spaceSummary formats help you orient yourself, but they do not replace reading the relevant patch. --stat gives a file-level size overview. --name-status names added, modified, deleted, and renamed paths. --word-diff can clarify prose changes. --check catches whitespace errors introduced by the patch.
Use a pathspec after -- to reduce noise: git diff -- src/ README.md. Path filtering answers a smaller question; it can hide related changes elsewhere, so return to the complete staged patch before committing.
Renames, binary files, and generated output
Renames are detected
Git stores snapshots, not a permanent rename event. Diff compares similar deleted and added content and may present a rename with a similarity score.
Binary content is summarized
Ordinary line patches may not be meaningful. Verify the file, source, size, rights, and whether it belongs in Git.
Generated files add noise
If policy requires committed output, review both source and output. Otherwise, ignore reproducible artifacts rather than burying behavior changes.
Whitespace options change evidence
Ignoring whitespace can reveal logic changes, but it can also hide meaningful formatting in whitespace-sensitive files.
Guided practice: build and review one snapshot
Use a disposable repository or your learning journal. Begin from a clean status and never use real secrets in practice files.
- 01Create two tracked files
Add a README and a small source file, then commit the baseline.
- 02Make two coherent edits
Change both files and use plain diff to explain every hunk against the index.
- 03Stage only one path
Run cached diff and prove only that path is proposed for the next commit.
- 04Edit the staged path again
Use plain diff, cached diff, and diff HEAD to identify the three distinct views.
- 05Review and commit
Run
git diff --cached --check, inspect the complete staged patch, and commit only when its message matches the evidence.
Independent lab: audit a mixed working state
Create a disposable repository with at least three committed files. Produce a state containing one staged modification, one unstaged modification, one file with both staged and unstaged changes, and one untracked file. Then produce an audit that:
- Uses short status to classify all four paths.
- Uses plain diff to explain only the unstaged tracked patches.
- Uses cached diff to explain exactly what the next commit would record.
- Uses diff HEAD to explain the combined tracked difference.
- Uses a pathspec to inspect the file with both states independently.
- Runs
--stat,--name-status, and--checkand explains what each view omits. - Creates a branch, makes one additional commit there, and compares it with main using both two-dot and three-dot diff forms.
- Leaves the repository in a clean state with two coherent commits you can explain.
Common diff mistakes
Assuming empty means clean
Plain diff can be empty while staged or untracked changes exist. Check status and cached diff.
Reviewing only the file list
Name-status finds scope; it does not reveal unsafe values or incorrect behavior inside the patch.
Forgetting direction
git diff A B describes A transformed into B. Reverse the endpoints and the patch reverses.
Confusing log three-dot with diff
The same punctuation has command-specific meaning. Diff uses the merge base and second endpoint.
Hiding whitespace too early
Ignore options alter the evidence. Read the normal patch before deciding whitespace is irrelevant.
Committing without cached review
The working-tree patch is not necessarily the staged patch. Review the index directly.
Lesson review
You can now treat git diff as a precise comparison tool instead of one command with mysterious output. You can review unstaged work, inspect the proposed next snapshot, compare historical endpoints, read patch structure, and explain the limits of each view.
- I can name both sides of plain, cached, and HEAD diff comparisons.
- I use status to find untracked paths and diff to inspect tracked content changes.
- I understand unified patch headers, context, additions, and deletions.
- I can compare revisions and explain two-dot versus three-dot diff behavior.
- I review the complete cached patch and whitespace check before committing.