diff mbox series

[v2,5/7] merge-recursive: add merge function arg to 'merge_recursive_generic'

Message ID 4537d473b937b182cd79b2f3c5673b75d92cab23.1651083378.git.gitgitgadget@gmail.com (mailing list archive)
State New, archived
Headers show
Series Sparse index: integrate with 'git stash' | expand

Commit Message

Victoria Dye April 27, 2022, 6:16 p.m. UTC
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.

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(-)

Comments

Elijah Newren May 6, 2022, 7:23 a.m. UTC | #1
On Wed, Apr 27, 2022 at 11:16 AM Victoria Dye via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> 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...

> 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.

>         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
Victoria Dye May 9, 2022, 7:24 p.m. UTC | #2
Elijah Newren wrote:
> On Wed, Apr 27, 2022 at 11:16 AM Victoria Dye via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
>>
>> 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...
> 
>> 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.
> 

All of that makes sense, thanks for the context!

> 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.
> 

I'm a bit confused about this. The non-recursive merge functions
('merge_trees()' & 'merge_ort_nonrecursive()' or the lower-level
'merge_incore_nonrecursive()') merge trees, not commits, so performing a
non-recursive merge requires dereferencing commits to trees anyway. I think
I agree with your other message [1] that the 'stash' merge doesn't need to
merge recursively, but that would mean it also doesn't use the commits
*directly* (i.e., as arguments in the merge). 

Apologies if I'm missing something obvious, but I want to make sure I
understand your suggestion.

[1] https://lore.kernel.org/git/CABPp-BFANwZn73w8zrVySB7mh0bQQBdGJjBuSJy50UOeyYT6aA@mail.gmail.com/ 


>>         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
Elijah Newren May 10, 2022, 7:06 a.m. UTC | #3
On Mon, May 9, 2022 at 12:24 PM Victoria Dye <vdye@github.com> wrote:
>
> Elijah Newren wrote:
> > On Wed, Apr 27, 2022 at 11:16 AM Victoria Dye via GitGitGadget
> > <gitgitgadget@gmail.com> wrote:
> >>
> >> 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...
> >
> >> 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.
> >
>
> All of that makes sense, thanks for the context!
>
> > 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.
> >
>
> I'm a bit confused about this. The non-recursive merge functions
> ('merge_trees()' & 'merge_ort_nonrecursive()' or the lower-level
> 'merge_incore_nonrecursive()') merge trees, not commits, so performing a
> non-recursive merge requires dereferencing commits to trees anyway. I think
> I agree with your other message [1] that the 'stash' merge doesn't need to
> merge recursively, but that would mean it also doesn't use the commits
> *directly* (i.e., as arguments in the merge).
>
> Apologies if I'm missing something obvious, but I want to make sure I
> understand your suggestion.
>
> [1] https://lore.kernel.org/git/CABPp-BFANwZn73w8zrVySB7mh0bQQBdGJjBuSJy50UOeyYT6aA@mail.gmail.com/

Oh, right, it's only the recursive merge that needs commits (so that
it can find ancestors and ancestors of ancestors, etc.).  So, ignore
my comments about making fake commits; that's not needed.
diff mbox series

Patch

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"));
 
 	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