BuySellAds.com

Our DNA is written in Objective-C
Jump

How to Make and Apply Patches

Sometimes you want to tell somebody how you fixed a problem in their code, but for some reason the code is not on github so you cannot send them a pull request. If you felt really smart then you might put the changes you made into an e-mail, like “in file1.m:102 you change it to x, in file2.m:54 you make change y”. Thought this doesn’t really help the developer you are trying to help. Even when following your change instructions to the letter it is a tedious and error-prone method of applying your changes. Thanks to M. Douglas McIlroy, Adjunct Professor at Dartmouth Colleague – one of the early Unix pioneers – there is a better way. He invented the form of diff that we are using today to get the Difference between two versions of a file. And Larry Wall who invented the patch command which can take a diff file and effectively apply it to files.

Honestly I was afraid of using patch before, never having played with it. What you don’t know you fear…. but since you are reading this article I now finally did, so that I can explain its usage.

Making a Patch

Let’s try with a simple text file first. file1.txt

Here's some text.
And a second line.
And a third line.
A fourth line.

file1_changed.txt

Here's some text.
And a third line.
A fourth line changed.

To get the difference between these files we just diff them:

diff file1.txt file1_changed.txt

This produces this output:

2d1
< And a second line.
4c3
< A fourth line. --- > A fourth line changed.

We see that diff features each changed block with a sort of address. We removed line two, so we see that the d in 2d1 probably means deleted. And the c in 4c3 probably means changed. If a line was changed you first see the original line prefixed by a less-than, then three dashes and then the changed line prefixed by a greater-than. diff also has a -y flag that puts the changes side-by-side, which more clearly visualizes the changes. This kind of output is called the “normal” format. To be able to use the diff for patching this is missing some vital information, namely which file the changes belong to. For this purpose you have to use the “unified” format which you get with the -u flag. We also pipe the output into a new file.

diff -u file1.txt file1_changed.txt > patch.diff

This results in the patch file to have this content:

--- file1.txt	2011-12-25 12:17:30.000000000 +0100
+++ file1_changed.txt	2011-12-25 12:01:30.000000000 +0100
@@ -1,4 +1,3 @@
 Here's some text.
-And a second line.
 And a third line.
-A fourth line.
+A fourth line changed.

You can see that has information about which files are involved. Also you have minus and plus prefixes. A minus with a plus following means that the line was changed. Just a minus means that the line was only present in the original, i.e. deleted. Just a plus means that line was only present in the modified version, i.e. inserted.

Let’s Patch

Appling the patch we just created is very simple. If the current working directory contains both the patch and the to-be-patched file all we need is:

patch < patch.diff

You get an output telling you while files where touched. As soon as you have applied a patch, calling the patch command again with the same patch will get you a message about this giving you the option to ignore individual “hunks” which is what these blocks of changes are called.

Multiple Files

For multiple files the process is only slightly more complex. Say you have a a folder “original” and a folder “changed” and the latter contains multiple changed versions of the files in the former. If you call diff passing a folder name instead of a file name then this causes diff to compare file1 in one folder against file1 in the second folder, exactly what we need. There are a couple of additional flags for diff that are of use in this case:

  • -r to recurse through all subfolders of the passed folders
  • -u to get the “unified format” output
  • -p prints the c-function a change was contained in
  • -N treats absent files as empty

So we go into the folder where our “original” and “changed” folders are located in and do:

diff -rupN original changed > original.patch

With this patch file it is easiest if you just move into the root of the original and copy the patch there. Then you can apply it without any additional parameters. Even though the diff contains the original and changed path prefixes it figures out that you must have meant the file contained in the current working directory.

cd original
cp ../original.patch .
patch < original.patch

There is one parameter of the patch command that you might need if the paths mentioned in the diff refer to a different folder structure than you have on your own machine. The man page of patch has this to say about -p:

-pnum or –strip=num Strip the smallest prefix containing num leading slashes from each file name found in the patch file. A sequence of one or more adjacent slashes is counted as a single slash. This controls how file names found in the patch file are treated, in case you keep your files in a different directory than the person who sent out the patch. For example, supposing the file name in the patch file was /u/howard/src/blurfl/blurfl.c setting -p0 gives the entire file name unmodified, -p1 gives u/howard/src/blurfl/blurfl.c without the leading slash, -p4 gives blurfl/blurfl.c and not specifying -p at all just gives you blurfl.c. Whatever you end up with is looked for either in the current directory, or the directory specified by the -d option.

