[PATCH] Fix segfault in merge-recursive
When there is no "common" tree (for whatever reason), we must not throw a segmentation fault.
Noticed by Dave O.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
Sorry, did not have much time, so I only fixed the segfault.
Could you verify that the result is correct?
merge-recursive.c | 23 +++++--
t/t3031-merge-criscross.sh | 135 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 151 insertions(+), 7 deletions(-)
create mode 100755 t/t3031-merge-criscross.sh
diff --git a/merge-recursive.c b/merge-recursive.c
index a3721ef..920ccc1 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -176,17 +176,26 @@ static int git_merge_trees(int index_only,
opts.index_only = 1;
else
opts.update = 1;
- opts.merge = 1;
- opts.head_idx = 2;
opts.fn = threeway_merge;
opts.src_index = &the_index;
opts.dst_index = &the_index;
- init_tree_desc_from_tree(t+0, common);
- init_tree_desc_from_tree(t+1, head);
- init_tree_desc_from_tree(t+2, merge);
+ if (common) {
+ opts.merge = 1;
+ opts.head_idx = 2;
+ init_tree_desc_from_tree(t+0, common);
+ init_tree_desc_from_tree(t+1, head);
+ init_tree_desc_from_tree(t+2, merge);
+ rc = unpack_trees(3, t, &opts);
+ }
+ else {
+ opts.merge = 0;
+ opts.head_idx = 1;
+ init_tree_desc_from_tree(t+0, head);
+ init_tree_desc_from_tree(t+1, merge);
+ rc = unpack_trees(2, t, &opts);
+ }
- rc = unpack_trees(3, t, &opts);
cache_tree_free(&active_cache_tree);
return rc;
}
@@ -1152,7 +1161,7 @@ int merge_trees(struct merge_options *o,
common = shift_tree_object(head, common);
}
- if (sha_eq(common->object.sha1, merge->object.sha1)) {
+ if (common && sha_eq(common->object.sha1, merge->object.sha1)) {
output(o, 0, "Already uptodate!");
*result = head;
return 1;
diff --git a/t/t3031-merge-criscross.sh b/t/t3031-merge-criscross.sh
new file mode 100755
index 0000000..525afea
--- /dev/null
+++ b/t/t3031-merge-criscross.sh
@@ -0,0 +1,135 @@
+#!/bin/sh
+
+test_description='merge-recursive backend test'
+
+. ./test-lib.sh
+
+# A <- create some files
+# / \
+# B C <- cause rename/delete conflicts between B and C
+# / \
+# -> 1 1 <- merge-bases for F and G: B1, C1
+# |\ /|
+# 2 D E 2
+# | | | |
+# | 1 1 | <- overload rename_limit in E1
+# | \ / |
+# | X |
+# | / \ |
+# | / \ |
+# |/ \|
+# F G <- merge E into B, D into C
+# \ /
+# \ /
+# \ /
+# H <- recursive merge crashes
+#
+
+# initialize
+test_expect_success 'setup' '
+ mkdir data &&
+
+ test_debug create a bunch of files &&
+ n=1 &&
+ while test $n -le 1000
+ do
+ echo $n > data/$n &&
+ n=$(($n+1)) ||
+ break
+ done &&
+
+ test_debug check them in &&
+ git add data &&
+ git commit -m A &&
+ git branch A &&
+
+ test_debug remove some files in one branch &&
+ git checkout -b B A &&
+ git rm data/99* &&
+ git add data &&
+ git commit -m B &&
+
+ test_debug few more commits on B &&
+ echo testB > data/testB &&
+ git add data &&
+ git commit -m B1 &&
+
+ test_debug with a branch off of it &&
+ git branch D &&
+
+ echo testB2 > data/testB2 &&
+ git add data &&
+ git commit -m B2 &&
+
+ test_debug put some commits on D &&
+ git checkout D &&
+ echo testD > data/testD &&
+ git add data &&
+ git commit -m D &&
+
+ echo testD1 > data/testD1 &&
+ git add data &&
+ git commit -m D1 &&
+
+ test_debug back up to the top, create another branch and cause a rename &&
+ test_debug conflict with the files we deleted earlier &&
+ git checkout -b C A &&
+ git rm --cached data/99* &&
+ rename "s!/9!/moved-9!" data/99* &&
+ git add data &&
+ git commit -m C &&
+
+ test_debug few more commits on C &&
+ echo testC > data/testC &&
+ git add data &&
+ git commit -m C1 &&
+
+ test_debug with a branch off of it &&
+ git branch E &&
+
+ echo testC2 > data/testC2 &&
+ git add data &&
+ git commit -m C2 &&
+
+ test_debug put a commits on E &&
+ git checkout E &&
+ echo testE > data/testE &&
+ git add data &&
+ git commit -m E &&
+
+ test_debug and now, overload add/delete &&
+ git rm data/[123456]* &&
+ n=10000 &&
+ while test $n -le 11000
+ do
+ echo $n > data/$n &&
+ n=$(($n+1)) ||
+ break
+ done &&
+ git add data &&
+ git commit -m E1 &&
+
+
+ test_debug now, merge E into B &&
+ git checkout B &&
+ test_must_fail git merge E &&
+ test_debug force-resolve? &&
+ git add data &&
+ git commit -m F &&
+ git branch F &&
+
+
+ test_debug and merge D into C &&
+ git checkout C &&
+ test_must_fail git merge D &&
+ test_debug force-resolve? &&
+ git add data &&
+ git commit -m G &&
+ git branch G
+'
+
+test_expect_failure 'now, force a recursive merge between F and G' '
+ git merge F
+'
+
+test_done
--
1.6.2.1.493.g67cf3