git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] merge-tree: accept 3 trees as arguments

From
Junio C Hamano <gitster@pobox.com>
Date
Jan 26, 2024, 16:01 UTC
Message-ID
<xmqqplxoozh9.fsf@gitster.g>
In-Reply-To
<pull.1647.git.1706277694231.gitgitgadget@gmail.com>

"Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com> writes:

Show 5 quoted lines
> 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.
yes, Yes, YES, YEAHHHHH!
>     I was asked to implement this at $dayjob and it seems like a feature
>     that might be useful to other users, too.

Yup, I think it is an obvious building block for machinery to perform any mergy operation to grow history. Many of the time you may have a commit, but requiring them to be commits when you know you will not do a virtual ancestor synthesis smells fundamentally wrong. Thanks for fixing it.

Show 8 quoted lines
> ---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
> +o specify commits; it is sufficient if they specify trees.
"... do not need to specify commits; trees are enough"?
Show 65 quoted lines
> 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
OK.  The changes here look quite straight-forward.
Show 15 quoted lines
> diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
> index 12ac4368736..71f21bb834f 100755
> --- a/t/t4301-merge-tree-write-tree.sh
> +++ b/t/t4301-merge-tree-write-tree.sh
> @@ -945,4 +945,12 @@ 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 >tree &&
> +	tree=$(cat tree) &&
> +	git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >tree2 &&
> +	tree2=$(cat tree2) &&
> +	test $tree = $tree2
> +'

You do not need $tree and $tree2 variables that would make it harder to diagnose a failure case when we break merge-tree. You have tree and tree2 as files, and I think it is sufficient to do

	git merge-tree ... >result-from-commits &&
	git merge-tree ... >result-from-trees &&
	test_cmp result-from-commits result-from-trees

(no, I am not suggesting to rename these tree and tree2 files; I just needed them to be descriptive in my illustration to show what is going on to myself).

Thanks.
Previous: Eric SunshineNext: Johannes Schindelin
Message 3 of 12 in “merge-tree: accept 3 trees as arguments”
  1. merge-tree: accept 3 trees as argumentsJohannes Schindelin via GitGitGadget, Jan 26, 2024
  2. Eric SunshineJan 26, 2024
  3. Junio C HamanoJan 26, 2024
  4. Johannes SchindelinJan 30, 2024
  5. merge-tree: accept 3 trees as argumentsJohannes Schindelin via GitGitGadget, Jan 28, 2024
  6. Junio C HamanoJan 29, 2024
  7. Elijah NewrenJan 30, 2024
  8. Junio C HamanoJan 30, 2024
  9. Elijah NewrenJan 31, 2024
  10. Junio C HamanoJan 31, 2024
  11. Johannes SchindelinJan 30, 2024
  12. Elijah NewrenJan 31, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.