Why a text diff flags lines that look identical: line endings, whitespace and invisible characters

Updated 5 min read

A diff compares characters, and the most common reason two lines look identical but are marked as changed is that a character you cannot see is different. Each kind has its own signature, and a few of them are invisible to some diff tools, including the browser one linked below, so it is worth knowing which.

Every line is changed: line endings

The symptom is a diff that marks the whole file, or every line, as removed and re-added although a side-by-side read shows nothing different. Windows tools write lines ending in \r\n (CRLF), Unix tools write \n (LF), and a byte-level comparison sees every line as different. It usually appears after an editor, a copy from a Windows machine, or a git setting such as core.autocrlf converts a file.

A few ways to confirm which endings a file has:

file notes.txt              # "... with CRLF line terminators" when it has them
git ls-files --eol          # i/ is the index, w/ the working tree: i/lf w/crlf
grep -c $'\r' notes.txt     # number of lines containing a carriage return

In git, git diff --ignore-cr-at-eol hides this class of change, and a .gitattributes line such as * text=auto normalises endings on commit so the problem stops recurring.

One limit of the Text Diff tool on this site applies here. It splits both texts on CRLF, lone CR and LF alike, so it deliberately does not flag a pure line-ending difference. If two files differ only by endings, the tool reports no differences, which is correct for the question “did the content change” and unhelpful for “why does my other tool say they differ”. Use the commands above to check endings, and the tool to check content.

One line differs by trailing spaces or tabs

Invisible trailing whitespace is the next most common source of noise: a line ending in a space, or a tab where the other side has spaces. The Text Diff tool draws trailing spaces as · and every tab as →, so a line that looks unchanged but is marked will show the marker at its end or in its middle. Tabs inside a line are marked anywhere, while leading spaces are not drawn, so a tab-versus-spaces indentation difference shows up as → on one side only.

The Ignore trailing whitespace checkbox strips spaces and tabs from the end of each line before comparing, and it changes nothing else. Trailing whitespace is ignored in the comparison but still drawn, so you can see which lines would have been marked. The setting is remembered in your browser. In git the equivalents are --ignore-space-at-eol, which matches that behaviour, and -w, which ignores all whitespace and is too broad for languages where indentation means something, such as Python or YAML.

The last line: a missing final newline

A file that ends ...end and one that ends ...end\n have the same lines but are not the same file. Git reports it with \ No newline at end of file, and the Text Diff tool shows a note when one side ends with a newline and the other does not. Because both sides still contain the same lines, the line list reports no difference, and the note is the only sign. When you press Copy diff on a diff that has this mismatch, the copied text ends with that same \ No newline at end of file line. The usual cause is an editor that adds or removes the final newline on save.

Characters that look like another character

When no · or → appears and a line is still marked, suspect a character that renders as nothing, as an ordinary space or as a near-twin of an ASCII symbol:

CharacterCode pointWhere it comes from
No-break spaceU+00A0web pages, word processors, some chat apps
Zero-width spaceU+200Bcopying from web pages and chat
Byte order markU+FEFFfiles saved as “UTF-8 with BOM”; it sits before the first character
Ideographic spaceU+3000Chinese and Japanese input methods
Full-width comma and periodU+FF0C, U+3002, and 。 look like , and . in many fonts

A byte order mark explains the case where only line 1 differs. To identify any suspect character, paste it into a browser console and read its code: "a b".charCodeAt(1).toString(16) prints a0 for a no-break space. Accents can also differ without any visible change: é can be one character, U+00E9, or e followed by a combining U+0301, and a normalisation step such as "é".normalize("NFC") makes them equal. macOS file names are a well-known source of the second form.

Why a one-word edit marks the whole line

The Text Diff tool compares whole lines. It does not highlight the changed word inside a line, so a changed comma and a rewritten sentence look the same, and the unchanged text on that line appears under both the removed and the added row. That matters for prose. A paragraph that was re-wrapped to a different width changes every line even though no word moved. Before diffing prose, put one sentence on each line on both sides, or unwrap both to the same width; the diff then points at the sentence that changed.

A moved block also appears as removed in one place and added in another. A line diff has no concept of “moved”. And two correct diffs of the same pair can differ in how they pair lines up, because several alignments are equally short. The tool’s output is a valid shortest edit script, not the only one, so the line numbers may not match what git prints for a file full of repeated lines.

Size limits and what the numbers mean

Each side is limited to 4,000 lines. Within that, the tool trims the lines that match at the start and end, so a one-line edit in a long file is cheap, and it asks you to paste a smaller range when the area between the first and last change is too large to align. The similarity figure is the share of lines that are unchanged, counted over both sides, and it is capped at 99% whenever any line differs so that it never reads 100% beside a list of changes.

Copying the result

Copy diff produces unified-diff text: a header such as @@ -3,4 +3,5 @@ for each changed region, three lines of unchanged context, then lines prefixed with +, - or a space. It has no file-name header lines, so paste it into a review comment or a ticket rather than feeding it to patch. As with the rest of the tool, comparison and copying happen in the page and nothing is uploaded.

Open the tool: Text Diff & Line Compare

Back to guides

More guides