Read

Module 10 · Putting it all together

diff and patch

See exactly what changed between two files or directories with diff, save the change as a patch, and apply or undo it with patch.

What you will learn

  • Read a unified diff: headers, hunks, `-` and `+` lines.
  • Compare files and directories with `diff -u`, `-q` and `-r`, and use its exit status.
  • Create a patch file and apply or reverse it with `patch`.

Before you change a configuration file, copy it: cp app.conf app.conf.orig. After the change, diff tells you precisely what you did, line by line. That output is not just for reading. Saved to a file it becomes a patch, a recipe that patch can replay on another copy of the original, on another machine, or undo later. This is how changes to source code travelled by email for decades, and it is still how git shows you changes.

~% cd lab/l95
l95% cat old/app.conf
host=localhost
port=8080
debug=yes
l95% diff -u old/app.conf new/app.conf
--- old/app.conf
+++ new/app.conf
@@ -1,3 +1,3 @@
 host=localhost
-port=8080
-debug=yes
+port=9090
+debug=no
l95% echo $?
1

Reading a unified diff

The --- line names the old file and +++ the new one. Each block starting with @@ is a hunk: -1,3 +1,3 means "lines 1 to 3 of the old file become lines 1 to 3 of the new one". Inside a hunk, lines starting with a space are unchanged context, - lines were removed and + lines were added. A changed line therefore shows up as a - followed by a +. The context lines are what lets patch find the right spot even if the file has moved a little.

Other systems print an older "normal" format by default and need -u for this one. BusyBox diff only knows the unified format, so here diff and diff -u print the same thing; write -u anyway, it is the habit that works everywhere. The exit status is useful in scripts: 0 means identical, 1 means different and 2 means trouble, such as a missing file.

OptionMeaning
-uUnified format (the only one in BusyBox)
-qOnly say whether the files differ
-rRecurse into directories
-NTreat missing files as empty (shows new files in full)
-i / -w / -BIgnore case / all whitespace / blank lines
l95% diff -rq site site.bak
Files site/index.html and site.bak/index.html differ
Only in site: new.html

Making and applying a patch

l95% diff -u old/app.conf new/app.conf > app.patch
l95% cp old/app.conf live.conf
l95% patch live.conf app.patch
patching file live.conf
l95% cat live.conf
host=localhost
port=9090
debug=no
l95% patch -R live.conf app.patch
patching file live.conf

patch FILE PATCHFILE (or patch FILE < PATCHFILE) applies the recipe to FILE. -R reverses it, turning + into -, which is your undo button. Without a FILE argument patch reads the file names from the ---/+++ headers, and -p1 strips the first directory from them, the usual way to apply a patch for a whole project from its top directory: patch -p1 < fix.patch. Without an explicit FILE, BusyBox patches the file named on the +++ line, so name the target when in doubt.

Commands in this lesson

CommandWhat it does
diff -u old newShow changes in unified format.
diff -q a bOnly report whether they differ.
diff -rq dir1 dir2Compare two directory trees briefly.
diff -u old new > fix.patchSave the change as a patch.
patch file fix.patchApply a patch to a file.
patch -R file fix.patchUndo (reverse) a patch.
patch -p1 < fix.patchApply a project patch from its top directory.

Quiz

  1. In a unified diff, what does a line starting with `+` mean?

    • It exists only in the new file (added).
    • It is unchanged context.
    • It was removed.
  2. `diff a b` exits with status 1. What does that mean?

    • An error occurred.
    • The files differ.
    • The files are identical.
  3. How do you undo a patch you just applied to `app.conf`?

    • `patch -R app.conf fix.patch`
    • `diff -R app.conf`
    • It cannot be undone.
  4. You only want to know which files differ between two directories, not the details. Which command?

    • `diff -u dir1 dir2`
    • `diff -rq dir1 dir2`
    • `patch -r dir1 dir2`
  5. What is the job of the context lines (starting with a space) in a patch?

    • They are comments for humans only.
    • They help `patch` find the right place to apply the change.
    • They are deleted by `patch`.

Practice

  1. Save the unified diff between `/root/lab/l95/old/app.conf` (first) and `/root/lab/l95/new/app.conf` (second) into `/root/lab/l95/app.patch`.

  2. Apply the patch `/root/lab/l95/fix.patch` to `/root/lab/l95/live.conf`, so that it ends up with `port=9090` and `debug=no`.

  3. Compare the directories `/root/lab/l95/site` and `/root/lab/l95/site.bak` recursively and save only the brief report (which files differ, which exist in only one) into `/root/lab/l95/report.txt`.

Open this lesson in the app to do the tasks in a real Linux machine and have them checked.