summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElijah Newren <newren@gmail.com>2026-08-29 07:00:30 +0000
committerJunio C Hamano <gitster@pobox.com>2026-08-30 13:37:42 -0700
commit22eef58fba36a6dd9cadfe606b9f711e89266ae3 (patch)
treecdbda1250c76edb9af1fc8c585e550ac1fb0e245
parent6272f3bd174fcd1b394d8b5e05b2ba384bd9f63e (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.c1
-rwxr-xr-xt/t1010-mktree.sh48
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