diff mbox series

[v2] merge-tree: accept 3 trees as arguments

Message ID pull.1647.v2.git.1706474063109.gitgitgadget@gmail.com (mailing list archive)
State Accepted
Commit 5f43cf5b2e4b68386d3774bce880b0f74d801635
Headers show
Series [v2] merge-tree: accept 3 trees as arguments | expand

Commit Message

Johannes Schindelin Jan. 28, 2024, 8:34 p.m. UTC
From: Johannes Schindelin <johannes.schindelin@gmx.de>

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.

This commit is best viewed with `--color-moved
--color-moved-ws=allow-indentation-change`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
    merge-tree: accept 3 trees as arguments
    
    I was asked to implement this at $dayjob and it seems like a feature
    that might be useful to other users, too.
    
    Changes since v1:
    
     * Fixed a typo in the manual page, simplified the part after the
       semicolon.
     * Simplified the test case.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1647%2Fdscho%2Fallow-merge-tree-to-accept-3-trees-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1647/dscho/allow-merge-tree-to-accept-3-trees-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1647

Range-diff vs v1:

 1:  29234754c06 ! 1:  233a25e9071 merge-tree: accept 3 trees as arguments
     @@ Documentation/git-merge-tree.txt: OPTIONS
       	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.
     ++to specify commits; trees are enough.
       
       [[OUTPUT]]
       OUTPUT
     @@ t/t4301-merge-tree-write-tree.sh: test_expect_success 'check the input format wh
       '
       
      +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
     ++	git merge-tree --merge-base=side1^ side1 side3 >with-commits &&
     ++	git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >with-trees &&
     ++	test_cmp with-commits with-trees
      +'
      +
       test_done


 Documentation/git-merge-tree.txt |  5 +++-
 builtin/merge-tree.c             | 42 +++++++++++++++++++-------------
 t/t4301-merge-tree-write-tree.sh |  6 +++++
 3 files changed, 35 insertions(+), 18 deletions(-)


base-commit: e02ecfcc534e2021aae29077a958dd11c3897e4c

Comments

Junio C Hamano Jan. 29, 2024, 5:49 p.m. UTC | #1
"Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> From: Johannes Schindelin <johannes.schindelin@gmx.de>
>
> 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.
>
> This commit is best viewed with `--color-moved
> --color-moved-ws=allow-indentation-change`.

Thanks, let's merge it down to 'next'.
diff mbox series

Patch

diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index b50acace3bc..dd388fa21d5 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -64,10 +64,13 @@  OPTIONS
 	share no common history.  This flag can be given to override that
 	check and make the merge proceed anyway.
 
---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
+to specify commits; trees are enough.
 
 [[OUTPUT]]
 OUTPUT
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
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index 12ac4368736..c5628c4e613 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -945,4 +945,10 @@  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 >with-commits &&
+	git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >with-trees &&
+	test_cmp with-commits with-trees
+'
+
 test_done