Skip to content

Commit b0f266d

Browse files
jonathantanmygitster
authored andcommitted
apply: when -R, also reverse list of sections
A patch changing a symlink into a file is written with 2 sections (in the code, represented as "struct patch"): firstly, the deletion of the symlink, and secondly, the creation of the file. When applying that patch with -R, the sections are reversed, so we get: (1) creation of a symlink, then (2) deletion of a file. This causes an issue when the "deletion of a file" section is checked, because Git observes that the so-called file is not a file but a symlink, resulting in a "wrong type" error message. What we want is: (1) deletion of a file, then (2) creation of a symlink. In the code, this is reflected in the behavior of previous_patch() when invoked from check_preimage() when the deletion is checked. Creation then deletion means that when the deletion is checked, previous_patch() returns the creation section, triggering a mode conflict resulting in the "wrong type" error message. But deletion then creation means that when the deletion is checked, previous_patch() returns NULL, so the deletion mode is checked against lstat, which is what we want. There are also other ways a patch can contain 2 sections referencing the same file, for example, in 7a07841 ("git-apply: handle a patch that touches the same path more than once better", 2008-06-27). "git apply -R" fails in the same way, and this commit makes this case succeed. Therefore, when building the list of sections, build them in reverse order (by adding to the front of the list instead of the back) when -R is passed. Helped-by: Junio C Hamano <[email protected]> Signed-off-by: Jonathan Tan <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 69986e1 commit b0f266d

File tree

3 files changed

+23
-2
lines changed

3 files changed

+23
-2
lines changed

apply.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4699,8 +4699,13 @@ static int apply_patch(struct apply_state *state,
46994699
reverse_patches(patch);
47004700
if (use_patch(state, patch)) {
47014701
patch_stats(state, patch);
4702-
*listp = patch;
4703-
listp = &patch->next;
4702+
if (!list || !state->apply_in_reverse) {
4703+
*listp = patch;
4704+
listp = &patch->next;
4705+
} else {
4706+
patch->next = list;
4707+
list = patch;
4708+
}
47044709

47054710
if ((patch->new_name &&
47064711
ends_with_path_components(patch->new_name,

t/t4114-apply-typechange.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ test_expect_success 'symlink becomes file' '
8888
'
8989
test_debug 'cat patch'
9090

91+
test_expect_success 'symlink becomes file, in reverse' '
92+
git checkout -f foo-symlinked-to-bar &&
93+
git diff-tree -p HEAD foo-back-to-file > patch &&
94+
git checkout foo-back-to-file &&
95+
git apply -R --index < patch
96+
'
97+
9198
test_expect_success 'binary file becomes symlink' '
9299
git checkout -f foo-becomes-binary &&
93100
git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch &&

t/t4127-apply-same-fn.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ test_expect_success 'apply same filename with independent changes' '
3232

3333
test_expect_success 'apply same filename with overlapping changes' '
3434
git reset --hard &&
35+
36+
# Store same_fn so that we can check apply -R in next test
37+
cp same_fn same_fn1 &&
38+
3539
modify "s/^d/z/" same_fn &&
3640
git diff > patch0 &&
3741
git add same_fn &&
@@ -43,6 +47,11 @@ test_expect_success 'apply same filename with overlapping changes' '
4347
test_cmp same_fn same_fn2
4448
'
4549

50+
test_expect_success 'apply same filename with overlapping changes, in reverse' '
51+
git apply -R patch0 &&
52+
test_cmp same_fn same_fn1
53+
'
54+
4655
test_expect_success 'apply same new filename after rename' '
4756
git reset --hard &&
4857
git mv same_fn new_fn &&

0 commit comments

Comments
 (0)