merge-tree: add option parsing and initial shell for real merge function

Let merge-tree accept a `--write-tree` parameter for choosing real merges instead of trivial merges, and accept an optional `--trivial-merge` option to get the traditional behavior. Note that these accept different numbers of arguments, though, so these names need not actually be used. Note that real merges differ from trivial merges in that they handle: - three way content merges - recursive ancestor consolidation - renames - proper directory/file conflict handling - etc. Basically all the stuff you'd expect from `git merge`, just without updating the index and working tree. The initial shell added here does nothing more than die with "real merges are not yet implemented", but that will be fixed in subsequent commits. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Jun 18, 2022 at 00:20 UTC 6ec755a0e152dfb0ed6bffa70b511c45a1f29ebd
2 files changed +76 -10
builtin/merge-tree.c
+75 -9
@@ -3,13 +3,12 @@
3 #include "tree-walk.h"
4 #include "xdiff-interface.h"
5 #include "object-store.h"
6 +#include "parse-options.h"
7 #include "repository.h"
8 #include "blob.h"
9 #include "exec-cmd.h"
10 #include "merge-blobs.h"
11
11 -static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>";
12 -
12 struct merge_list {
13 struct merge_list *next;
14 struct merge_list *link; /* other stages for this object */
@@ -366,15 +365,17 @@ static void *get_tree_descriptor(struct repository *r,
365 return buf;
366 }
367
369 -static int trivial_merge(int argc, const char **argv)
368 +static int trivial_merge(const char *base,
369 + const char *branch1,
370 + const char *branch2)
371 {
372 struct repository *r = the_repository;
373 struct tree_desc t[3];
374 void *buf1, *buf2, *buf3;
375
375 - buf1 = get_tree_descriptor(r, t+0, argv[1]);
376 - buf2 = get_tree_descriptor(r, t+1, argv[2]);
377 - buf3 = get_tree_descriptor(r, t+2, argv[3]);
376 + buf1 = get_tree_descriptor(r, t+0, base);
377 + buf2 = get_tree_descriptor(r, t+1, branch1);
378 + buf3 = get_tree_descriptor(r, t+2, branch2);
379 trivial_merge_trees(t, "");
380 free(buf1);
381 free(buf2);
@@ -384,9 +385,74 @@ static int trivial_merge(int argc, const char **argv)
385 return 0;
386 }
387
388 +enum mode {
389 + MODE_UNKNOWN,
390 + MODE_TRIVIAL,
391 + MODE_REAL,
392 +};
393 +
394 +struct merge_tree_options {
395 + int mode;
396 +};
397 +
398 +static int real_merge(struct merge_tree_options *o,
399 + const char *branch1, const char *branch2)
400 +{
401 + die(_("real merges are not yet implemented"));
402 +}
403 +
404 int cmd_merge_tree(int argc, const char **argv, const char *prefix)
405 {
389 - if (argc != 4)
390 - usage(merge_tree_usage);
391 - return trivial_merge(argc, argv);
406 + struct merge_tree_options o = { 0 };
407 + int expected_remaining_argc;
408 +
409 + const char * const merge_tree_usage[] = {
410 + N_("git merge-tree [--write-tree] <branch1> <branch2>"),
411 + N_("git merge-tree [--trivial-merge] <base-tree> <branch1> <branch2>"),
412 + NULL
413 + };
414 + struct option mt_options[] = {
415 + OPT_CMDMODE(0, "write-tree", &o.mode,
416 + N_("do a real merge instead of a trivial merge"),
417 + MODE_REAL),
418 + OPT_CMDMODE(0, "trivial-merge", &o.mode,
419 + N_("do a trivial merge only"), MODE_TRIVIAL),
420 + OPT_END()
421 + };
422 +
423 + /* Parse arguments */
424 + argc = parse_options(argc, argv, prefix, mt_options,
425 + merge_tree_usage, PARSE_OPT_STOP_AT_NON_OPTION);
426 + switch (o.mode) {
427 + default:
428 + BUG("unexpected command mode %d", o.mode);
429 + case MODE_UNKNOWN:
430 + switch (argc) {
431 + default:
432 + usage_with_options(merge_tree_usage, mt_options);
433 + case 2:
434 + o.mode = MODE_REAL;
435 + break;
436 + case 3:
437 + o.mode = MODE_TRIVIAL;
438 + break;
439 + }
440 + expected_remaining_argc = argc;
441 + break;
442 + case MODE_REAL:
443 + expected_remaining_argc = 2;
444 + break;
445 + case MODE_TRIVIAL:
446 + expected_remaining_argc = 3;
447 + break;
448 + }
449 +
450 + if (argc != expected_remaining_argc)
451 + usage_with_options(merge_tree_usage, mt_options);
452 +
453 + /* Do the relevant type of merge */
454 + if (o.mode == MODE_REAL)
455 + return real_merge(&o, argv[0], argv[1]);
456 + else
457 + return trivial_merge(argv[0], argv[1], argv[2]);
458 }
git.c
+1 -1
@@ -565,7 +565,7 @@ static struct cmd_struct commands[] = {
565 { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
566 { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
567 { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
568 - { "merge-tree", cmd_merge_tree, RUN_SETUP | NO_PARSEOPT },
568 + { "merge-tree", cmd_merge_tree, RUN_SETUP },
569 { "mktag", cmd_mktag, RUN_SETUP | NO_PARSEOPT },
570 { "mktree", cmd_mktree, RUN_SETUP },
571 { "multi-pack-index", cmd_multi_pack_index, RUN_SETUP },