object-file: generalize packfile writes to use odb_write_stream

The `index_blob_packfile_transaction()` function streams blob data directly from an fd. This makes it difficult to reuse as part of a generic transactional object writing interface. Refactor the packfile write path to operate on a `struct odb_write_stream`, allowing callers to supply data from arbitrary sources. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Justin Tobler committed May 14, 2026 at 13:37 UTC 45a75d6187dd8e85470e70aaada6346576333370
1 file changed +30 -26
object-file.c
+30 -26
@@ -1447,18 +1447,19 @@ static int hash_blob_stream(struct odb_write_stream *stream,
1447 }
1448
1449 /*
1450 - * Read the contents from fd for size bytes, streaming it to the
1450 + * Read the contents from the stream provided, streaming it to the
1451 * packfile in state while updating the hash in ctx.
1452 */
1453 static void stream_blob_to_pack(struct transaction_packfile *state,
1454 - struct git_hash_ctx *ctx, int fd, size_t size,
1455 - const char *path)
1454 + struct git_hash_ctx *ctx, size_t size,
1455 + struct odb_write_stream *stream)
1456 {
1457 git_zstream s;
1458 unsigned char ibuf[16384];
1459 unsigned char obuf[16384];
1460 unsigned hdrlen;
1461 int status = Z_OK;
1462 + size_t bytes_read = 0;
1463
1464 git_deflate_init(&s, pack_compression_level);
1465
@@ -1467,23 +1468,21 @@ static void stream_blob_to_pack(struct transaction_packfile *state,
1468 s.avail_out = sizeof(obuf) - hdrlen;
1469
1470 while (status != Z_STREAM_END) {
1470 - if (size && !s.avail_in) {
1471 - size_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
1472 - ssize_t read_result = read_in_full(fd, ibuf, rsize);
1473 - if (read_result < 0)
1474 - die_errno("failed to read from '%s'", path);
1475 - if ((size_t)read_result != rsize)
1476 - die("failed to read %u bytes from '%s'",
1477 - (unsigned)rsize, path);
1471 + if (!stream->is_finished && !s.avail_in) {
1472 + ssize_t rsize = odb_write_stream_read(stream, ibuf,
1473 + sizeof(ibuf));
1474 +
1475 + if (rsize < 0)
1476 + die("failed to read blob data");
1477
1478 git_hash_update(ctx, ibuf, rsize);
1479
1480 s.next_in = ibuf;
1481 s.avail_in = rsize;
1483 - size -= rsize;
1482 + bytes_read += rsize;
1483 }
1484
1486 - status = git_deflate(&s, size ? 0 : Z_FINISH);
1485 + status = git_deflate(&s, stream->is_finished ? Z_FINISH : 0);
1486
1487 if (!s.avail_out || status == Z_STREAM_END) {
1488 size_t written = s.next_out - obuf;
@@ -1503,6 +1502,11 @@ static void stream_blob_to_pack(struct transaction_packfile *state,
1502 die("unexpected deflate failure: %d", status);
1503 }
1504 }
1505 +
1506 + if (bytes_read != size)
1507 + die("read %" PRIuMAX " bytes of blob data, but expected %" PRIuMAX " bytes",
1508 + (uintmax_t)bytes_read, (uintmax_t)size);
1509 +
1510 git_deflate_end(&s);
1511 }
1512
@@ -1574,10 +1578,13 @@ clear_exit:
1578 * binary blobs, they generally do not want to get any conversion, and
1579 * callers should avoid this code path when filters are requested.
1580 */
1577 -static int index_blob_packfile_transaction(struct odb_transaction_files *transaction,
1578 - struct object_id *result_oid, int fd,
1579 - size_t size, const char *path)
1581 +static int index_blob_packfile_transaction(struct odb_transaction *base,
1582 + struct odb_write_stream *stream,
1583 + size_t size, struct object_id *result_oid)
1584 {
1585 + struct odb_transaction_files *transaction = container_of(base,
1586 + struct odb_transaction_files,
1587 + base);
1588 struct transaction_packfile *state = &transaction->packfile;
1589 struct git_hash_ctx ctx;
1590 unsigned char obuf[16384];
@@ -1611,7 +1618,7 @@ static int index_blob_packfile_transaction(struct odb_transaction_files *transac
1618 hashfile_checkpoint(state->f, &checkpoint);
1619 idx->offset = state->offset;
1620 crc32_begin(state->f);
1614 - stream_blob_to_pack(state, &ctx, fd, size, path);
1621 + stream_blob_to_pack(state, &ctx, size, stream);
1622 git_hash_final_oid(result_oid, &ctx);
1623
1624 idx->crc32 = crc32_end(state->f);
@@ -1655,15 +1662,12 @@ int index_fd(struct index_state *istate, struct object_id *oid,
1662
1663 if (flags & INDEX_WRITE_OBJECT) {
1664 struct object_database *odb = the_repository->objects;
1658 - struct odb_transaction_files *files_transaction;
1659 - struct odb_transaction *transaction;
1660 -
1661 - transaction = odb_transaction_begin(odb);
1662 - files_transaction = container_of(odb->transaction,
1663 - struct odb_transaction_files,
1664 - base);
1665 - ret = index_blob_packfile_transaction(files_transaction, oid, fd,
1666 - xsize_t(st->st_size), path);
1665 + struct odb_transaction *transaction = odb_transaction_begin(odb);
1666 +
1667 + ret = index_blob_packfile_transaction(odb->transaction,
1668 + &stream,
1669 + xsize_t(st->st_size),
1670 + oid);
1671 odb_transaction_commit(transaction);
1672 } else {
1673 ret = hash_blob_stream(&stream,