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.txtdiff -s <file1> <file2> — Report when two files are identical.
diff -s config.ini config.ini.bakdiff -q <file1> <file2> — Only report whether files differ, not the actual differences.
diff -q v1.0/app.js v2.0/app.jsdiff <file1> <file2> > <patch> — Save the diff output to a patch file.
diff original.c fixed.c > bugfix.patchOutput Formats
diff -u <file1> <file2> — Unified format — the most common format, used by git and patch tools.
diff -u old/config.php new/config.phpdiff -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.pydiff -y <file1> <file2> — Side-by-side format — shows both files in parallel columns.
diff -y left.txt right.txtdiff -y -W <width> <file1> <file2> — Side-by-side format with a specific column width.
diff -y -W 120 left.txt right.txtdiff --suppress-common-lines -y <file1> <file2> — Side-by-side format showing only differing lines.
diff --suppress-common-lines -y old.conf new.confdiff -e <file1> <file2> — Output an ed script that transforms file1 into file2.
diff -e original.txt modified.txtContext Control
diff -U <n> <file1> <file2> — Unified format with n lines of context (default is 3).
diff -U 5 old.js new.jsdiff -C <n> <file1> <file2> — Context format with n lines of context.
diff -C 10 old.py new.pydiff -U 0 <file1> <file2> — Show only changed lines with no surrounding context.
diff -U 0 before.txt after.txtWhitespace & Formatting
diff -b <file1> <file2> — Ignore changes in the amount of whitespace.
diff -b old.py new.pydiff -w <file1> <file2> — Ignore all whitespace differences.
diff -w old.html new.htmldiff -B <file1> <file2> — Ignore changes that only insert or delete blank lines.
diff -B old.css new.cssdiff -i <file1> <file2> — Case-insensitive comparison.
diff -i readme.txt README.txtdiff -Z <file1> <file2> — Ignore trailing whitespace at the end of lines.
diff -Z old.txt new.txtdiff -E <file1> <file2> — Ignore changes due to tab expansion.
diff -E tabs.py spaces.pydiff --strip-trailing-cr <file1> <file2> — Strip trailing carriage returns (useful for Windows vs. Unix line endings).
diff --strip-trailing-cr unix.txt windows.txtDirectory 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.patchdiff -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.patchpatch < <file.patch> — Apply a patch file to the current directory.
patch < fix.patchpatch -p1 < <file.patch> — Apply a patch stripping the first path component (common for git-style patches).
patch -p1 < upgrade.patchpatch -R < <file.patch> — Reverse (undo) a previously applied patch.
patch -R < fix.patchpatch --dry-run < <file.patch> — Test a patch without actually applying it.
patch --dry-run -p1 < upgrade.patchColored & Visual Output
diff --color <file1> <file2> — Show differences with colored output (GNU diff).
diff --color old.conf new.confdiff --color=always <file1> <file2> | less -R — Pipe colored diff output through less while preserving colors.
diff --color=always -u old.js new.js | less -Rcolordiff <file1> <file2> — Use the colordiff wrapper for automatically colored output.
colordiff -u config.old config.newvimdiff <file1> <file2> — Open both files in Vim with a side-by-side diff view.
vimdiff original.py modified.pyRelated Tools
sdiff <file1> <file2> — Side-by-side merge tool — interactive alternative to diff -y.
sdiff -o merged.txt left.txt right.txtdiff3 <mine> <original> <yours> — Three-way diff — compare three files (useful for merge conflicts).
diff3 my-branch.txt base.txt their-branch.txtcomm <file1> <file2> — Compare two sorted files line by line (shows unique and common lines).
comm -12 sorted1.txt sorted2.txtcmp <file1> <file2> — Byte-by-byte comparison — reports the first difference found.
cmp binary1.dat binary2.datwdiff <file1> <file2> — Word-by-word diff — highlights individual word changes.
wdiff draft.txt final.txtPractical 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.patchdiff <(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/ | diffstatdiff -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
- GNU Diffutils manual – official reference for all formats and options
- Wikipedia: diff – background on the diff utility and the unified format