@matheusbsbs / matheus-bagliano / commits / 1f7903a371
Fork 0

builtin-apply: prevent non-explicit permission changes

A git patch that does not change the executable bit records the mode bits on its "index" line. "git apply" used to interpret this mode exactly the same way as it interprets the mode recorded on "new mode" line, as the wish by the patch submitter to set the mode to the one recorded on the line. The reason the mode does not agree between the submitter and the receiver in the first place is because there is _another_ commit that only appears on one side but not the other since their histories diverged, and that commit changes the mode. The patch has "index" line but not "new mode" line because its change is about updating the contents without affecting the mode. The application of such a patch is an explicit wish by the submitter to only cherry-pick the commit that updates the contents without cherry-picking the commit that modifies the mode. Viewed this way, the current behaviour is problematic, even though the command does warn when the mode of the path being patched does not match this mode, and a careful user could detect this inconsistencies between the patch submitter and the patch receiver. This changes the semantics of the mode recorded on the "index" line; instead of interpreting it as the submitter's wish to set the mode to the recorded value, it merely informs what the mode submitter happened to have, and the presense of the "index" line is taken as submitter's wish to keep whatever the mode is on the receiving end. This is based on the patch originally done by Alexander Potashev with a minor fix; the tests are mine. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jan 2, 2009 at 02:55 UTC 1f7903a371f90f025cfc3b4aa3a0b4a8f8b05b94
2 files changed +65 -1
builtin-apply.c
+3 -1
@@ -609,7 +609,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
609 memcpy(patch->new_sha1_prefix, line, len);
610 patch->new_sha1_prefix[len] = 0;
611 if (*ptr == ' ')
612 - patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
612 + patch->old_mode = strtoul(ptr+1, NULL, 8);
613 return 0;
614 }
615
@@ -2316,6 +2316,8 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
2316 if (st_mode != patch->old_mode)
2317 fprintf(stderr, "warning: %s has type %o, expected %o\n",
2318 old_name, st_mode, patch->old_mode);
2319 + if (!patch->new_mode)
2320 + patch->new_mode = st_mode;
2321 return 0;
2322
2323 is_new:
t/t4129-apply-samemode.sh new
+62
@@ -0,0 +1,62 @@
1 +#!/bin/sh
2 +
3 +test_description='applying patch with mode bits'
4 +
5 +. ./test-lib.sh
6 +
7 +test_expect_success setup '
8 + echo original >file &&
9 + git add file &&
10 + test_tick &&
11 + git commit -m initial &&
12 + git tag initial &&
13 + echo modified >file &&
14 + git diff --stat -p >patch-0.txt &&
15 + chmod +x file &&
16 + git diff --stat -p >patch-1.txt
17 +'
18 +
19 +test_expect_success 'same mode (no index)' '
20 + git reset --hard &&
21 + chmod +x file &&
22 + git apply patch-0.txt &&
23 + test -x file
24 +'
25 +
26 +test_expect_success 'same mode (with index)' '
27 + git reset --hard &&
28 + chmod +x file &&
29 + git add file &&
30 + git apply --index patch-0.txt &&
31 + test -x file &&
32 + git diff --exit-code
33 +'
34 +
35 +test_expect_success 'same mode (index only)' '
36 + git reset --hard &&
37 + chmod +x file &&
38 + git add file &&
39 + git apply --cached patch-0.txt &&
40 + git ls-files -s file | grep "^100755"
41 +'
42 +
43 +test_expect_success 'mode update (no index)' '
44 + git reset --hard &&
45 + git apply patch-1.txt &&
46 + test -x file
47 +'
48 +
49 +test_expect_success 'mode update (with index)' '
50 + git reset --hard &&
51 + git apply --index patch-1.txt &&
52 + test -x file &&
53 + git diff --exit-code
54 +'
55 +
56 +test_expect_success 'mode update (index only)' '
57 + git reset --hard &&
58 + git apply --cached patch-1.txt &&
59 + git ls-files -s file | grep "^100755"
60 +'
61 +
62 +test_done