diff options
| author | Elijah Newren <newren@gmail.com> | 2026-08-29 07:00:30 +0000 |
|---|---|---|
| committer | Junio C Hamano <gitster@pobox.com> | 2026-08-30 13:37:42 -0700 |
| commit | 22eef58fba36a6dd9cadfe606b9f711e89266ae3 (patch) | |
| tree | cdbda1250c76edb9af1fc8c585e550ac1fb0e245 | |
| parent | 6272f3bd174fcd1b394d8b5e05b2ba384bd9f63e (diff) | |
mktree: do not use OBJECT_INFO_QUICK when checking objects
mktree_line() checks each referenced object's type with
odb_read_object_info_extended() under OBJECT_INFO_QUICK. QUICK skips the
reprepare-and-retry that reloads the on-disk pack set, so a resident
"git mktree --batch" reader reports an object that a concurrent repack
just relocated into a new pack as missing, and rejects the entry.
QUICK entered this lookup in 817b0f602710 (mktree: do not check type of
remote objects, 2022-06-21) only to avoid lazily fetching promisor
objects; OBJECT_INFO_SKIP_FETCH_OBJECT already provides that. Drop
OBJECT_INFO_QUICK and keep OBJECT_INFO_SKIP_FETCH_OBJECT, so mktree still
avoids a promisor fetch but recovers an object that was merely repacked.
Add a regression test driving a resident mktree --batch reader across a
concurrent repack that retires a pack.
Assisted-by: Claude Opus 4.8 & GPT-5.6 Sol
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
| -rw-r--r-- | builtin/mktree.c | 1 | ||||
| -rwxr-xr-x | t/t1010-mktree.sh | 48 |
2 files changed, 48 insertions, 1 deletions
diff --git a/builtin/mktree.c b/builtin/mktree.c index dc2d293c3d..45ae2af3b5 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -125,7 +125,6 @@ static void mktree_line(struct repository *repo, char *buf, int nul_term_line, i oi.typep = &obj_type; if (odb_read_object_info_extended(repo->objects, &oid, &oi, OBJECT_INFO_LOOKUP_REPLACE | - OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) < 0) obj_type = -1; diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index 312fe6717a..cecba55d45 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -69,4 +69,52 @@ test_expect_success 'mktree refuses to read ls-tree -r output (2)' ' test_must_fail git mktree <all.withsub ' +test_expect_success PIPE 'mktree --batch survives a concurrent repack retiring a pack' ' + test_when_finished "rm -fr race" && + git init race && + ( + cd race && + test_commit seed && + a=$(echo A | git hash-object -w --stdin) && + b=$(echo B | git hash-object -w --stdin) && + echo "$a" | git pack-objects .git/objects/pack/pack >pack-a && + echo "$b" | git pack-objects .git/objects/pack/pack >pack-b && + + # Drop the loose copies so the blobs resolve only through the + # packs the multi-pack-index names. + git prune-packed && + git multi-pack-index write && + printf "100644 blob %s\ta\n" "$a" >tree-a && + printf "100644 blob %s\tb\n" "$b" >tree-b && + + victim=".git/objects/pack/pack-$(cat pack-b)" && + mkfifo in out && + + # mktree --batch stays resident, so its pack view predates the + # repack below; feed it one tree at a time over a fifo. The + # subshell exit closes the fifos, letting mktree see EOF and quit. + (git mktree --batch <in >out 2>err &) && + exec 9>in && + exec 8<out && + + # The first tree makes the reader cache its (soon stale) view. + cat tree-a >&9 && echo >&9 && read tree_a <&8 && + + # Mimic a concurrent repack: a replacement pack holds every + # object, and the pack for b loses its .idx (its .pack lingers), + # matching the order in which unlink_pack_path() removes files. + git cat-file --batch-all-objects --batch-check="%(objectname)" >oids && + git pack-objects .git/objects/pack/pack <oids >/dev/null && + rm -f "$victim.idx" && + + # Resolving b used to fail, as its QUICK lookup accepted the + # miss; without QUICK the reader repreps and finds b in the + # replacement pack. + cat tree-b >&9 && echo >&9 && read tree_b <&8 && + exec 9>&- && + + test -n "$tree_b" + ) +' + test_done |
