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>
This commit is contained in:
parent
4258c212ca
commit
c94736a27f
@ -947,9 +947,31 @@ static int process_renames(struct merge_options *o,
|
|||||||
"%s added in %s",
|
"%s added in %s",
|
||||||
ren1_src, ren1_dst, branch1,
|
ren1_src, ren1_dst, branch1,
|
||||||
ren1_dst, branch2);
|
ren1_dst, branch2);
|
||||||
new_path = unique_path(o, ren1_dst, branch2);
|
if (o->call_depth) {
|
||||||
output(o, 1, "Adding as %s instead", new_path);
|
struct merge_file_info mfi;
|
||||||
update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
|
struct diff_filespec one, a, b;
|
||||||
|
|
||||||
|
one.path = a.path = b.path =
|
||||||
|
(char *)ren1_dst;
|
||||||
|
hashcpy(one.sha1, null_sha1);
|
||||||
|
one.mode = 0;
|
||||||
|
hashcpy(a.sha1, ren1->pair->two->sha1);
|
||||||
|
a.mode = ren1->pair->two->mode;
|
||||||
|
hashcpy(b.sha1, dst_other.sha1);
|
||||||
|
b.mode = dst_other.mode;
|
||||||
|
mfi = merge_file(o, &one, &a, &b,
|
||||||
|
branch1,
|
||||||
|
branch2);
|
||||||
|
output(o, 1, "Adding merged %s", ren1_dst);
|
||||||
|
update_file(o, 0,
|
||||||
|
mfi.sha,
|
||||||
|
mfi.mode,
|
||||||
|
ren1_dst);
|
||||||
|
} else {
|
||||||
|
new_path = unique_path(o, ren1_dst, branch2);
|
||||||
|
output(o, 1, "Adding as %s instead", new_path);
|
||||||
|
update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
|
||||||
|
}
|
||||||
} else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
|
} else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
|
||||||
ren2 = item->util;
|
ren2 = item->util;
|
||||||
clean_merge = 0;
|
clean_merge = 0;
|
||||||
|
55
t/t6036-recursive-corner-cases.sh
Executable file
55
t/t6036-recursive-corner-cases.sh
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='recursive merge corner cases'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# L1 L2
|
||||||
|
# o---o
|
||||||
|
# / \ / \
|
||||||
|
# o X ?
|
||||||
|
# \ / \ /
|
||||||
|
# o---o
|
||||||
|
# R1 R2
|
||||||
|
#
|
||||||
|
|
||||||
|
test_expect_success setup '
|
||||||
|
ten="0 1 2 3 4 5 6 7 8 9"
|
||||||
|
for i in $ten
|
||||||
|
do
|
||||||
|
echo line $i in a sample file
|
||||||
|
done >one &&
|
||||||
|
for i in $ten
|
||||||
|
do
|
||||||
|
echo line $i in another sample file
|
||||||
|
done >two &&
|
||||||
|
git add one two &&
|
||||||
|
test_tick && git commit -m initial &&
|
||||||
|
|
||||||
|
git branch L1 &&
|
||||||
|
git checkout -b R1 &&
|
||||||
|
git mv one three &&
|
||||||
|
test_tick && git commit -m R1 &&
|
||||||
|
|
||||||
|
git checkout L1 &&
|
||||||
|
git mv two three &&
|
||||||
|
test_tick && git commit -m L1 &&
|
||||||
|
|
||||||
|
git checkout L1^0 &&
|
||||||
|
test_tick && git merge -s ours R1 &&
|
||||||
|
git tag L2 &&
|
||||||
|
|
||||||
|
git checkout R1^0 &&
|
||||||
|
test_tick && git merge -s ours L1 &&
|
||||||
|
git tag R2
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success merge '
|
||||||
|
git reset --hard &&
|
||||||
|
git checkout L2^0 &&
|
||||||
|
|
||||||
|
test_must_fail git merge -s recursive R2^0
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
Loading…
Reference in New Issue
Block a user