When I saw someone use patch for the first time he totally confused me by claiming that -p is short for the “patch level” which – as you can see above – is utter nonsense. It is probably short for prefix and the long form of it –strip actually means something entirely different. It is essentially the number of slashes that are to be ignored, including the part of the path to the left of each ignored slash. A strip level of 0 does not modify the paths at all. A level of 1 ignores the path up to and including the first slash. Omitting the p parameter causes patch to ignore the complete paths and only look at the file names. If a patch without specified strip level doesn’t work then you will have to take a look at the patch file and figure out how much of the paths you need to ignore so that these paths fit with the situation on your own file system. In our example above (with the original and changed folders) we could have used -p1 to have patch ignore the relative paths, but it seems to do that by itself.

Creating Patches from Repositories

Above we discussed one way to create diffs that can be used for patches by having two versions of your files in two folders and diff these two. This typically suites those people who have not yet warmed to the use of source code management systems (SCM) like git or Subversion. In an SCM you don’t have multiple copies of each file, instead you have only one current version of the source code in the project directory and all previous versions are somehow hidden from view, but can be retrieved. In git this versions are called “commits” in Subversion “revisions”. For the sake of trying it out, let’s create an empty Xcode project with a git repo. On the dialog where you specify the folder for the project you can set a checkbox to have Xcode also put it under version control. It is probably an oversight in Xcode but if you create the project like this the first commit is being done for you with comment “Initial Commit”, however there are changes to the project file adding a “lastKnownFileType” to the project.pbxproj – which is contained in the xcodeproj bundle – that causes the project file to show as modified if you do “git status”. To get to a clean starting state for the project I go to the folder where I created the project and do an additional commit.

cd ~/scmtest
git status
git commit -a
git log

I have vi set up as my editor so on the file that opens from the commit I enter some comment and then ESC : w q to save and finish the commit. The git log now shows no uncommitted changes, two commits and two untracked files containing some user workspace settings which you probably don’t need or want in your repo. Now let’s modify some code. For this test I just added an NSLog into the app delegate file. What’s really interesting now is how to create a patch that contains the changes from our second commit to the current modified state of our working directory. Simple, really:

git diff --no-prefix > diff.patch

We need the option to omit the prefix because otherwise git diff adds an “a” and a “b” prefix in the paths which never correspond to actual paths. Of course you can also apply patches that still contain the prefixes if you use the -p1 strip level. Our patch now looks like this:

diff --git scmtest/DTAppDelegate.m scmtest/DTAppDelegate.m
index 7730864..795fa6d 100644
--- scmtest/DTAppDelegate.m
+++ scmtest/DTAppDelegate.m
@@ -20,6 +20,8 @@

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 {
+    NSLog(@"Hello World");
+
     self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
     // Override point for customization after application launch.
     self.window.backgroundColor = [UIColor whiteColor];

You can see the inserted lines prefixed with plus, also there are a couple of lines of code before and after the insertion so that patch can find the place even when there have been other modifications to the file that changed the line numbers. A simple git diff always contains all modifications made on the latest commit. To test this patch we discard our changes and see that we ended up on the second commit.

git reset --hard
git log

Then we apply the patch, we need a strip level of zero so that the relative paths are used as they are.

patch -p0 < diff.patch
git status

After this patch we see that the changes appeared again. You can also create a patch between any two commits by specifying the commit identifiers as parameters. You can get the identifiers from git log.

git log
git diff 92cef602e685970e6d4316cd383217d39d169b89 ec24c6d3435a2ae9ddaabd76f958561e86f9f02e > b.patch

Finally let’s also see how the same can be gotten from an SVN repo. Same technique:

svn status
svn diff > diff.patch

Between two specific revisions the same is possible. Subversion numbers revisions incrementally instead of with identifiers as git does. So the command for the changes from a specific version to the next is this:

svn diff -r 126:127 > c.patch

Contrary to git the diff command for svn does not prefix the file paths, instead the revision info is contained in brackets after the files which is ignored by patch.

Conclusion

Knowing how to create and apply patches is a great skill to have as a programmer. So the next time you find that you want to precisely communicate what changes you made in somebody else’s code you should send them a patch instead of an email containing line numbers and code fragments. And if they don’t know what to do with that, also send them a link to this here tutorial. :-) PS: AlBlue points out that on git your can also directly send patches via e-mail. Those even include your commit messages and identity.


Categories: Recipes

%d bloggers like this: