fetch --negotiate-only: do not update submodules

`git fetch --negotiate-only` is an implementation detail of push negotiation and, unlike most `git fetch` invocations, does not actually update the main repository. Thus it should not update submodules even if submodule recursion is enabled. This is not just slow, it is wrong e.g. push negotiation with "submodule.recurse=true" will cause submodules to be updated because it invokes `git fetch --negotiate-only`. Fix this by disabling submodule recursion if --negotiate-only was given. Since this makes --negotiate-only and --recurse-submodules incompatible, check for this invalid combination and die. This does not use the "goto cleanup" introduced in the previous commit because we want to recurse through submodules whenever a ref is fetched, and this can happen without introducing new objects. Signed-off-by: Glen Choo <chooglen@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Glen Choo committed Jan 18, 2022 at 16:00 UTC 386c076a863cfafd733b71564245be973e3d1bda
4 files changed +48 -1
Documentation/fetch-options.txt
+1
@@ -71,6 +71,7 @@ configuration variables documented in linkgit:git-config[1], and the
71 ancestors of the provided `--negotiation-tip=*` arguments,
72 which we have in common with the server.
73 +
74 +This is incompatible with `--recurse-submodules=[yes|on-demand]`.
75 Internally this is used to implement the `push.negotiate` option, see
76 linkgit:git-config[1].
77
builtin/fetch.c
+23 -1
@@ -75,6 +75,7 @@ static struct transport *gtransport;
75 static struct transport *gsecondary;
76 static const char *submodule_prefix = "";
77 static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
78 +static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
79 static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
80 static int shown_url = 0;
81 static struct refspec refmap = REFSPEC_INIT_FETCH;
@@ -166,7 +167,7 @@ static struct option builtin_fetch_options[] = {
167 N_("prune remote-tracking branches no longer on remote")),
168 OPT_BOOL('P', "prune-tags", &prune_tags,
169 N_("prune local tags no longer on remote and clobber changed tags")),
169 - OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
170 + OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
171 N_("control recursive fetching of submodules"),
172 PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
173 OPT_BOOL(0, "dry-run", &dry_run,
@@ -1996,6 +1997,27 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
1997
1998 argc = parse_options(argc, argv, prefix,
1999 builtin_fetch_options, builtin_fetch_usage, 0);
2000 +
2001 + if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
2002 + recurse_submodules = recurse_submodules_cli;
2003 +
2004 + if (negotiate_only) {
2005 + switch (recurse_submodules_cli) {
2006 + case RECURSE_SUBMODULES_OFF:
2007 + case RECURSE_SUBMODULES_DEFAULT:
2008 + /*
2009 + * --negotiate-only should never recurse into
2010 + * submodules. Skip it by setting recurse_submodules to
2011 + * RECURSE_SUBMODULES_OFF.
2012 + */
2013 + recurse_submodules = RECURSE_SUBMODULES_OFF;
2014 + break;
2015 +
2016 + default:
2017 + die(_("--negotiate-only and --recurse-submodules cannot be used together"));
2018 + }
2019 + }
2020 +
2021 if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
2022 int *sfjc = submodule_fetch_jobs_config == -1
2023 ? &submodule_fetch_jobs_config : NULL;
t/t5516-fetch-push.sh
+12
@@ -229,6 +229,18 @@ test_expect_success 'push with negotiation proceeds anyway even if negotiation f
229 test_i18ngrep "push negotiation failed" err
230 '
231
232 +test_expect_success 'push with negotiation does not attempt to fetch submodules' '
233 + mk_empty submodule_upstream &&
234 + test_commit -C submodule_upstream submodule_commit &&
235 + git submodule add ./submodule_upstream submodule &&
236 + mk_empty testrepo &&
237 + git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
238 + test_commit -C testrepo unrelated_commit &&
239 + git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
240 + git -c submodule.recurse=true -c protocol.version=2 -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
241 + ! grep "Fetching submodule" err
242 +'
243 +
244 test_expect_success 'push without wildcard' '
245 mk_empty testrepo &&
246
t/t5702-protocol-v2.sh
+12
@@ -628,6 +628,18 @@ test_expect_success 'usage: --negotiate-only without --negotiation-tip' '
628 test_cmp err.expect err.actual
629 '
630
631 +test_expect_success 'usage: --negotiate-only with --recurse-submodules' '
632 + cat >err.expect <<-\EOF &&
633 + fatal: --negotiate-only and --recurse-submodules cannot be used together
634 + EOF
635 +
636 + test_must_fail git -c protocol.version=2 -C client fetch \
637 + --negotiate-only \
638 + --recurse-submodules \
639 + origin 2>err.actual &&
640 + test_cmp err.expect err.actual
641 +'
642 +
643 test_expect_success 'file:// --negotiate-only' '
644 SERVER="server" &&
645 URI="file://$(pwd)/server" &&