Re: [PATCH v2 5/7] merge-recursive: add merge function arg to 'merge_recursive_generic'
- From
Elijah Newren <newren@gmail.com>
- Date
- May 6, 2022, 07:23 UTC
- Message-ID
- <CABPp-BHcWjOeVhRD_XKTko0OH2pwYsuCt8PzH=C_0u_gUWe0GQ@mail.gmail.com>
- In-Reply-To
- <4537d473b937b182cd79b2f3c5673b75d92cab23.1651083378.git.gitgitgadget@gmail.com>
On Wed, Apr 27, 2022 at 11:16 AM Victoria Dye via GitGitGadget <gitgitgadget@gmail.com> wrote:
Show 12 quoted lines
> > From: Victoria Dye <vdye@github.com> > > Replace the hardcoded 'merge_recursive()' function used by the > 'merge_recursive_generic()' with a caller-specific merge function. This will > allow us to use 'merge_ort_recursive()' (and therefore avoid the index > expansion of 'merge_recursive()') in commands that perform merges with > 'merge_recursive_generic()', such as 'git stash pop'. > > Note that this patch is strictly a refactor; all callers still use > 'merge_recursive()', and any changing to 'merge_ort_recursive()' will be > done in a later commit.
I'm not sure if we can gut merge_recursive_generic(), but I don't think stash should use it...
Show 60 quoted lines
> Signed-off-by: Victoria Dye <vdye@github.com>
> ---
> builtin/am.c | 2 +-
> builtin/merge-recursive.c | 2 +-
> builtin/stash.c | 2 +-
> merge-ort.c | 3 ++-
> merge-recursive.c | 4 ++--
> merge-recursive.h | 9 ++++++++-
> 6 files changed, 15 insertions(+), 7 deletions(-)
>
> diff --git a/builtin/am.c b/builtin/am.c
> index 0f4111bafa0..6d01185d122 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -1614,7 +1614,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
> if (state->quiet)
> o.verbosity = 0;
>
> - if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
> + if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, merge_recursive, &result)) {
> repo_rerere(the_repository, state->allow_rerere_autoupdate);
> free(their_tree_name);
> return error(_("Failed to merge in the changes."));
> diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
> index b9acbf5d342..687ed1e527b 100644
> --- a/builtin/merge-recursive.c
> +++ b/builtin/merge-recursive.c
> @@ -81,7 +81,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
> if (o.verbosity >= 3)
> printf(_("Merging %s with %s\n"), o.branch1, o.branch2);
>
> - failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, &result);
> + failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, merge_recursive, &result);
>
> free(better1);
> free(better2);
> diff --git a/builtin/stash.c b/builtin/stash.c
> index 1bfba532044..16171eb1dab 100644
> --- a/builtin/stash.c
> +++ b/builtin/stash.c
> @@ -554,7 +554,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
> bases[0] = &info->b_tree;
>
> ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
> - &result);
> + merge_recursive, &result);
> if (ret) {
> rerere(0);
>
> diff --git a/merge-ort.c b/merge-ort.c
> index 8545354dafd..4bccdfcf355 100644
> --- a/merge-ort.c
> +++ b/merge-ort.c
> @@ -4737,7 +4737,8 @@ void merge_incore_recursive(struct merge_options *opt,
> trace2_region_enter("merge", "incore_recursive", opt->repo);
>
> /* We set the ancestor label based on the merge_bases */
> - assert(opt->ancestor == NULL);
> + assert(opt->ancestor == NULL ||
> + !strcmp(opt->ancestor, "constructed merge base"));...and here's one of the reasons why. The fact that merge_recursive_generic() uses this string when exactly one merge base is passed is something that is only correct for git-am; it is wrong and actively misleading for git-stash since it has a real merge base that is not internally constructed by the operation using the merge machinery. (The merge base it uses is something like $STASH^1, IIRC.)
In fact, this was half the coin around why merge_recursive_generic() wasn't converted when merge-ort was written; see https://lore.kernel.org/git/CABPp-BHW61zA+MefvWK47iVZKY97rxc2XZ-NjXzuJxEhgSLqUw@mail.gmail.com/ and https://lore.kernel.org/git/CABPp-BFr=1iVY739cfh-1Hp82x-Mny-k4y0f3zZ_YuP3PxiGfQ@mail.gmail.com/ for more details.
The use of merge_recursive_generic() by stash is also a bit weird; most of the time, stash is going to have actual commits instead of just trees. But stash dereferences those commits to trees, passes them to merge_recursive_generic(), and then merge_recursive_generic() has to create fake commits containing those trees, because the merge machinery wants commits. It feels a bit like a Rube Goldberg machine. Also, stash also always calls merge_recursive_generic() with exactly one merge base, which together with having real commits both kind of defeat the need for "generic". I think stash should just use merge_trees()/merge_incore_nonrecursive() directly (much as sequencer.c does). The only special case to worry about with stash is when c_tree != HEAD^{tree}, i.e. when applying changes on top of already present changes instead of just on top of HEAD. But in that case, I think stash should be the thing to create a fake commit rather than invoking some wrapper that will create fake commits for all three trees.
Show 60 quoted lines
> trace2_region_enter("merge", "merge_start", opt->repo);
> merge_start(opt, result);
> diff --git a/merge-recursive.c b/merge-recursive.c
> index 1ee6364e8b1..2088f5c5fb3 100644
> --- a/merge-recursive.c
> +++ b/merge-recursive.c
> @@ -3806,6 +3806,7 @@ int merge_recursive_generic(struct merge_options *opt,
> const struct object_id *merge,
> int num_merge_bases,
> const struct object_id **merge_bases,
> + recursive_merge_fn_t merge_fn,
> struct commit **result)
> {
> int clean;
> @@ -3829,8 +3830,7 @@ int merge_recursive_generic(struct merge_options *opt,
> }
>
> repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);
> - clean = merge_recursive(opt, head_commit, next_commit, ca,
> - result);
> + clean = merge_fn(opt, head_commit, next_commit, ca, result);
> if (clean < 0) {
> rollback_lock_file(&lock);
> return clean;
> diff --git a/merge-recursive.h b/merge-recursive.h
> index b88000e3c25..6a21f2da538 100644
> --- a/merge-recursive.h
> +++ b/merge-recursive.h
> @@ -53,6 +53,12 @@ struct merge_options {
> struct merge_options_internal *priv;
> };
>
> +typedef int (*recursive_merge_fn_t)(struct merge_options *opt,
> + struct commit *h1,
> + struct commit *h2,
> + struct commit_list *merge_bases,
> + struct commit **result);
> +
> void init_merge_options(struct merge_options *opt, struct repository *repo);
>
> /* parse the option in s and update the relevant field of opt */
> @@ -105,7 +111,7 @@ int merge_recursive(struct merge_options *opt,
>
> /*
> * merge_recursive_generic can operate on trees instead of commits, by
> - * wrapping the trees into virtual commits, and calling merge_recursive().
> + * wrapping the trees into virtual commits, and calling the provided merge_fn.
> * It also writes out the in-memory index to disk if the merge is successful.
> *
> * Outputs:
> @@ -120,6 +126,7 @@ int merge_recursive_generic(struct merge_options *opt,
> const struct object_id *merge,
> int num_merge_bases,
> const struct object_id **merge_bases,
> + recursive_merge_fn_t merge_fn,
> struct commit **result);
>
> #endif
> --
> gitgitgadget