got patch: require exact match when removing files
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
diff --git a/lib/patch.c b/lib/patch.c
index 478ade4..71695e6 100644
--- a/lib/patch.c
+++ b/lib/patch.c
@@ -26,6 +26,7 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/uio.h>
#include <errno.h>
@@ -456,7 +457,15 @@ patch_file(struct got_patch *p, const char *path, FILE *tmp)
}
}
- if (!feof(orig))
+
+ if (p->new == NULL) {
+ struct stat sb;
+
+ if (fstat(fileno(orig), &sb) == -1)
+ err = got_error_from_errno("fstat");
+ else if (sb.st_size != copypos)
+ err = got_error(GOT_ERR_PATCH_DONT_APPLY);
+ } else if (!feof(orig))
err = copy(tmp, orig, copypos, -1);
done:
@@ -584,16 +593,6 @@ apply_patch(struct got_worktree *worktree, struct got_repository *repo,
if (err)
goto done;
- if (p->old != NULL && p->new == NULL) {
- /*
- * special case: delete a file. don't try to match
- * the lines but just schedule the removal.
- */
- err = got_worktree_schedule_delete(worktree, &oldpaths,
- 0, NULL, delete_cb, delete_arg, repo, 0, 0);
- goto done;
- }
-
if (asprintf(&template, "%s/got-patch",
got_worktree_get_root_path(worktree)) == -1) {
err = got_error_from_errno(template);
@@ -607,6 +606,12 @@ apply_patch(struct got_worktree *worktree, struct got_repository *repo,
if (err)
goto done;
+ if (p->old != NULL && p->new == NULL) {
+ err = got_worktree_schedule_delete(worktree, &oldpaths,
+ 0, NULL, delete_cb, delete_arg, repo, 0, 0);
+ goto done;
+ }
+
if (rename(tmppath, newpath) == -1) {
err = got_error_from_errno3("rename", tmppath, newpath);
goto done;
diff --git a/regress/cmdline/patch.sh b/regress/cmdline/patch.sh
index 36c9ef5..9a300a8 100755
--- a/regress/cmdline/patch.sh
+++ b/regress/cmdline/patch.sh
@@ -439,6 +439,44 @@ EOF
return 1
fi
+ # try to delete a file with a patch that doesn't match
+ jot 100 > $testroot/wt/numbers
+ (cd $testroot/wt && got add numbers && got commit -m 'add numbers') \
+ >/dev/null
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ test_done $testroot $ret
+ return 1
+ fi
+
+ cat <<EOF > $testroot/wt/patch
+--- numbers
++++ /dev/null
+@@ -1,9 +0,0 @@
+-1
+-2
+-3
+-4
+-5
+-6
+-7
+-8
+-9
+EOF
+
+ (cd $testroot/wt && got patch patch) > /dev/null 2> $testroot/stderr
+ ret=$?
+ if [ $ret -eq 0 ]; then # should fail
+ test_done $testroot 1
+ return 1
+ fi
+
+ echo "got: patch doesn't apply" > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ fi
test_done $testroot $ret
}