Lesson 25 of 30
A diff is Git computing the difference between two trees on the fly — and that same computed difference can be saved as a file and applied somewhere else entirely.
14 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Git doesn't store diffs — this track's very first lesson covered that: it stores full snapshots per commit. A diff is something Git computes, on demand, by comparing two trees (or a tree against your working directory, or two arbitrary commits) and describing what changed between them in a standard textual format. git diff is the command that runs that comparison and prints the result.
The critical thing to understand is that git diff means different things depending on what two things you're comparing, because Git has three distinct "areas" a change can live in: the working directory (your actual files, unstaged), the staging area / index (what you've git added, about to be committed), and the last commit (HEAD). Bare git diff compares working directory against the index — "what have I changed but not staged yet." git diff --staged (or --cached) compares the index against HEAD — "what will actually go into the next commit." git diff main..feature compares two branches' tip commits directly. All of these produce the exact same textual format; only the two things being compared change.
Because the format is just text, it's portable. Save a diff to a file and you have a patch — something you can email, attach to a ticket, or apply to a completely different checkout of the same repo with git apply.
Here's what an actual diff looks like — change one line in a file and stage nothing:
$ git diff
diff --git a/lib/auth.ts b/lib/auth.ts
index e69de29..8f3a1b2 100644
--- a/lib/auth.ts
+++ b/lib/auth.ts
@@ -12,7 +12,7 @@ export function validateSession(token: string) {
if (!token) return null;
- const decoded = jwt.verify(token, SECRET);
+ const decoded = jwt.verify(token, SECRET, { algorithms: ["HS256"] });
return decoded;
}
Reading it: --- a/lib/auth.ts and +++ b/lib/auth.ts name the "before" and "after" versions of the file. The @@ -12,7 +12,7 @@ line is a hunk header — it means "starting at line 12 of the old file, 7 lines are shown here; starting at line 12 of the new file, 7 lines are shown here." Inside the hunk, a line starting with - was removed, + was added, and a line with no prefix is unchanged context, shown so a human (or git apply) can locate exactly where the change belongs even if line numbers have shifted slightly elsewhere in the file.
Stage that same change and compare variants:
$ git add lib/auth.ts
$ git diff # empty — nothing unstaged now
$ git diff --staged # shows the same hunk, now against HEAD
$ git diff main..feature/jwt-fix # same format, comparing two branch tips instead
Now turn a diff into a portable patch file and apply it somewhere else — a second clone, a teammate's machine, anywhere:
$ git diff > jwt-fix.patch
$ cat jwt-fix.patch
diff --git a/lib/auth.ts b/lib/auth.ts
...
# on a different checkout of the same repo:
$ git apply jwt-fix.patch
$ git diff --staged --stat
lib/auth.ts | 2 +-
git apply reads the patch and reproduces the exact same edit against that checkout's files, using the hunk headers and context lines to find the right spot even if it's not byte-identical to where the diff was originally taken.
git diff alone shows nothing after git add. People stage a change, run git diff, see no output, and think Git lost their edit. It didn't — the change moved from "unstaged" to "staged," and plain git diff only shows unstaged changes. git diff --staged is what you want.git apply and git am are not the same tool. git apply applies the raw patch content as a file change with no commit created — you still have to git add and git commit yourself. git am ("apply mailbox") expects a patch that includes commit metadata (author, message) and creates a real commit directly, which is the tool actual mailing-list-based projects like the Linux kernel use for patch-based contribution.git apply relies on the context lines around a hunk matching the target file closely. If the surrounding lines have drifted (someone else edited nearby code), the patch can reject with "patch does not apply" even though the actual intended change is small and clean.A merge and a git diff are doing structurally the same work — comparing trees to find what changed — which is why this track's lesson on merge conflicts talks about Git finding a common ancestor and diffing forward from it in two directions at once. Once you can read a hunk header and a +/- line by eye, a conflict marker in the middle of a botched merge stops being mysterious — it's just two diffs against the same base, shown side by side instead of resolved automatically.