Understanding unified diff
@@-lines, plus, minus, that's the whole format.
The structure of a unified diff, the hunk header, and why inline vs side-by-side is a matter of context.
The format, line by line.
A unified diff has three layers. Header lines: --- a/file and +++ b/file identify the before and after. Hunk headers: @@ -10,5 +10,7 @@ means "at line 10 in the old file (5 lines), shown as line 10 in the new file (7 lines)". Body lines: lines prefixed - are removed, + are added, space is unchanged (context). That's the entire format; Git, Mercurial, Subversion all emit the same thing.
A worked diff.
--- a/greet.js
+++ b/greet.js
@@ -1,4 +1,4 @@
function greet(name) {
- return "Hello " + name;
+ return `Hello ${name}`;
} One hunk, four lines of context, one line removed, one added. The change modernises string concatenation to a template literal. The unchanged surrounding lines are shown for context — typically three on each side, so reviewers can see what's around the change without opening the file.
A one-line edit
@@ -1,4 +1,4 @@
Before: 4 lines starting at line 1. After: same.
one - line, one + line, two unchanged each side
= Minimal diff, full context
Inline vs side-by-side.
Two viewing modes. Inline (the format above) interleaves removed and added lines vertically; compact, shows small changes well, used by command-line git, GitHub's "unified" view. Side-by-side shows old on the left, new on the right; uses more space, better for large changes where you want to scan both versions; used by GitHub's split view, GitLens, every IDE diff viewer. The right mode depends on screen real estate and change size, not on which is "better".
Context lines, the configurable bit.
Default is 3 lines of context on each side. git diff -U10 shows 10 lines on each side. git diff -U0 shows none — just the changed lines. More context makes the diff easier to review (you see surroundings) but harder to scan (more lines per hunk). For automated tools (LLM-generated patches, refactor scripts), -U0 is sometimes useful; for human review, the default is fine.
Word-level diff.
The unified format is line-based. For prose or text where a one-word change is hidden in a line that "changed" entirely (one word swapped), word-level diff is more readable. git diff --word-diff changes the format: removed words wrapped in [-...-] and added words in {+...+}. Tools like difft (difftastic) go further with AST-aware diffing — they understand that "moved a function" isn't "the whole function was removed and re-added".
The patch is the diff.
A diff and a patch are the same format. git apply patch.diffre-applies the changes to a clean checkout. The format's most powerful property: it's self-contained — given the diff and the original file, the result is reconstructible. Patches travel through email, gists, and PR descriptions perfectly; reviewers reproduce the change locally without a remote ref. Git's older workflow (kernel mailing-list patches) was built entirely on this property.