merge-tree: accept 3 trees as arguments

When specifying a merge base explicitly, there is actually no good reason why the inputs need to be commits: that's only needed if the merge base has to be deduced from the commit graph. This commit is best viewed with `--color-moved --color-moved-ws=allow-indentation-change`. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Jan 28, 2024 at 20:34 UTC 5f43cf5b2e4b68386d3774bce880b0f74d801635
3 files changed +35 -18
Documentation/git-merge-tree.txt
+4 -1
@@ -64,10 +64,13 @@ OPTIONS
64 share no common history. This flag can be given to override that
65 check and make the merge proceed anyway.
66
67 ---merge-base=<commit>::
67 +--merge-base=<tree-ish>::
68 Instead of finding the merge-bases for <branch1> and <branch2>,
69 specify a merge-base for the merge, and specifying multiple bases is
70 currently not supported. This option is incompatible with `--stdin`.
71 ++
72 +As the merge-base is provided directly, <branch1> and <branch2> do not need
73 +to specify commits; trees are enough.
74
75 [[OUTPUT]]
76 OUTPUT
builtin/merge-tree.c
+25 -17
@@ -430,35 +430,43 @@ static int real_merge(struct merge_tree_options *o,
430 struct merge_options opt;
431
432 copy_merge_options(&opt, &o->merge_options);
433 - parent1 = get_merge_parent(branch1);
434 - if (!parent1)
435 - help_unknown_ref(branch1, "merge-tree",
436 - _("not something we can merge"));
437 -
438 - parent2 = get_merge_parent(branch2);
439 - if (!parent2)
440 - help_unknown_ref(branch2, "merge-tree",
441 - _("not something we can merge"));
442 -
433 opt.show_rename_progress = 0;
434
435 opt.branch1 = branch1;
436 opt.branch2 = branch2;
437
438 if (merge_base) {
449 - struct commit *base_commit;
439 struct tree *base_tree, *parent1_tree, *parent2_tree;
440
452 - base_commit = lookup_commit_reference_by_name(merge_base);
453 - if (!base_commit)
454 - die(_("could not lookup commit '%s'"), merge_base);
441 + /*
442 + * We actually only need the trees because we already
443 + * have a merge base.
444 + */
445 + struct object_id base_oid, head_oid, merge_oid;
446 +
447 + if (repo_get_oid_treeish(the_repository, merge_base, &base_oid))
448 + die(_("could not parse as tree '%s'"), merge_base);
449 + base_tree = parse_tree_indirect(&base_oid);
450 + if (repo_get_oid_treeish(the_repository, branch1, &head_oid))
451 + die(_("could not parse as tree '%s'"), branch1);
452 + parent1_tree = parse_tree_indirect(&head_oid);
453 + if (repo_get_oid_treeish(the_repository, branch2, &merge_oid))
454 + die(_("could not parse as tree '%s'"), branch2);
455 + parent2_tree = parse_tree_indirect(&merge_oid);
456
457 opt.ancestor = merge_base;
457 - base_tree = repo_get_commit_tree(the_repository, base_commit);
458 - parent1_tree = repo_get_commit_tree(the_repository, parent1);
459 - parent2_tree = repo_get_commit_tree(the_repository, parent2);
458 merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
459 } else {
460 + parent1 = get_merge_parent(branch1);
461 + if (!parent1)
462 + help_unknown_ref(branch1, "merge-tree",
463 + _("not something we can merge"));
464 +
465 + parent2 = get_merge_parent(branch2);
466 + if (!parent2)
467 + help_unknown_ref(branch2, "merge-tree",
468 + _("not something we can merge"));
469 +
470 /*
471 * Get the merge bases, in reverse order; see comment above
472 * merge_incore_recursive in merge-ort.h
t/t4301-merge-tree-write-tree.sh
+6
@@ -945,4 +945,10 @@ test_expect_success 'check the input format when --stdin is passed' '
945 test_cmp expect actual
946 '
947
948 +test_expect_success '--merge-base with tree OIDs' '
949 + git merge-tree --merge-base=side1^ side1 side3 >with-commits &&
950 + git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >with-trees &&
951 + test_cmp with-commits with-trees
952 +'
953 +
954 test_done