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 <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
seen
Justin Tobler 2026-09-13 15:26:22 -05:00 committed by Junio C Hamano
parent dae07bd6c8
commit 60c4aa758c
2 changed files with 18 additions and 2 deletions

View File

@ -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);


View File

@ -87,6 +87,22 @@ 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 &&

git -C batch config core.bigFileThreshold 5 &&
echo foo >batch/1-small &&
echo foobar >batch/2-large &&

git -C batch -c core.fsync=loose-object -c core.fsyncMethod=batch \
add 1-small 2-large &&

# Neither object may be left behind in a temporary location.
git -C batch cat-file -e :1-small &&
git -C batch 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 &&