From: Junio C Hamano <gitster@pobox.com>
To: Paulius Zaleckas <paulius.zaleckas@gmail.com>
Cc: git@vger.kernel.org, "Glen Choo" <glencbz@gmail.com>,
"Ævar Arnfjörð Bjarmason" <avarab@gmail.com>,
"Patrick Steinhardt" <ps@pks.im>
Subject: Re: [PATCH v3 2/2] fetch: add fetch.submoduleErrors to make submodule fetch errors non-fatal
Date: Fri, 10 Jul 2026 15:21:02 -0700 [thread overview]
Message-ID: <xmqqmrvybi5t.fsf@gitster.g> (raw)
In-Reply-To: <20260710122655.3066377-3-paulius.zaleckas@gmail.com> (Paulius Zaleckas's message of "Fri, 10 Jul 2026 15:26:53 +0300")
Paulius Zaleckas <paulius.zaleckas@gmail.com> writes:
> When fetching with --recurse-submodules, a submodule commit that is not
> yet reachable from any of the submodule's remote refs causes the entire
> fetch to fail. This is overly strict when the missing commit belongs to
> an upstream branch that is still being prepared (e.g. an in-progress
> merge topic): the local branch does not need that commit, so there is no
> reason to treat its absence as fatal.
>
> Add a new config key fetch.submoduleErrors (values: fail/warn) and a
> corresponding --submodule-errors=(fail|warn) command-line option that
> control this behaviour. The default remains fail (existing behaviour);
> setting the value to warn causes submodule fetch failures to be reported
> on stderr without affecting the overall exit status of git fetch / git
> pull.
>
> Forward the option to child fetches in add_options_to_argv() so that it
> also takes effect for `git fetch --all` / `--multiple` (where per-remote
> child processes handle the submodule recursion themselves) and for
> nested submodule recursion.
>
> Signed-off-by: Paulius Zaleckas <paulius.zaleckas@gmail.com>
> ---
> Documentation/config/fetch.adoc | 14 ++++++
> Documentation/fetch-options.adoc | 8 ++++
> builtin/fetch.c | 41 ++++++++++++++++-
> submodule.c | 8 +++-
> submodule.h | 7 ++-
> t/t5526-fetch-submodules.sh | 76 ++++++++++++++++++++++++++++++++
> 6 files changed, 150 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/config/fetch.adoc b/Documentation/config/fetch.adoc
> index 04ac90912d..5c9c942a70 100644
> --- a/Documentation/config/fetch.adoc
> +++ b/Documentation/config/fetch.adoc
> @@ -10,6 +10,20 @@
> reference.
> Defaults to `on-demand`, or to the value of `submodule.recurse` if set.
>
> +`fetch.submoduleErrors`::
> + Controls how errors from submodule fetches are handled when
> + `--recurse-submodules` is in effect. When set to `fail` (the default),
> + any submodule fetch error causes the overall `git fetch` or `git pull`
> + to exit with a non-zero status. When set to `warn`, submodule fetch
> + errors are reported to standard error but do not affect the exit
> + status of the command. This is useful when working in repositories
> + where some branches reference submodule commits that are not yet
> + available on the submodule remote, but those commits are not needed
> + for the currently checked-out branch.
> ++
> +The value of this option can be overridden by the `--submodule-errors`
> +option of linkgit:git-fetch[1].
> +
> `fetch.fsckObjects`::
> If it is set to true, git-fetch-pack will check all fetched
> objects. See `transfer.fsckObjects` for what's
> diff --git a/Documentation/fetch-options.adoc b/Documentation/fetch-options.adoc
> index 035f780e58..78525f6848 100644
> --- a/Documentation/fetch-options.adoc
> +++ b/Documentation/fetch-options.adoc
> @@ -294,6 +294,14 @@ ifndef::git-pull[]
> `--no-recurse-submodules`::
> Disable recursive fetching of submodules (this has the same effect as
> using the `--recurse-submodules=no` option).
> +
> +`--submodule-errors=(fail|warn)`::
> + Control how errors from submodule fetches are handled when
> + `--recurse-submodules` is in effect. When set to `fail` (the default),
> + any submodule fetch error causes the overall `git fetch` to exit with a
> + non-zero status. When set to `warn`, submodule fetch errors are reported
> + to standard error but do not affect the exit status of the command. Can
> + also be configured via `fetch.submoduleErrors`. See linkgit:git-config[1].
> endif::git-pull[]
>
> `--set-upstream`::
> diff --git a/builtin/fetch.c b/builtin/fetch.c
> index c1d7c672f4..40daaf5cc7 100644
> --- a/builtin/fetch.c
> +++ b/builtin/fetch.c
> @@ -110,6 +110,7 @@ struct fetch_config {
> int recurse_submodules;
> int parallel;
> int submodule_fetch_jobs;
> + int submodule_errors;
> };
>
> static int git_fetch_config(const char *k, const char *v,
> @@ -152,6 +153,19 @@ static int git_fetch_config(const char *k, const char *v,
> return 0;
> }
>
> + if (!strcmp(k, "fetch.submoduleerrors")) {
> + if (!v)
> + return config_error_nonbool(k);
> + else if (!strcasecmp(v, "fail"))
> + fetch_config->submodule_errors = SUBMODULE_ERRORS_FAIL;
> + else if (!strcasecmp(v, "warn"))
> + fetch_config->submodule_errors = SUBMODULE_ERRORS_WARN;
> + else
> + die(_("invalid value for '%s': '%s'"),
> + "fetch.submoduleErrors", v);
> + return 0;
> + }
> +
> if (!strcmp(k, "fetch.parallel")) {
> fetch_config->parallel = git_config_int(k, v, ctx->kvi);
> if (fetch_config->parallel < 0)
> @@ -2205,6 +2219,8 @@ static void add_options_to_argv(struct strvec *argv,
> strvec_push(argv, "--no-recurse-submodules");
> else if (config->recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
> strvec_push(argv, "--recurse-submodules=on-demand");
> + if (config->submodule_errors == SUBMODULE_ERRORS_WARN)
> + strvec_push(argv, "--submodule-errors=warn");
> if (tags == TAGS_SET)
> strvec_push(argv, "--tags");
> else if (tags == TAGS_UNSET)
If (config->submodule_errors != SUBMODULE_ERRORS_WARN), then the argv[]
would not see any --submodule-errors=<anything> to propagate down.
Specifically, this function is called when recurse-submodules is not
disabled, and prepares argv[] used to call fetch_submodules().
> int cmd_fetch(int argc,
> const char **argv,
> const char *prefix,
> @@ -2477,6 +2506,7 @@ int cmd_fetch(int argc,
> .recurse_submodules = RECURSE_SUBMODULES_DEFAULT,
> .parallel = 1,
> .submodule_fetch_jobs = -1,
> + .submodule_errors = SUBMODULE_ERRORS_FAIL,
> };
Here, .submodule_errors member is initialized to
SUBMODULE_ERRORS_FAIL (i.e. 0).
> @@ -2491,6 +2521,7 @@ int cmd_fetch(int argc,
> int max_jobs = -1;
> int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
> int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
> + int submodule_errors_cli = -1; /* -1: not set on command line */
> int fetch_write_commit_graph = -1;
> int stdin_refspecs = 0;
> int negotiate_only = 0;
> @@ -2527,6 +2558,10 @@ int cmd_fetch(int argc,
> 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_CALLBACK_F(0, "submodule-errors", &submodule_errors_cli,
> + N_("(fail|warn)"),
> + N_("control how submodule fetch errors are handled"),
> + 0, option_parse_submodule_errors),
And command line option "--submodule-errors={warn,fail}" may update
the local variable submodule_errors_cli (initialied to -1) to one of
SUBMODULE_ERRORS_{WARN,FAIL}. These are different from -1, so we
can reliably tell if we saw a command line override, which is good.
> OPT_BOOL(0, "dry-run", &dry_run,
> N_("dry run")),
> OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
> @@ -2616,6 +2651,9 @@ int cmd_fetch(int argc,
> if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
> config.recurse_submodules = recurse_submodules_cli;
>
> + if (submodule_errors_cli != -1)
> + config.submodule_errors = submodule_errors_cli;
And we override what we read from the configuration if we got a
command line override.
And the value in config.submodule_errors is used much later, in a
call to add_options_to_argv() we saw earlier, but this patch does
not touch the caller so we do not see the calling site.
I do not do submodules, so my expectation here may be a bit skewed,
but what happens when we configure fetch.submoduleErrors to warn,
but override it from the command line to fail? .submodule_errors is
set to SUBMODULE_ERRORS_FAIL here? As we saw, add_options_to_argv()
stuff --submodule-error=<setting> only when config.submodule_errors
is set to SUBMODULE_ERRORS_WARN, so we do not pass command line
override. Is this desirable? Don't we want to pass down not just
--submodule-error=warn but --submodule-error=fail if that is what
was given from the command line? Or does it not matter because fail
is the default?
Thanks.
next prev parent reply other threads:[~2026-07-10 22:21 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-10 12:26 [PATCH v3 0/2] fetch: make submodule fetch errors configurable Paulius Zaleckas
2026-07-10 12:26 ` [PATCH v3 1/2] submodule: fix premature failure in recursive submodule fetch Paulius Zaleckas
2026-07-10 12:26 ` [PATCH v3 2/2] fetch: add fetch.submoduleErrors to make submodule fetch errors non-fatal Paulius Zaleckas
2026-07-10 22:21 ` Junio C Hamano [this message]
2026-07-14 13:29 ` Paulius Zaleckas
2026-07-14 13:29 ` [PATCH v4 0/2] fetch: make submodule fetch errors configurable Paulius Zaleckas
2026-07-14 13:29 ` [PATCH v4 1/2] submodule: fix premature failure in recursive submodule fetch Paulius Zaleckas
2026-07-14 13:29 ` [PATCH v4 2/2] fetch: add fetch.submoduleErrors to make submodule fetch errors non-fatal Paulius Zaleckas
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=xmqqmrvybi5t.fsf@gitster.g \
--to=gitster@pobox.com \
--cc=avarab@gmail.com \
--cc=git@vger.kernel.org \
--cc=glencbz@gmail.com \
--cc=paulius.zaleckas@gmail.com \
--cc=ps@pks.im \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox