diff — Compare Files and Directories

Compare files and directories line by line with diff — unified, context and side-by-side output, recursive comparison and patch files.

diff compares two files line by line and shows you exactly what changed – lines added, removed and modified. By far the most important output format is the unified format (-u), which Git and patch also use; alongside it are the context format (-c) and the two-column side-by-side view (-y). With -r you compare entire directory trees recursively, and by redirecting into a .patch file you create patches that patch can apply again later. Handy for scripting: the exit code of diff is 0 when the files are identical, 1 when they differ, and 2 on error.

Basic Comparison

diff <file1> <file2> — Compare two files and show the differences in default (normal) format.

diff original.txt modified.txt

diff -s <file1> <file2> — Report when two files are identical.

diff -s config.ini config.ini.bak

diff -q <file1> <file2> — Only report whether files differ, not the actual differences.

diff -q v1.0/app.js v2.0/app.js

diff <file1> <file2> > <patch> — Save the diff output to a patch file.

diff original.c fixed.c > bugfix.patch

Output Formats

diff -u <file1> <file2> — Unified format — the most common format, used by git and patch tools.

diff -u old/config.php new/config.php

diff -u <file1> <file2> -L '<label1>' -L '<label2>' — Unified format with custom labels instead of filenames in the header.

diff -u old.conf new.conf -L 'production' -L 'staging'

diff -c <file1> <file2> — Context format — shows changes with surrounding context lines.

diff -c original.py patched.py

diff -y <file1> <file2> — Side-by-side format — shows both files in parallel columns.

diff -y left.txt right.txt

diff -y -W <width> <file1> <file2> — Side-by-side format with a specific column width.

diff -y -W 120 left.txt right.txt

diff --suppress-common-lines -y <file1> <file2> — Side-by-side format showing only differing lines.

diff --suppress-common-lines -y old.conf new.conf

diff -e <file1> <file2> — Output an ed script that transforms file1 into file2.

diff -e original.txt modified.txt

Context Control

diff -U <n> <file1> <file2> — Unified format with n lines of context (default is 3).

diff -U 5 old.js new.js

diff -C <n> <file1> <file2> — Context format with n lines of context.

diff -C 10 old.py new.py

diff -U 0 <file1> <file2> — Show only changed lines with no surrounding context.

diff -U 0 before.txt after.txt

Whitespace & Formatting

diff -b <file1> <file2> — Ignore changes in the amount of whitespace.

diff -b old.py new.py

diff -w <file1> <file2> — Ignore all whitespace differences.

diff -w old.html new.html

diff -B <file1> <file2> — Ignore changes that only insert or delete blank lines.

diff -B old.css new.css

diff -i <file1> <file2> — Case-insensitive comparison.

diff -i readme.txt README.txt

diff -Z <file1> <file2> — Ignore trailing whitespace at the end of lines.

diff -Z old.txt new.txt

diff -E <file1> <file2> — Ignore changes due to tab expansion.

diff -E tabs.py spaces.py

diff --strip-trailing-cr <file1> <file2> — Strip trailing carriage returns (useful for Windows vs. Unix line endings).

diff --strip-trailing-cr unix.txt windows.txt

Directory Comparison

diff -r <dir1> <dir2> — Recursively compare two directories and their contents.

diff -r project-v1/ project-v2/

diff -rq <dir1> <dir2> — Recursively compare directories and only report which files differ.

diff -rq /etc/nginx/ /etc/nginx.bak/

diff -r --exclude='<pattern>' <dir1> <dir2> — Compare directories but exclude files matching a pattern.

diff -r --exclude='*.pyc' src/ src-backup/

diff -r --exclude-from=<file> <dir1> <dir2> — Compare directories excluding patterns listed in a file.

diff -r --exclude-from=.diffignore old/ new/

diff -r -x '*.log' -x '*.tmp' <dir1> <dir2> — Compare directories excluding multiple patterns.

