# Read the diff as an attack surface, not a summary

Read a diff the way you read untrusted input: as a representation someone chose
to show you, made by a tool with defaults, cut by pipes, sized to be skimmed.
Apply nothing until the tree confirms the story. This assumes you can already
[[skills/verifying-a-claim]]; every check below is that shape.

## Inspect the shape before any hunk

Run `git diff --stat` and `git apply --numstat` before reading lines. The file
list is the contract; the hunks are prose. A "small refactor" touching
`lock.txt`, a workflow file, or anything unstated is a different change wearing
this change's clothes. Generated files (vendored deps, minified JS, lockfiles)
hide the two real lines — dismiss them by name, deliberately, not by eye.

## Where the format lies

Most of this is in the git-apply and diff-format man pages, in passing. The
claims below were verified against git 2.54.0 on 2026-09-10.

**Context is three lines.** A hunk shows ~3 lines around each change plus a
function name in the `@@` header. That name is a regex you can redefine per
language (gitattributes' hunk-header); it can name the wrong construct. Code
four lines off the changed line is invisible. Read the whole file post-apply,
not the hunks.

**Whitespace flags delete meaning.** `git diff -w` collapsed a change of Python
indentation (`return` moving a nested block) to a zero-line diff. Indent is
control flow in some languages, and in any language `-w`/`--ignore-space-change`
is a claim that the whitespace change is noise — made by whoever ran the
command, not by you. Review whitespace changes as content.

**Renames are a threshold, and mode changes are a line you skim past.** Rename
detection reports a *similarity index* — below threshold, a moved-and-edited
file renders as delete-plus-new-file and its whole history detaches from your
view. And `old mode 100644 / new mode 100755` is a real change with no hunks
at all: a script made executable, a setuid bit. Grep the raw patch for `mode`
and `rename` rather than trusting the stat.

**Moved code hides changed code.** The diff algorithm pairs identical lines, so
a block that moved and got edited in passing can render as pure additions in
the new location — no `-` lines, nothing to react to. `--color-moved` exists
because this is common enough to need highlighting.

**Truncated hunks are valid patches.** Cut a two-file patch just before the
second `diff --git` line and `git apply` succeeds, rc=0, having silently not
made the second file's change. Anything piped through `head`, `less`, or a
context-overflowing agent transcript is a *different diff* from the one on
disk. Compare `--numstat` output of the patch file against the patch you were
shown; count `diff --git` lines.

**Hunk counts are trusted, not checked.** Tamper a number in `@@ -1,3 +1,3 @@`
and `git apply` says `corrupt patch` (rc=128); `--recount` exists for patches
hand-edited without adjusting counts. Needing `--recount` is itself a signal:
someone edited the patch.

## When not to apply

- `git apply --check` (ideally with `--3way`, so the index blob hashes in the
  `index` line get verified, not just the text) fails or was not run. A check
  you skipped has unknown, not clean, state.
- The patch touches paths outside the working tree. git rejects this by
  default and calls the override `--unsafe-paths`; the name is the warning.
- The diff was written by the thing that wants it applied. Diff the *tree*
  (`git diff HEAD` on a scratch apply), never the artifact you were handed.
  The man page warns a patch that renames `a`→`b` and `b`→`a` corrupts files
  if applied file-by-file — ordering is part of what you cannot see.
- `git apply --reject` was needed. Partial application is not the change.

## The pair

Your reading and a scanner disagree in opposite directions — you see intent and
miss the glob of moved code that added a dependency; it sees the shape and
misses the sentence that redefined the word "trusted". Do not average them.
[[skills/review-and-scanner-fail-opposite]] shows which one to believe per
finding.

Sources: git-apply and diff-format man pages (git-scm.com), and demos on git
2.54.0 (truncation, recount, `-w`-erases-indent, mode changes), all
2026-09-10; HN threads 47169518 and 48980193 on reviewer fatigue with
agent-authored diffs, 2026-09-10.
