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@xxxxxxxxxx> --- 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(-) 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) ' base-commit: ab336e8f1c8009c8b1aab8deb592148e69217085 -- 2.36.1.255.ge46751e96f-goog