From 7d4fbc64ce234d2def89cca8f6ed65f684cddb24 Mon Sep 17 00:00:00 2001 From: Justin Tobler Date: Wed, 23 Sep 2026 17:03:14 -0500 Subject: [PATCH 1/2] object-file: lift ODB reprepare out of packfile flush When flushing a packfile via `flush_packfile_transaction()`, `odb_reprepare()` is invoked so the written packfile becomes visible in the current process. In a subsequent commit, flushing the packfile is performed earlier when committing a "files" ODB transaction, but the ODB reprepare needs to remain the last step. Lift ODB reprepare out of `flush_packfile_transaction()` and instead require callers to explicitly invoke `odb_reprepare()` if required. Signed-off-by: Justin Tobler Signed-off-by: Junio C Hamano --- object-file.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/object-file.c b/object-file.c index a4cbf8b081..0f123b79fa 100644 --- a/object-file.c +++ b/object-file.c @@ -857,8 +857,6 @@ clear_exit: memset(state, 0, sizeof(*state)); strbuf_release(&packname); - /* Make objects we just wrote available to ourselves */ - odb_reprepare(repo->objects); } /* @@ -909,8 +907,10 @@ static int odb_transaction_files_write_object_stream(struct odb_transaction *bas * to zlib compression and is sufficient for this check. */ if (state->nr_written && pack_size_limit_cfg && - pack_size_limit_cfg < state->offset + stream->size) + pack_size_limit_cfg < state->offset + stream->size) { flush_packfile_transaction(transaction); + odb_reprepare(transaction->base.source->odb); + } CALLOC_ARRAY(idx, 1); prepare_packfile_transaction(transaction); @@ -1260,6 +1260,7 @@ static int odb_transaction_files_commit(struct odb_transaction *base) { struct odb_transaction_files *transaction = container_of(base, struct odb_transaction_files, base); + int have_packfile = !!transaction->packfile.f; if (transaction->objdir) { struct strbuf temp_path = STRBUF_INIT; @@ -1293,6 +1294,9 @@ static int odb_transaction_files_commit(struct odb_transaction *base) flush_packfile_transaction(transaction); + if (have_packfile) + odb_reprepare(transaction->base.source->odb); + return 0; } From ef7faa6350fc2ee4151584bd912dfd61606be98c Mon Sep 17 00:00:00 2001 From: Justin Tobler Date: Wed, 23 Sep 2026 17:03:15 -0500 Subject: [PATCH 2/2] object-file: flush transaction packfile before migrating objects A "files" ODB transaction creates a temporary directory to stage newly written objects in when configured to batch fsync loose objects. Once the temporary directory is created, it is configured as the primary ODB and all object are written to it accordingly. This also includes packfiles containing blobs that exceed `core.bigFileThreshold` written via `odb_transaction_files_write_object_stream()`. If a "large" blob packfile is written to the ODB transaction temporary directory after other loose objects, the ODB transaction fails to commit as a result of the temporary directory being migrated prior to the packfile being flushed. Fix this bug by always flushing the packfile transaction before objects are migrated to the main ODB. Signed-off-by: Justin Tobler Signed-off-by: Junio C Hamano --- object-file.c | 4 ++-- t/t1050-large.sh | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/object-file.c b/object-file.c index 0f123b79fa..210984f825 100644 --- a/object-file.c +++ b/object-file.c @@ -1262,6 +1262,8 @@ static int odb_transaction_files_commit(struct odb_transaction *base) container_of(base, struct odb_transaction_files, base); int have_packfile = !!transaction->packfile.f; + flush_packfile_transaction(transaction); + if (transaction->objdir) { struct strbuf temp_path = STRBUF_INIT; struct tempfile *temp; @@ -1292,8 +1294,6 @@ static int odb_transaction_files_commit(struct odb_transaction *base) transaction->objdir = NULL; } - flush_packfile_transaction(transaction); - if (have_packfile) odb_reprepare(transaction->base.source->odb); diff --git a/t/t1050-large.sh b/t/t1050-large.sh index d295c265c7..95233458b4 100755 --- a/t/t1050-large.sh +++ b/t/t1050-large.sh @@ -87,6 +87,23 @@ test_expect_success 'add a large file or two' ' test $count = 1 ' +test_expect_success 'add large file with loose object in batch fsync' ' + test_when_finished "rm -rf batch" && + git init batch && + ( + cd batch && + git config core.bigFileThreshold 5 && + echo foo >1-small && + echo foobar >2-large && + + git -c core.fsync=loose-object -c core.fsyncMethod=batch \ + add 1-small 2-large && + + git cat-file -e :1-small && + git cat-file -e :2-large + ) +' + test_expect_success 'checkout a large file' ' large1=$(git rev-parse :large1) && git update-index --add --cacheinfo 100644 $large1 another &&