When two versions of a file disagree, the worst thing you can do is scroll through both side by side and trust your eyes. Humans miss the changed character on line 400. The right move is to let a diff tool show you precisely what moved. This is the toolkit I reach for.
Why not just read both files
Your brain is good at meaning and bad at spotting a single swapped character between two long blocks of similar text. A diff tool compares mechanically and marks exactly the added, removed and changed spans. It is faster and it does not get tired. Reserve your judgement for understanding the change, not for finding it.
git diff basics worth memorising
The plain git diff shows unstaged changes. A few flags turn it from noisy to surgical:
git diff # unstaged changes vs last commit
git diff --cached # staged changes
git diff --stat # just the summary: files and line counts
git diff -w # ignore all whitespace
git diff --ignore-blank-lines
git diff --word-diff # highlight changed words, not whole lines
git diff --color-words
--word-diff is the one people underuse. When a long line changed by a single word, line mode marks the entire line as deleted-plus-added; word mode shows just the word. --color-words is the same idea with colored inline highlights instead of delimiters.
Comparing across commits and branches
You are rarely comparing just the working tree. Two forms cover most needs:
git diff main..feature # changes on feature not in main
git diff main...feature # changes since feature branched from main
git diff abc123 def456 # two specific commits
git diff main~3 main # last three commits on main
The three-dot form main...feature is the one you usually want for a PR review: it shows what feature introduced relative to their common ancestor, rather than everything that diverged in both branches. The two-dot form shows the total difference between the tips.
Reading a unified diff
diff -u is the lingua franca of patches and the format Git speaks. Knowing how to read it means you can review a patch file without tooling:
--- a/config.yaml
+++ b/config.yaml
timeout: 30
-retries: 2
+retries: 5
logging: true
The --- line is the old file, +++ the new. Between them sits a hunk header enclosed in at-signs that records the line ranges, for example "old started at line 1 for 4 lines, new at line 1 for 4 lines" — I have omitted that header line above so the essential parts stay readable. Lines with - were removed, + added, and unmarked lines are context that stayed the same. A patch is just a collection of these hunks.
Side-by-side and editor views
For a closer look, vimdiff opens both files in synchronized panes:
vimdiff old.txt new.txt
# inside: ]c next diff, [c prev diff, :diffupdate
VS Code, IntelliJ and most editors have a built-in compare view triggered from the file explorer or the Source Control panel. These are better than eyeballing because they jump between changes for you.
The classic accident: CRLF vs LF
Open a file saved with Windows line endings (CRLF) in a Unix tool, or vice versa, and the entire file shows as changed even though no content differs. That is the single most common "why is everything red" moment. The fix is git diff -w to confirm, then normalise the line endings:
# see the real, content-only difference
git diff -w
# normalise via .gitattributes so it never recurs
echo "* text=auto eol=lf" > .gitattributes
git add --renormalize .
The .gitattributes line tells Git to store text as LF and convert on checkout, so contributors on different platforms stop fighting over line endings.
Comparing files that differ in encoding
A file saved as GBK and the same content saved as UTF-8 are byte-different but human-identical. A raw diff will scream "everything changed". Convert to one encoding first:
iconv -f GBK -t UTF-8 old.txt > old.utf8.txt
git diff --no-index old.utf8.txt new.utf8.txt
git diff --no-index compares two arbitrary files outside any repository, which is handy for quick one-offs.
Comparing binary-ish files
For files where a text diff is meaningless — images, archives, PDFs — fall back to hashes to answer "are they the same at all":
sha256sum a.bin b.bin
If the hashes match, the bytes match; if not, a text diff will not help and you need a domain-specific tool.
What to look at during code review
Do not read diffs line by line hoping to catch bugs. Diff to locate the change, then review with intent:
- Intent first. Does the change do what the ticket asks, and nothing extra?
- Edges and errors. New code paths, empty inputs, off-by-one, null handling.
- Tests. Is the behaviour covered, not just the function touched?
- Whitespace noise. Reformatting mixed into logic changes hides the real edit — ask for a separate commit.
When the file is too big or sensitive for a web tool
Online diff sites are fine for public snippets, but anything with secrets or anything large belongs on your machine. For everyday files the commands above are enough; for very large or structured data, the diff checker runs locally so nothing leaves your browser.
The habit to build: reach for git diff (or diff -u) before your eyes. It is the difference between "I think these are the same" and "here is exactly what changed".