Skip to content

Commit e771f7f

Browse files
committed
merge-tree: add a new --dry-run flag
Git Forges may be interested in whether two branches can be merged while not being interested in what the resulting merge tree is nor which files conflicted. For such cases, add a new --dry-run flag which will make use of the new mergeability_only flag added to merge-ort in the previous commit. This option allows the merge machinery to, in the outer layer of the merge: * exit early when a conflict is detected * avoid writing (most) merged blobs/trees to the object store Signed-off-by: Elijah Newren <[email protected]>
1 parent 4757c48 commit e771f7f

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

Documentation/git-merge-tree.adoc

+6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ OPTIONS
6565
default is to include these messages if there are merge
6666
conflicts, and to omit them otherwise.
6767
68+
--dry-run::
69+
Disable all output from the program. Useful when you are only
70+
interested in the exit status. Allows merge-tree to exit
71+
early on the first conflict it finds, and allows it to avoid
72+
writing most objects created by merges.
73+
6874
--allow-unrelated-histories::
6975
merge-tree will by default error out if the two branches specified
7076
share no common history. This flag can be given to override that

builtin/merge-tree.c

+22
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,9 @@ static int real_merge(struct merge_tree_options *o,
490490
if (result.clean < 0)
491491
die(_("failure to merge"));
492492

493+
if (o->merge_options.mergeability_only)
494+
goto cleanup;
495+
493496
if (show_messages == -1)
494497
show_messages = !result.clean;
495498

@@ -522,6 +525,8 @@ static int real_merge(struct merge_tree_options *o,
522525
}
523526
if (o->use_stdin)
524527
putchar(line_termination);
528+
529+
cleanup:
525530
merge_finalize(&opt, &result);
526531
clear_merge_options(&opt);
527532
return !result.clean; /* result.clean < 0 handled above */
@@ -538,6 +543,7 @@ int cmd_merge_tree(int argc,
538543
int original_argc;
539544
const char *merge_base = NULL;
540545
int ret;
546+
int dry_run = 0;
541547

542548
const char * const merge_tree_usage[] = {
543549
N_("git merge-tree [--write-tree] [<options>] <branch1> <branch2>"),
@@ -552,6 +558,10 @@ int cmd_merge_tree(int argc,
552558
N_("do a trivial merge only"), MODE_TRIVIAL),
553559
OPT_BOOL(0, "messages", &o.show_messages,
554560
N_("also show informational/conflict messages")),
561+
OPT_BOOL_F(0, "dry-run",
562+
&dry_run,
563+
N_("suppress all output; only exit status wanted"),
564+
PARSE_OPT_NONEG),
555565
OPT_SET_INT('z', NULL, &line_termination,
556566
N_("separate paths with the NUL character"), '\0'),
557567
OPT_BOOL_F(0, "name-only",
@@ -583,6 +593,18 @@ int cmd_merge_tree(int argc,
583593
argc = parse_options(argc, argv, prefix, mt_options,
584594
merge_tree_usage, PARSE_OPT_STOP_AT_NON_OPTION);
585595

596+
if (dry_run && o.show_messages == -1)
597+
o.show_messages = 0;
598+
o.merge_options.mergeability_only = dry_run;
599+
die_for_incompatible_opt2(dry_run, "--dry-run",
600+
o.show_messages, "--messages");
601+
die_for_incompatible_opt2(dry_run, "--dry-run",
602+
o.name_only, "--name-only");
603+
die_for_incompatible_opt2(dry_run, "--dry-run",
604+
o.use_stdin, "--stdin");
605+
die_for_incompatible_opt2(dry_run, "--dry-run",
606+
!line_termination, "-z");
607+
586608
if (xopts.nr && o.mode == MODE_TRIVIAL)
587609
die(_("--trivial-merge is incompatible with all other options"));
588610
for (size_t x = 0; x < xopts.nr; x++)

t/t4301-merge-tree-write-tree.sh

+38
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,25 @@ test_expect_success setup '
5454
git commit -m first-commit
5555
'
5656

57+
test_expect_success '--dry-run on clean merge' '
58+
# Get rid of loose objects to start with
59+
git gc &&
60+
echo "0 objects, 0 kilobytes" >expect &&
61+
git count-objects >actual &&
62+
test_cmp expect actual &&
63+
64+
# Ensure merge is successful (exit code of 0)
65+
git merge-tree --write-tree --dry-run side1 side3 >output &&
66+
67+
# Ensure there is no output
68+
test_must_be_empty output &&
69+
70+
# Ensure no loose objects written (all new objects written would have
71+
# been in "outer layer" of the merge)
72+
git count-objects >actual &&
73+
test_cmp expect actual
74+
'
75+
5776
test_expect_success 'Clean merge' '
5877
TREE_OID=$(git merge-tree --write-tree side1 side3) &&
5978
q_to_tab <<-EOF >expect &&
@@ -72,6 +91,25 @@ test_expect_success 'Failed merge without rename detection' '
7291
grep "CONFLICT (modify/delete): numbers deleted" out
7392
'
7493

94+
test_expect_success '--dry-run on conflicted merge' '
95+
# Get rid of loose objects to start with
96+
git gc &&
97+
echo "0 objects, 0 kilobytes" >expect &&
98+
git count-objects >actual &&
99+
test_cmp expect actual &&
100+
101+
# Ensure merge has conflict
102+
test_expect_code 1 git merge-tree --write-tree --dry-run side1 side2 >output &&
103+
104+
# Ensure there is no output
105+
test_must_be_empty output &&
106+
107+
# Ensure no loose objects written (all new objects written would have
108+
# been in "outer layer" of the merge)
109+
git count-objects >actual &&
110+
test_cmp expect actual
111+
'
112+
75113
test_expect_success 'Content merge and a few conflicts' '
76114
git checkout side1^0 &&
77115
test_must_fail git merge side2 &&

0 commit comments

Comments
 (0)