Generating and reading patch files

By thomas, 26 September, 2007

We are working on a short piece of code, called count.c. I first copy count.c to a backup, count.c.orig and begin correcting count.c

patched code


#include
#define LENGTH 100
#define HEIGHT 50

int main (int argc, char* argv[]) {
int i;
int j;
int k;
k=1;
for (i=0; i
original code


#include
#define LENGTH 100
#define HEIGHT 50

int main (int argc, char* argv[]) {
int i;
int j;
int k;
for (i=0; i

Generating the patch

In this very simple case we only have one line different in the files, to generate a patch file, we only need use diff -u

[thomas@host ~]$ diff -u count.c.orig count.c
--- count.c 2007-09-26 15:09:16.000000000 -0400
+++ count.c.orig 2007-09-26 15:09:24.000000000 -0400
@@ -6,7 +6,6 @@
int i;
int j;
int k;
+ k=1;
for (i=0; i
To store this in a patch file, we would use diff -u count.c count.c.orig >count.patch. Alternatively we could use the gendiff script provided by the rpm rpm. Gendiff is a nice little script that runs through a directory and makes a patch file for you based on the extension you gave to your original files.

[thomas@host ~]$ gendiff `pwd` .orig
--- /home/thomas/count.c.orig 2007-09-26 15:09:24.000000000 -0400
+++ /home/thomas/count.c 2007-09-26 15:18:55.000000000 -0400
@@ -6,6 +6,7 @@
int i;
int j;
int k;
+ k=1;
for (i=0; i
Gendiff is particularly nice when working with directories.

Applying the patch

To apply the patch we could rename count.c.orig to count.c and run patch.

[thomas@host ~]$ mv count.c.orig count.c
[thomas@host ~]$ patch
#define LENGTH 100
#define HEIGHT 50

int main (int argc, char* argv[]) {
int i;
int j;
int k;
k=1;
for (i=0; i

This adds our line to the file or patches it.

Reading the patch file

The first two lines of the patch file indicate which two files are being compared. The line with --- is the old file, the +++ is the new file. At the end of the patch, the file will be the same as the +++ file. The dates after the name of the file are the last modification time of the file.

The next line indicates the context for the change, in our example @@ -6,6 +6,7 @@ is the context information for the "hunk" that is changing.
-6,6 means the original file starting at line 6 and continuing 6 lines.
+6,7 means the new file starting at line 6 and continuing 7 lines.

Next we need to know that each line that is unchanged between the two files is indented by a space, a line which is missing in the original file is prepended by a +, a line that is missing in the target file is prepended by a -.

Looking at our example we know that starting at line 6 we have 3 lines that are the same in both files, then we add another line to the target file at "+ k=1;" making our total in the target file 7.

In this simple example it's very easy to read the line numbers and make sense of them. Here is a more complex example.

--- libtool-1.4.2/ltmain.in~ Mon Feb 4 15:12:15 2002
+++ libtool-1.4.2/ltmain.in Mon Feb 4 15:12:15 2002
@@ -745,6 +745,7 @@
linker_flags=
dllsearchpath=
lib_search_path=`pwd`
+ inst_prefix_dir=

avoid_version=no
dlfiles=
@@ -875,6 +876,11 @@
prev=
continue
;;
+ inst_prefix)
+ inst_prefix_dir="$arg"
+ prev=
+ continue
+ ;;
release)
release="-$arg"
prev=
@@ -976,6 +982,11 @@
continue
;;

+ -inst-prefix-dir)
+ prev=inst_prefix
+ continue
+ ;;
+
# The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
# so, if we see these flags be careful not to treat them like -L
-L[A-Z][A-Z]*:*)
@@ -1851,7 +1862,16 @@
if test "$hardcode_direct" = yes; then
add="$libdir/$linklib"
elif test "$hardcode_minus_L" = yes; then
- add_dir="-L$libdir"
+ # Try looking first in the location we're being installed to.
+ add_dir=
+ if test -n "$inst_prefix_dir"; then
+ case "$libdir" in
+ [\\/]*)
+ add_dir="-L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add_dir="-L$libdir $add_dir"
add="-l$name"
elif test "$hardcode_shlibpath_var" = yes; then
case :$finalize_shlibpath: in
@@ -1861,7 +1881,16 @@
add="-l$name"
else
# We cannot seem to hardcode it, guess we'll fake it.
- add_dir="-L$libdir"
+ # Try looking first in the location we're being installed to.
+ add_dir=
+ if test -n "$inst_prefix_dir"; then
+ case "$libdir" in
+ [\\/]*)
+ add_dir="-L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add_dir="-L$libdir $add_dir "
add="-l$name"
fi

@@ -3823,7 +3852,7 @@
fi
done
# Quote the link command for shipping.
- relink_command="(cd `pwd`; $SHELL $0 --mode=relink $libtool_args)"
+ relink_command="(cd `pwd`; $SHELL $0 --mode=relink $libtool_args @inst_prefix_dir@)"
relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"`

# Only create the output if not a dry run.
@@ -4124,12 +4153,30 @@
dir="$dir$objdir"

if test -n "$relink_command"; then
+ # Determine the prefix the user has applied to our future dir.
+ inst_prefix_dir=`$echo "$destdir" | sed "s%$libdir\$%%"`
+
+ # Don't allow the user to place us outside of our expected
+ # location b/c this prevents finding dependent libraries that
+ # are installed to the same prefix.
+ if test "$inst_prefix_dir" = "$destdir"; then
+ $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2
+ exit 1
+ fi
+
+ if test -n "$inst_prefix_dir"; then
+ # Stick the inst_prefix_dir data into the link command.
+ relink_command=`$echo "$relink_command" | sed "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+ else
+ relink_command=`$echo "$relink_command" | sed "s%@inst_prefix_dir@%%"`
+ fi
+
$echo "$modename: warning: relinking \`$file'" 1>&2
$show "$relink_command"
if $run eval "$relink_command"; then :
else
$echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2
- continue
+ exit 1
fi
fi

This is a patch from libtool, it's a bit old.
Starting at the first two lines we see that we are working with the file ltmain.in
The original file is ltmain.in~, the new one is ltmain.in

@@ -745,6 +745,7 @@

The next line should be read that starting at line 745 in the original with 6 lines of context we have the change at line 745 in the new file with 7 lines after patching. The patch then has 3 lines that are the same in both files, an addition and three more lines that are the same (3+1+3=7). Now, the key is that we are not done with these files, so the new file has one more line than the old file.

@@ -875,6 +876,11 @@

The next @@ lines say at line 875 in the original with 6 lines of context, but line 876 in the new file with 11 lines of context (3+5+3=11). We are at line 876 in the new file because the new file has one more line than the original file, and we have 11 lines of context because we have 5 new lines to add to the file.

@@ -976,6 +982,11 @@

Our next hunk makes this more clear, our original file reference starts at line 976, but the new file starts at line 982. 982 -976 = 6, which is 5 lines from hunk 2 and 1 line from hunk 1. Again, we are adding 5 lines so our context for the new file is 11 lines.

@@ -1851,7 +1862,16 @@

Now, in this hunk we have 10 additions and 1 deletion for a total of 9 new lines, so our context is 16 lines. You might think the context is 15 (3+9+3), but it is 16 because have to reference the context lines based on the file before we apply our hunk. Our new file is starting at line 1862 because we are now 11 lines longer in the new file.

@@ -1861,7 +1881,16 @@

Adding up all our changes we see that we added 9 lines in hunk 4, 5 lines in hunk 3, 5 lines in hunk 2 and 1 line in hunk 1 for a total of 20 (1881-1861=20). We have 10 lines of changes so our context is 16 lines.

That should be about all you need to know to read patch files in unified format. For more information try the wikipedia diff page.