@@ -1034,7 +1034,7 @@ static int merge_3way(struct merge_options *opt,
{
mmfile_t orig, src1, src2;
struct ll_merge_options ll_opts = {0};
- char *base_name, *name1, *name2;
+ char *base, *name1, *name2;
int merge_status;
ll_opts.renormalize = opt->renormalize;
@@ -1058,16 +1058,13 @@ static int merge_3way(struct merge_options *opt,
}
}
- assert(a->path && b->path && o->path);
- if (strcmp(a->path, b->path) ||
- (opt->ancestor != NULL && strcmp(a->path, o->path) != 0)) {
- base_name = opt->ancestor == NULL ? NULL :
- mkpathdup("%s:%s", opt->ancestor, o->path);
+ assert(a->path && b->path && o->path && opt->ancestor);
+ if (strcmp(a->path, b->path) || strcmp(a->path, o->path) != 0) {
+ base = mkpathdup("%s:%s", opt->ancestor, o->path);
name1 = mkpathdup("%s:%s", branch1, a->path);
name2 = mkpathdup("%s:%s", branch2, b->path);
} else {
- base_name = opt->ancestor == NULL ? NULL :
- mkpathdup("%s", opt->ancestor);
+ base = mkpathdup("%s", opt->ancestor);
name1 = mkpathdup("%s", branch1);
name2 = mkpathdup("%s", branch2);
}
@@ -1076,11 +1073,11 @@ static int merge_3way(struct merge_options *opt,
read_mmblob(&src1, &a->oid);
read_mmblob(&src2, &b->oid);
- merge_status = ll_merge(result_buf, a->path, &orig, base_name,
+ merge_status = ll_merge(result_buf, a->path, &orig, base,
&src1, name1, &src2, name2,
opt->repo->index, &ll_opts);
- free(base_name);
+ free(base);
free(name1);
free(name2);
free(orig.ptr);
@@ -3517,6 +3514,8 @@ static int merge_recursive_internal(struct merge_options *opt,
struct commit *merged_merge_bases;
struct tree *result_tree;
int clean;
+ int num_merge_bases;
+ struct strbuf commit_name = STRBUF_INIT;
if (show(opt, 4)) {
output(opt, 4, _("Merging:"));
@@ -3538,6 +3537,7 @@ static int merge_recursive_internal(struct merge_options *opt,
output_commit_title(opt, iter->item);
}
+ num_merge_bases = commit_list_count(merge_bases);
merged_merge_bases = pop_commit(&merge_bases);
if (merged_merge_bases == NULL) {
/* if there is no common ancestor, use an empty tree */
@@ -3579,13 +3579,26 @@ static int merge_recursive_internal(struct merge_options *opt,
if (!opt->priv->call_depth)
repo_read_index(opt->repo);
- opt->ancestor = "merged common ancestors";
+ switch (num_merge_bases) {
+ case 0:
+ opt->ancestor = "<empty tree>";
+ break;
+ case 1:
+ strbuf_add_unique_abbrev(&commit_name,
+ &merged_merge_bases->object.oid,
+ DEFAULT_ABBREV);
+ opt->ancestor = commit_name.buf;
+ break;
+ default:
+ opt->ancestor = "merged common ancestors";
+ }
clean = merge_trees_internal(opt,
repo_get_commit_tree(opt->repo, h1),
repo_get_commit_tree(opt->repo, h2),
repo_get_commit_tree(opt->repo,
merged_merge_bases),
&result_tree);
+ strbuf_release(&commit_name);
if (clean < 0) {
flush_output(opt);
return clean;
@@ -1562,6 +1562,7 @@ test_expect_success 'check nested conflicts' '
cd nested_conflicts &&
git clean -f &&
+ MASTER=$(git rev-parse --short master) &&
git checkout L2^0 &&
# Merge must fail; there is a conflict
@@ -1582,7 +1583,7 @@ test_expect_success 'check nested conflicts' '
git cat-file -p R1:a >theirs &&
test_must_fail git merge-file --diff3 \
-L "Temporary merge branch 1" \
- -L "merged common ancestors" \
+ -L "$MASTER" \
-L "Temporary merge branch 2" \
ours \
base \
@@ -1594,7 +1595,7 @@ test_expect_success 'check nested conflicts' '
git cat-file -p R1:b >theirs &&
test_must_fail git merge-file --diff3 \
-L "Temporary merge branch 1" \
- -L "merged common ancestors" \
+ -L "$MASTER" \
-L "Temporary merge branch 2" \
ours \
base \
@@ -1732,6 +1733,7 @@ test_expect_success 'check virtual merge base with nested conflicts' '
(
cd virtual_merge_base_has_nested_conflicts &&
+ MASTER=$(git rev-parse --short master) &&
git checkout L3^0 &&
# Merge must fail; there is a conflict
@@ -1760,7 +1762,7 @@ test_expect_success 'check virtual merge base with nested conflicts' '
cp left merged-once &&
test_must_fail git merge-file --diff3 \
-L "Temporary merge branch 1" \
- -L "merged common ancestors" \
+ -L "$MASTER" \
-L "Temporary merge branch 2" \
merged-once \
base \
new file mode 100755
@@ -0,0 +1,191 @@
+#!/bin/sh
+
+test_description='recursive merge diff3 style conflict markers'
+
+. ./test-lib.sh
+
+# Setup:
+# L1
+# \
+# ?
+# /
+# R1
+#
+# Where:
+# L1 and R1 both have a file named 'content' but have no common history
+#
+
+test_expect_success 'setup no merge base' '
+ test_create_repo no_merge_base &&
+ (
+ cd no_merge_base &&
+
+ git checkout --orphan L &&
+ test_seq 1 9 >content &&
+ echo "A" >>content &&
+ git add content &&
+ test_tick &&
+ git commit -m "version L1 of content" &&
+
+ # Create R
+ git checkout --orphan R &&
+ test_seq 1 9 >content &&
+ echo "10" >>content &&
+ git add content &&
+ test_tick &&
+ git commit -m "version R1 of content"
+ )
+'
+
+test_expect_success 'check no merge base' '
+ (
+ cd no_merge_base &&
+
+ git checkout L^0 &&
+
+ test_must_fail git -c merge.conflictstyle=diff3 merge --allow-unrelated-histories -s recursive R^0 &&
+
+ grep "|||||| <empty tree>" content
+ )
+'
+
+# Setup:
+# L1
+# / \
+# master ?
+# \ /
+# R1
+#
+# Where:
+# L1 and R1 have modified the same file ('content') in conflicting ways
+#
+
+test_expect_success 'setup unique merge base' '
+ test_create_repo unique_merge_base &&
+ (
+ cd unique_merge_base &&
+
+ test_seq 1 9 >content &&
+ git add content &&
+ test_tick &&
+ git commit -m initial &&
+
+ git branch L &&
+ git branch R &&
+
+ # Create L1
+ git checkout L &&
+ test_seq 0 9 >content &&
+ echo "A" >>content &&
+ git add content &&
+ test_tick &&
+ git commit -m "version L1 of content" &&
+
+ # Create R1
+ git checkout R &&
+ test_seq 0 9 >content &&
+ echo "ten" >>content &&
+ git add content &&
+ git mv content renamed &&
+ test_tick &&
+ git commit -m "version R1 of content"
+ )
+'
+
+test_expect_success 'check unique merge base' '
+ (
+ cd unique_merge_base &&
+
+ git checkout L^0 &&
+ MASTER=$(git rev-parse --short master) &&
+
+ test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R^0 &&
+
+ grep "|||||| $MASTER:content" renamed
+ )
+'
+
+# Setup:
+# L1---L2--L3
+# / \ / \
+# master X1 ?
+# \ / \ /
+# R1---R2--R3
+#
+# Where:
+# commits L1 and R1 have modified the same file in non-conflicting ways
+# X1 is an auto-generated merge-base used when merging L1 and R1
+# commits L2 and R2 are merges of R1 and L1 into L1 and R1, respectively
+# commits L3 and R3 both modify 'content' in conflicting ways
+#
+
+test_expect_success 'setup multiple merge bases' '
+ test_create_repo multiple_merge_bases &&
+ (
+ cd multiple_merge_bases &&
+
+ # Create some related files now
+ test_seq 1 9 >content &&
+ git add content &&
+ test_tick &&
+ git commit -m initial &&
+
+ git branch L &&
+ git branch R &&
+
+ # Create L1
+ git checkout L &&
+ test_seq 0 9 >content &&
+ git add content &&
+ test_tick &&
+ git commit -m "version L1 of content" &&
+ git tag L1 &&
+
+ # Create R1
+ git checkout R &&
+ test_seq 1 10 >content &&
+ git add content &&
+ test_tick &&
+ git commit -m "verson R1 of content" &&
+ git tag R1 &&
+
+ # Create L2
+ git checkout L &&
+ git merge R1 &&
+
+ # Create R2
+ git checkout R &&
+ git merge L1 &&
+
+ # Create L3
+ git checkout L &&
+ test_seq 0 9 >content &&
+ echo "A" >>content &&
+ git add content &&
+ test_tick &&
+ git commit -m "version L3 of content" &&
+
+ # Create R3
+ git checkout R &&
+ test_seq 0 9 >content &&
+ echo "ten" >>content &&
+ git add content &&
+ git mv content renamed &&
+ test_tick &&
+ git commit -m "version R3 of content"
+ )
+'
+
+test_expect_success 'check multiple merge bases' '
+ (
+ cd multiple_merge_bases &&
+
+ git checkout L^0 &&
+
+ test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R^0 &&
+
+ grep "|||||| merged common ancestors:content" renamed
+ )
+'
+
+test_done