Merge branch 'gc/fetch-negotiate-only-early-return'

"git fetch --negotiate-only" is an internal command used by "git
push" to figure out which part of our history is missing from the
other side.  It should never recurse into submodules even when
fetch.recursesubmodules configuration variable is set, nor it
should trigger "gc".  The code has been tightened up to ensure it
only does common ancestry discovery and nothing else.

* gc/fetch-negotiate-only-early-return:
  fetch: help translators by reusing the same message template
  fetch --negotiate-only: do not update submodules
  fetch: skip tasks related to fetching objects
  fetch: use goto cleanup in cmd_fetch()
This commit is contained in:
Junio C Hamano 2022-02-09 14:20:59 -08:00
commit 472a219f8d
4 changed files with 63 additions and 3 deletions

View file

@ -71,6 +71,7 @@ configuration variables documented in linkgit:git-config[1], and the
ancestors of the provided `--negotiation-tip=*` arguments,
which we have in common with the server.
+
This is incompatible with `--recurse-submodules=[yes|on-demand]`.
Internally this is used to implement the `push.negotiate` option, see
linkgit:git-config[1].

View file

@ -76,6 +76,7 @@ static struct transport *gtransport;
static struct transport *gsecondary;
static const char *submodule_prefix = "";
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
static int shown_url = 0;
static struct refspec refmap = REFSPEC_INIT_FETCH;
@ -167,7 +168,7 @@ static struct option builtin_fetch_options[] = {
N_("prune remote-tracking branches no longer on remote")),
OPT_BOOL('P', "prune-tags", &prune_tags,
N_("prune local tags no longer on remote and clobber changed tags")),
OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
N_("control recursive fetching of submodules"),
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
OPT_BOOL(0, "dry-run", &dry_run,
@ -2019,6 +2020,28 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix,
builtin_fetch_options, builtin_fetch_usage, 0);
if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
recurse_submodules = recurse_submodules_cli;
if (negotiate_only) {
switch (recurse_submodules_cli) {
case RECURSE_SUBMODULES_OFF:
case RECURSE_SUBMODULES_DEFAULT:
/*
* --negotiate-only should never recurse into
* submodules. Skip it by setting recurse_submodules to
* RECURSE_SUBMODULES_OFF.
*/
recurse_submodules = RECURSE_SUBMODULES_OFF;
break;
default:
die(_("options '%s' and '%s' cannot be used together"),
"--negotiate-only", "--recurse-submodules");
}
}
if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
int *sfjc = submodule_fetch_jobs_config == -1
? &submodule_fetch_jobs_config : NULL;
@ -2100,7 +2123,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
gtransport->smart_options->acked_commits = &acked_commits;
} else {
warning(_("protocol does not support --negotiate-only, exiting"));
return 1;
result = 1;
goto cleanup;
}
if (server_options.nr)
gtransport->server_options = &server_options;
@ -2156,7 +2180,16 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
strvec_clear(&options);
}
string_list_clear(&list, 0);
/*
* Skip irrelevant tasks because we know objects were not
* fetched.
*
* NEEDSWORK: as a future optimization, we can return early
* whenever objects were not fetched e.g. if we already have all
* of them.
*/
if (negotiate_only)
goto cleanup;
prepare_repo_settings(the_repository);
if (fetch_write_commit_graph > 0 ||
@ -2175,5 +2208,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (enable_auto_gc)
run_auto_maintenance(verbosity < 0);
cleanup:
string_list_clear(&list, 0);
return result;
}

View file

@ -229,6 +229,18 @@ test_expect_success 'push with negotiation proceeds anyway even if negotiation f
test_i18ngrep "push negotiation failed" err
'
test_expect_success 'push with negotiation does not attempt to fetch submodules' '
mk_empty submodule_upstream &&
test_commit -C submodule_upstream submodule_commit &&
git submodule add ./submodule_upstream submodule &&
mk_empty testrepo &&
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
test_commit -C testrepo unrelated_commit &&
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
git -c submodule.recurse=true -c protocol.version=2 -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
! grep "Fetching submodule" err
'
test_expect_success 'push without wildcard' '
mk_empty testrepo &&

View file

@ -628,6 +628,18 @@ test_expect_success 'usage: --negotiate-only without --negotiation-tip' '
test_cmp err.expect err.actual
'
test_expect_success 'usage: --negotiate-only with --recurse-submodules' '
cat >err.expect <<-\EOF &&
fatal: options '\''--negotiate-only'\'' and '\''--recurse-submodules'\'' cannot be used together
EOF
test_must_fail git -c protocol.version=2 -C client fetch \
--negotiate-only \
--recurse-submodules \
origin 2>err.actual &&
test_cmp err.expect err.actual
'
test_expect_success 'file:// --negotiate-only' '
SERVER="server" &&
URI="file://$(pwd)/server" &&