If reversing a patch succeeds does that always mean the patch had been fully applied

patch

This is touched on in two questions, 'Check if a file or folder has been patched already' and 'Make patch return 0 when skipping an already applied patch' however neither had a satisfying answer.

I am writing a script and want to test the following for a patch:

Fully applied: continue

Partially applied: exit

Not applied: if it can be applied successfully do it and continue, otherwise exit

The problem is handling the partially applied case:

mkdir test && cd test

cat << EOF > foobar.patch
--- /dev/null
+++ foo
@@ -0,0 +1 @@
+foo
--- /dev/null
+++ bar
@@ -0,0 +1 @@
+bar
EOF

patch --forward -i foobar.patch
rm foo

So bar exists but foo doesn't because at some point it was removed. Now if I apply the patch forward in a dry-run the exit code is 1 since it's not applied successfully.

$ patch --dry-run --forward --force -i foobar.patch
checking file foo
The next patch would create the file bar,
which already exists!  Skipping patch.
1 out of 1 hunk ignored
$ echo $?
1

That does not tell me whether the patch is fully applied though, just that it failed the dry-run. I don't know why that's marked correct as the stackoverflow answer. I tried reversing but since it's a non-interactive script it only worked with force:

$ patch --dry-run --reverse --force -i foobar.patch
The next patch, when reversed, would delete the file foo,
which does not exist!  Applying it anyway.
checking file foo
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED
checking file bar
$ echo $?
1

So does it always hold that if I try to forcibly reverse a patch in a dry-run and it succeeds that the patch is fully applied, and if it fails that it's not fully applied (or applied at all)? Because if so then I can do something like

patch --dry-run --reverse --force -i foobar.patch ||
(patch --dry-run --forward --force -i foobar.patch &&
 patch --forward --force -i foobar.patch) ||
exit 1

Best Answer

With this diff:

diff --git a/bar b/bar
new file mode 100644
index 0000000..e69de29
diff --git a/foo b/foo
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/foo
@@ -0,0 +1 @@
+foo

this happens:

$ cd /tmp/test
$ patch --forward -i foobar.patch
patching file bar
patching file foo
$ echo $?
0
$ rm bar
$ patch --dry-run --reverse --force -i foobar.patch
The next patch, when reversed, would delete the file bar,
which does not exist!  Applying it anyway.
checking file bar
checking file foo
$ echo $?
0

So the answer to your question is no.