Re: [PATCH v8 08/14] merge-resolve: rewrite in C
- From
Johannes Schindelin <johannes.schindelin@gmx.de>
- Date
- Aug 16, 2022, 12:17 UTC
- Message-ID
- <128n8n08-23ss-pnsr-n910-o39nr32q42n5@tzk.qr>
- In-Reply-To
- <08ea1eec-58fb-cbfa-d405-0d4159c99515@gmail.com>
Hi Phillip,
On Wed, 10 Aug 2022, Phillip Wood wrote:
Show 41 quoted lines
> On 09/08/2022 19:54, Alban Gruin wrote: > > This rewrites `git merge-resolve' from shell to C. As for `git > > merge-one-file', this port is not completely straightforward and removes > > calls to external processes to avoid reading and writing the index over > > and over again. > > > > - The call to `update-index -q --refresh' is replaced by a call to > > refresh_index(). > > > > - The call to `read-tree' is replaced by a call to unpack_trees() (and > > all the setup needed). > > > > - The call to `write-tree' is replaced by a call to > > cache_tree_update(). This call is wrapped in a new function, > > write_tree(). It is made to mimick write_index_as_tree() with > > WRITE_TREE_SILENT flag, but without locking the index; this is taken > > care directly in merge_strategies_resolve(). > > > > - The call to `diff-index ...' is replaced by a call to > > repo_index_has_changes(). > > > > - The call to `merge-index', needed to invoke `git merge-one-file', is > > replaced by a call to the new merge_all_index() function. > > > > The index is read in cmd_merge_resolve(), and is wrote back by > > merge_strategies_resolve(). This is to accomodate future applications: > > in `git-merge', the index has already been read when the merge strategy > > is called, so it would be redundant to read it again when the builtin > > will be able to use merge_strategies_resolve() directly. > > > > The parameters of merge_strategies_resolve() will be surprising at first > > glance: why using a commit list for `bases' and `remote', where we could > > use an oid array, and a pointer to an oid? Because, in a later commit, > > try_merge_strategy() will be able to call merge_strategies_resolve() > > directly, and it already uses a commit list for `bases' (`common') and > > `remote' (`remoteheads'), and a string for `head_arg'. To reduce > > frictions later, merge_strategies_resolve() takes the same types of > > parameters. > > git-merge-resolve will happily merge three trees, unfortunately using > lists of commits will break that.
But isn't `merge-resolve` specifically implemented as a merge strategy? I do not see any contract in Git's documentation that commits to supporting direct calls to the implementation detail that is `git merge-resolve`:
$ man git-merge-resolve No manual entry for git-merge-resolve
Show 43 quoted lines
> > merge_strategies_resolve() locks the index only once, at the beginning
> > of the merge, and releases it when the merge has been completed.
> >
> > Signed-off-by: Alban Gruin <alban.gruin@gmail.com>
> > ---
> > diff --git a/builtin/merge-resolve.c b/builtin/merge-resolve.c
> > new file mode 100644
> > index 0000000000..a51158ebf8
> > --- /dev/null
> > +++ b/builtin/merge-resolve.c
> > @@ -0,0 +1,63 @@
> > +/*
> > + * Builtin "git merge-resolve"
> > + *
> > + * Copyright (c) 2020 Alban Gruin
> > + *
> > + * Based on git-merge-resolve.sh, written by Linus Torvalds and Junio C
> > + * Hamano.
> > + *
> > + * Resolve two trees, using enhanced multi-base read-tree.
> > + */
> > +
> > +#include "cache.h"
> > +#include "builtin.h"
> > +#include "merge-strategies.h"
> > +
> > +static const char builtin_merge_resolve_usage[] =
> > + "git merge-resolve <bases>... -- <head> <remote>";
> > +
> > +int cmd_merge_resolve(int argc, const char **argv, const char *prefix)
> > +{
> > + int i, sep_seen = 0;
> > + const char *head = NULL;
> > + struct commit_list *bases = NULL, *remote = NULL;
> > + struct commit_list **next_base = &bases;
> > + struct repository *r = the_repository;
> > +
> > + if (argc < 5)
> > + usage(builtin_merge_resolve_usage);
>
> I think it would be better to call parse_options() and then check argc. That
> would give better error messages for unknown options and supports '-h' for
> free.Again, we are talking about a merge strategy, a program that is not meant to be called directly by the user. Why should we complicate the code by using the `parse_options` machinery?
> I think we also need to call git_config(). I see that read-tree respects > submodule.recurse so I think we need the same here. I suspect we should > also be reading the merge config to respect merge.conflictStyle.
Valid concerns. Extra brownie points if you can provide a simple test case that demonstrates the current behavior.
Show 8 quoted lines
> > +
> > + if (repo_index_has_changes(r, head_tree, &sb)) {
> > + error(_("Your local changes to the following files "
> > + "would be overwritten by merge:\n %s"),
> > + sb.buf);
>
> This matches the script but I wonder why that did not check for unstaged
> changes.Any deviations from the scripted behavior should be done on top of this patch series, unless the deviations make the conversion substantially cleaner.
Thanks, Dscho