diff, one comparison per page
Twenty-eight lessons on the diff command, one comparison concept or one diff skill per page: the line-by-line comparator that reports the smallest set of changes; the normal format's add, delete and change commands with real line ranges; the unified format's hunks, @@ header, minus and plus lines and three lines of context; the context format as the older cousin; the dash for stdin, directories and -r trees; -q with -s for same-or-different verdicts; ignoring honestly with -i, -b and -w; the exit law of 0, 1 and 2 and the && trap it sets in scripts; cmp byte by byte and comm for sorted files; patch with offsets, fuzz and reversed patches, read before applied; ed scripts; the family in the wild; and teaching a friend one hunk in one sitting.
One comparison, one page, one honest read. That's the rhythm.
diff, one comparison per page
Twenty-eight lessons on the diff command, one comparison concept or one diff skill per page: the line-by-line comparator that reports the smallest set of changes; the normal format's add, delete and change commands with real line ranges; the unified format's hunks, @@ header, minus and plus lines and three lines of context; the context format as the older cousin; the dash for stdin, directories and -r trees; -q with -s for same-or-different verdicts; ignoring honestly with -i, -b and -w; the exit law of 0, 1 and 2 and the && trap it sets in scripts; cmp byte by byte and comm for sorted files; patch with offsets, fuzz and reversed patches, read before applied; ed scripts; the family in the wild; and teaching a friend one hunk in one sitting.
Set in Space Grotesk, Inter and JetBrains Mono (SIL Open Font License).
Every fact in this book is as the official source states it, fetched and read during this build: the local manual pages diff(1), patch(1), cmp(1) and comm(1) (GNU diffutils 3.10, GNU patch 2.7.6, GNU coreutils 9.4), the definition of truth for this book, and the Wikipedia articles Diff, Patch (Unix), Cmp (Unix), Sort (Unix), Standard streams, Pipeline (Unix) and Regular expression. No facts are sourced from Reddit, Stack Overflow, vendor blogs or commercial sites. Every command and output pair was run on this machine with temporary files under the build scratch directory only, each capture cached verbatim so the pair is reproducible. SAFETY LAW: no file outside the book directory's scratch area was written by any demonstration; patch wrote only to scratch copies, and every patch check ran with --dry-run first. Honest notes: the Wikipedia titles Diff_utility, Comm (Unix), Uniq (Unix) and Unified diff format are redirects, so their subjects are covered by the Diff article and the local manual pages instead. An independent guide, not affiliated with or endorsed by the Free Software Foundation, the IEEE, or the Wikimedia Foundation.
Your purchase is for personal use only. You do not have redistribution rights: please do not share, resell, or republish this book or its pages.
© 2026 Steve Hodgkiss. All rights reserved. Personal use only; no redistribution rights.
Edition 1.0 · stevehodgkiss.net
Contents
Per the Wikipedia Diff article, c indicates changed, the line number of the original file appears before the single-letter code and the line number of the new file appears after, and the less-than and greater-than signs indicate which file the lines appear in. Per the diff(1) manual page, exit status is 0 if inputs are the same, 1 if different, 2 if trouble. This machine's capture: diff old_chg.txt new_chg.txt printed 2c2, then < beta, a divider of three dashes, then > BETA, and exited 1.
c is for changed
The third letter, c, indicates changed: a block of file one replaced by a block of file two, a divider of three dashes between them.
Our capture: diff old_chg.txt new_chg.txt printed 2c2, then < beta, the divider, then > BETA. One line each side; the two counts can differ.
Old line 2 becomes new line 2. Matching counts are coincidence, not law: the ranges say exactly what they say.
Change one word in a copy, run diff, and label the < and > lines by hand.
Per the Wikipedia Diff article, a hunk begins with range information surrounded by double at signs, a minus symbol precedes the range for the hunk of the original file and a plus symbol precedes the range for the new file, each hunk range is of the format l,s where l is the starting line number and s is the number of lines the change hunk applies to, and if hunk size information does not correspond with the number of lines in the hunk the diff could be considered invalid and rejected. This machine's capture: the header read @@ -1,7 +1,7 @@, start at line 1 with seven lines, on both sides.
The @@ header
Every hunk opens with range information surrounded by double at signs, per the Wikipedia Diff article. Our capture printed @@ -1,7 +1,7 @@.
The minus range belongs to the original, the plus range to the new file. Each reads l comma s: a starting line and a count.
Here both ranges say: start at line 1, hold seven lines. If the body does not match those counts, the diff could be considered invalid and rejected.
Cover the body of one hunk and reconstruct it from the @@ ranges alone.
Per the diff(1) manual page, -q, --brief reports only when files differ, and -s, --report-identical-files reports when two files are the same. This machine's captures: diff -q h_old.txt h_new.txt printed Files h_old.txt and h_new.txt differ and exited 1; diff -q on an identical pair printed nothing and exited 0; diff -s same1.txt same2.txt printed Files same1.txt and same2.txt are identical.
Same or different
Sometimes you only want the verdict. -q, --brief: report only when files differ, says the manual page. Our capture printed Files h_old.txt and h_new.txt differ.
Its quiet partner is -s: report when two files are the same; our capture printed Files same1.txt and same2.txt are identical.
The same pair under -q prints nothing at all. Silence with exit 0 is the smallest honest report a tool can give.
Run diff -q and diff -s on an identical pair and a different pair: four runs, two silences.
Per the diff(1) manual page, -i, --ignore-case ignores case differences in file contents, and exit status is 0 if inputs are the same. Per this book's plan, the closing rule of the ignore family reads: what you suppress is what you vouch for. This machine's captures: the case-blind run exited 0 while the bytes on disk never changed, and the plain run exited 1 on the same pair.
Ignored, not equal
Here is the honesty of the ignore flags: under -i our capture exited 0, and a script would read the pair as the same. The bytes on disk never changed.
An ignored difference is a claim about relevance, not a fact about the files. Write the flag into the command, never into your memory of the files.
Per this book's plan, the rule reads: what you suppress is what you vouch for. Every ignore flag is a small promise.
Say aloud what -i vouches for in your last comparison, then write it in the script's comment.
Per the comm(1) manual page, comm compares sorted files FILE1 and FILE2 line by line, with no options it produces three-column output where column one contains lines unique to FILE1, column two contains lines unique to FILE2 and column three contains lines common to both files, and -1, -2 and -3 suppress the matching column. This machine's captures: comm s1.txt s2.txt printed apple in column one, banana and cherry in the common column and date in column two; comm -12 printed banana and cherry; comm -23 printed apple.
comm, three columns
comm compares two sorted files line by line, says comm(1), and prints three columns: unique to file one, unique to file two, common to both.
Our capture: apple in column one, banana and cherry in the common column, date in column two. The tabs are the fences; read them as walls.
The flags remove columns: -12 kept banana and cherry, the shared lines; -23 kept apple alone. Sorted input is the law, not advice.
comm two sorted lists, then comm -12 to keep only what both hold.
Per the patch(1) manual page, when the line numbers mentioned in the patch are incorrect patch attempts to find the correct place to apply each hunk, taking the line number mentioned for the hunk plus or minus any offset used in applying the previous hunk, then scanning both forwards and backwards for lines matching the context, and if the hunk is installed at a different line from the one specified in the diff you are told the offset; -F num, --fuzz=num sets the maximum fuzz factor, the default fuzz factor is 2, and a larger fuzz factor increases the odds of a faulty patch. This machine's capture: patch --dry-run -F 2 on a file with two lines prepended answered Hunk #1 succeeded at 3 (offset 2 lines).
Offsets and fuzz
When line numbers have drifted, patch scans for a place where the context matches, the manual page says, starting from the hunk's line number plus or minus any offset.
Our capture: two lines prepended, and the dry run answered Hunk #1 succeeded at 3 (offset 2 lines). Found, but displaced.
If plain context fails, patch ignores edge lines of context, called fuzz; the default maximum fuzz factor is 2, and a larger factor increases the odds of a faulty patch.
Prepend two lines to a copy, dry-run your patch, and read the offset line verbatim.
Per this book's plan, the closing lesson teaches one friend diff in one sitting: run diff --version, read one 4c4 change, read one @@ -1,7 +1,7 @@ hunk, watch the exit status answer 1, and dry-run one small patch. Per the Wikipedia Diff article, the original utility was developed in the early 1970s at Bell Labs, was part of the 5th Edition of Unix released in 1974, and was written by Douglas McIlroy and James Hunt. This machine's captures, re-read from this book: diff (GNU diffutils) 3.10, 4c4, @@ -1,7 +1,7 @@, changed pair exit: 1, checking file h_copy.txt.
Teaching diff
The whole book in one sitting with a friend: version, a change, a hunk, a verdict, a dry run. Five lines, one story: the smallest honest change set.
Teach the readings as much as the typing: what 2c2 says, what @@ -1,7 +1,7 @@ counts, why exit 1 is an answer, why the dry run comes first.
The lines they will meet are this book's own captures: diff (GNU diffutils) 3.10, 4c4, @@ -1,7 +1,7 @@, changed pair exit: 1, checking file h_copy.txt. Let them type every one.
Sit with one friend and teach diff in one sitting; they type the five lines, you read the ranges aloud.