diff mbox series

submodule merge: update conflict error message

Message ID 20220606235449.2890858-1-calvinwan@google.com (mailing list archive)
State Superseded
Headers show
Series submodule merge: update conflict error message | expand

Commit Message

Calvin Wan June 6, 2022, 11:54 p.m. UTC
When attempting to do a non-fast-forward merge in a project with
conflicts in the submodules, the merge fails and git prints the
following error:

Failed to merge submodule <submodules>
CONFLICT (submodule):Merge conflict in <submodule>
Automatic merge failed; fix conflicts and then commit the result.

Git is left in a conflicted state, which requires the user to:
 1. abort the merge
 2. merge submodules
 3. merge superproject
These steps are non-obvious for newer submodule users to figure out
based on the error message and neither `git submodule status` nor `git
status` provide any useful pointers. 

Update error message to the following when attempting to do a
non-fast-forward merge in a project with conflicts in the submodules.
The error message is based off of what would happen when `merge
--recurse-submodules` is eventually supported

Failed to merge submodule <submodule>
CONFLICT (submodule): Merge conflict in <submodule>
Automatic merge failed; recursive merging with submodules is currently
not supported. To manually merge, the following steps are recommended:
 - abort the current merge
 - merge submodules individually
 - merge superproject

I considered automatically aborting the merge if git detects the merge
failed because of a submodule conflict, however, doing so causes a
significant amount of tests in `t7610-mergetool.sh` (and some other test
scripts as well) to fail, suggesting users have come to expect this
state and have their workarounds with `git mergetool`

Signed-off-by: Calvin Wan <calvinwan@google.com>

---
 builtin/merge.c            | 12 +++++++++++-
 merge-ort.c                |  4 +++-
 merge-recursive.c          |  4 +++-
 merge-recursive.h          |  1 +
 t/t6437-submodule-merge.sh |  5 ++++-
 5 files changed, 22 insertions(+), 4 deletions(-)


base-commit: ab336e8f1c8009c8b1aab8deb592148e69217085

Comments

Junio C Hamano June 7, 2022, 12:48 a.m. UTC | #1
Calvin Wan <calvinwan@google.com> writes:

> When attempting to do a non-fast-forward merge in a project with
> conflicts in the submodules, the merge fails and git prints the
> following error:
>
> Failed to merge submodule <submodules>
> CONFLICT (submodule):Merge conflict in <submodule>
> Automatic merge failed; fix conflicts and then commit the result.
>
> Git is left in a conflicted state, which requires the user to:
>  1. abort the merge
>  2. merge submodules
>  3. merge superproject
> These steps are non-obvious for newer submodule users to figure out

Hmph.  Is 1. necessary?

IOW, based on the information we already have (we may not be
surfacing, which can be corrected), wouldn't it be easier to instead
(A) go to submodule and make a merge and then (B) come back to the
superproject, "git add <submodule" to record the result of submodule
merge, and say "git commit" to conclude?

The thing I am worried most about is that you may be throwing away
information that would help the user by aborting the superproject
merge.  Before doing so, you have stage #2 and stage #3 of the
submodule commit, so which commits in the submodule you need to
merge in (A) above should be fairly clear.  If you abort the merge
first, how does the user know which commits in the submodule the
user needs to merge?

> The error message is based off of what would happen when `merge
> --recurse-submodules` is eventually supported

OK.

> Failed to merge submodule <submodule>
> CONFLICT (submodule): Merge conflict in <submodule>
> Automatic merge failed; recursive merging with submodules is currently
> not supported. To manually merge, the following steps are recommended:
>  - abort the current merge
>  - merge submodules individually
>  - merge superproject

Again, I am not sure about the recommendation.  The message saying
"currently not supported" I think is a good idea.

> I considered automatically aborting the merge if git detects the merge
> failed because of a submodule conflict, however, doing so causes a
> significant amount of tests in `t7610-mergetool.sh` (and some other test
> scripts as well) to fail, suggesting users have come to expect this
> state and have their workarounds with `git mergetool`

With or without test failures, my gut feeling sais that it cannot be
a good idea to automatically abort the merge, without first grabbing
some information out of the conflicted state.

Thanks.
Calvin Wan June 8, 2022, 5:19 p.m. UTC | #2
> Hmph.  Is 1. necessary?

I just tested it and it is not, so I do agree recommending to abort the
merge is unnecessary/bad advice. How does this sound?

Failed to merge submodule <submodule>
CONFLICT (submodule): Merge conflict in <submodule>
Automatic merge failed; recursive merging with submodules is currently
not supported. To manually merge, merge conflicted submodules first
before merging the superproject.

