Using Patch

Overview
While revising for some Kubernetes exams, I realised I havent done a blog post on some useful commands.
The third blog post covers the patch command.
For alot of situations patch can be a more straightforward approach to updating similar files.
If you are not familiar with patch, it takes the ouput from a diff
and adds it to a file.
Typically you would use something like sed
for a lot of in file manipulation.
In this blog post, I will assume you already know how to use diff
.
If you dont you will most likely want to read a previous post on how to use diff
.
A patch
is a file that contains the differences between two versions of a
file. You create it using the diff
command. The patch
command then applies
these differences to an older version of a file, transforming it into the
newer version. This is useful for distributing changes to code or configuration
files without having to send the entire file. It's efficient because only the
modifications are transmitted.
Creating a patch file:
-
Use the
diff
command: The core of creating a patch is thediff
command. Its basic syntax is:1diff [options] original_file modified_file > patch_file.patch
original_file
: The original, unmodified file.modified_file
: The modified version of the file.>
: This redirects the output of thediff
command (the patch data) into a new file.patch_file.patch
: The name you choose for your patch file. It's convention to use the.patch
extension.
-
Choose appropriate options: The
diff
command has several options that control the format and content of the patch file. Here are the most common and useful ones:-
-u
or-unified
: Creates a "unified diff" which is the most common and preferred format. It provides context lines around the changes, making it easier to apply the patch even if the target file has been slightly modified. This is highly recommended.1diff -u original_file modified_file > patch_file.patch
-
-N
or--new-file
: Treats absent files as empty. Essential when adding a new file. -
-r
or--recursive
: Recursively compare directories. Useful when creating patches for entire directory trees. -
-a
or--text
: Treat all files as text. This is useful ifdiff
incorrectly identifies a file as binary. -
-x <pattern>
or--exclude=<pattern>
: Exclude files matching a pattern. For example,-x "*.o"
would exclude object files. Useful with-r
. -
-i
or--ignore-case
: Ignores case differences in the files. -
-w
or--ignore-all-space
: Ignores all whitespace. -
-B
or--ignore-blank-lines
: Ignores changes where lines are all blank. -
--strip-trailing-cr
: Strip any trailing carriage return on input.
-
-
Example: Let's say you have an original file named
myfile.txt
and you've made changes, saving it asmyfile_modified.txt
. To create a unified patch file, you would use:1diff -u myfile.txt myfile_modified.txt > myfile.patch
-
Directory diffs: If you need to create a patch for an entire directory, use the
-r
option. It's good practice to create the patch from the top-level directory that contains both versions of the directory you are diffing. For example, if you have directory treesoriginal_dir
andmodified_dir
, whereoriginal_dir
is a copy of the original code andmodified_dir
is the modified code:1diff -ur original_dir modified_dir > mydirectory.patch
Important: When creating directory diffs, the paths stored inside the patch file are relative to the paths provided to the
diff
command. This is critical for applying the patch correctly. Iforiginal_dir
andmodified_dir
have different parent directories, you'll need to adjust the--strip
option when applying the patch (explained in the "Applying the Patch" section). -
Verifying the patch file: After creating the
.patch
file, it's a good idea to examine it with a text editor orless
orcat
to confirm it contains the changes you expect and that the file paths are correct. Pay close attention to lines that begin with---
(original file) and+++
(modified file) as they specify the filenames the patch will affect. The lines starting with@@
indicate the line numbers where changes will be applied along with the context around them.
Patch file walkthrough
-
Create Original File: Let's start with a simple original file named
original.txt
:1echo "This is the original file." > original.txt 2echo "It contains some text." >> original.txt 3echo "This is line three." >> original.txt
-
Create Modified File: Now, modify the file slightly. For example, change the first line and add a new line at the end. Let's call this
modified.txt
:1echo "This is the modified original file." > modified.txt 2echo "It contains some text." >> modified.txt 3echo "This is line three." >> modified.txt 4echo "This is the new line." >> modified.txt
-
Generate the Diff File: Use
diff
to create the patch file. The-u
option creates a unified diff, which is generally preferred:1diff -u original.txt modified.txt > mypatch.patch
Examine the contents of
mypatch.patch
. It will look something like this:1--- original.txt 2023-10-27 14:30:00.000000000 -0400 2+++ modified.txt 2023-10-27 14:30:30.000000000 -0400 3@@ -1,3 +1,4 @@ 4-This is the original file. 5+This is the modified original file. 6 It contains some text. 7 This is line three. 8+This is the new line.
--- original.txt
: Indicates the original file.+++ modified.txt
: Indicates the modified file.@@ -1,3 +1,4 @@
: This is the hunk header.-1,3
means "starting at line 1, 3 lines are being removed/changed from the original file".+1,4
means "starting at line 1, 4 lines are being added/changed in the modified file".-
: Lines prefixed with a minus sign are being removed.+
: Lines prefixed with a plus sign are being added.
-
Apply the Patch: Now, let's revert the
modified.txt
file back to the original. Then, apply the patch tooriginal.txt
:1cp original.txt modified.txt #Reset modified.txt to be same as original.txt. 2patch original.txt mypatch.patch
-
Verify the Result: Check the contents of
original.txt
. It should now match the contents ofmodified.txt
after patching:1cat original.txt
Output:
1This is the modified original file. 2It contains some text. 3This is line three. 4This is the new line.
-
Reverse Patching (Optional): You can also reverse the patch. This is useful if you accidentally applied a patch and want to undo it. Use the
-R
option:1patch -R original.txt mypatch.patch
Now,
original.txt
should be back to its original state. You can verify this withcat original.txt
.
Important Considerations:
-
File Paths:
patch
often uses the file paths stored within the patch file itself. The-p
option is crucial for tellingpatch
how many directory levels to strip from the pathnames in the patch file. If the patch fails, experiment with-p0
,-p1
,-p2
, etc. to find the correct level. The correct level depends on how the diff was generated relative to where you are applying it. The most common value is-p1
. -
Backup Files: By default,
patch
creates backup files (usually with a.orig
extension) of the original files before patching. You can disable this behavior with the-N
option. -
Dry Run: Use the
-N
option for a dry run. This will show you what changespatch
would make without actually modifying any files. -
Fuzz: Sometimes, the context lines in the patch file don't exactly match the original file. This can happen if the original file has been slightly modified since the patch was created.
patch
has a "fuzz factor" that allows it to tolerate some differences. You can control the fuzz factor with the-F
option, but it's generally best to avoid using fuzz unless absolutely necessary.
Conclusion
The diff
and patch
utilities provide a robust and efficient way to
distribute and apply changes to text-based files.
Understanding how to create and apply patches can streamline collaboration, simplify updates, and manage configuration changes effectively. Remember to thoroughly test patches after application to ensure the changes have been applied correctly and haven't introduced any unintended side effects. By mastering these tools, you can significantly improve your workflow when working with code and configuration files in a collaborative or version-controlled environment.