Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> --- builtin/merge-tree.c | 72 ++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 58c0ddc5a3..1007aaaede 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -396,6 +396,7 @@ struct merge_tree_options { int allow_unrelated_histories; int show_messages; int exclude_modes_oids_stages; + const char *nonrecursive_base; }; static int real_merge(struct merge_tree_options *o, @@ -409,34 +410,58 @@ static int real_merge(struct merge_tree_options *o, struct merge_options opt; struct merge_result result = { 0 }; - 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")); - init_merge_options(&opt, the_repository); opt.show_rename_progress = 0; - opt.branch1 = branch1; - opt.branch2 = branch2; + if (o->nonrecursive_base) { + struct object_id base_oid, head_oid, merge_oid; + struct tree *base_tree, *head_tree, *merge_tree; + + opt.ancestor = "(base)"; + opt.branch1 = "(branch1)"; + opt.branch2 = "(branch2)"; + + if (get_oid_treeish(o->nonrecursive_base, &base_oid)) + die("could not parse base '%s'", o->nonrecursive_base); + base_tree = parse_tree_indirect(&base_oid); + if (get_oid_treeish(branch1, &head_oid)) + die("could not parse head '%s'", branch1); + head_tree = parse_tree_indirect(&head_oid); + if (get_oid_treeish(branch2, &merge_oid)) + die("could not parse merge '%s'", branch2); + merge_tree = parse_tree_indirect(&merge_oid); + + merge_incore_nonrecursive(&opt, + base_tree, head_tree, merge_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")); + + opt.branch1 = branch1; + opt.branch2 = branch2; - /* - * Get the merge bases, in reverse order; see comment above - * merge_incore_recursive in merge-ort.h - */ - common = get_merge_bases(parent1, parent2); - if (!common && !o->allow_unrelated_histories) - die(_("refusing to merge unrelated histories")); - for (j = common; j; j = j->next) - commit_list_insert(j->item, &merge_bases); + /* + * Get the merge bases, in reverse order; see comment above + * merge_incore_recursive in merge-ort.h + */ + common = get_merge_bases(parent1, parent2); + if (!common && !o->allow_unrelated_histories) + die(_("refusing to merge unrelated histories")); + for (j = common; j; j = j->next) + commit_list_insert(j->item, &merge_bases); + + merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result); + } - merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result); if (result.clean < 0) die(_("failure to merge")); @@ -501,6 +526,9 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) &o.allow_unrelated_histories, N_("allow merging unrelated histories"), PARSE_OPT_NONEG), + OPT_STRING(0, "force-non-recursive-base", &o.nonrecursive_base, + N_("base-tree"), + N_("force a simple three-way merge")), OPT_END() }; -- snap -- I do strongly agree that this should _not_ enter core Git's code, I just provide this in case someone else wants to play with merge-ort on the server side in an existing code base. > We'll certainly have discussions on what that should look like. But a > plumbing-ish replacement for merge was much simpler, and made sense to > do first. I would prefer to concentrate on getting that hammered down > first. Then I'll start discussions on a plumbing-ish > rebase/cherry-pick. And if that doesn't fulfill all the needs that > folks think they want out of merge-tree, then we can add a > merge_incore_nonrecursive()-based mode to merge-tree. It's all > coming, but having fought transliterations-of-scripts in > merge-recursive.c, sequencer.c, stash.c, rebase.c, etc. for years I > really, really don't want any more of that. Let's end that insanity. Being the driving force behind many a "built-in-ification" of scripted commands, I wholeheartedly agree. You can still see the fall-out of designing commands in a scripted fashion, without any way to represent data structures other than strings. I wish we had come up with a better design to prototype commands than to write shell scripts. But I have to admit that even I do not have any better idea than to work on a proper API for libgit.a (which has historically invariably seen push-back from Junio). While I agree that this discussion is a valuable one, right now I would like to focus on getting the server-side merges done, and once that has happened, move on to the replay/sequencer/API discussion (which will probably be a big one, not so much for technical reasons but more for all too human ones). Ciao, Dscho