Two files "look identical" and the build still fails. That sentence is a diagnosis, not a complaint: it means whatever file diff you ran stopped short of the level where the actual change lives. A trailing space, a swapped quote character, a line-ending byte — none of it shows up if your comparison only checks whether entire lines match. Every SERP result for this topic is a list of desktop apps ranked by feature count. None of them start from the one variable that decides whether a diff finds anything: granularity — line, word, or character. This guide starts there, covers how diff algorithms actually decide what changed, and works through reading diff output, picking a viewer category, and the encoding traps that make a correct diff look wrong.
What a File Diff Actually Is
A file diff is a computed answer to one question: what is the minimal set of insertions and deletions that turns file A into file B? "Minimal" is the operative word — there are usually many possible sequences of edits that would work, and a diff algorithm's job is to find one of the shortest ones, because the shortest edit script is the one that actually reads as "what changed" to a human. A diff that reported every line as deleted and every line as re-added would technically be correct and completely useless.
That framing matters because "diff" gets used loosely to mean three different things: the algorithm (the comparison logic that finds the edit script), the output format (unified, context, side-by-side — how the edit script gets printed), and the tool (the command or app that runs the algorithm and renders the format). A plain-language definition of diff covers the term's broader usage, including outside computing; this guide stays inside the technical sense — comparing two versions of text, code, or structured data and surfacing exactly what moved.
The practical stakes are concrete. diff compare is what catches an accidentally-reverted config value before it ships. check diff is what a code reviewer does before approving a pull request they didn't write. A file diff that resolves only at the wrong granularity does both of those jobs badly — it either buries a one-character change in a wall of "line changed" noise, or it misses the change outright because two lines happen to differ in a way the algorithm wasn't built to isolate.
Line, Word, and Character: Three Levels of String Difference
Every diff tool picks a unit of comparison before it does anything else, and that choice — more than the algorithm, more than the UI — determines what the tool can and can't see. There are three levels in practical use, and they nest: character-level output is built on top of a line-level match, not instead of it.
Line-level is the default for almost every command-line tool, including the
classic Unix diff. Two lines are compared as atomic units — either they're
byte-for-byte identical, or the whole line is marked changed. This is fast, and it's correct for
the overwhelming majority of code and config changes, where an edit really does replace one full
line with another. Its failure mode is granularity, not accuracy: change one character inside a
180-character line and line-level diff reports "this entire line changed," which is technically
true but tells you nothing about where inside the line to look.
Word-level sits in between. Instead of treating a line as one indivisible token, the algorithm tokenizes on word boundaries — usually whitespace and punctuation — and diffs those tokens against each other within a matched line pair. This is common in prose diff tools and some code reviewers because it isolates which specific word changed in a sentence or identifier list without going all the way down to individual letters, which for natural-language text is usually the more human-readable unit anyway.
Character-level is the finest grain that's practically useful — here you
compare characters (or, more precisely, contiguous runs of them) inside a changed line rather
than treating the line as one unit. This is what catches a swapped = for
==, a missing semicolon, or a renamed
variable that shares most of its letters with the old name. Monaco Editor — the diff engine
behind VS Code's built-in diff view and behind Diff Checker — runs exactly this: once it
identifies that a line pair changed, it runs a second, finer comparison inside that pair and
highlights only the specific characters that actually differ, leaving the unchanged parts of the
line unhighlighted. That's automatic behavior baked into how the engine renders a change — there
isn't a separate toggle for it, because it's not an optional mode, it's what a "changed line"
display means in that engine.
The practical rule: pick the coarsest granularity that still answers your question, because finer granularity costs readability. Compare characters across two entire minified JavaScript files and you get a wall of tiny colored fragments that's harder to parse than the line-level version would have been, even though it's more "accurate" in a literal sense. Reach for character-level highlighting when the suspected change is small and buried inside long lines — exactly the "files look identical" scenario — and trust line-level output for everything else.
How Diff Algorithms Decide What Changed
Underneath every diff tool sits a solution to the Longest Common Subsequence (LCS) problem: given two sequences, find the longest sequence of elements that appears in both, in the same relative order, without requiring the elements to be contiguous. Whatever isn't part of that longest common subsequence is, by definition, what changed — removed from one side, added on the other. A naive dynamic-programming solution to LCS runs in O(N×M) time, where N and M are the lengths of the two inputs; for two 10,000-line files, that's on the order of 100 million operations, which gets slow fast.
Eugene Myers'
1986
paper, "An O(ND) Difference Algorithm and Its Variations", describes an algorithm that
reframes the same problem as finding the
shortest edit script between two sequences, and solves it in O(ND) time, where D is the size of
the edit script (the number of actual differences) rather than the size of the inputs. That's
the key insight: when two files are mostly similar — the common case for version-controlled code
— D is small even when N and M are large, so the algorithm's real-world performance scales with
how different the files are, not how big they are. This is the algorithm (or a close variant of
it) behind GNU diff, Git's default diff engine, and most modern diff libraries.
Histogram diff, popularized by JGit and available in Git via
--diff-algorithm=histogram, is a variant tuned for code diffing specifically. It
weighs rare lines — ones that appear infrequently in either file — more heavily when choosing
which lines to anchor the match on, which tends to produce more intuitive results on code with
repeated boilerplate (closing braces, blank lines) than the plain Myers algorithm does by
default. The output format doesn't change; the difference is in which edit script the algorithm
picks when more than one minimal-length option exists — and code has far more of those ties than
prose does.
None of this requires memorizing to use a diff tool well, but one consequence is worth
internalizing: diff algorithms compare structure (sequences of lines or characters), not
meaning. A diff has no idea that foo(a, b) and foo(b, a) might behave
identically or completely differently — it just reports that the argument order differs. That's
a different problem from comparison coding — writing
the ==, ===, and equality checks that decide at runtime whether two
values are equal — even though the two share vocabulary. One asks "are these two values equal";
the other asks "what's the edit path between these two sequences." Comparison coding runs inside
your program; a diff runs over your program's text.
Reading the Output: Unified vs Side-by-Side
The same computed diff can be rendered two fundamentally different ways, and picking the wrong one for the job slows down review more than any tool feature does.
Unified diff is a single column of text. Unchanged lines appear once, prefixed
with a space; removed lines are prefixed with -; added lines are prefixed with
+. It's the format diff -u, git diff, and every patch file
use, because it's compact and — critically — it's directly appliable: a unified diff is also a
valid patch you can hand to patch or git apply. The
GNU
diffutils manual specifies the exact hunk-header grammar if you ever need to parse this
format rather than just read it. Its cost is that
removed and added lines are stacked sequentially rather than aligned, so matching "this old line
became that new line" requires reading both and holding the mapping in your head. The
guide to git diff between two files covers
the full flag set for generating this format from git specifically.
Side-by-side diff (also called split view) is what most people mean by a
visual code compare: the two versions sit in two columns, line-aligned, with changed regions
colored on both sides at the same vertical position.
This trades compactness for direct visual correspondence — you don't have to reconstruct the
mapping, it's drawn for you. That's the format Diff Checker defaults to, with a one-click toggle
to unified when you need the patch-compatible text instead. For a deeper breakdown of when each
format wins, including terminal-only workflows with sdiff, see the dedicated
diff side-by-side guide.
Neither format is strictly better. Unified wins when the diff needs to be piped, emailed, or applied as a patch — anywhere the output itself is a machine-readable artifact and not just a human-facing view. Side-by-side wins for review, because spatial alignment is faster for a human eye to parse than sequential +/- lines, especially once a file has more than a handful of changes scattered through it.
Four Kinds of Diff Viewer
Every diff viewer on the market falls into one of four categories, and each has a genuinely different cost structure — not just a different UI skin. Product naming varies — one tool calls itself a difference viewer, the next a code diff viewer — but the underlying job is identical, so category is the useful axis to sort by, not branding.
Online / browser-based tools like Diff Checker run in a tab, need no install, and — when the engine runs client-side, as Monaco does in the browser — never upload the content you're comparing. This is the category most guides treat as a fallback for "quick one-off comparisons" and then spend the rest of the article recommending desktop installs instead. That framing has it backwards for a lot of real workflows: if the comparison is happening because you copy-pasted something from Slack, an email, or two browser tabs, a browser tool is already where the content lives — round-tripping it through a desktop app is the extra step, not the shortcut.
Desktop apps — Beyond Compare, WinMerge, Meld, KDiff3 — install locally and typically add folder/directory comparison, three-way merge for conflict resolution, and deep format-specific comparisons (some support binary or image diffing) that a browser sandbox generally can't offer. The tradeoff is installation, OS-specific builds, and in several cases a license fee. The Beyond Compare alternatives roundup and the Notepad++ compare guide cover this category's specific tools in depth.
IDE-integrated viewers — VS Code's built-in diff editor, JetBrains' diff view, GitHub's PR file view — put code diffing inside the tool you're already coding in, wired directly to version control so you're diffing against a commit, branch, or the working tree without leaving the editor. The guide to comparing two files in VS Code covers the full set of built-in commands and shortcuts for this category.
CLI tools — diff, cmp, git diff — are
scriptable, work over SSH with no display required, and compose with the rest of a Unix
toolchain (pipe into grep, feed into a CI check, run inside a pre-commit hook). Their
cost is upfront: flags and output conventions have to be learned once, and there's no visual
highlighting unless you pipe the output into something that adds it. The next section covers this
category directly.
| Category | Example | Granularity | Cost | Best for |
|---|---|---|---|---|
| Online / browser | Diff Checker | Line + character (auto), split or unified | Free | Ad-hoc pastes, content from tabs or chat, no-install review |
| Desktop | Beyond Compare, WinMerge, Meld | Line, some support word/char modes | Free–$70 | Folder trees, three-way merges, binary/image diffs |
| IDE-integrated | VS Code diff editor, JetBrains diff | Line + character | Free (bundled) | Reviewing changes against VCS state without leaving the editor |
| CLI | diff, cmp, git diff | Line (cmp: byte) | Free (bundled) | Scripts, CI checks, headless servers, piping into other tools |
Check Diff from the Command Line: diff, cmp, git diff
Three commands cover almost every terminal comparison, and they answer different questions.
# Line-level text diff, unified format
diff -u file1.txt file2.txt
# Byte-level comparison — reports the first differing byte, nothing more
cmp file1.bin file2.bin
# Diff against git's tracked history: working tree, staged index, or a commit/branch
git diff file.txt
git diff --staged file.txt
git diff HEAD~1 HEAD -- file.txt diff is the general text-comparison tool — the full flag reference, including
context format and how to read the @@ hunk markers, lives in the
diff command in Unix/Linux guide.
cmp answers a narrower, faster question: are these two files byte-identical, and if
not, where's the first mismatch? It doesn't try to compute an edit script at all, which makes it
the right tool for verifying an exact copy — a downloaded file against its checksum source, or a
build artifact against a known-good reference — rather than for understanding what changed in a
text file. git diff layers version-control awareness on top of the same underlying
comparison: it knows what "HEAD", "staged", and a branch name refer to, so you're diffing
against a meaningful reference point instead of two arbitrary file paths.
On Linux specifically, seven more terminal and GUI options beyond these three — including
comm, sdiff, and vimdiff — are covered with real output
examples in the Linux compare files guide. On
Windows, PowerShell's Compare-Object plays the same role
diff plays on Unix, with its own output conventions worth learning separately rather
than assuming a one-to-one flag mapping.
How to Find Diff Between Two Files in the Browser
For content that already lives in a browser — two tabs, a chat message, a pasted email, a support ticket — routing it through a terminal or desktop install is friction the task doesn't need. The browser workflow: open Diff Checker, paste the "before" text into the left pane and the "after" text into the right, and the split view renders immediately with every added, removed, and unchanged region color-coded. Both panes stay editable, so a small fix doesn't require re-pasting the whole block.
The engine underneath is Monaco — the same diff editor VS Code ships — running entirely client-side in the browser tab. Nothing you paste is uploaded anywhere, there's no account to create, and the comparison happens the instant you stop typing. Three compare methods are available: Smart (the default, tuned to skip noise), Ignore Whitespace (for reformatted code that didn't actually change semantically), and Classic (a straightforward LCS-based diff with no smoothing). For long files where most content is unchanged, "Show Diff Only" collapses the identical regions and leaves a context window — configurable at 0, 1, 2, 3, or 5 lines — around each real change, and inside the editor Alt+Down / Alt+Up jump the cursor between changes without scrolling manually.
The extension adds one workflow a paste-based tool can't: comparing the raw HTML source of two open browser tabs directly, without copying either one out first — useful for checking whether a staging page and a production page actually serve the same markup. For files on disk, the file input accepts plain text and code files directly, plus DOCX and XLSX with their text content extracted automatically, so a shared spreadsheet or document doesn't need manual copy-paste either. Once a diff is in front of you, Format runs Prettier for supported languages and Normalize sorts JSON keys, sorts CSS properties, and cleans up whitespace — both useful before comparing, since inconsistent formatting is one of the most common sources of noise in an otherwise-real diff.
A comparison history (the last 50 records, stored locally in the browser via IndexedDB) means a session can be resumed later without re-pasting, and a per-change revert arrow in the gutter lets you selectively pull one side's version of a specific change back into the other pane while you're editing. None of this requires installing anything or creating an account — the entire workflow above runs in one browser tab.
Where Diffs Lie to You: Whitespace, Line Endings, Encoding, BOM
A diff never actually lies — it reports exactly what's there — but four categories of difference are invisible to the eye while being completely real to the algorithm, which produces the specific "these files look identical but the diff says everything changed" experience that this article opened with.
Trailing and leading whitespace. A line ending in three invisible spaces is not the same line as one without them, and most editors don't render trailing whitespace at all. This is the single most common cause of a diff flagging every line in a file as changed when a human reading both versions sees no difference — usually the result of one editor's "trim trailing whitespace on save" setting and another editor without it.
Line endings. Windows terminates lines with \r\n (CRLF); Unix and
macOS use \n (LF) alone. A file edited on Windows and compared against its Unix
counterpart can be byte-identical in every visible character and still diff as "every single
line changed," because the invisible \r at the end of each line is a real,
comparable byte that a naive text diff sees.
Character encoding. The same visible text can be stored as different byte sequences — UTF-8, UTF-16, Windows-1252, and others all encode at least some characters differently. A byte-level comparison of two files that display identically but were saved with different encodings reports differences at every position where the encodings diverge, even though no human editor ever touched that content.
Byte-order mark (BOM). Some tools prepend a BOM to a UTF-8 file — three invisible bytes at the very start, used historically to signal encoding and byte order. A file with a BOM and the same file without one are visually identical and differ at byte zero, which a strict diff reports as a change on line one even though nothing a human would call "content" is different.
Ignore Whitespace mode handles the first case directly — it normalizes
whitespace differences before comparing, so reformatted-but-unchanged code doesn't drown the
real edits. For the other three, the fix is upstream of the diff itself: normalize both files to
the same line-ending convention and the same encoding before comparing, using dos2unix
/ unix2dos for line endings and iconv for encoding conversion, or an
editor that reports and lets you change both settings explicitly. A tool that decodes text before
rendering a comparison — rather than diffing raw bytes — sidesteps some of this automatically,
but it's worth knowing which category a "phantom" diff falls into rather than assuming the tool
is simply wrong.
Beyond Source Code: HTML Difference, Config, and Data Compare
Line-oriented diffing assumes the input is naturally line-shaped — which code and most config formats are, but several common formats aren't, and treating them as plain line-delimited text produces technically-correct diffs that are hard to read.
HTML difference is the sharpest example. Minified or auto-generated HTML often arrives as one enormous line, or with attribute order that a templating engine reshuffles on every render without changing anything a browser would display differently. A line-level diff on raw HTML either reports "the entire document changed" (one giant line) or flags noise from attribute reordering that has zero visual effect. The fix is comparing pretty-printed, consistently-formatted HTML rather than raw server output — run both versions through a formatter first, then diff. The compare HTML online guide covers this workflow along with semantic-comparison approaches that ignore attribute order deliberately.
Config files — YAML, TOML, .env, INI — are usually fine at line level since
they're already one-setting-per-line by convention, but key order is a frequent false positive:
many config formats don't care whether timeout comes before or after
retries, but a line diff reports a reorder as a full remove-and-add pair for both
lines even when neither value actually changed.
JSON has the same key-order problem, compounded by inconsistent indentation between a hand-edited file and one written by a formatter. Normalize sorts object keys before comparing, which turns a reordered-but-otherwise-identical JSON object into a clean, empty diff instead of a wall of false positives. The JSON comparison guide covers structural versus textual JSON diffing in more depth, including when a proper JSON-aware diff (one that understands object equality regardless of key order) beats a text-level one entirely. If you need that difference as data rather than colored lines, the RFC 6902 JSON Patch format is the standard way to express and replay it.
Data compare — CSV exports, database dumps, tabular data — behaves differently again: row order frequently doesn't matter semantically even though every text-diff tool treats it as significant, and a single inserted row shifts every subsequent line, cascading into a huge false diff from what was actually a one-row change. Tools built for tabular data (spreadsheet compare features, or a dedicated data-diff tool) match on row identity rather than row position specifically to avoid this cascade — a distinction line-oriented text diff has no way to make on its own.
Choosing a Diff Tool for the Job
Four questions narrow the field fast, in this order.
Where does the content live right now? If it's already in a browser tab, an email, or a chat message, a browser-based viewer is zero extra steps; if it's two files on disk that need bulk comparison, a desktop or CLI tool avoids manual copy-paste entirely.
Is this a one-off or a repeated check? A single ad-hoc comparison favors whatever's fastest to open — a browser tab beats launching a desktop app almost every time. A comparison that needs to run automatically — on every commit, every CI run, every deploy — needs a CLI tool that can be scripted, because a GUI can't be wired into a pipeline.
Does privacy or data sensitivity matter? Pasting proprietary code or a document with personal data into a tool that uploads content to a server is a real risk most guides skip entirely. Confirm the comparison runs client-side — in the browser, with nothing sent over the network — before pasting anything sensitive into any online diff tool, not just this one.
What format is being compared? Plain code and text: any general tool works.
Folders or entire directory trees: a desktop tool with recursive folder compare. Binary files:
a byte-level tool like cmp or a hex-aware comparator — the
binary compare guide covers that category specifically,
since a text-oriented diff viewer isn't built for it. Structured data: a format-aware compare
(JSON, XML, CSV) rather than a plain text diff, for the row-order and key-order reasons above.
For the common case — code, config, or prose, one-off or occasional, no bulk folder operations needed — a browser-based viewer with character-level highlighting covers the job without installing anything. Reach for a desktop tool specifically when folder trees or three-way merge conflict resolution enter the picture, and reach for the CLI the moment the comparison needs to run unattended.
Frequently Asked Questions
What's the difference between a diff compare and a diff viewer?
"Diff compare" describes the operation — computing which lines, words, or characters changed
between two inputs. A diff viewer is the interface that renders that computation for a human
to read, usually as unified output (one column, +/- prefixes) or side-by-side output (two
columns, colored highlights). The same comparison engine can feed multiple viewers:
git diff computes a compare and prints it as unified text in a terminal, while a
GUI tool like Diff Checker or Beyond Compare runs a similar compare and renders it as two
aligned panes. The compare is the math; the viewer is the presentation.
Why does my diff viewer show a whole line as changed when I only edited one word?
Most command-line diff tools, including the classic Unix diff, only operate at
line granularity — a line either matches exactly or it doesn't, with no concept of a partial
match. Change one character in a 200-character line and the whole line is flagged as
removed-and-added, because the algorithm never looks inside it. Tools built on a code editor
engine, including Monaco (which powers VS Code's diff view and Diff Checker), run a second
comparison pass inside each changed line pair and highlight only the specific characters that
differ. If your tool shows entire lines as red/green blocks with no inner highlighting, it's
comparing at line level only.
Is a git diff the same thing as a file diff?
git diff is a specific, git-aware application of the same underlying idea. Under the hood it runs a Myers-algorithm-family comparison — the same class of algorithm a generic diff command uses — but it adds git-specific context: which commit, branch, or index state each side comes from, rename detection, and a patch-formatted unified output. A plain file diff, by contrast, compares two arbitrary files or strings with no awareness of version control at all. If both files aren't tracked in the same git repository, git diff can't compare them directly; use a plain diff command, cmp, or a standalone diff viewer instead.
How do I compare two files when the encoding might not match?
Check the encoding before you trust the diff, not after. On Linux/macOS, run
file -i <filename> to print the detected charset for each file; on
Windows, an editor that reports encoding (VS Code shows it in the status bar) does the same
job. UTF-8 with a byte-order mark and UTF-8 without one are visually identical text but
byte-different at position zero, so a byte-level or naive text diff flags a phantom change on
line one. Convert both files to the same encoding first, then diff the normalized versions —
a diff viewer that decodes text before comparing sidesteps some of this, but not mismatched
actual content.
What's the fastest way to find diff between two files without installing anything?
Open a browser-based diff tool and paste both versions in — no download, no account, no file upload to a server if the tool runs client-side. Diff Checker runs entirely in the browser on Monaco Editor's diff engine: paste two files' worth of text into the split view, and it highlights every changed line with character-level detail inside each change, with nothing sent anywhere. For files already open in two browser tabs, the extension can also diff the raw HTML source of those tabs directly, without copy-pasting into panes first.
Why do two files look identical but the diff says they're different?
Because four categories of difference are real to the algorithm but invisible on screen. Trailing whitespace: a line ending in two spaces is not the same line as one without them, and most editors never render it. Line endings: Windows CRLF versus Unix LF puts an extra carriage-return byte at the end of every line. Character encoding: the same visible character occupies different bytes in UTF-8 than in Windows-1252. Byte-order mark: three invisible bytes at the very start of a UTF-8 file that a strict comparison reports as a change on line one. Normalize both files, then compare again.
What is the difference between diff and patch?
diff computes the change; patch applies it. Running
diff -u old.txt new.txt prints a unified edit script describing how to turn the
first file into the second, and that printed text is itself the patch file. Feeding it to
patch — or to git apply — replays those edits onto a target file so it
ends up matching the new version. The relationship is one-directional by design: a patch is
just diff output stored for later, which is why unified format won over side-by-side for
distribution. Side-by-side is easier to read; unified is the one a machine can replay.
What is a code diff viewer?
A code diff viewer is a difference viewer specialized for source code: it adds syntax highlighting on both sides, character-level highlighting inside changed lines, and usually collapsing of unchanged regions so long files stay navigable. VS Code's built-in diff editor, GitHub's pull-request file view, and Diff Checker are all examples. The distinction from a general text diff is presentation rather than algorithm — the same Myers-family comparison runs underneath either way. What a code-oriented viewer adds is the ability to see, at a glance, whether a change touched a string literal, a comparison operator, or an identifier.
What is the best free diff viewer?
There isn't one winner, because the categories solve different problems. For code you're
already editing, the diff view bundled with VS Code or a JetBrains IDE costs nothing extra and
is wired to version-control state. For scripted or headless comparison, GNU diff
and git diff are free and already installed on most systems. For content that
lives in a browser tab, a chat message, or an email, a client-side browser tool like Diff
Checker is free, needs no install, and uploads nothing. Meld, WinMerge, and KDiff3 are the free
desktop options when folder trees or three-way merge are required.