diff -r -x '*.log' -x '*.tmp' -x '.git' prod/ staging/

diff -rq <dir1> <dir2> | grep 'Only in' — Show files that exist in one directory but not the other.

diff -rq src/ src-backup/ | grep 'Only in'

Patch Workflow

diff -u <original> <modified> > <file.patch> — Create a unified patch file from two files.

diff -u main.c main_fixed.c > fix.patch

diff -ruN <dir1> <dir2> > <file.patch> — Create a recursive patch for entire directories (-N treats missing files as empty).

diff -ruN project-v1/ project-v2/ > upgrade.patch

patch < <file.patch> — Apply a patch file to the current directory.

patch < fix.patch

patch -p1 < <file.patch> — Apply a patch stripping the first path component (common for git-style patches).

patch -p1 < upgrade.patch

patch -R < <file.patch> — Reverse (undo) a previously applied patch.

patch -R < fix.patch

patch --dry-run < <file.patch> — Test a patch without actually applying it.

patch --dry-run -p1 < upgrade.patch

Colored & Visual Output

diff --color <file1> <file2> — Show differences with colored output (GNU diff).

diff --color old.conf new.conf

diff --color=always <file1> <file2> | less -R — Pipe colored diff output through less while preserving colors.

diff --color=always -u old.js new.js | less -R

colordiff <file1> <file2> — Use the colordiff wrapper for automatically colored output.

colordiff -u config.old config.new

vimdiff <file1> <file2> — Open both files in Vim with a side-by-side diff view.

vimdiff original.py modified.py

sdiff <file1> <file2> — Side-by-side merge tool — interactive alternative to diff -y.

sdiff -o merged.txt left.txt right.txt

diff3 <mine> <original> <yours> — Three-way diff — compare three files (useful for merge conflicts).

diff3 my-branch.txt base.txt their-branch.txt

comm <file1> <file2> — Compare two sorted files line by line (shows unique and common lines).

comm -12 sorted1.txt sorted2.txt

cmp <file1> <file2> — Byte-by-byte comparison — reports the first difference found.

cmp binary1.dat binary2.dat

wdiff <file1> <file2> — Word-by-word diff — highlights individual word changes.

wdiff draft.txt final.txt

Practical Examples

diff -u <(sort file1.txt) <(sort file2.txt) — Compare two files after sorting them (using process substitution).

diff <(ssh host1 cat /etc/config) <(ssh host2 cat /etc/config) — Compare a config file on two remote servers.

diff -rq src/ deploy/ --exclude='.git' | sort — Check which files differ between local source and deployed version.

diff -u /dev/null <newfile> — Create a patch that adds an entirely new file.

diff -u /dev/null src/newfeature.js > add-feature.patch

diff <(xxd file1.bin) <(xxd file2.bin) — Compare two binary files using hex dump output.

diff -u <file1> <file2> | diffstat — Show a summary of changes (insertions, deletions per file).

diff -ruN v1/ v2/ | diffstat

diff -y -W 80 <(curl -s <url1>) <(curl -s <url2>) — Compare the contents of two URLs side-by-side.

find . -name '*.bak' -exec sh -c 'diff -q "$1" "${1%.bak}"' _ {} \; — Compare each .bak file with its original counterpart.

Conclusion

diff is the standard tool for making changes between two states visible – whether comparing two configurations, reviewing code or spotting drift between source and deployment. For everyday use, remember above all -u (unified, the Git/patch format), -r (recursive over directories) and -q (report only what differs). The exit code makes diff script-friendly: 0 identical, 1 different, 2 error. Some flags like -Z, -E or --color are GNU extensions and are missing on BSD/macOS. For output, less -R is worth it for paging with colors preserved; to filter the results reach for grep, and for scripted text edits reach for sed.

Further Reading

  • grep – filter lines by pattern, often applied to diff output
  • sed – replace and transform text in a scripted way
  • less – page through output, with -R for colors