Skip to content

Commit c94736a

Browse files
committed
merge-recursive: don't segfault while handling rename clashes
When a branch moves A to B while the other branch created B (or moved C to B), the code tried to rename one of them to B~something to preserve both versions, and failed to register temporary resolution for the original path B at stage#0 during virtual ancestor computation. This left the index in unmerged state and caused a segfault. A better solution is to merge these two versions of B's in place and use the (potentially conflicting) result as the intermediate merge result in the virtual ancestor. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4258c21 commit c94736a

File tree

2 files changed

+80
-3
lines changed

2 files changed

+80
-3
lines changed

merge-recursive.c

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -947,9 +947,31 @@ static int process_renames(struct merge_options *o,
947947
"%s added in %s",
948948
ren1_src, ren1_dst, branch1,
949949
ren1_dst, branch2);
950-
new_path = unique_path(o, ren1_dst, branch2);
951-
output(o, 1, "Adding as %s instead", new_path);
952-
update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
950+
if (o->call_depth) {
951+
struct merge_file_info mfi;
952+
struct diff_filespec one, a, b;
953+
954+
one.path = a.path = b.path =
955+
(char *)ren1_dst;
956+
hashcpy(one.sha1, null_sha1);
957+
one.mode = 0;
958+
hashcpy(a.sha1, ren1->pair->two->sha1);
959+
a.mode = ren1->pair->two->mode;
960+
hashcpy(b.sha1, dst_other.sha1);
961+
b.mode = dst_other.mode;
962+
mfi = merge_file(o, &one, &a, &b,
963+
branch1,
964+
branch2);
965+
output(o, 1, "Adding merged %s", ren1_dst);
966+
update_file(o, 0,
967+
mfi.sha,
968+
mfi.mode,
969+
ren1_dst);
970+
} else {
971+
new_path = unique_path(o, ren1_dst, branch2);
972+
output(o, 1, "Adding as %s instead", new_path);
973+
update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
974+
}
953975
} else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
954976
ren2 = item->util;
955977
clean_merge = 0;

t/t6036-recursive-corner-cases.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/bin/sh
2+
3+
test_description='recursive merge corner cases'
4+
5+
. ./test-lib.sh
6+
7+
#
8+
# L1 L2
9+
# o---o
10+
# / \ / \
11+
# o X ?
12+
# \ / \ /
13+
# o---o
14+
# R1 R2
15+
#
16+
17+
test_expect_success setup '
18+
ten="0 1 2 3 4 5 6 7 8 9"
19+
for i in $ten
20+
do
21+
echo line $i in a sample file
22+
done >one &&
23+
for i in $ten
24+
do
25+
echo line $i in another sample file
26+
done >two &&
27+
git add one two &&
28+
test_tick && git commit -m initial &&
29+
30+
git branch L1 &&
31+
git checkout -b R1 &&
32+
git mv one three &&
33+
test_tick && git commit -m R1 &&
34+
35+
git checkout L1 &&
36+
git mv two three &&
37+
test_tick && git commit -m L1 &&
38+
39+
git checkout L1^0 &&
40+
test_tick && git merge -s ours R1 &&
41+
git tag L2 &&
42+
43+
git checkout R1^0 &&
44+
test_tick && git merge -s ours L1 &&
45+
git tag R2
46+
'
47+
48+
test_expect_success merge '
49+
git reset --hard &&
50+
git checkout L2^0 &&
51+
52+
test_must_fail git merge -s recursive R2^0
53+
'
54+
55+
test_done

0 commit comments

Comments
 (0)