On Mon, Jun 6, 2022 at 5:48 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Calvin Wan <calvinwan@google.com> writes:
>
> > When attempting to do a non-fast-forward merge in a project with
> > conflicts in the submodules, the merge fails and git prints the
> > following error:
> >
> > Failed to merge submodule <submodules>
> > CONFLICT (submodule):Merge conflict in <submodule>
> > Automatic merge failed; fix conflicts and then commit the result.
> >
> > Git is left in a conflicted state, which requires the user to:
> >  1. abort the merge
> >  2. merge submodules
> >  3. merge superproject
> > These steps are non-obvious for newer submodule users to figure out
>
> Hmph.  Is 1. necessary?
>
> IOW, based on the information we already have (we may not be
> surfacing, which can be corrected), wouldn't it be easier to instead
> (A) go to submodule and make a merge and then (B) come back to the
> superproject, "git add <submodule" to record the result of submodule
> merge, and say "git commit" to conclude?
>
> The thing I am worried most about is that you may be throwing away
> information that would help the user by aborting the superproject
> merge.  Before doing so, you have stage #2 and stage #3 of the
> submodule commit, so which commits in the submodule you need to
> merge in (A) above should be fairly clear.  If you abort the merge
> first, how does the user know which commits in the submodule the
> user needs to merge?
>
> > The error message is based off of what would happen when `merge
> > --recurse-submodules` is eventually supported
>
> OK.
>
> > Failed to merge submodule <submodule>
> > CONFLICT (submodule): Merge conflict in <submodule>
> > Automatic merge failed; recursive merging with submodules is currently
> > not supported. To manually merge, the following steps are recommended:
> >  - abort the current merge
> >  - merge submodules individually
> >  - merge superproject
>
> Again, I am not sure about the recommendation.  The message saying
> "currently not supported" I think is a good idea.
>
> > I considered automatically aborting the merge if git detects the merge
> > failed because of a submodule conflict, however, doing so causes a
> > significant amount of tests in `t7610-mergetool.sh` (and some other test
> > scripts as well) to fail, suggesting users have come to expect this
> > state and have their workarounds with `git mergetool`
>
> With or without test failures, my gut feeling sais that it cannot be
> a good idea to automatically abort the merge, without first grabbing
> some information out of the conflicted state.
>
> Thanks.
Glen Choo June 8, 2022, 5:34 p.m. UTC | #3
Calvin Wan <calvinwan@google.com> writes:

>> Hmph.  Is 1. necessary?
>
> I just tested it and it is not, so I do agree recommending to abort the
> merge is unnecessary/bad advice. How does this sound?
>
> Failed to merge submodule <submodule>
> CONFLICT (submodule): Merge conflict in <submodule>
> Automatic merge failed; recursive merging with submodules is currently
> not supported. To manually merge, merge conflicted submodules first
> before merging the superproject.

This message sounds ok to me, since this is probably what the user wants
90% of the time. Since we don't abort the merge, this just a 'regular'
merge conflict resolution (albeit with submodules). The user probably
wants to merge the submodules, but they can choose however they want to
resolve the merge conflict, e.g. maybe they'd prefer to just pick one
side (or even more exotically, a different commit altogether.)

An improvement for that other 10% would be to print this help message
with the advice() API so that users can turn it off if they don't find
it helpful. Or maybe it's confusing to some new users who use a
different merging workflow and so an admin might turn off this advice
for them.
Calvin Wan June 8, 2022, 6:01 p.m. UTC | #4
> The user probably
> wants to merge the submodules, but they can choose however they want to
> resolve the merge conflict

It sounds like I should reword "merge conflicted submodules" to
"resolve conflicted submodules". That should cover those 10% cases.

I would prefer to find a generic, but still helpful message that doesn't
require going into the advice() API or require some config change

On Wed, Jun 8, 2022 at 10:35 AM Glen Choo <chooglen@google.com> wrote:
>
> Calvin Wan <calvinwan@google.com> writes:
>
> >> Hmph.  Is 1. necessary?
> >
> > I just tested it and it is not, so I do agree recommending to abort the
> > merge is unnecessary/bad advice. How does this sound?
> >
> > Failed to merge submodule <submodule>
> > CONFLICT (submodule): Merge conflict in <submodule>
> > Automatic merge failed; recursive merging with submodules is currently
> > not supported. To manually merge, merge conflicted submodules first
> > before merging the superproject.
>
> This message sounds ok to me, since this is probably what the user wants
> 90% of the time. Since we don't abort the merge, this just a 'regular'
> merge conflict resolution (albeit with submodules). The user probably
> wants to merge the submodules, but they can choose however they want to
> resolve the merge conflict, e.g. maybe they'd prefer to just pick one
> side (or even more exotically, a different commit altogether.)
>
> An improvement for that other 10% would be to print this help message
> with the advice() API so that users can turn it off if they don't find
> it helpful. Or maybe it's confusing to some new users who use a
> different merging workflow and so an admin might turn off this advice
> for them.
Junio C Hamano June 8, 2022, 7:13 p.m. UTC | #5
Calvin Wan <calvinwan@google.com> writes:

>> The user probably
>> wants to merge the submodules, but they can choose however they want to
>> resolve the merge conflict
>
> It sounds like I should reword "merge conflicted submodules" to
> "resolve conflicted submodules". That should cover those 10% cases.
>
> I would prefer to find a generic, but still helpful message that doesn't
> require going into the advice() API or require some config change

