43

Sometimes, for whatever reason, I have to produce patch-files (under Linux) that are in the wrong direction. I know that I can deal with this by using the -R switch when applying it via patch, but it would be nice if there were a way of permanently reversing the patch-file. Is there a utility that can do this, or e.g. a regex that would be guaranteed to work?

UPDATE

Lie Ryan has suggested a neat way of doing this. However, it requires access to the original source file(s). So I suppose I should update my question to state that I'm more after a way of achieving this given only the patch-file itself.

Community
  • 1
  • 1
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680

4 Answers4

60

You can use the tool interdiff(1) from patchutils. In particular, the man page for interdiff says:

To reverse a patch, use /dev/null for diff2.

So,

$ interdiff -q file.patch /dev/null > reversed.patch

The -q / --quiet prevents the insertion of reverted: lines.

Tom Hale
  • 40,825
  • 36
  • 187
  • 242
camh
  • 40,988
  • 13
  • 62
  • 70
  • 1
    doesn't work. I've tried to apply the reversed patch to the file patched with the forward patch, and it fails. So this does not produce a correct reversed patch, at least not for all patches. I don't know whether interdiff is broken or this method is just wrong – matteo Mar 09 '17 at 17:00
  • I see, this *is* supposed to work according to interdiff's man page, so it's a bug in interdiff – matteo Mar 09 '17 at 17:01
  • 1
    @matteo `-q` was required for me. Edited. – Tom Hale Dec 11 '18 at 04:04
22

Try:

patch -R file.txt file.patch
diff file.txt.orig file.txt > file.patch.rev
// you can then `rm file.txt.orig file.patch`

EDIT:

To reverse a unified diff, you need to change three things:

  • the patch header
  • the chunk header
  • the + to - and - to +

So here's how a patch header for a looks like:

--- b.asm   2010-09-24 12:03:43.000000000 +1000    
+++ a.asm   2010-09-24 23:28:43.000000000 +1000

you need to reverse it so it looks like this:

--- a.asm   2010-09-24 23:28:43.000000000 +1000
+++ b.asm   2010-09-24 12:03:43.000000000 +1000    

basically switch the order, and switch +++ to --- and vice versa.

Next, the chunk header:

@@ -29,5 +27,7 @@

You need to reverse the numbers, so it look like this:

@@ -27,7 +29,5 @@

basically, switch the number pairs

and last, switch every line beginning with + and every line beginning with -.

EDIT:

to switch the chunk header, you can do:

sed -e "s/@@ -\([0-9]\+,[0-9]\+\) +\([0-9]\+,[0-9]\+\) @@/@@ -\2 +\1 @@/"

to switch + to - and - to +, you can do:

sed -e "s/^+/P/" -e "s/^-/+/" -e "s/^P/-/"

FINALLY:

to reverse the patch header, do:

head -2 orig.diff | tac | sed -e "s/+++/PPP/" -e "s/---/+++/" -e "s/PPP/---/" > head
tail orig.diff -n+3 > tail
cat head tail > headtail
rm head tail

So, finally, our (quick and dirty) script looks like:

#!/usr/bin/env sh
F="$1"
head -2 $F | tac | sed -e "s/+++/PPP/" -e "s/---/+++/" -e "s/PPP/---/" > $F.head
tail $F -n+3 | sed -e "s/@@ -\([0-9]\+,[0-9]\+\) +\([0-9]\+,[0-9]\+\) @@/@@ -\2 +\1 @@/" -e "s/^+/P/" -e "s/^-/+/" -e "s/^P/-/" > $F.tail
cat $F.head $F.tail 
rm $F.head $F.tail

I tested it, and it seems to work.

though, to make things more maintainable, and cleaner:

#!/usr/bin/env sh
swap() {
    sed -e "s/^$1/PPP/" -e "s/^$2/$1/" -e "s/^PPP/$2/"
}
file_header() {
    head -2 $1 | tac | swap +++ ---
}
fix_chunk_header() {
    sed -e "s/@@ -\([0-9]\+,[0-9]\+\) +\([0-9]\+,[0-9]\+\) @@/@@ -\2 +\1 @@/" 
}
fix_lines() {
    swap + -
}
file="$1"
file_header $file
tail $file -n+3 | fix_chunk_header | fix_lines
Lie Ryan
  • 62,238
  • 13
  • 100
  • 144
  • 1
    +1: I hadn't thought of this. But out of interest, is there any way to do it without access to the original files? – Oliver Charlesworth Oct 10 '10 at 22:16
  • @Oli Charlesworth: The file.txt.orig file are automatic backup generated by patch when you call it in the first line. I simply use that backup to regenerate the reversed patch. – Lie Ryan Oct 10 '10 at 22:18
  • 1
    @Lie Ryan: What I meant was, how could I do this if I *only* have the patch-file? – Oliver Charlesworth Oct 10 '10 at 22:20
  • @Oli Charlesworth: hmm.. don't think there's a way to do that with `diff` and `patch`. I can probably concoct some sed script that substitute > with < and vice versa, if you give me some moment. – Lie Ryan Oct 10 '10 at 22:22
  • 1
    @Lie Ryan: Don't put yourself out on my account! I can probably do so myself, if I knew the specific things that need reversing (e.g. `+` and `-`). – Oliver Charlesworth Oct 10 '10 at 22:25
  • @Oli Charlesworth: hmm... sorry about that, apparently it's not as trivial as I originally thought it'd be (i.e. not a one-liner sed). Anyway, there's three things you'd need to change. The patch header, chunk header, and the + to - (or > to <), I'll explain a bit above. – Lie Ryan Oct 10 '10 at 22:51
  • @Lie Ryan: Thanks very much for all that. I will give this a try tomorrow! – Oliver Charlesworth Oct 10 '10 at 23:12
  • @LieRyan Thank you for posting this detailed account of reverse patching. I think this should be the correct answer since if we do not understand the tools then we cannot fully use them and that's why you get my +1. – Dwight Spencer Jan 06 '14 at 00:36
5

I had applied a patch patch -N -p0 < path/file.patch but i started facing compilation issues due to incomplete code all i did was to run this command patch -p0 -R < path/file.patch . Referred this link

tinker_fairy
  • 1,323
  • 1
  • 15
  • 18
2

I reversed / restored original file with: patch -Ri sample.patch

Additional -p0 or -p1 options might be of help as well.