Skip to content

Commit 66265a6

Browse files
keyu98ttaylorr
authored andcommitted
merge-tree.c: add --merge-base=<commit> option
This patch will give our callers more flexibility to use `git merge-tree`, such as: git merge-tree --write-tree --merge-base=branch^ HEAD branch This does a merge of HEAD and branch, but uses branch^ as the merge-base. And the reason why using an option flag instead of a positional argument is to allow additional commits passed to merge-tree to be handled via an octopus merge in the future. Signed-off-by: Kyle Zhao <[email protected]> Signed-off-by: Taylor Blau <[email protected]>
1 parent ec1edbc commit 66265a6

File tree

3 files changed

+66
-11
lines changed

3 files changed

+66
-11
lines changed

Documentation/git-merge-tree.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ OPTIONS
6464
share no common history. This flag can be given to override that
6565
check and make the merge proceed anyway.
6666

67+
--merge-base=<commit>::
68+
Instead of finding the merge-bases for <branch1> and <branch2>,
69+
specify a merge-base for the merge.
70+
6771
[[OUTPUT]]
6872
OUTPUT
6973
------

builtin/merge-tree.c

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "tree-walk.h"
44
#include "xdiff-interface.h"
55
#include "help.h"
6+
#include "commit.h"
67
#include "commit-reach.h"
78
#include "merge-ort.h"
89
#include "object-store.h"
@@ -406,6 +407,7 @@ struct merge_tree_options {
406407
};
407408

408409
static int real_merge(struct merge_tree_options *o,
410+
const char *merge_base,
409411
const char *branch1, const char *branch2,
410412
const char *prefix)
411413
{
@@ -432,16 +434,31 @@ static int real_merge(struct merge_tree_options *o,
432434
opt.branch1 = branch1;
433435
opt.branch2 = branch2;
434436

435-
/*
436-
* Get the merge bases, in reverse order; see comment above
437-
* merge_incore_recursive in merge-ort.h
438-
*/
439-
merge_bases = get_merge_bases(parent1, parent2);
440-
if (!merge_bases && !o->allow_unrelated_histories)
441-
die(_("refusing to merge unrelated histories"));
442-
merge_bases = reverse_commit_list(merge_bases);
437+
if (merge_base) {
438+
struct commit *base_commit;
439+
struct tree *base_tree, *parent1_tree, *parent2_tree;
440+
441+
base_commit = lookup_commit_reference_by_name(merge_base);
442+
if (!base_commit)
443+
die(_("could not lookup commit %s"), merge_base);
444+
445+
opt.ancestor = merge_base;
446+
base_tree = get_commit_tree(base_commit);
447+
parent1_tree = get_commit_tree(parent1);
448+
parent2_tree = get_commit_tree(parent2);
449+
merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
450+
} else {
451+
/*
452+
* Get the merge bases, in reverse order; see comment above
453+
* merge_incore_recursive in merge-ort.h
454+
*/
455+
merge_bases = get_merge_bases(parent1, parent2);
456+
if (!merge_bases && !o->allow_unrelated_histories)
457+
die(_("refusing to merge unrelated histories"));
458+
merge_bases = reverse_commit_list(merge_bases);
459+
merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
460+
}
443461

444-
merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
445462
if (result.clean < 0)
446463
die(_("failure to merge"));
447464

@@ -487,6 +504,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
487504
struct merge_tree_options o = { .show_messages = -1 };
488505
int expected_remaining_argc;
489506
int original_argc;
507+
const char *merge_base = NULL;
490508

491509
const char * const merge_tree_usage[] = {
492510
N_("git merge-tree [--write-tree] [<options>] <branch1> <branch2>"),
@@ -515,6 +533,10 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
515533
&o.use_stdin,
516534
N_("perform multiple merges, one per line of input"),
517535
PARSE_OPT_NONEG),
536+
OPT_STRING(0, "merge-base",
537+
&merge_base,
538+
N_("commit"),
539+
N_("specify a merge-base for the merge")),
518540
OPT_END()
519541
};
520542

@@ -529,6 +551,8 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
529551

530552
if (o.mode == MODE_TRIVIAL)
531553
die(_("--trivial-merge is incompatible with all other options"));
554+
if (merge_base)
555+
die(_("--merge-base is incompatible with --stdin"));
532556
line_termination = '\0';
533557
while (strbuf_getline_lf(&buf, stdin) != EOF) {
534558
struct strbuf **split;
@@ -538,7 +562,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
538562
if (!split[0] || !split[1] || split[2])
539563
die(_("malformed input line: '%s'."), buf.buf);
540564
strbuf_rtrim(split[0]);
541-
result = real_merge(&o, split[0]->buf, split[1]->buf, prefix);
565+
result = real_merge(&o, merge_base, split[0]->buf, split[1]->buf, prefix);
542566
if (result < 0)
543567
die(_("merging cannot continue; got unclean result of %d"), result);
544568
strbuf_list_free(split);
@@ -581,7 +605,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
581605

582606
/* Do the relevant type of merge */
583607
if (o.mode == MODE_REAL)
584-
return real_merge(&o, argv[0], argv[1], prefix);
608+
return real_merge(&o, merge_base, argv[0], argv[1], prefix);
585609
else
586610
return trivial_merge(argv[0], argv[1], argv[2]);
587611
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,4 +860,31 @@ test_expect_success '--stdin with both a successful and a conflicted merge' '
860860
test_cmp expect actual
861861
'
862862

863+
# specify merge-base as parent of branch2
864+
# git merge-tree --write-tree --merge-base=c2 c1 c3
865+
# Commit c1: add file1
866+
# Commit c2: add file2 after c1
867+
# Commit c3: add file3 after c2
868+
# Expected: add file3, and file2 does NOT appear
869+
870+
test_expect_success 'specify merge-base as parent of branch2' '
871+
# Setup
872+
test_when_finished "rm -rf base-b2-p" &&
873+
git init base-b2-p &&
874+
test_commit -C base-b2-p c1 file1 &&
875+
test_commit -C base-b2-p c2 file2 &&
876+
test_commit -C base-b2-p c3 file3 &&
877+
878+
# Testing
879+
TREE_OID=$(git -C base-b2-p merge-tree --write-tree --merge-base=c2 c1 c3) &&
880+
881+
q_to_tab <<-EOF >expect &&
882+
100644 blob $(git -C base-b2-p rev-parse c1:file1)Qfile1
883+
100644 blob $(git -C base-b2-p rev-parse c3:file3)Qfile3
884+
EOF
885+
886+
git -C base-b2-p ls-tree $TREE_OID >actual &&
887+
test_cmp expect actual
888+
'
889+
863890
test_done

0 commit comments

Comments
 (0)