]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ps/setup-wo-the-repository'
authorJunio C Hamano <gitster@pobox.com>
Wed, 27 May 2026 05:15:46 +0000 (14:15 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 May 2026 05:15:46 +0000 (14:15 +0900)
Many uses of the_repository has been updated to use a more
appropriate struct repository instance in setup.c codepath.

* 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

1  2 
builtin/grep.c
builtin/receive-pack.c
builtin/update-index.c
git.c
refs.c
remote-curl.c
revision.c
setup.c
setup.h
t/helper/test-bitmap.c
t/helper/test-synthesize.c

diff --cc builtin/grep.c
Simple merge
Simple merge
Simple merge
diff --cc git.c
Simple merge
diff --cc refs.c
Simple merge
diff --cc remote-curl.c
Simple merge
diff --cc revision.c
Simple merge
diff --cc setup.c
Simple merge
diff --cc setup.h
index b5bc5f280c34f09e107727ca4c146c25e34ffec8,9409326fe47c7098c8d4ca2d3b3d717b6432bc31..7878c9d2673d2f121c4ab865e5d82251282393b6
+++ b/setup.h
@@@ -134,36 -134,22 +134,37 @@@ enum 
   * links.  User relative paths are also returned as they are given,
   * except DWIM suffixing.
   */
- const char *enter_repo(const char *path, unsigned flags);
+ const char *enter_repo(struct repository *repo, const char *path, unsigned flags);
  
- const char *setup_git_directory_gently(int *);
- const char *setup_git_directory(void);
- char *prefix_path(const char *prefix, int len, const char *path);
- char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
+ const char *setup_git_directory_gently(struct repository *repo, int *);
+ const char *setup_git_directory(struct repository *repo);
+ char *prefix_path(struct repository *repo, const char *prefix, int len, const char *path);
+ char *prefix_path_gently(struct repository *repo, const char *prefix, int len, int *remaining, const char *path);
  
  int check_filename(const char *prefix, const char *name);
- void verify_filename(const char *prefix,
+ void verify_filename(struct repository *repo,
+                    const char *prefix,
                     const char *name,
                     int diagnose_misspelt_rev);
- void verify_non_filename(const char *prefix, const char *name);
- int path_inside_repo(const char *prefix, const char *path);
+ void verify_non_filename(struct repository *repo, const char *prefix, const char *name);
+ int path_inside_repo(struct repository *repo, const char *prefix, const char *path);
  
  void sanitize_stdfds(void);
 +
 +/*
 + * Daemonize the current process by forking and then exiting the parent
 + * process. Returns 0 when successful, in which case the parent process will
 + * have exited and it's the child process that continues to run the code.
 + * Otherwise, a negative error code is returned and the parent process will
 + * continue execution.
 + *
 + * Note that this function will also perform the following changes:
 + *
 + *   - Standard file descriptors in the child process are closed.
 + *   - The child process is made a session leader via setsid(3p).
 + *   - All tempfiles owned by the parent process are reassigned to the
 + *     daemonized child process.
 + */
  int daemonize(void);
  
  /*
index 381e9b58b2c42b26981d0d7b88efd0b9f9201b5e,d9b9a83b8f059629694f0b487a982ae3e2e899ec..b130832b81ecce5fde292e5a39206296d4ead89e
@@@ -38,114 -35,9 +38,114 @@@ static int bitmap_dump_pseudo_merge_obj
        return test_bitmap_pseudo_merge_objects(the_repository, n);
  }
  
 +static int add_packed_object(const struct object_id *oid,
 +                           struct packed_git *pack,
 +                           uint32_t pos,
 +                           void *_data)
 +{
 +      struct packing_data *packed = _data;
 +      struct object_entry *entry;
 +      struct object_info oi = OBJECT_INFO_INIT;
 +      enum object_type type;
 +
 +      oi.typep = &type;
 +
 +      entry = packlist_alloc(packed, oid);
 +      entry->idx.offset = nth_packed_object_offset(pack, pos);
 +      if (packed_object_info(pack, entry->idx.offset, &oi) < 0)
 +              die("could not get type of object %s",
 +                  oid_to_hex(oid));
 +      oe_set_type(entry, type);
 +      oe_set_in_pack(packed, entry, pack);
 +
 +      return 0;
 +}
 +
 +static int idx_oid_cmp(const void *va, const void *vb)
 +{
 +      const struct pack_idx_entry *a = *(const struct pack_idx_entry **)va;
 +      const struct pack_idx_entry *b = *(const struct pack_idx_entry **)vb;
 +
 +      return oidcmp(&a->oid, &b->oid);
 +}
 +
 +static int bitmap_write(const char *basename)
 +{
 +      struct packed_git *p = NULL;
 +      struct packing_data packed = { 0 };
 +      struct bitmap_writer writer;
 +      struct pack_idx_entry **index;
 +      struct strbuf buf = STRBUF_INIT;
 +      uint32_t i;
 +
 +      prepare_repo_settings(the_repository);
 +      repo_for_each_pack(the_repository, p) {
 +              if (!strcmp(pack_basename(p), basename))
 +                      break;
 +      }
 +
 +      if (!p)
 +              die("could not find pack '%s'", basename);
 +
 +      if (open_pack_index(p))
 +              die("cannot open pack index for '%s'", p->pack_name);
 +
 +      prepare_packing_data(the_repository, &packed);
 +
 +      for_each_object_in_pack(p, add_packed_object, &packed,
 +                              ODB_FOR_EACH_OBJECT_PACK_ORDER);
 +
 +      /*
 +       * Build the index array now that data.packed.objects[] is
 +       * fully allocated (packlist_alloc() may have reallocated it
 +       * during the loop above).
 +       */
 +      ALLOC_ARRAY(index, p->num_objects);
 +      for (i = 0; i < p->num_objects; i++)
 +              index[i] = &packed.objects[i].idx;
 +
 +      bitmap_writer_init(&writer, the_repository, &packed, NULL);
 +      bitmap_writer_build_type_index(&writer, index);
 +
 +      while (strbuf_getline_lf(&buf, stdin) != EOF) {
 +              struct object_id oid;
 +              struct commit *c;
 +
 +              if (get_oid_hex(buf.buf, &oid))
 +                      die("invalid OID: %s", buf.buf);
 +
 +              c = lookup_commit(the_repository, &oid);
 +              if (!c || repo_parse_commit(the_repository, c))
 +                      die("could not parse commit %s", buf.buf);
 +
 +              bitmap_writer_push_commit(&writer, c, 0);
 +      }
 +
 +      select_pseudo_merges(&writer);
 +      if (bitmap_writer_build(&writer) < 0)
 +              die("failed to build bitmaps");
 +
 +      bitmap_writer_set_checksum(&writer, p->hash);
 +
 +      QSORT(index, p->num_objects, idx_oid_cmp);
 +
 +      strbuf_reset(&buf);
 +      strbuf_addstr(&buf, p->pack_name);
 +      strbuf_strip_suffix(&buf, ".pack");
 +      strbuf_addstr(&buf, ".bitmap");
 +      bitmap_writer_finish(&writer, index, buf.buf, 0);
 +
 +      bitmap_writer_free(&writer);
 +      strbuf_release(&buf);
 +      free(index);
 +      clear_packing_data(&packed);
 +
 +      return 0;
 +}
 +
  int cmd__bitmap(int argc, const char **argv)
  {
-       setup_git_directory();
+       setup_git_directory(the_repository);
  
        if (argc == 2 && !strcmp(argv[1], "list-commits"))
                return bitmap_list_commits();
index 1f28ecf0f2c9eeb4dc75ef1b6f191b0a38927452,0000000000000000000000000000000000000000..3fa534fbdf8fab2d88312623b91041b24ae47af4
mode 100644,000000..100644
--- /dev/null
@@@ -1,541 -1,0 +1,541 @@@
-       setup_git_directory_gently(&non_git);
 +#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 <author@example.com> 1234567890 +0000\n"
 +                  "committer C O Mitter <committer@example.com> 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 <author@example.com> 1234567890 +0000\n"
 +                  "committer C O Mitter <committer@example.com> 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 <blob-size> <filename>",
 +              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 <options>",
 +              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);
 +}