Code Diff Explained: How to Compare Files and Spot Changes
Learn how diff algorithms like Myers work, how to read unified diffs, and how to compare files accurately with command-line and online tools.
.jpg)
Every developer compares files, whether they call it that or not. You review a pull request, check what a deployment changed, spot the difference between two config files, or work out why a test passed yesterday and fails today. Behind all of these is the same idea: a diff, a compact description of how one text became another. This guide explains how diffs work, how to read the common formats, which algorithms produce them, and how to compare files quickly and accurately.
What is a diff?
A diff is a list of changes that turns one version of a text into another. The classic Unix command diff was written in the 1970s and is still the basis of nearly every comparison tool. Version control systems such as Git, code review platforms and editors all rely on the same idea.
A diff answers a simple question: what is the smallest set of additions and deletions that converts text A into text B? Anything not mentioned in the diff is unchanged. That is why a good diff is short even when the files are long, and why reading a diff is much faster than reading both files side by side.
Two terms are worth knowing:
- Hunk. A contiguous block of changes, along with a few unchanged lines of context.
- Context. Unchanged lines shown around a change so you can tell where it belongs.
How diff algorithms work
At the core of most diff tools is a classic computer science problem: the longest common subsequence, or LCS. Given two sequences of lines, the LCS is the longest list of lines that appear in both, in the same order, though not necessarily next to each other. Everything in the first file that is not in the LCS was deleted, and everything in the second file that is not in the LCS was added. The Wikipedia article on the longest common subsequence explains the idea and its relationship to diff.
A simple dynamic programming solution compares every line of one file with every line of the other, which takes time and memory proportional to the product of the file lengths. That is too slow for large files. Practical tools use cleverer methods.
Myers' algorithm
The most influential method is Eugene Myers' 1986 algorithm, described in the paper "An O(ND) Difference Algorithm and Its Variations". It finds a shortest edit script, using time proportional to the size of the input multiplied by the number of differences, N × D. When two files are similar, so D is small, it is very fast. This is the default algorithm in GNU diff and in Git.
The intuition is to treat the problem as finding a path through a grid. Moving right means deleting a line from the first file, moving down means inserting a line from the second, and moving diagonally means the lines match and cost nothing. The algorithm finds the path with the fewest non-diagonal moves.
Other strategies
The shortest possible diff is not always the most readable one. Git offers alternatives through the --diff-algorithm option, documented in the git-diff manual:
myers: the default, fast and minimal.minimal: spends extra time to guarantee the smallest diff.patience: matches unique, meaningful lines first, which often produces cleaner results for code that was moved or reorganised.histogram: a faster refinement of patience that handles repeated lines well.
If a diff looks strange, with braces or blank lines matched to the wrong place, switching algorithm often gives a clearer picture.
Reading diff formats
You will meet a few standard formats.
Normal format
The original diff output, showing line numbers and a change type: a for added, d for deleted and c for changed. It is compact but lacks context, so it is rarely used now.
Unified format
The format used by Git and most review tools. It shows context lines and marks changes with + and -:
--- a/config.json
+++ b/config.json
@@ -3,7 +3,7 @@
"name": "devutilx",
"version": "1.2.0",
- "debug": true,
+ "debug": false,
"port": 8080,
"features": {
"blog": true
Reading it from the top:
--- a/config.jsonand+++ b/config.jsonname the old and new files.@@ -3,7 +3,7 @@is the hunk header. It means "starting at line 3, seven lines from the old file correspond to starting at line 3, seven lines in the new file".- Lines starting with a space are unchanged context.
- Lines starting with
-exist only in the old file. - Lines starting with
+exist only in the new file.
A changed line always appears as a removal followed by an addition. That is why one edited line shows up as two lines in a diff.
Context format
An older format that prints the old and new blocks separately, marked with ! for changed lines. It is largely replaced by unified format.
Side-by-side format
Two columns, old on the left and new on the right, with changes highlighted. It is the easiest to read for humans, and it is what most visual tools, including the DevUtilX code diff tool, display.
Line diffs and word diffs
Most diffs work line by line. If you change one word in a long line, the whole line shows as removed and re-added. That is accurate, but hard to read for prose or minified code.
Word-level or character-level diffs highlight only the part that changed within a line. Many tools combine both, showing a line-level diff and then highlighting the exact characters inside changed lines. In Git, git diff --word-diff does something similar. Word diffs are especially useful for documentation, Markdown, configuration values and long strings.
Real-world use cases
Code review. Pull requests are diffs. Reviewers read what changed, not the whole file.
Debugging regressions. When something breaks, comparing the working version with the broken one narrows the search quickly. Tools like git bisect automate that search.
Comparing configuration. Spot the difference between the staging and production versions of a config file, an environment file or a Kubernetes manifest.
Checking generated output. Compare build output, API responses or exported data before and after a change to confirm nothing unexpected moved.
Merging. Three-way merges use a diff between each branch and their common ancestor to combine changes automatically.
Plagiarism and change detection. Diffs show how much of a document was edited, for contracts, policies or essays.
Testing. Snapshot and golden-file tests fail with a diff, which is the quickest way to see what changed.
Common mistakes
- Whitespace noise. Changes in indentation, trailing spaces or line endings flood the diff. Use "ignore whitespace" options, or normalise formatting first.
- Line-ending differences. Windows files use CRLF, and Unix files use LF. A file converted between them appears to have every line changed.
- Formatting and logic in the same change. Mixing a reformat with a bug fix hides the real change. Format in a separate commit.
- Comparing minified code. A one-line file produces a one-line diff. Format both versions first, with a tool such as the JavaScript formatter or a JSON formatter.
- Trusting a diff blindly for moved code. Many tools show a moved block as a deletion in one place and an addition in another. Look for that pattern before concluding that code was rewritten.
- Ignoring encoding. Comparing files saved in different encodings can show differences that are not visible on screen.
Step-by-step: compare two files with DevUtilX
When you just need to compare two pieces of text, without installing anything or creating a repository, use the DevUtilX Code Diff Tool. It runs in your browser.
- Open the code diff tool.
- Paste the original text into the left editor.
- Paste the changed text into the right editor.
- Review the highlighted additions, deletions and modified lines.
- Use the result to decide what to keep, copy or report.
Both inputs are processed locally, so your code and data are not uploaded. For the best results, make sure both sides use the same indentation and line endings. If the content is minified or messy, format it first, so that real changes are visible instead of whitespace noise. The DevUtilX JSON beautifier and other formatters help here.
Using diff on the command line
Command-line tools are the right choice for large files and automation.
# Compare two files in unified format
diff -u old.txt new.txt
# Ignore whitespace changes
diff -u -w old.txt new.txt
# Compare two directories recursively
diff -ru dir-a dir-b
# Compare any two files with Git, even outside a repository
git diff --no-index old.txt new.txt
# Show a word-level diff
git diff --word-diff
The GNU diffutils manual documents every option. For patches, the companion patch command applies a saved unified diff to a file, which is how changes were shared by email long before pull requests existed.
Best practices
- Normalise before comparing. Align whitespace, line endings and formatting so the diff shows meaning, not noise.
- Keep changes small and focused. A small diff is easy to review. A thousand-line diff is rarely reviewed properly.
- Separate refactors from behaviour changes.
- Read the context lines. The surrounding code often explains whether a change is safe.
- Try another algorithm when a diff looks confusing.
patienceorhistogramcan help. - Use word-level diffs for prose, long strings and configuration values.
- Never compare files containing secrets in a tool you do not trust. Prefer local tools for credentials and private data.
- Save diffs as patches when you need to share or reapply a change.
Comparison: ways to compare files
| Method | Best for | Limitation |
|---|---|---|
diff -u |
Quick comparisons, scripts, patches | Plain text only |
git diff |
Tracked changes, branches, history | Needs a repository (or --no-index) |
| Editor diff view | Interactive review while editing | Depends on the editor |
| Online diff tool | Quick comparisons of pasted text | Check that the tool runs locally |
| Three-way merge tool | Resolving conflicts | Needs a common ancestor |
| Semantic or structural diff | Code or data where formatting changes meaning | Language-specific, less widely available |
Where diff came from
The first diff tools solved a practical problem. Programmers on early Unix systems needed to see what had changed between two versions of a file without printing both. The original diff used an approach published by James Hunt and Douglas McIlroy in 1976, and it was designed to be fast on real source files, where most lines are unique and most edits are small.
Two design choices from that era still shape every tool you use today. First, the unit of comparison is the line. Source code of the time was short lines of text, and a line is a natural unit for a human to accept or reject. Second, the output is meant to be machine-applicable. The edit script format was built so that another program, ed and later patch, could replay it. That is why a diff carries line numbers and context instead of being a simple highlighted comparison.
Myers' 1986 algorithm kept the same output contract while improving the worst cases. Later work in the Git community, such as patience and histogram, did not change what a diff means. It changed which of several equally valid answers the tool picks.
Inside the LCS: a worked example
The best way to see why diffs behave as they do is to build one. Here is a small line diff in JavaScript, based on the textbook dynamic programming table. It is not Myers' algorithm, but it produces the same kind of result and fits on one screen.
function diffLines(a, b) {
const n = a.length, m = b.length;
const t = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
for (let i = n - 1; i >= 0; i--)
for (let j = m - 1; j >= 0; j--)
t[i][j] = a[i] === b[j]
? t[i + 1][j + 1] + 1
: Math.max(t[i + 1][j], t[i][j + 1]);
const out = [];
let i = 0, j = 0;
while (i < n && j < m) {
if (a[i] === b[j]) { out.push(' ' + a[i]); i++; j++; }
else if (t[i + 1][j] >= t[i][j + 1]) out.push('- ' + a[i++]);
else out.push('+ ' + b[j++]);
}
while (i < n) out.push('- ' + a[i++]);
while (j < m) out.push('+ ' + b[j++]);
return out;
}
console.log(diffLines(
['alpha', 'beta', 'gamma', 'delta'],
['alpha', 'gamma', 'delta', 'epsilon']
).join('\n'));
Running it prints:
alpha
- beta
gamma
delta
+ epsilon
The table t[i][j] stores the length of the longest common subsequence of the tails a[i..] and b[j..]. When two lines match, the answer grows by one. When they do not, the best answer comes from skipping a line on one side or the other. Walking the table from the top left then reads off the edit script.
Three lessons follow from this small program.
- The table is the cost. It has
n × mcells, so two files of ten thousand lines each would need a hundred million cells. Myers' algorithm avoids filling most of that table. - Ties are real. When
t[i + 1][j]equalst[i][j + 1], either choice is a correct diff. The tie-break rule decides whether a deletion appears before or after an insertion, which is why two tools can show different but equally valid output for the same pair of files. - Lines are compared as whole strings. A trailing space makes two lines different, which is why whitespace noise is so disruptive.
How Myers avoids the full table
Myers' insight is that you only need to explore the grid along "diagonals", where each diagonal is a fixed difference between the position in file A and the position in file B. For each number of edits d, the algorithm remembers the furthest point reached on each diagonal. It then extends each point for free along matching lines, called the snake, and tries one more insertion or deletion.
When the endpoint of the grid is reached, d is the size of the shortest edit script. In the example from Myers' paper, comparing the sequences ABCABBA and CBABAC gives a longest common subsequence of four elements and a shortest edit script of five insertions and deletions. Because the search stops at the first d that works, similar files finish quickly.
Many real implementations add a heuristic on top, so that very large, very different files do not take a long time. GNU diff, for example, can fall back to a faster but possibly non-minimal answer. Its --minimal option turns the heuristic off. Git has the same trade-off, which is why --diff-algorithm=minimal exists.
Patience and histogram in practice
Minimal does not always mean readable. Suppose you add a new function above two existing functions that end with the same closing brace and blank line. A minimal diff may anchor on those common lines and attribute them in a confusing way, so the new function's braces appear to be "shared" with the old one.
Patience diff, invented by Bram Cohen, takes a different approach.
- Find lines that appear exactly once in each file. These are the strongest anchors.
- Compute the longest common subsequence of those unique lines.
- Use them to split the files into smaller regions, and repeat inside each region.
- Fall back to a plain diff when no unique lines remain.
Common lines such as }, end and blank lines never become anchors, so the diff tends to follow the structure a human would describe. Histogram diff keeps this idea but tolerates repeated lines by preferring the least frequently occurring lines in the old file.
You can try all four algorithms on the same pair of files and compare:
for algo in myers minimal patience histogram; do
echo "== $algo =="
git diff --no-index --diff-algorithm=$algo old.c new.c
done
On many files the output is identical. The difference shows up when code has been moved, when large blocks repeat, or when a block was inserted between near-identical neighbours. To make one algorithm the default in your own repository or machine, run git config diff.algorithm histogram.
Reading the details most people skip
A few markers in diff output explain problems that otherwise look mysterious.
\ No newline at end of file. Diff compares lines, and a final line without a terminating newline is a different string from the same text with one. If you add the missing newline, the last line shows as removed and re-added, with this marker beside the old version. Here is a real example in unified format:
--- x.txt
+++ y.txt
@@ -1,2 +1,2 @@
one
-two
\ No newline at end of file
+two
Nothing visible changed, but the byte content did. Configure your editor to end files with a newline so this noise disappears.
Git's extended headers. git diff adds lines such as diff --git a/f b/f, index 40a5189..dcba3ff 100644, new file mode, deleted file mode, rename from and similarity index. The index line lists abbreviated object IDs and the file mode. A mode-only change, such as making a script executable, appears as old mode and new mode lines with no content hunks.
Hunk sizes. In @@ -3,7 +3,7 @@, a count of zero, as in @@ -0,0 +1,10 @@, means the file had no lines on that side, which is how a brand-new file is described. If the count is omitted, as in @@ -5 +5 @@, it defaults to one.
Context size. Use git diff -U0 to show no context lines, which is handy for scripts, or -U10 to see more of the surroundings. The diff -U option works the same way in GNU diff.
Summaries. When a diff is too large to read, step back with git diff --stat for a per-file change count, or git diff --name-status for a list of added, modified, deleted and renamed files.
Moved code. git diff --color-moved colours blocks that were relocated rather than rewritten, using different colours from ordinary additions and deletions. It turns a scary hundred-line diff into a quick check that code simply changed place.
Whitespace and line endings under control
Whitespace is the most common reason a diff becomes unusable. Git and GNU diff both provide options to hide it, and each one hides a different amount.
| Option | What it ignores |
|---|---|
-w / --ignore-all-space |
All whitespace differences |
-b / --ignore-space-change |
Changes in the amount of whitespace, but not whitespace versus none |
--ignore-space-at-eol |
Trailing whitespace only |
--ignore-cr-at-eol |
A carriage return before the line feed (Git) |
-B / --ignore-blank-lines |
Added or removed blank lines (GNU diff) |
Hiding whitespace in a review is fine for reading, but remember that it can hide real changes. In Python, YAML and Makefiles, whitespace carries meaning. A diff that ignores all whitespace may show nothing for a change that breaks the file.
Fix the cause, not the symptom
Ignore flags treat the symptom. To stop line-ending noise at its source, tell Git how to handle it in a .gitattributes file at the root of the repository:
* text=auto
*.sh text eol=lf
*.bat text eol=crlf
*.png binary
With text=auto, Git normalises line endings in the repository while letting working copies use the platform's convention. The explicit eol settings pin files that must keep a particular ending, such as shell scripts that fail with a carriage return in the shebang line. Pair this with an .editorconfig file so every editor uses the same indentation and final-newline rules.
To catch trailing whitespace before it is committed, run git diff --check. It reports whitespace errors and conflict markers that were left in the changes, and it exits with a non-zero status, so it works in scripts.
Comparing structured data
A line diff treats JSON, XML and YAML as text, so a harmless reordering of keys can look like a rewrite. The fix is to canonicalise both sides first, then diff.
# Sort object keys and use consistent indentation, then diff
diff -u <(jq -S . before.json) <(jq -S . after.json)
The <( ... ) syntax is process substitution in Bash and Zsh. It lets diff read the output of a command as if it were a file. Sorting keys with jq -S removes ordering noise, so only real value changes remain. If your files are small, the DevUtilX JSON beautifier does the formatting step in the browser.
Keep in mind what canonicalisation means. Sorting keys is safe for JSON objects, because the format defines them as unordered. It is not safe for arrays, where order matters, and the sort does not touch arrays. For XML, attribute order is insignificant but element order often is, and comments and whitespace may or may not matter to your application. Our guides to JSON formatting, XML formatting and SQL formatting cover the conventions that keep these formats comparable.
Semantic diff tools go a step further. They parse the file and compare the tree, so they can say "the value at features.blog changed from true to false" regardless of layout. They need a parser for each language, which is why they are less common than plain text diffs.
Turning diffs into patches
A unified diff is also an instruction set. Saving and applying it is the basis of many workflows.
# Save a change as a patch
git diff > fix.patch
# Check that a patch applies cleanly, without changing anything
git apply --check fix.patch
# Apply it
git apply fix.patch
# With the classic tool: strip one leading path component
patch -p1 < fix.patch
# Export commits as email-ready patches, and apply them with authorship intact
git format-patch -3
git am 0001-*.patch
The -p1 option tells patch to remove one leading directory from file names, which is why the a/ and b/ prefixes Git adds do not break anything. If a hunk cannot be applied, patch writes the rejected hunks to a .rej file, and git apply --reject does the same. Open the reject file, read the hunk and apply it by hand.
Context lines are what let patches survive small drifts. If the surrounding lines still match, patch can apply a hunk a few lines away from where it was created, and it reports the offset. Patches made with -U0 have no context, so they are brittle, and Git needs --unidiff-zero to accept them.
Merges and the three-way diff
When two people edit the same file, a two-way diff cannot say who changed what. A three-way merge compares each version against their common ancestor. If only one side changed a region, take that change. If both made the same change, take it once. If both changed the same region differently, there is a conflict.
Git shows a conflict by default like this:
<<<<<<< HEAD
timeout = 30
=======
timeout = 60
>>>>>>> feature
That shows both sides but not the original value, which is often what you need to decide. Ask Git to include the ancestor:
git config --global merge.conflictStyle zdiff3
The diff3 and zdiff3 styles add a ||||||| section holding the base version. The zdiff3 option, available in newer Git releases, also moves lines common to both sides out of the conflict, which makes the conflicting part smaller. Seeing the base turns a guessing game into a simple question: what did each side change relative to this?
Two tools help afterwards. git diff --check finds leftover conflict markers, and git mergetool opens your configured visual merge tool with local, remote and base panes.
Performance and memory
Diffing is cheap for typical source files and expensive in a few specific cases.
- Very large files. Memory and time grow with the file size and the number of differences. Logs and database dumps of many megabytes may be slow in a browser tab. Use a command-line tool for those.
- Files with nothing in common. Myers' running time grows with the number of differences, so two unrelated large files are the worst case. Heuristics can cut the search short at the cost of a larger diff.
- Many repeated lines. Files full of identical lines, such as generated tables, give algorithms many equal-cost matches. Patience and histogram handle this better than plain Myers.
- Minified files. One huge line means a huge comparison unit, and word diff inside it is expensive. Format first.
- Sort when order does not matter. If you are comparing sets, such as a list of dependencies or hostnames, run
sorton both sides first. A line diff then becomes a set difference.
For sets there is an even better tool. The comm command, which expects sorted input, prints lines unique to each file and lines common to both:
comm -3 <(sort a.txt) <(sort b.txt)
Another practical tip is to exclude generated files from review diffs. In Git, you can mark them in .gitattributes with -diff or a linguist-generated attribute, which many hosting platforms use to collapse them.
Security considerations when you compare files
A diff is a view of data, and views can mislead.
Secrets leak through diffs. A diff that includes an .env file, a private key or an access token puts that secret in terminal scrollback, CI logs and chat messages. Treat diffs of configuration the way you treat the files themselves. Use local tools, and redact before sharing.
Hidden characters. Unicode includes characters that change how text is displayed without being visible, such as bidirectional override characters, zero-width spaces and look-alike letters from other scripts. A review that reads the diff on screen can miss them. Some code hosts warn about hidden or bidirectional Unicode, and you can search for them yourself, for example with git diff | grep -P '[\x{200B}-\x{200F}\x{202A}-\x{202E}\x{2066}-\x{2069}]' on a system whose grep supports Perl regular expressions.
Untrusted patches. Applying a patch from someone else is running their edit on your files. Read the file paths in the headers before applying. Git's apply refuses paths that escape the working tree, and GNU patch has similar protections, but reading the headers is still the safest habit. After applying, review the result with git diff, just as you would review any change.
Generated and vendored code. A tiny change to a minified or compiled file is nearly impossible to audit. Review the source and the build process, then compare rebuilt output with the committed artifact.
Online tools. Before you paste anything into a web page, check whether it is processed locally. A tool that sends your text to a server has access to it.
Accessibility and readability of diffs
Red and green are the traditional colours for removed and added lines, and they are indistinguishable for many people with red-green colour blindness. Good diff views never rely on colour alone. The + and - markers in unified format, and icons or labels in graphical tools, carry the meaning in a form that works for everyone, including screen reader users.
A few habits make diffs more readable for all reviewers:
- Prefer the unified view on a narrow screen, since two columns become cramped.
- Increase context when a change is hard to place.
- Use a colour theme with enough contrast, and avoid very pale backgrounds for highlights.
- In Git,
git config color.ui autokeeps colour in terminals and removes it when output is piped.
Setting up diffs in your editor, terminal and CI
A little configuration pays off every day.
Editor and terminal
Most editors can open a diff from the command line. Visual Studio Code, for example, accepts code --diff file1 file2. You can also make it Git's difftool:
git config --global diff.tool vscode
git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'
git difftool HEAD~1 -- src/app.js
For terminal reading, a pager such as less -R shows colour, and Git uses a pager automatically. Third-party pagers can add syntax highlighting and side-by-side layout, and you configure them through core.pager.
Continuous integration
Diffs are useful as automated checks. The pattern is to regenerate something and fail the build if the result differs from what is committed.
# Fail if formatting or generated files are out of date
npm run format
git diff --exit-code
The --exit-code option makes git diff return 1 when there are differences. It keeps documentation, lockfiles, API schemas and code generators honest. Add --stat or print the diff on failure so the log tells contributors exactly what to commit.
Pre-commit checks
A pre-commit hook can run git diff --cached --check to block commits with trailing whitespace or leftover conflict markers. The --cached flag compares the staging area with the last commit, so you check exactly what is about to be committed. Formatting tools run from a hook keep the diff free of noise before it ever reaches review. Our guides on CSS formatting and HTML formatting cover rules worth automating.
Troubleshooting diffs
When a diff surprises you, work through this table.
| Symptom | Likely cause | Fix |
|---|---|---|
| Every line shows as changed | CRLF versus LF, or tabs versus spaces | Normalise endings with .gitattributes, or use --ignore-cr-at-eol or -w to inspect |
| Last line shows as changed, nothing visible differs | Missing newline at end of file | Add a final newline, and configure the editor to keep it |
| Diff says "Binary files differ" | File contains NUL bytes or is marked binary | Use a format-specific tool, or --text to force a text diff |
| A moved function shows as delete plus add | Line diffs have no notion of movement | Use --color-moved or try patience |
| Braces and blank lines look attached to the wrong function | Minimal algorithm anchored on common lines | Try --diff-algorithm=patience or histogram |
| Diff is huge after a small edit | Formatter or reflow touched the whole file | Format in a separate commit, then review with -w |
| Same files differ on two machines | core.autocrlf or filter settings differ |
Check git config --list --show-origin and commit a .gitattributes |
| Patch fails to apply | File drifted, or wrong -p level |
Try git apply --3way, check headers, read the .rej files |
| Strange characters in diff output | Different encodings, or a byte order mark | Convert both files to UTF-8 and compare again |
| Diff tool is slow or freezes | Very large or minified input | Use the command line, or format and split the files first |
Team conventions that keep diffs useful
Tools only go so far. A team that agrees on a few conventions gets the most from every diff.
- One idea per commit. A commit that does one thing has a diff that can be reviewed, reverted or bisected on its own.
- Formatting is automated. Use a formatter and enforce it in CI, so no pull request contains style churn. See JavaScript code formatting best practices.
- Generated files are labelled. Mark lockfiles and build output so reviewers know to skim them.
- Sorted, stable output. Configuration and data files should be written in a deterministic order, so the diff shows only intended changes.
- Final newline and consistent endings. Enforce these through
.editorconfigand.gitattributes. - Review the diff before you ask others to. Reading your own diff in the review tool catches stray debug statements, accidental files and commented-out code.
- Describe the why. The diff shows what changed. The commit message should say why.
Minification adds its own wrinkle. Compare source, not minified output, and read our guides on JavaScript minification and JSON minification to see how build steps change what ends up in a diff.
A short case study: finding a config regression
Imagine a service that behaves differently in production than in staging. Nothing in the code changed, so you suspect configuration. The two config files are each several hundred lines, formatted by different people.
A direct diff shows nearly every line changed, because one file uses tabs and the other spaces, and keys appear in different orders. Normalising both with a JSON formatter and sorting keys cuts the diff to four lines. Three are expected differences, such as host names. The fourth is a cache time-to-live of 0 instead of 300, which explains the extra load.
The workflow is general: normalise first, then diff, then explain each remaining difference before you declare the cause found. When two files should differ in a few known ways, keep a small list of the expected differences and treat anything beyond it as a bug.
FAQ
What does the @@ line mean in a diff?
It is the hunk header. @@ -10,6 +10,8 @@ means the hunk starts at line 10 in the old file, covers six lines there, and starts at line 10 in the new file, covering eight lines.
Why is a changed line shown as two lines?
A diff only knows additions and deletions. An edit is expressed as removing the old line and adding the new one. Tools with word-level highlighting make it clearer which part changed.
What is the difference between a diff and a patch?
A diff is a description of differences. A patch is a diff saved in a format that the patch command or Git can apply to the original file to reproduce the change.
Why does my diff show every line as changed?
The usual causes are line-ending differences (CRLF versus LF), a change of indentation from tabs to spaces, or a reformat. Normalise both files, or use an option that ignores whitespace.
Is a shorter diff always better?
Not always. The minimal diff is correct, but another algorithm may group changes in a way that reads more naturally. Both describe the same change.
Can I diff binary files?
Standard text diffs do not work on binary files. Tools report only that the files differ. Specialised tools exist for images, archives and documents.
Is it safe to paste code into an online diff tool?
Only if the comparison runs in your browser. DevUtilX does not upload your input, but you should check any tool's privacy statement, and avoid pasting secrets.
How do I see only the names of changed files?
Use git diff --name-only for names, or git diff --name-status to include whether each file was added, modified, deleted or renamed. With plain diff, the -q option reports only whether files differ, and -rq does so for two directories.
How does a diff detect a renamed file?
Git does not record renames. It compares the content of deleted and added files and reports a rename when they are similar enough, shown as a similarity percentage. A rename combined with heavy edits can fall below the threshold and appear as a delete plus an add.
What is the difference between git diff, git diff --staged and git diff HEAD?
By default, git diff compares the working tree with the staging area. git diff --staged compares the staging area with the last commit. git diff HEAD compares the working tree with the last commit, so it covers both staged and unstaged changes.
What do the two and three dots mean in git diff a..b and a...b?
Two dots compare the tips of two branches directly. Three dots compare the second branch with the point where the branches diverged, which shows what the second branch introduced. The three-dot form is usually what a pull request shows.
Can I diff two strings or API responses from the command line?
Yes. Use process substitution, as in diff <(echo "$a") <(echo "$b"), or save each response to a file. For JSON, canonicalise both sides with jq -S first.
Why does diff exit with status 1?
GNU diff returns 0 when the files are identical, 1 when they differ and 2 when there was trouble. A status of 1 is therefore not an error, but scripts running under set -e can treat it as one, so handle it explicitly.
Should I ignore whitespace in code review?
It helps when a change mixes indentation and logic, but switch it off before approving. In whitespace-sensitive languages and formats, ignoring whitespace can hide a real change.
Conclusion
A diff is the most useful summary of change in software. Understand the unified format, know that most tools use Myers' algorithm, and remember that readability depends on tidy input: consistent whitespace, formatted code and focused changes. For a quick comparison of any two pieces of text, try the Code Diff Tool. To keep going, read our guides on JavaScript code formatting and JSON formatting, which make files easier to compare.
Further reading
Try the tools
Easily compare two pieces of code side-by-side and instantly highlight the differences. This tool is perfect for tracking changes, reviewing code, and merging different versions of a file with precision and clarity.
Format and validate your JSON data with ease. This tool ensures proper indentation and a clear structure, making complex JSON files readable, understandable, and free of syntax errors for seamless data handling.
.jpg)
.jpg)
.jpg)