From: Junio C Hamano Date: Thu, 21 May 2026 13:37:12 +0000 (+0900) Subject: Merge branch 'ps/setup-wo-the-repository' into ps/setup-centralize-odb-creation X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2fb444ae8a4ae24bafb0fad07c161486f8d556e5;p=thirdparty%2Fgit.git Merge branch 'ps/setup-wo-the-repository' into ps/setup-centralize-odb-creation * ps/setup-wo-the-repository: setup: stop using `the_repository` in `init_db()` setup: stop using `the_repository` in `create_reference_database()` setup: stop using `the_repository` in `initialize_repository_version()` setup: stop using `the_repository` in `check_repository_format()` setup: stop using `the_repository` in `upgrade_repository_format()` setup: stop using `the_repository` in `setup_git_directory()` setup: stop using `the_repository` in `setup_git_directory_gently()` setup: stop using `the_repository` in `setup_git_env()` setup: stop using `the_repository` in `set_git_work_tree()` setup: stop using `the_repository` in `setup_work_tree()` setup: stop using `the_repository` in `enter_repo()` setup: stop using `the_repository` in `verify_non_filename()` setup: stop using `the_repository` in `verify_filename()` setup: stop using `the_repository` in `path_inside_repo()` setup: stop using `the_repository` in `prefix_path()` setup: stop using `the_repository` in `is_inside_work_tree()` setup: stop using `the_repository` in `is_inside_git_dir()` setup: replace use of `the_repository` in static functions --- 2fb444ae8a4ae24bafb0fad07c161486f8d556e5 diff --cc t/helper/test-synthesize.c index 1f28ecf0f2,0000000000..3fa534fbdf mode 100644,000000..100644 --- a/t/helper/test-synthesize.c +++ b/t/helper/test-synthesize.c @@@ -1,541 -1,0 +1,541 @@@ +#define USE_THE_REPOSITORY_VARIABLE + +#include "test-tool.h" +#include "git-compat-util.h" +#include "git-zlib.h" +#include "hash.h" +#include "hex.h" +#include "object-file.h" +#include "object.h" +#include "pack.h" +#include "parse-options.h" +#include "parse.h" +#include "repository.h" +#include "setup.h" +#include "strbuf.h" +#include "write-or-die.h" + +#define BLOCK_SIZE 0xffff +static const unsigned char zeros[BLOCK_SIZE]; + +/* + * Write data as an uncompressed zlib stream. + * For data larger than 64KB, writes multiple uncompressed blocks. + * If data is NULL, writes zeros. + * Updates the pack checksum context. + */ +static void write_uncompressed_zlib(FILE *f, struct git_hash_ctx *pack_ctx, + const void *data, size_t len, + const struct git_hash_algo *algo) +{ + unsigned char zlib_header[2] = { 0x78, 0x01 }; /* CMF, FLG */ + unsigned char block_header[5]; + const unsigned char *p = data; + size_t remaining = len; + uint32_t adler = 1L; /* adler32 initial value */ + unsigned char adler_buf[4]; + + /* Write zlib header */ + fwrite_or_die(f, zlib_header, sizeof(zlib_header)); + algo->update_fn(pack_ctx, zlib_header, 2); + + /* Write uncompressed blocks (max 64KB each) */ + do { + size_t block_len = remaining > BLOCK_SIZE ? BLOCK_SIZE : remaining; + int is_final = (block_len == remaining); + const unsigned char *block_data = data ? p : zeros; + + block_header[0] = is_final ? 0x01 : 0x00; + block_header[1] = block_len & 0xff; + block_header[2] = (block_len >> 8) & 0xff; + block_header[3] = block_header[1] ^ 0xff; + block_header[4] = block_header[2] ^ 0xff; + + fwrite_or_die(f, block_header, sizeof(block_header)); + algo->update_fn(pack_ctx, block_header, 5); + + if (block_len) { + fwrite_or_die(f, block_data, block_len); + algo->update_fn(pack_ctx, block_data, block_len); + adler = adler32(adler, block_data, block_len); + } + + if (data) + p += block_len; + remaining -= block_len; + } while (remaining > 0); + + /* Write adler32 checksum */ + put_be32(adler_buf, adler); + fwrite_or_die(f, adler_buf, sizeof(adler_buf)); + algo->update_fn(pack_ctx, adler_buf, 4); +} + +/* + * Write an uncompressed object to the pack file. + * If `data == NULL`, it is treated like a buffer to NUL bytes. + * Updates the pack checksum context. + */ +static void write_pack_object(FILE *f, struct git_hash_ctx *pack_ctx, + enum object_type type, + const void *data, size_t len, + struct object_id *oid, + const struct git_hash_algo *algo) +{ + unsigned char pack_header[MAX_PACK_OBJECT_HEADER]; + char object_header[32]; + int pack_header_len, object_header_len; + struct git_hash_ctx ctx; + + /* Write pack object header */ + pack_header_len = encode_in_pack_object_header(pack_header, + sizeof(pack_header), + type, len); + fwrite_or_die(f, pack_header, pack_header_len); + algo->update_fn(pack_ctx, pack_header, pack_header_len); + + /* Write the data as uncompressed zlib */ + write_uncompressed_zlib(f, pack_ctx, data, len, algo); + + algo->init_fn(&ctx); + object_header_len = format_object_header(object_header, + sizeof(object_header), + type, len); + algo->update_fn(&ctx, object_header, object_header_len); + if (data) + algo->update_fn(&ctx, data, len); + else { + for (size_t i = len / BLOCK_SIZE; i; i--) + algo->update_fn(&ctx, zeros, BLOCK_SIZE); + algo->update_fn(&ctx, zeros, len % BLOCK_SIZE); + } + algo->final_oid_fn(oid, &ctx); +} + +/* + * Fast path: precomputed pack data for a 4 GiB + 1 all-NUL blob. + * + * The generated pack is almost entirely zeros with a small constant + * prefix, periodic deflate block headers, and a constant suffix + * containing the tree, two commits, and the pack checksum. Because + * every byte is deterministic for a given blob size and hash algorithm, + * we can write the pack without computing any hashes at all, reducing + * runtime from minutes of hash computation to seconds of pure I/O. + * + * The blob is stored as an uncompressed deflate stream: a two-byte + * zlib header, then 65538 blocks of up to 0xffff bytes each, followed + * by an adler32 checksum. The pack header and deflate framing are + * shared across hash algorithms; only the suffix (which contains OIDs + * and the pack checksum) differs. + * + * Constants were generated by running the generic path and extracting + * the non-zero bytes from the resulting pack file. + */ + +#define FAST_PACK_4G1_BLOB_SIZE ((size_t)4 * 1024 * 1024 * 1024 + 1) +#define FAST_PACK_4G1_N_FULL_BLOCKS 65537 + +/* + * Per-hash-algorithm constants for the fast path. The prefix and + * deflate block structure are identical across algorithms; only the + * suffix (tree, commits, pack checksum) and the commit OID differ. + */ +struct fast_pack { + uint32_t format_id; + const unsigned char *suffix; + size_t suffix_len; + const char *commit_oid; +}; + +/* Pack header + pack object header + zlib header + first block header */ +static const unsigned char fast_pack_prefix[] = { + /* PACK header: signature, version 2, 5 objects */ + 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x05, + /* pack object header: blob, size = 4294967297 */ + 0xb1, 0x80, 0x80, 0x80, 0x80, 0x01, + /* zlib header: CMF=0x78, FLG=0x01 */ + 0x78, 0x01, + /* first non-final block header: BFINAL=0, LEN=0xffff, NLEN=0x0000 */ + 0x00, 0xff, 0xff, 0x00, 0x00 +}; + +/* Every non-final deflate block header is identical */ +static const unsigned char fast_pack_block_header[] = { + 0x00, 0xff, 0xff, 0x00, 0x00 +}; + +/* Final block (2 data bytes) + adler32 of 4294967297 NUL bytes */ +static const unsigned char fast_pack_final_block[] = { + /* BFINAL=1, LEN=2, NLEN=0xfffd */ + 0x01, 0x02, 0x00, 0xfd, 0xff, + /* 2 NUL data bytes */ + 0x00, 0x00, + /* adler32 */ + 0x00, 0xe2, 0x00, 0x01 +}; + +/* + * SHA-1 suffix: tree, commit, empty tree, final commit, pack checksum. + */ +static const unsigned char fast_pack_sha1_suffix[] = { + 0xa0, 0x02, 0x78, 0x01, 0x01, 0x20, 0x00, 0xdf, + 0xff, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, + 0x66, 0x69, 0x6c, 0x65, 0x00, 0x3e, 0xb7, 0xfe, + 0xb1, 0x41, 0x3c, 0x75, 0x7f, 0x0d, 0x81, 0x81, + 0xde, 0xb2, 0x8d, 0x1d, 0xab, 0x03, 0xd6, 0x48, + 0x46, 0xb4, 0xb4, 0x0c, 0x60, 0x95, 0x0b, 0x78, + 0x01, 0x01, 0xb5, 0x00, 0x4a, 0xff, 0x74, 0x72, + 0x65, 0x65, 0x20, 0x63, 0x36, 0x38, 0x33, 0x66, + 0x63, 0x63, 0x37, 0x64, 0x31, 0x64, 0x38, 0x33, + 0x65, 0x66, 0x32, 0x66, 0x65, 0x31, 0x61, 0x66, + 0x35, 0x35, 0x32, 0x31, 0x35, 0x64, 0x30, 0x31, + 0x36, 0x38, 0x64, 0x62, 0x35, 0x32, 0x61, 0x33, + 0x61, 0x33, 0x62, 0x0a, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55, 0x20, 0x54, + 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, + 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x30, 0x20, 0x2b, 0x30, 0x30, 0x30, + 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x72, 0x20, 0x43, 0x20, 0x4f, 0x20, + 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x3c, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, + 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x30, 0x20, 0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a, + 0x0a, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x20, 0x62, + 0x6c, 0x6f, 0x62, 0x20, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x0a, 0xc6, 0x55, 0x37, 0x6b, 0x20, + 0x78, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x01, 0x95, 0x0e, 0x78, 0x01, 0x01, + 0xe5, 0x00, 0x1a, 0xff, 0x74, 0x72, 0x65, 0x65, + 0x20, 0x34, 0x62, 0x38, 0x32, 0x35, 0x64, 0x63, + 0x36, 0x34, 0x32, 0x63, 0x62, 0x36, 0x65, 0x62, + 0x39, 0x61, 0x30, 0x36, 0x30, 0x65, 0x35, 0x34, + 0x62, 0x66, 0x38, 0x64, 0x36, 0x39, 0x32, 0x38, + 0x38, 0x66, 0x62, 0x65, 0x65, 0x34, 0x39, 0x30, + 0x34, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x20, 0x63, 0x35, 0x62, 0x32, 0x31, 0x63, 0x36, + 0x31, 0x31, 0x61, 0x61, 0x35, 0x39, 0x34, 0x65, + 0x63, 0x39, 0x66, 0x64, 0x37, 0x65, 0x39, 0x32, + 0x63, 0x66, 0x39, 0x36, 0x34, 0x38, 0x39, 0x31, + 0x34, 0x63, 0x61, 0x34, 0x63, 0x32, 0x34, 0x31, + 0x32, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x20, 0x41, 0x20, 0x55, 0x20, 0x54, 0x68, 0x6f, + 0x72, 0x20, 0x3c, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x30, 0x20, 0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, + 0x72, 0x20, 0x43, 0x20, 0x4f, 0x20, 0x4d, 0x69, + 0x74, 0x74, 0x65, 0x72, 0x20, 0x3c, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x40, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x20, + 0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x20, 0x74, 0x72, 0x65, + 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x0a, 0xaa, 0xb8, 0x45, 0x01, 0x8e, 0xfc, 0xf0, + 0x2f, 0x9c, 0xc5, 0xcc, 0x4f, 0x6a, 0x1a, 0xc9, + 0x2b, 0x23, 0xa9, 0xff, 0x91, 0x06, 0xc2, 0x70, + 0xe3 +}; + +/* + * SHA-256 suffix: same structure, but with 32-byte OIDs and SHA-256 + * pack checksum (609 bytes vs 513 for SHA-1). + */ +static const unsigned char fast_pack_sha256_suffix[] = { + 0xac, 0x02, 0x78, 0x01, 0x01, 0x2c, 0x00, 0xd3, + 0xff, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, + 0x66, 0x69, 0x6c, 0x65, 0x00, 0x42, 0x53, 0xc1, + 0x8a, 0x9f, 0x5e, 0xc3, 0xbb, 0x47, 0xb0, 0x83, + 0x8a, 0x19, 0xdb, 0x31, 0xbb, 0x7b, 0x0f, 0x3b, + 0x80, 0xa4, 0xbc, 0x2f, 0xaf, 0x72, 0x6b, 0xdb, + 0x62, 0xaa, 0xba, 0xdd, 0xde, 0x77, 0xc6, 0x13, + 0xeb, 0x9d, 0x0c, 0x78, 0x01, 0x01, 0xcd, 0x00, + 0x32, 0xff, 0x74, 0x72, 0x65, 0x65, 0x20, 0x62, + 0x36, 0x30, 0x39, 0x37, 0x37, 0x64, 0x37, 0x63, + 0x34, 0x63, 0x32, 0x64, 0x31, 0x65, 0x63, 0x63, + 0x33, 0x66, 0x62, 0x61, 0x31, 0x64, 0x39, 0x38, + 0x65, 0x65, 0x31, 0x32, 0x30, 0x61, 0x64, 0x63, + 0x32, 0x34, 0x38, 0x33, 0x34, 0x39, 0x35, 0x30, + 0x62, 0x65, 0x34, 0x31, 0x32, 0x64, 0x39, 0x34, + 0x63, 0x38, 0x30, 0x39, 0x34, 0x38, 0x30, 0x66, + 0x35, 0x38, 0x62, 0x61, 0x39, 0x64, 0x61, 0x0a, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, + 0x20, 0x55, 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, + 0x3c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x20, + 0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, + 0x43, 0x20, 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, + 0x65, 0x72, 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x30, 0x20, 0x2b, 0x30, + 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x4c, 0x61, 0x72, + 0x67, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x62, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0xb7, + 0x80, 0x3d, 0xd7, 0x20, 0x78, 0x01, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x95, + 0x11, 0x78, 0x01, 0x01, 0x15, 0x01, 0xea, 0xfe, + 0x74, 0x72, 0x65, 0x65, 0x20, 0x36, 0x65, 0x66, + 0x31, 0x39, 0x62, 0x34, 0x31, 0x32, 0x32, 0x35, + 0x63, 0x35, 0x33, 0x36, 0x39, 0x66, 0x31, 0x63, + 0x31, 0x30, 0x34, 0x64, 0x34, 0x35, 0x64, 0x38, + 0x64, 0x38, 0x35, 0x65, 0x66, 0x61, 0x39, 0x62, + 0x30, 0x35, 0x37, 0x62, 0x35, 0x33, 0x62, 0x31, + 0x34, 0x62, 0x34, 0x62, 0x39, 0x62, 0x39, 0x33, + 0x39, 0x64, 0x64, 0x37, 0x34, 0x64, 0x65, 0x63, + 0x63, 0x35, 0x33, 0x32, 0x31, 0x0a, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x20, 0x37, 0x35, 0x62, + 0x66, 0x30, 0x63, 0x34, 0x37, 0x61, 0x65, 0x34, + 0x62, 0x62, 0x33, 0x30, 0x38, 0x65, 0x37, 0x63, + 0x63, 0x32, 0x34, 0x38, 0x32, 0x65, 0x32, 0x32, + 0x65, 0x66, 0x61, 0x65, 0x33, 0x37, 0x38, 0x37, + 0x61, 0x39, 0x36, 0x38, 0x34, 0x38, 0x62, 0x64, + 0x31, 0x37, 0x34, 0x39, 0x35, 0x36, 0x37, 0x31, + 0x34, 0x37, 0x31, 0x35, 0x32, 0x34, 0x36, 0x64, + 0x64, 0x62, 0x64, 0x35, 0x34, 0x0a, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55, + 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x30, 0x20, 0x2b, 0x30, + 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20, + 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72, + 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, + 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x30, 0x20, 0x2b, 0x30, 0x30, 0x30, + 0x30, 0x0a, 0x0a, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x20, 0x74, 0x72, 0x65, 0x65, 0x20, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x6d, 0x6d, 0x51, + 0x9a, 0xc9, 0x11, 0x76, 0x61, 0xa3, 0x89, 0x49, + 0xb7, 0xa1, 0x58, 0xc6, 0x1d, 0x8c, 0x33, 0x75, + 0x8d, 0x7e, 0x4d, 0x8e, 0x58, 0x91, 0xf8, 0x5c, + 0x57, 0xd9, 0x89, 0x9e, 0xb8, 0xd2, 0x9a, 0xd8, + 0xc9 +}; + +static const struct fast_pack fast_packs[] = { + { + .format_id = GIT_SHA1_FORMAT_ID, + .suffix = fast_pack_sha1_suffix, + .suffix_len = sizeof(fast_pack_sha1_suffix), + .commit_oid = "aac43daf40d0377af31aa9c798a4ae8a31b55c1d", + }, + { + .format_id = GIT_SHA256_FORMAT_ID, + .suffix = fast_pack_sha256_suffix, + .suffix_len = sizeof(fast_pack_sha256_suffix), + .commit_oid = "63c46ca51267b1d45be69a044bb84b4bf0559f09" + "d727f861d2ae94ddebdddbc9", + }, +}; + +/* + * Try the fast path for known blob sizes. Returns 1 if the pack was + * written from precomputed constants, 0 if the caller should fall + * through to the generic path. + */ +static int generate_fast_pack(const char *path, size_t blob_size, + const struct git_hash_algo *algo) +{ + const struct fast_pack *fp = NULL; + FILE *f; + size_t i; + + if (blob_size != FAST_PACK_4G1_BLOB_SIZE) + return 0; + + for (i = 0; i < ARRAY_SIZE(fast_packs); i++) { + if (fast_packs[i].format_id == algo->format_id) { + fp = &fast_packs[i]; + break; + } + } + if (!fp) + return 0; + + f = xfopen(path, "wb"); + + fwrite_or_die(f, fast_pack_prefix, sizeof(fast_pack_prefix)); + + /* First full block: 0xffff zero bytes (header already in prefix) */ + fwrite_or_die(f, zeros, BLOCK_SIZE); + + /* Remaining non-final full blocks */ + for (i = 1; i < FAST_PACK_4G1_N_FULL_BLOCKS; i++) { + fwrite_or_die(f, fast_pack_block_header, + sizeof(fast_pack_block_header)); + fwrite_or_die(f, zeros, BLOCK_SIZE); + } + + /* Final block (2 data bytes) + adler32 */ + fwrite_or_die(f, fast_pack_final_block, + sizeof(fast_pack_final_block)); + + /* Tree, commits, and pack checksum */ + fwrite_or_die(f, fp->suffix, fp->suffix_len); + + if (fclose(f)) + die_errno(_("could not close '%s'"), path); + + printf("%s\n", fp->commit_oid); + return 1; +} + +/* + * Generate a pack file with a single large (>4GB) reachable object. + * + * Creates: + * 1. A large blob (all NUL bytes) + * 2. A tree containing that blob as "file" + * 3. A commit using that tree + * 4. The empty tree + * 5. A child commit using the empty tree + * + * This is useful for testing that Git can handle objects larger than 4GB. + */ +static int generate_pack_with_large_object(const char *path, size_t blob_size, + const struct git_hash_algo *algo) +{ + FILE *f; + struct git_hash_ctx pack_ctx; + unsigned char pack_hash[GIT_MAX_RAWSZ]; + struct object_id blob_oid, tree_oid, commit_oid, empty_tree_oid, final_commit_oid; + struct strbuf buf = STRBUF_INIT; + const uint32_t object_count = 5; + struct pack_header pack_header = { + .hdr_signature = htonl(PACK_SIGNATURE), + .hdr_version = htonl(PACK_VERSION), + .hdr_entries = htonl(object_count), + }; + + if (generate_fast_pack(path, blob_size, algo)) + return 0; + + f = xfopen(path, "wb"); + + algo->init_fn(&pack_ctx); + + /* Write pack header */ + fwrite_or_die(f, &pack_header, sizeof(pack_header)); + algo->update_fn(&pack_ctx, &pack_header, sizeof(pack_header)); + + /* 1. Write the large blob */ + write_pack_object(f, &pack_ctx, OBJ_BLOB, NULL, blob_size, &blob_oid, algo); + + /* 2. Write tree containing the blob as "file" */ + strbuf_addf(&buf, "100644 file%c", '\0'); + strbuf_add(&buf, blob_oid.hash, algo->rawsz); + write_pack_object(f, &pack_ctx, OBJ_TREE, buf.buf, buf.len, &tree_oid, algo); + + /* 3. Write commit using that tree */ + strbuf_reset(&buf); + strbuf_addf(&buf, + "tree %s\n" + "author A U Thor 1234567890 +0000\n" + "committer C O Mitter 1234567890 +0000\n" + "\n" + "Large blob commit\n", + oid_to_hex(&tree_oid)); + write_pack_object(f, &pack_ctx, OBJ_COMMIT, buf.buf, buf.len, &commit_oid, algo); + + /* 4. Write the empty tree */ + write_pack_object(f, &pack_ctx, OBJ_TREE, "", 0, &empty_tree_oid, algo); + + /* 5. Write final commit using empty tree, with previous commit as parent */ + strbuf_reset(&buf); + strbuf_addf(&buf, + "tree %s\n" + "parent %s\n" + "author A U Thor 1234567890 +0000\n" + "committer C O Mitter 1234567890 +0000\n" + "\n" + "Empty tree commit\n", + oid_to_hex(&empty_tree_oid), + oid_to_hex(&commit_oid)); + write_pack_object(f, &pack_ctx, OBJ_COMMIT, buf.buf, buf.len, &final_commit_oid, algo); + + /* Write pack trailer (checksum) */ + algo->final_fn(pack_hash, &pack_ctx); + fwrite_or_die(f, pack_hash, algo->rawsz); + if (fclose(f)) + die_errno(_("could not close '%s'"), path); + + strbuf_release(&buf); + + /* Print the final commit OID so caller can set up refs */ + printf("%s\n", oid_to_hex(&final_commit_oid)); + + return 0; +} + +static int cmd__synthesize__pack(int argc, const char **argv, + const char *prefix UNUSED, + struct repository *repo) +{ + int non_git; + int reachable_large = 0; + const struct git_hash_algo *algo; + size_t blob_size; + uintmax_t blob_size_u; + const char *path; + const char * const usage[] = { + "test-tool synthesize pack " + "--reachable-large ", + NULL + }; + struct option options[] = { + OPT_BOOL(0, "reachable-large", &reachable_large, + N_("write a pack with a single reachable large blob")), + OPT_END() + }; + - setup_git_directory_gently(&non_git); ++ setup_git_directory_gently(the_repository, &non_git); + repo = the_repository; + algo = unsafe_hash_algo(repo->hash_algo); + + argc = parse_options(argc, argv, NULL, options, usage, + PARSE_OPT_KEEP_ARGV0); + if (argc != 3 || !reachable_large) + usage_with_options(usage, options); + + if (!git_parse_unsigned(argv[1], &blob_size_u, + maximum_unsigned_value_of_type(size_t))) + die(_("'%s' is not a valid blob size"), argv[1]); + blob_size = blob_size_u; + path = argv[2]; + + return !!generate_pack_with_large_object(path, blob_size, algo); +} + +int cmd__synthesize(int argc, const char **argv) +{ + const char *prefix = NULL; + char const * const synthesize_usage[] = { + "test-tool synthesize pack ", + NULL, + }; + parse_opt_subcommand_fn *fn = NULL; + struct option options[] = { + OPT_SUBCOMMAND("pack", &fn, cmd__synthesize__pack), + OPT_END() + }; + argc = parse_options(argc, argv, prefix, options, synthesize_usage, 0); + return !!fn(argc, argv, prefix, NULL); +}