#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) { 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)); git_hash_update(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)); git_hash_update(pack_ctx, block_header, 5); if (block_len) { fwrite_or_die(f, block_data, block_len); git_hash_update(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)); git_hash_update(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); git_hash_update(pack_ctx, pack_header, pack_header_len); /* Write the data as uncompressed zlib */ write_uncompressed_zlib(f, pack_ctx, data, len); git_hash_init(&ctx, algo); object_header_len = format_object_header(object_header, sizeof(object_header), type, len); git_hash_update(&ctx, object_header, object_header_len); if (data) git_hash_update(&ctx, data, len); else { for (size_t i = len / BLOCK_SIZE; i; i--) git_hash_update(&ctx, zeros, BLOCK_SIZE); git_hash_update(&ctx, zeros, len % BLOCK_SIZE); } git_hash_final_oid(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"); git_hash_init(&pack_ctx, algo); /* Write pack header */ fwrite_or_die(f, &pack_header, sizeof(pack_header)); git_hash_update(&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) */ git_hash_final(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(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); }