We want the users not to blow away the half-merged state in the
working tree.  We are guiding them to first go into submodules and
merge (in which case, we should tell them merge what with what---I
think the first parent should be what they have checked out there,
but the other parent, which is what is recorded in the tree of the
superproject commit being merged as gitlink, may not be at the tip
of any branch you have in the submodule).  And then they come back
to the superproject and resolve the conflict in the working tree and
the index.

> > Failed to merge submodule <submodule>
> > CONFLICT (submodule): Merge conflict in <submodule>
> > Automatic merge failed; recursive merging with submodules is currently
> > not supported. To manually merge, merge conflicted submodules first
> > before merging the superproject.

So,

    to manually complete the merge:
    - go to submodule A, and merge commit a24c4e37d0
    - go to submodule B, and merge commit a6f14c960b
    - come back to superproject, and "git add A B" to record the above merge
    - in superproject, resolve the other conflicts
    - commit the resulting index in the superproject

or something along that line, perhaps?
diff mbox series

Patch

diff --git a/builtin/merge.c b/builtin/merge.c
index f178f5a3ee..39f5ee66d6 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -88,6 +88,7 @@  static const char *sign_commit;
 static int autostash;
 static int no_verify;
 static char *into_name;
+static int submodule_conflict = 0;
 
 static struct strategy all_strategy[] = {
 	{ "recursive",  NO_TRIVIAL },
@@ -757,6 +758,8 @@  static int try_merge_strategy(const char *strategy, struct commit_list *common,
 		else
 			clean = merge_recursive(&o, head, remoteheads->item,
 						reversed, &result);
+		if (o.submodule_conflict)
+			submodule_conflict = 1;
 		if (clean < 0)
 			exit(128);
 		if (write_locked_index(&the_index, &lock,
@@ -973,7 +976,14 @@  static int suggest_conflicts(void)
 	strbuf_release(&msgbuf);
 	fclose(fp);
 	repo_rerere(the_repository, allow_rerere_auto);
-	printf(_("Automatic merge failed; "
+	if (submodule_conflict)
+		printf(_("Automatic merge failed; recursive merging with submodules is currently\n"
+			"not supported. To manually merge, the following steps are recommended:\n"
+			" - abort the current merge\n"
+			" - merge submodules individually\n"
+			" - merge superproject\n"));
+	else
+		printf(_("Automatic merge failed; "
 			"fix conflicts and then commit the result.\n"));
 	return 1;
 }
diff --git a/merge-ort.c b/merge-ort.c
index 0d3f42592f..205d6658bc 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -3866,8 +3866,10 @@  static void process_entry(struct merge_options *opt,
 			const char *reason = _("content");
 			if (ci->filemask == 6)
 				reason = _("add/add");
-			if (S_ISGITLINK(merged_file.mode))
+			if (S_ISGITLINK(merged_file.mode)) {
 				reason = _("submodule");
+				opt->submodule_conflict = 1;
+			}
 			path_msg(opt, path, 0,
 				 _("CONFLICT (%s): Merge conflict in %s"),
 				 reason, path);
diff --git a/merge-recursive.c b/merge-recursive.c
index fd1bbde061..535b8cc758 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3149,8 +3149,10 @@  static int handle_content_merge(struct merge_file_info *mfi,
 	}
 
 	if (!mfi->clean) {
-		if (S_ISGITLINK(mfi->blob.mode))
+		if (S_ISGITLINK(mfi->blob.mode)) {
 			reason = _("submodule");
+			opt->submodule_conflict = 1;
+		}
 		output(opt, 1, _("CONFLICT (%s): Merge conflict in %s"),
 				reason, path);
 		if (ci && !df_conflict_remains)
diff --git a/merge-recursive.h b/merge-recursive.h
index b88000e3c2..6fd31644ad 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -51,6 +51,7 @@  struct merge_options {
 
 	/* internal fields used by the implementation */
 	struct merge_options_internal *priv;
+	unsigned submodule_conflict : 1;
 };
 
 void init_merge_options(struct merge_options *opt, struct repository *repo);
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh
index 178413c22f..bfcc81cd06 100755
--- a/t/t6437-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -141,6 +141,7 @@  test_expect_success 'merging should conflict for non fast-forward' '
 		test_must_fail git merge c 2> actual
 	  fi &&
 	 grep $(cat expect) actual > /dev/null &&
+	 test_i18ngrep "recursive merging with submodules is currently" actual &&
 	 git reset --hard)
 '
 
@@ -167,6 +168,7 @@  test_expect_success 'merging should fail for ambiguous common parent' '
 	 fi &&
 	grep $(cat expect1) actual > /dev/null &&
 	grep $(cat expect2) actual > /dev/null &&
+	test_i18ngrep "recursive merging with submodules is currently" actual &&
 	git reset --hard)
 '
 
@@ -205,7 +207,8 @@  test_expect_success 'merging should fail for changes that are backwards' '
 	git commit -a -m "f" &&
 
 	git checkout -b test-backward e &&
-	test_must_fail git merge f)
+	test_must_fail git merge f >actual &&
+	test_i18ngrep "recursive merging with submodules is currently" actual)
 '