@matheusbsbs / matheus-bagliano / commits / c94736a27f
Fork 0

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 <gitster@pobox.com>

Junio C Hamano committed Jul 30, 2009 at 17:38 UTC c94736a27f89bfc268aed0de7264deecdc136a58
2 files changed +80 -3
merge-recursive.c
+25 -3
@@ -947,9 +947,31 @@ static int process_renames(struct merge_options *o,
947 "%s added in %s",
948 ren1_src, ren1_dst, branch1,
949 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 + }
975 } else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
976 ren2 = item->util;
977 clean_merge = 0;
t/t6036-recursive-corner-cases.sh new
+55
@@ -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