From: Junio C Hamano Date: Thu, 30 Jul 2026 17:44:39 +0000 (-0700) Subject: Merge branch 'pz/fetch-submodule-errors-config' into seen X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f522d05898dce5ae5aedb325e6a916e75bf3be65;p=thirdparty%2Fgit.git Merge branch 'pz/fetch-submodule-errors-config' into seen The 'git fetch' command has been updated to allow configuring how submodule fetch errors are handled. A new configuration variable 'fetch.submoduleErrors' and a corresponding '--submodule-errors' command-line option have been introduced, allowing users to make submodule fetch errors non-fatal (warn instead of fail). Additionally, a premature failure during recursive submodule fetches has been fixed by deferring the error until the OID-based retry phase also fails. * pz/fetch-submodule-errors-config: fetch: add fetch.submoduleErrors to make submodule fetch errors non-fatal submodule: fix premature failure in recursive submodule fetch --- f522d05898dce5ae5aedb325e6a916e75bf3be65 diff --cc t/t5526-fetch-submodules.sh index 7b3b7359da,19d17440cf..3456e36644 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@@ -1259,7 -1259,168 +1259,168 @@@ test_expect_success "fetch --all with - git config submodule.recurse true && git fetch --all --no-recurse-submodules 2>../fetch-log ) && - ! grep "Fetching submodule" fetch-log + test_grep ! "Fetching submodule" fetch-log ' + # Create an isolated environment for submodule fetch error tests. + # + # Sets up sub_bare (the submodule upstream), super_bare (the superproject + # upstream), super_work (a working clone of super_bare with an initialized + # submodule), and clone (a clone of super_bare with an initialized submodule + # at a reachable commit). The caller can then create an unreachable commit + # and push the superproject to put the clone one commit behind a state it + # cannot fully fetch. + # + # Usage: create_err_env + create_err_env () { + local envdir="$1" && + mkdir "$envdir" && + + git init --bare "$envdir/sub_bare" && + git clone "$envdir/sub_bare" "$envdir/sub_work" && + test_commit -C "$envdir/sub_work" "${envdir}_base" && + git -C "$envdir/sub_work" push && + + git init --bare "$envdir/super_bare" && + git clone "$envdir/super_bare" "$envdir/super_work" && + git -C "$envdir/super_work" submodule add \ + "$pwd/$envdir/sub_bare" sub && + git -C "$envdir/super_work" commit -m "add submodule" && + git -C "$envdir/super_work" push && + + git clone "$envdir/super_bare" "$envdir/clone" && + git -C "$envdir/clone" submodule update --init + } + + # Push a commit to /super_bare that records a submodule SHA that is + # present locally in super_work/sub but NOT pushed to sub_bare, making the + # submodule commit unreachable from clone's sub remote. + push_unreachable_commit () { + local envdir="$1" && + git -C "$envdir/super_work/sub" commit --allow-empty -m "unreachable" && + git -C "$envdir/super_work" add sub && + git -C "$envdir/super_work" commit -m "point sub to unreachable commit" && + git -C "$envdir/super_work" push + } + + test_expect_success 'setup for submodule fetch error tests' ' + git config --global protocol.file.allow always + ' + + test_expect_success 'fetch --recurse-submodules fails when submodule commit is unreachable (default)' ' + test_when_finished "rm -fr env_default" && + create_err_env env_default && + push_unreachable_commit env_default && + test_must_fail git -C env_default/clone fetch --recurse-submodules 2>err && + test_grep "Errors during submodule fetch" err + ' + + test_expect_success 'fetch.submoduleErrors=warn: unreachable submodule commit is non-fatal' ' + test_when_finished "rm -fr env_warn_cfg" && + create_err_env env_warn_cfg && + push_unreachable_commit env_warn_cfg && + git -C env_warn_cfg/clone -c fetch.submoduleErrors=warn \ + fetch --recurse-submodules 2>err && + test_grep "Errors during submodule fetch" err + ' + + test_expect_success '--submodule-errors=warn: unreachable submodule commit is non-fatal' ' + test_when_finished "rm -fr env_warn_cli" && + create_err_env env_warn_cli && + push_unreachable_commit env_warn_cli && + git -C env_warn_cli/clone fetch --recurse-submodules \ + --submodule-errors=warn 2>err && + test_grep "Errors during submodule fetch" err + ' + + test_expect_success '--submodule-errors=fail: unreachable submodule commit is fatal' ' + test_when_finished "rm -fr env_fail_cli" && + create_err_env env_fail_cli && + push_unreachable_commit env_fail_cli && + test_must_fail git -C env_fail_cli/clone fetch --recurse-submodules \ + --submodule-errors=fail 2>err && + test_grep "Errors during submodule fetch" err + ' + + test_expect_success 'fetch.submoduleErrors=warn does not suppress successful fetch' ' + # A new reachable submodule commit (pushed to sub_bare) should be + # fetched without any error summary. + test_when_finished "rm -fr env_ok" && + create_err_env env_ok && + test_commit -C env_ok/sub_work reachable_ok && + git -C env_ok/sub_work push && + git -C env_ok/super_work submodule update --remote && + git -C env_ok/super_work add sub && + git -C env_ok/super_work commit -m "point sub to reachable commit" && + git -C env_ok/super_work push && + git -C env_ok/clone -c fetch.submoduleErrors=warn \ + fetch --recurse-submodules 2>err && + test_grep ! "Errors during submodule fetch" err + ' + + test_expect_success 'failed submodule fetch is fatal even when its commits are present locally' ' + # Create the same commit (unreferenced, via commit-tree with fixed + # dates) in both super_work/sub and clone/sub, point the gitlink at + # it, and break clone/sub'\''s remote. The commit exists in clone/sub + # but is unreachable, so the submodule stays in the changed list; the + # fetch failure must still be reported even though there is nothing + # left to fetch by commit hash. + test_when_finished "rm -fr env_phase1" && + create_err_env env_phase1 && + commit=$(GIT_AUTHOR_DATE="1234567890 +0000" \ + GIT_COMMITTER_DATE="1234567890 +0000" \ + git -C env_phase1/super_work/sub commit-tree \ + "HEAD^{tree}" -p HEAD -m present) && + present=$(GIT_AUTHOR_DATE="1234567890 +0000" \ + GIT_COMMITTER_DATE="1234567890 +0000" \ + git -C env_phase1/clone/sub commit-tree \ + "HEAD^{tree}" -p HEAD -m present) && + test "$commit" = "$present" && + git -C env_phase1/super_work/sub checkout "$commit" && + git -C env_phase1/super_work add sub && + git -C env_phase1/super_work commit -m "gitlink to locally-present commit" && + git -C env_phase1/super_work push && + git -C env_phase1/clone/sub remote set-url origin "$pwd/env_phase1/missing" && + test_must_fail git -C env_phase1/clone fetch --recurse-submodules 2>err && + test_grep "Errors during submodule fetch" err + ' + + test_expect_success '--submodule-errors=warn is honored by fetch --all' ' + # A second remote forces fetch_multiple(), which hands the submodule + # recursion off to per-remote child processes; the option must be + # forwarded to them. + test_when_finished "rm -fr env_all" && + create_err_env env_all && + push_unreachable_commit env_all && + git -C env_all/clone remote add second "$pwd/env_all/super_bare" && + git -C env_all/clone fetch --all --recurse-submodules \ + --submodule-errors=warn 2>err && + test_grep "Errors during submodule fetch" err + ' + + test_expect_success '--submodule-errors=fail overrides warn config for fetch --all' ' + # The per-remote child processes re-read the repository config, so + # the command-line override must be forwarded to them explicitly. + test_when_finished "rm -fr env_override" && + create_err_env env_override && + push_unreachable_commit env_override && + git -C env_override/clone remote add second "$pwd/env_override/super_bare" && + git -C env_override/clone config fetch.submoduleErrors warn && + test_must_fail git -C env_override/clone fetch --all --recurse-submodules \ + --submodule-errors=fail 2>err && + test_grep "Errors during submodule fetch" err + ' + + test_expect_success 'fetch.submoduleErrors=warn: inaccessible submodule is non-fatal' ' + test_when_finished "rm -fr env_access" && + create_err_env env_access && + rm env_access/clone/sub/.git && + rm -r env_access/clone/.git/modules/sub && + git -C env_access/clone -c fetch.submoduleErrors=warn \ + fetch --recurse-submodules 2>err && + test_grep "Could not access submodule" err && + test_must_fail git -C env_access/clone fetch --recurse-submodules 2>err && + test_grep "Could not access submodule" err + ' + test_done