git diff: Comparing Working Directory, Staged Changes, and Commits
While `git status` tells you *which* files have changed, `git diff` tells you exactly *what* changed inside them — line by line. This lesson covers the different comparisons `git diff` can perform depending on which flags you use: comparing your working directory against the staging area, comparing staged changes against the last commit, and comparing any two commits or branches directly against each other.
Learning Objectives
- Use plain git diff to see unstaged changes in the working directory.
- Use git diff --staged (or --cached) to see staged changes not yet committed.
- Compare two specific commits directly using git diff.
- Read diff output syntax: +, -, and hunk headers.
Key Terms to Know Before Learning git diff
- git diff: A command that shows line-by-line differences between two states of a project's content.
- Unstaged diff: The default git diff output, showing differences between the working directory and the staging area.
- Staged diff: The output of git diff --staged (or --cached), showing differences between the staging area and the last commit.
- Hunk: A contiguous block of changed lines shown in diff output, marked with a header like @@ -3,4 +3,6 @@ indicating line ranges.
- Diff between commits: git diff <commit1> <commit2> shows exactly what changed between any two specific points in history.
How git diff Actually Works
`git diff`, run with no arguments, compares your **working directory against the staging area**. In practice, this shows you every change you've made since the last time you staged (or, if nothing is staged, since the last commit) — exactly the 'Changes not staged for commit' files that `git status` mentions, but shown here with full line-by-line detail rather than just filenames.
Once you've staged some changes with `git add`, plain `git diff` will no longer show them (since they're no longer purely 'unstaged'). To see exactly what's currently staged and about to be included in your next commit, use:
```
git diff --staged
```
(or the equivalent, older synonym `git diff --cached`). This compares the **staging area against the last commit**, letting you review precisely what you're about to save before you actually run `git commit` — an excellent habit for catching mistakes before they become permanent history.
Git diff output follows a standard, consistent format. Each changed section (a 'hunk') is introduced by a header like:
```
@@ -12,6 +12,8 @@
```
which means: starting at line 12 of the old version, 6 lines are shown; starting at line 12 of the new version, 8 lines are shown. Lines prefixed with `-` (often shown in red) were removed from the old version; lines prefixed with `+` (often shown in green) were added in the new version; unprefixed lines provide unchanged surrounding context.
Finally, `git diff` isn't limited to comparing the working directory or staging area against the last commit — you can compare **any two commits** directly by supplying their hashes (or branch names):
```
git diff a1b2c3d 9f8e7d6
```
This shows exactly what changed between those two specific points in history, regardless of how much time or how many other commits separate them — extremely useful when investigating when a specific bug or behavior was introduced.
git diff: Visual Walkthrough
Draw three boxes in a row: 'Working Directory', 'Staging Area', 'Last Commit (HEAD)'. Draw a labeled bidirectional-style comparison arrow from 'Working Directory' to 'Staging Area' labeled 'git diff (default)'. Draw a second comparison arrow from 'Staging Area' to 'Last Commit' labeled 'git diff --staged'. Below, draw a separate pair of commit-history dots labeled 'commit A' and 'commit B' with a comparison arrow between them labeled 'git diff A B'.
git diff: Quick Reference Table
| Command | Compares | Typical Use |
|---|---|---|
| git diff | Working directory vs. staging area | See unstaged edits you've made since last git add |
| git diff --staged | Staging area vs. last commit | Review exactly what will be committed, before committing |
| git diff <commit1> <commit2> | Any two specific commits | See what changed between two points in history |
| git diff branchA branchB | Tip commits of two branches | Compare how two branches differ from each other |
git diff: Command Syntax and Examples
# See unstaged changes (working directory vs. staging area)
git diff
# Example output:
# diff --git a/app.js b/app.js
# @@ -12,6 +12,8 @@
# function login(user) {
# - console.log("logging in");
# + console.log("attempting login for", user);
# + validateCredentials(user);
# return true;
# }
# See what's staged and about to be committed
git diff --staged
# Compare two specific commits directly
git diff a1b2c3d 9f8e7d6
# Compare two branches
git diff main feature/login
Breaking Down the git diff Example
The sample output shows a typical hunk: the `-` line (`console.log("logging in")`) was removed, and the two `+` lines were added in its place, with the unprefixed `function login(user) {` and `return true;` lines shown as unchanged surrounding context. Running `git diff --staged` after staging this change would instead show it compared against the last commit, confirming exactly what's about to become permanent history. The last two commands demonstrate git diff's flexibility beyond the working directory/staging comparisons — directly comparing two arbitrary commits or two branches.
How git diff Is Used on Real Engineering Teams
- Code review platforms like GitHub and GitLab render pull request 'diffs' using exactly this same line-by-line format, making fluency in reading raw git diff output directly transferable to reading code reviews.
- Developers routinely run git diff --staged as a final sanity check right before committing, to catch accidental leftover debug code or unintended changes before they become permanent history.
- Bug investigation often involves git diff between an older known-good commit and the current one to narrow down exactly which lines changed and might have introduced a regression.
- Merge and rebase conflict resolution tools rely on the same diff concepts, showing 'ours' vs. 'theirs' changes in a very similar +/- format to help developers reconcile conflicting edits.
git diff Interview Questions and Answers
Q1. What is the difference between git diff and git diff --staged?
Plain git diff compares the working directory against the staging area, showing changes you've made that haven't been staged yet. git diff --staged (or --cached) compares the staging area against the last commit, showing exactly what will be included if you run git commit right now.
Q2. How do you read the +/- symbols in git diff output?
Lines prefixed with a minus sign (-) represent content that was removed from the old version, and lines prefixed with a plus sign (+) represent content added in the new version. Unprefixed lines are unchanged context shown to help orient the reader within the file.
Q3. Can git diff compare two commits that aren't adjacent in history?
Yes. Running git diff <commit1> <commit2> with any two commit hashes (or branch names) shows exactly what changed between those two specific points, regardless of how many other commits exist between them in the project's history.
git diff Quiz: Test Your Understanding
1. What does plain git diff (with no flags) compare by default?
- Two remote repositories
- The working directory against the staging area
- The staging area against the last commit
- Two arbitrary commits
Answer: B. The working directory against the staging area
Explanation: Without any flags, git diff shows unstaged changes by comparing your current working directory content against what's currently in the staging area.
2. Which command shows exactly what will be included in your next commit?
- git diff
- git diff --staged
- git log
- git status --all
Answer: B. git diff --staged
Explanation: git diff --staged compares the staging area against the last commit, showing precisely what changes are staged and will be saved if you commit now.
3. In git diff output, what does a line starting with a plus sign (+) indicate?
- A line that was deleted
- A line that was added in the newer version
- A line with a syntax error
- A merge conflict marker
Answer: B. A line that was added in the newer version
Explanation: Lines prefixed with + represent content that exists in the newer version being compared but not in the older one — i.e., an addition.
git diff: Common Mistakes Beginners Make
- Running git diff after staging changes and being confused when it shows nothing — remembering to use git diff --staged for staged changes.
- Misreading the +/- symbols, especially in files with many small, interleaved changes across a large hunk.
- Assuming git diff can only compare adjacent commits, rather than any two arbitrary commits or branches.
- Skipping git diff --staged before committing, missing an opportunity to catch mistakes before they become permanent history.
git diff: Exam-Ready Quick Notes
- git diff (no flags): working directory vs. staging area.
- git diff --staged (or --cached): staging area vs. last commit.
- git diff <commit1> <commit2>: compares any two arbitrary commits directly.
- Diff symbols: - removed, + added, unprefixed = unchanged context.
git diff: Key Takeaways
- git diff reveals exact line-by-line content changes, complementing the file-level summary that git status provides.
- Different diff comparisons — working directory vs. staging, staging vs. last commit, or commit vs. commit — serve distinct, important purposes.
- Reviewing git diff --staged before committing is a simple, high-value habit for catching mistakes early.
Frequently Asked Questions About git diff
Q1. What does git diff show?
It shows the exact line-by-line differences between two states of your project — by default, between your working directory and the staging area, revealing changes you haven't staged yet.
Q2. How do I see what's currently staged before committing?
Run git diff --staged (or the equivalent git diff --cached). This compares the staging area against your last commit, showing exactly what will be saved if you run git commit right now.
Q3. What do the plus and minus signs mean in git diff output?
A minus sign (-) marks a line that was present in the older version but removed. A plus sign (+) marks a line that's new in the current version. Lines with no prefix are unchanged and shown only for surrounding context.
Q4. Can I compare two commits that aren't next to each other in history?
Yes. git diff <commit1> <commit2> works with any two commit hashes or branch names, no matter how far apart they are in the project's history, showing exactly what changed between those two points.
Q5. Why does git diff show nothing after I've staged all my changes?
Because plain git diff only shows unstaged changes (working directory vs. staging area). Once everything is staged, there's nothing left to show there — use git diff --staged instead to see the staged changes compared to the last commit.
Summary
`git diff` shows precise, line-by-line differences between two states of a project. Run with no flags, it compares the working directory against the staging area, revealing unstaged edits. `git diff --staged` (or `--cached`) instead compares the staging area against the last commit, showing exactly what's about to be saved. `git diff <commit1> <commit2>` extends this further, comparing any two arbitrary commits or branches directly. Diff output uses a consistent format — lines prefixed with `-` were removed, lines prefixed with `+` were added — the same format used by pull request review tools like GitHub, making this a directly transferable skill for code review.