Commit Diff


commit - c71da4f733c5d4c5d25de860ff717363522a105b
commit + da09d8ed3bab86a28b37ed49300aad928922ede7
blob - 1c02d96fb4e9d928b1d57ab019a1c00492bf4331
blob + ba62c524e9821448547ce28529656a1475da9aa7
--- got/got.c
+++ got/got.c
@@ -7253,7 +7253,7 @@ cmd_patch(int argc, char *argv[])
 		goto done;
 
 #ifndef PROFILE
-	if (pledge("stdio rpath wpath cpath proc exec sendfd flock",
+	if (pledge("stdio rpath wpath cpath fattr proc exec sendfd flock",
 	    NULL) == -1)
 		err(1, "pledge");
 #endif
blob - d906c8c59ea5881a8801313236bdb6bc6966eac3
blob + 19a9c9ce3f9dddc450dbc0e368ae072adf2a1e24
--- lib/patch.c
+++ lib/patch.c
@@ -387,10 +387,12 @@ apply_hunk(FILE *tmp, struct got_patch_hunk *h, long *
 }
 
 static const struct got_error *
-patch_file(struct got_patch *p, const char *path, FILE *tmp, int nop)
+patch_file(struct got_patch *p, const char *path, FILE *tmp, int nop,
+    mode_t *mode)
 {
 	const struct got_error *err = NULL;
 	struct got_patch_hunk *h;
+	struct stat sb;
 	size_t i;
 	long lineno = 0;
 	FILE *orig;
@@ -417,6 +419,12 @@ patch_file(struct got_patch *p, const char *path, FILE
 		goto done;
 	}
 
+	if (fstat(fileno(orig), &sb) == -1) {
+		err = got_error_from_errno("fstat");
+		goto done;
+	}
+	*mode = sb.st_mode;
+
 	copypos = 0;
 	STAILQ_FOREACH(h, &p->head, entries) {
 		if (h->lines == NULL)
@@ -465,15 +473,9 @@ patch_file(struct got_patch *p, const char *path, FILE
 		}
 	}
 
-	
-	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 (!nop && !feof(orig))
+	if (p->new == NULL && sb.st_size != copypos)
+		err = got_error(GOT_ERR_PATCH_DONT_APPLY);
+	else if (!nop && !feof(orig))
 		err = copy(tmp, orig, copypos, -1);
 
 done:
@@ -595,6 +597,7 @@ apply_patch(struct got_worktree *worktree, struct got_
 	char *oldpath = NULL, *newpath = NULL;
 	char *tmppath = NULL, *template = NULL;
 	FILE *tmp = NULL;
+	mode_t mode = GOT_DEFAULT_FILE_MODE;
 
 	TAILQ_INIT(&oldpaths);
 	TAILQ_INIT(&newpaths);
@@ -637,7 +640,7 @@ apply_patch(struct got_worktree *worktree, struct got_
 		err = got_opentemp_named(&tmppath, &tmp, template);
 	if (err)
 		goto done;
-	err = patch_file(p, oldpath, tmp, nop);
+	err = patch_file(p, oldpath, tmp, nop, &mode);
 	if (err)
 		goto done;
 
@@ -650,6 +653,11 @@ apply_patch(struct got_worktree *worktree, struct got_
 		goto done;
 	}
 
+	if (fchmod(fileno(tmp), mode) == -1) {
+		err = got_error_from_errno2("chmod", newpath);
+		goto done;
+	}
+
 	if (rename(tmppath, newpath) == -1) {
 		err = got_error_from_errno3("rename", tmppath, newpath);
 		goto done;
blob - 41f290c529e09c20b73b0a6752c3351664a9559d
blob + 6d468d0334318dc22a5552f720bf777276c909d8
--- regress/cmdline/patch.sh
+++ regress/cmdline/patch.sh
@@ -965,6 +965,47 @@ EOF
 		diff -u $testroot/stdout.expected $testroot/stdout
 	fi
 	test_done $testroot $ret
+}
+
+test_patch_preserve_perm() {
+	local testroot=`test_init patch_preserve_perm`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		test_done $testroot $ret
+		return 1
+	fi
+
+	chmod +x $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m 'alpha executable') > /dev/null
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		test_done $testroot $ret
+		return 1
+	fi
+
+	cat <<EOF > $testroot/wt/patch
+--- alpha
++++ alpha
+@@ -1 +1,2 @@
+ alpha
++was edited
+EOF
+
+	(cd $testroot/wt && got patch patch) > /dev/null
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		test_done $testroot $ret
+		return 1
+	fi
+
+	if [ ! -x $testroot/wt/alpha ]; then
+		echo "alpha is no more executable!" >&2
+		test_done $testroot 1
+		return 1
+	fi
+	test_done $testroot 0
 }
 
 test_parseargs "$@"
@@ -982,3 +1023,4 @@ run_test test_patch_equals_for_context
 run_test test_patch_rename
 run_test test_patch_illegal_status
 run_test test_patch_nop
+run_test test_patch_preserve_perm