How a diff decides what changed
There is no single correct diff between two texts, only ones that are cheaper or more readable. The problem is formally the longest common subsequence: find the largest set of lines appearing in both documents in the same order, and everything outside it is an insertion or a deletion. The classic solution is Eugene Myers' 1986 algorithm, which is what most tools including Git use by default, and it runs in time proportional to the product of the document length and the number of differences.
That "number of differences" term is why diffing two similar files is fast and diffing two unrelated ones is slow. It is also why a file where every line changed — a re-indentation, or a line-ending conversion — takes far longer and produces a useless result.
Myers finds a minimal diff, but minimal is not always readable. When a block is inserted next to similar text, it will happily align the closing brace of the new block with the closing brace of the old one, producing hunks that start and end in visually strange places. Git ships alternative heuristics — patience and histogram — that prefer to anchor on lines that appear exactly once in both files, which usually yields hunks that match how a human would describe the change.
Line, word and character granularity
Comparing by line is the right default for code, because a line is a meaningful unit and the output maps onto how you edit. Its weakness is that changing one character marks the whole line as replaced, which is noisy for prose where a paragraph may be a single long line.
Word-level comparison suits prose and produces the inline highlighting you want when reviewing edited text. Character-level is finer still and is useful for short strings — a changed identifier, a modified URL — but on longer documents it degenerates into confetti, matching stray letters across unrelated words because they technically form a common subsequence.
A practical approach is to diff by line first to locate the changed region, then look at the word-level detail within it. That is exactly the hierarchy code review tools present.
Invisible differences that make files look identical
The most frustrating diff is one showing every line as changed when the text looks the same. Almost always the cause is line endings: Windows uses CRLF, Unix and macOS use LF, and the carriage return is invisible in most editors. A file that has been through a Windows editor once will differ from its original on every single line.
Trailing whitespace is the next candidate — spaces at the end of a line that no editor renders — followed by tabs versus spaces, which look identical at the same visual width. Then there are characters that are genuinely invisible: a UTF-8 byte-order mark at the start of a file, non-breaking spaces pasted in from a word processor or a web page, and smart quotes substituted for straight ones by autocorrect.
If two files should be identical and are not, comparing their byte lengths is the fastest first check, and normalising line endings before diffing usually resolves it.