mirror of
https://github.com/git/git.git
synced 2026-03-05 06:57:37 +01:00
promisor-remote: prevent lazy-fetch recursion in child fetch
fetch_objects() spawns a child `git fetch` to lazily fill in missing objects. That child's index-pack, when it receives a thin pack containing a REF_DELTA against a still-missing base, explicitly calls promisor_remote_get_direct() — which is fetch_objects() again. If the base is truly unavailable (e.g. because many refs in the local store point at objects that have been garbage-collected on the server), each recursive lazy-fetch can trigger another, leading to unbounded recursion with runaway disk and process consumption. The GIT_NO_LAZY_FETCH guard (introduced bye6d5479e7a(git: add --no-lazy-fetch option, 2021-08-31)) already exists at the top of fetch_objects(); the missing piece is propagating it into the child fetch's environment. Add that propagation so the child's index-pack, if it encounters a REF_DELTA against a missing base, hits the guard and fails fast instead of recursing. Depth-1 lazy fetch (the whole point of fetch_objects()) is unaffected: only the child and its descendants see the variable. With negotiationAlgorithm=noop the client advertises no "have" lines, so a well-behaved server sends requested objects un-deltified or deltified only against objects in the same pack; the child's index-pack should never need a depth-2 fetch. If it does, the server response was broken or the local store is already corrupt, and further fetching would not help. This is the same bug shape that3a1ea94a49(commit-graph.c: no lazy fetch in lookup_commit_in_graph(), 2022-07-01) addressed at a different entry point. Add a test that verifies the child fetch environment contains GIT_NO_LAZY_FETCH=1 via a reference-transaction hook. Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
committed by
Junio C Hamano
parent
67ad42147a
commit
087cc88450
@@ -42,6 +42,13 @@ static int fetch_objects(struct repository *repo,
|
||||
child.in = -1;
|
||||
if (repo != the_repository)
|
||||
prepare_other_repo_env(&child.env, repo->gitdir);
|
||||
/*
|
||||
* Prevent the child's index-pack from recursing back into
|
||||
* fetch_objects() when resolving REF_DELTA bases it does not
|
||||
* have. With noop negotiation the server should never need
|
||||
* to send such deltas, so a depth-2 fetch would not help.
|
||||
*/
|
||||
strvec_pushf(&child.env, "%s=1", NO_LAZY_FETCH_ENVIRONMENT);
|
||||
strvec_pushl(&child.args, "-c", "fetch.negotiationAlgorithm=noop",
|
||||
"fetch", remote_name, "--no-tags",
|
||||
"--no-write-fetch-head", "--recurse-submodules=no",
|
||||
|
||||
@@ -78,4 +78,30 @@ test_expect_success 'promisor lazy-fetching can be re-enabled' '
|
||||
test_path_is_file script-executed
|
||||
'
|
||||
|
||||
test_expect_success 'lazy-fetch child has GIT_NO_LAZY_FETCH=1' '
|
||||
test_create_repo nolazy-server &&
|
||||
test_commit -C nolazy-server foo &&
|
||||
git -C nolazy-server repack -a -d --write-bitmap-index &&
|
||||
|
||||
git clone "file://$(pwd)/nolazy-server" nolazy-client &&
|
||||
HASH=$(git -C nolazy-client rev-parse foo) &&
|
||||
rm -rf nolazy-client/.git/objects/* &&
|
||||
|
||||
git -C nolazy-client config core.repositoryformatversion 1 &&
|
||||
git -C nolazy-client config extensions.partialclone "origin" &&
|
||||
|
||||
# Install a reference-transaction hook to record the env var
|
||||
# as seen by processes inside the child fetch.
|
||||
test_hook -C nolazy-client reference-transaction <<-\EOF &&
|
||||
echo "$GIT_NO_LAZY_FETCH" >>../env-in-child
|
||||
EOF
|
||||
|
||||
rm -f env-in-child &&
|
||||
git -C nolazy-client cat-file -p "$HASH" &&
|
||||
|
||||
# The hook runs inside the child fetch, which should have
|
||||
# GIT_NO_LAZY_FETCH=1 in its environment.
|
||||
grep "^1$" env-in-child
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
Reference in New Issue
Block a user