"Johannes Schindelin via GitGitGadget" <gitgitgadget@xxxxxxxxx> writes: > From: Johannes Schindelin <johannes.schindelin@xxxxxx> > > 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. yes, Yes, YES, YEAHHHHH! > I was asked to implement this at $dayjob and it seems like a feature > that might be useful to other users, too. Yup, I think it is an obvious building block for machinery to perform any mergy operation to grow history. Many of the time you may have a commit, but requiring them to be commits when you know you will not do a virtual ancestor synthesis smells fundamentally wrong. Thanks for fixing it. > ---merge-base=<commit>:: > +--merge-base=<tree-ish>:: > Instead of finding the merge-bases for <branch1> and <branch2>, > specify a merge-base for the merge, and specifying multiple bases is > currently not supported. This option is incompatible with `--stdin`. > ++ > +As the merge-base is provided directly, <branch1> and <branch2> do not need > +o specify commits; it is sufficient if they specify trees. "... do not need to specify commits; trees are enough"? > diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c > index 3bdec53fbe5..cbd8e15af6d 100644 > --- a/builtin/merge-tree.c > +++ b/builtin/merge-tree.c > @@ -429,35 +429,43 @@ static int real_merge(struct merge_tree_options *o, > struct merge_options opt; > > copy_merge_options(&opt, &o->merge_options); > - parent1 = get_merge_parent(branch1); > - if (!parent1) > - help_unknown_ref(branch1, "merge-tree", > - _("not something we can merge")); > - > - parent2 = get_merge_parent(branch2); > - if (!parent2) > - help_unknown_ref(branch2, "merge-tree", > - _("not something we can merge")); > - > opt.show_rename_progress = 0; > > opt.branch1 = branch1; > opt.branch2 = branch2; > > if (merge_base) { > - struct commit *base_commit; > struct tree *base_tree, *parent1_tree, *parent2_tree; > > - base_commit = lookup_commit_reference_by_name(merge_base); > - if (!base_commit) > - die(_("could not lookup commit '%s'"), merge_base); > + /* > + * We actually only need the trees because we already > + * have a merge base. > + */ > + struct object_id base_oid, head_oid, merge_oid; > + > + if (repo_get_oid_treeish(the_repository, merge_base, &base_oid)) > + die(_("could not parse as tree '%s'"), merge_base); > + base_tree = parse_tree_indirect(&base_oid); > + if (repo_get_oid_treeish(the_repository, branch1, &head_oid)) > + die(_("could not parse as tree '%s'"), branch1); > + parent1_tree = parse_tree_indirect(&head_oid); > + if (repo_get_oid_treeish(the_repository, branch2, &merge_oid)) > + die(_("could not parse as tree '%s'"), branch2); > + parent2_tree = parse_tree_indirect(&merge_oid); > > opt.ancestor = merge_base; > - base_tree = repo_get_commit_tree(the_repository, base_commit); > - parent1_tree = repo_get_commit_tree(the_repository, parent1); > - parent2_tree = repo_get_commit_tree(the_repository, parent2); > merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result); > } else { > + parent1 = get_merge_parent(branch1); > + if (!parent1) > + help_unknown_ref(branch1, "merge-tree", > + _("not something we can merge")); > + > + parent2 = get_merge_parent(branch2); > + if (!parent2) > + help_unknown_ref(branch2, "merge-tree", > + _("not something we can merge")); > + > /* > * Get the merge bases, in reverse order; see comment above > * merge_incore_recursive in merge-ort.h OK. The changes here look quite straight-forward. > diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh > index 12ac4368736..71f21bb834f 100755 > --- a/t/t4301-merge-tree-write-tree.sh > +++ b/t/t4301-merge-tree-write-tree.sh > @@ -945,4 +945,12 @@ test_expect_success 'check the input format when --stdin is passed' ' > test_cmp expect actual > ' > > +test_expect_success '--merge-base with tree OIDs' ' > + git merge-tree --merge-base=side1^ side1 side3 >tree && > + tree=$(cat tree) && > + git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >tree2 && > + tree2=$(cat tree2) && > + test $tree = $tree2 > +' You do not need $tree and $tree2 variables that would make it harder to diagnose a failure case when we break merge-tree. You have tree and tree2 as files, and I think it is sufficient to do git merge-tree ... >result-from-commits && git merge-tree ... >result-from-trees && test_cmp result-from-commits result-from-trees (no, I am not suggesting to rename these tree and tree2 files; I just needed them to be descriptive in my illustration to show what is going on to myself). Thanks.