Merge branch 'js/larger-timestamps'
authorJunio C Hamano <gitster@pobox.com>
Tue, 16 May 2017 02:51:59 +0000 (11:51 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 16 May 2017 02:51:59 +0000 (11:51 +0900)
Some platforms have ulong that is smaller than time_t, and our
historical use of ulong for timestamp would mean they cannot
represent some timestamp that the platform allows. Invent a
separate and dedicated timestamp_t (so that we can distingiuish
timestamps and a vanilla ulongs, which along is already a good
move), and then declare uintmax_t is the type to be used as the
timestamp_t.

* js/larger-timestamps:
archive-tar: fix a sparse 'constant too large' warning
use uintmax_t for timestamps
date.c: abort if the system time cannot handle one of our timestamps
timestamp_t: a new data type for timestamps
PRItime: introduce a new "printf format" for timestamps
parse_timestamp(): specify explicitly where we parse timestamps
t0006 & t5000: skip "far in the future" test when time_t is too limited
t0006 & t5000: prepare for 64-bit timestamps
ref-filter: avoid using `unsigned long` for catch-all data type

16 files changed:
1  2 
archive-zip.c
builtin/am.c
builtin/fsck.c
builtin/gc.c
builtin/receive-pack.c
builtin/worktree.c
cache.h
config.c
fetch-pack.c
git-compat-util.h
refs.c
refs.h
refs/files-backend.c
t/helper/test-ref-store.c
t/test-lib.sh
wt-status.c
diff --combined archive-zip.c
index e81c5ac15a05d7d333005ff85fca767740693fc7,68df3d6440270c3049349548fd4ce7a2d2518039..27563e9e2602108997033e6fa79c660bc08aa863
  static int zip_date;
  static int zip_time;
  
 -static unsigned char *zip_dir;
 -static unsigned int zip_dir_size;
 +/* We only care about the "buf" part here. */
 +static struct strbuf zip_dir;
  
 -static unsigned int zip_offset;
 -static unsigned int zip_dir_offset;
 +static uintmax_t zip_offset;
  static uint64_t zip_dir_entries;
  
  static unsigned int max_creator_version;
  
 -#define ZIP_DIRECTORY_MIN_SIZE        (1024 * 1024)
  #define ZIP_STREAM    (1 <<  3)
  #define ZIP_UTF8      (1 << 11)
  
@@@ -45,11 -47,24 +45,11 @@@ struct zip_data_desc 
        unsigned char _end[1];
  };
  
 -struct zip_dir_header {
 +struct zip64_data_desc {
        unsigned char magic[4];
 -      unsigned char creator_version[2];
 -      unsigned char version[2];
 -      unsigned char flags[2];
 -      unsigned char compression_method[2];
 -      unsigned char mtime[2];
 -      unsigned char mdate[2];
        unsigned char crc32[4];
 -      unsigned char compressed_size[4];
 -      unsigned char size[4];
 -      unsigned char filename_length[2];
 -      unsigned char extra_length[2];
 -      unsigned char comment_length[2];
 -      unsigned char disk[2];
 -      unsigned char attr1[2];
 -      unsigned char attr2[4];
 -      unsigned char offset[4];
 +      unsigned char compressed_size[8];
 +      unsigned char size[8];
        unsigned char _end[1];
  };
  
@@@ -73,14 -88,6 +73,14 @@@ struct zip_extra_mtime 
        unsigned char _end[1];
  };
  
 +struct zip64_extra {
 +      unsigned char magic[2];
 +      unsigned char extra_size[2];
 +      unsigned char size[8];
 +      unsigned char compressed_size[8];
 +      unsigned char _end[1];
 +};
 +
  struct zip64_dir_trailer {
        unsigned char magic[4];
        unsigned char record_size[8];
@@@ -110,15 -117,11 +110,15 @@@ struct zip64_dir_trailer_locator 
   */
  #define ZIP_LOCAL_HEADER_SIZE offsetof(struct zip_local_header, _end)
  #define ZIP_DATA_DESC_SIZE    offsetof(struct zip_data_desc, _end)
 +#define ZIP64_DATA_DESC_SIZE  offsetof(struct zip64_data_desc, _end)
  #define ZIP_DIR_HEADER_SIZE   offsetof(struct zip_dir_header, _end)
  #define ZIP_DIR_TRAILER_SIZE  offsetof(struct zip_dir_trailer, _end)
  #define ZIP_EXTRA_MTIME_SIZE  offsetof(struct zip_extra_mtime, _end)
  #define ZIP_EXTRA_MTIME_PAYLOAD_SIZE \
        (ZIP_EXTRA_MTIME_SIZE - offsetof(struct zip_extra_mtime, flags))
 +#define ZIP64_EXTRA_SIZE      offsetof(struct zip64_extra, _end)
 +#define ZIP64_EXTRA_PAYLOAD_SIZE \
 +      (ZIP64_EXTRA_SIZE - offsetof(struct zip64_extra, size))
  #define ZIP64_DIR_TRAILER_SIZE        offsetof(struct zip64_dir_trailer, _end)
  #define ZIP64_DIR_TRAILER_RECORD_SIZE \
        (ZIP64_DIR_TRAILER_SIZE - \
@@@ -165,26 -168,6 +165,26 @@@ static void copy_le16_clamp(unsigned ch
        copy_le16(dest, clamp_max(n, 0xffff, clamped));
  }
  
 +static void copy_le32_clamp(unsigned char *dest, uint64_t n, int *clamped)
 +{
 +      copy_le32(dest, clamp_max(n, 0xffffffff, clamped));
 +}
 +
 +static int strbuf_add_le(struct strbuf *sb, size_t size, uintmax_t n)
 +{
 +      while (size-- > 0) {
 +              strbuf_addch(sb, n & 0xff);
 +              n >>= 8;
 +      }
 +      return -!!n;
 +}
 +
 +static uint32_t clamp32(uintmax_t n)
 +{
 +      const uintmax_t max = 0xffffffff;
 +      return (n < max) ? n : max;
 +}
 +
  static void *zlib_deflate_raw(void *data, unsigned long size,
                              int compression_level,
                              unsigned long *compressed_size)
@@@ -222,23 -205,23 +222,23 @@@ static void write_zip_data_desc(unsigne
                                unsigned long compressed_size,
                                unsigned long crc)
  {
 -      struct zip_data_desc trailer;
 -
 -      copy_le32(trailer.magic, 0x08074b50);
 -      copy_le32(trailer.crc32, crc);
 -      copy_le32(trailer.compressed_size, compressed_size);
 -      copy_le32(trailer.size, size);
 -      write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE);
 -}
 -
 -static void set_zip_dir_data_desc(struct zip_dir_header *header,
 -                                unsigned long size,
 -                                unsigned long compressed_size,
 -                                unsigned long crc)
 -{
 -      copy_le32(header->crc32, crc);
 -      copy_le32(header->compressed_size, compressed_size);
 -      copy_le32(header->size, size);
 +      if (size >= 0xffffffff || compressed_size >= 0xffffffff) {
 +              struct zip64_data_desc trailer;
 +              copy_le32(trailer.magic, 0x08074b50);
 +              copy_le32(trailer.crc32, crc);
 +              copy_le64(trailer.compressed_size, compressed_size);
 +              copy_le64(trailer.size, size);
 +              write_or_die(1, &trailer, ZIP64_DATA_DESC_SIZE);
 +              zip_offset += ZIP64_DATA_DESC_SIZE;
 +      } else {
 +              struct zip_data_desc trailer;
 +              copy_le32(trailer.magic, 0x08074b50);
 +              copy_le32(trailer.crc32, crc);
 +              copy_le32(trailer.compressed_size, compressed_size);
 +              copy_le32(trailer.size, size);
 +              write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE);
 +              zip_offset += ZIP_DATA_DESC_SIZE;
 +      }
  }
  
  static void set_zip_header_data_desc(struct zip_local_header *header,
@@@ -280,14 -263,12 +280,14 @@@ static int write_zip_entry(struct archi
                           unsigned int mode)
  {
        struct zip_local_header header;
 -      struct zip_dir_header dirent;
 +      uintmax_t offset = zip_offset;
        struct zip_extra_mtime extra;
 +      struct zip64_extra extra64;
 +      size_t header_extra_size = ZIP_EXTRA_MTIME_SIZE;
 +      int need_zip64_extra = 0;
        unsigned long attr2;
        unsigned long compressed_size;
        unsigned long crc;
 -      unsigned long direntsize;
        int method;
        unsigned char *out;
        void *deflated = NULL;
        int is_binary = -1;
        const char *path_without_prefix = path + args->baselen;
        unsigned int creator_version = 0;
 +      unsigned int version_needed = 10;
 +      size_t zip_dir_extra_size = ZIP_EXTRA_MTIME_SIZE;
 +      size_t zip64_dir_extra_payload_size = 0;
  
        crc = crc32(0, NULL, 0);
  
        extra.flags[0] = 1;     /* just mtime */
        copy_le32(extra.mtime, args->time);
  
 -      /* make sure we have enough free space in the dictionary */
 -      direntsize = ZIP_DIR_HEADER_SIZE + pathlen + ZIP_EXTRA_MTIME_SIZE;
 -      while (zip_dir_size < zip_dir_offset + direntsize) {
 -              zip_dir_size += ZIP_DIRECTORY_MIN_SIZE;
 -              zip_dir = xrealloc(zip_dir, zip_dir_size);
 -      }
 +      if (size > 0xffffffff || compressed_size > 0xffffffff)
 +              need_zip64_extra = 1;
 +      if (stream && size > 0x7fffffff)
 +              need_zip64_extra = 1;
  
 -      copy_le32(dirent.magic, 0x02014b50);
 -      copy_le16(dirent.creator_version, creator_version);
 -      copy_le16(dirent.version, 10);
 -      copy_le16(dirent.flags, flags);
 -      copy_le16(dirent.compression_method, method);
 -      copy_le16(dirent.mtime, zip_time);
 -      copy_le16(dirent.mdate, zip_date);
 -      set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
 -      copy_le16(dirent.filename_length, pathlen);
 -      copy_le16(dirent.extra_length, ZIP_EXTRA_MTIME_SIZE);
 -      copy_le16(dirent.comment_length, 0);
 -      copy_le16(dirent.disk, 0);
 -      copy_le32(dirent.attr2, attr2);
 -      copy_le32(dirent.offset, zip_offset);
 +      if (need_zip64_extra)
 +              version_needed = 45;
  
        copy_le32(header.magic, 0x04034b50);
 -      copy_le16(header.version, 10);
 +      copy_le16(header.version, version_needed);
        copy_le16(header.flags, flags);
        copy_le16(header.compression_method, method);
        copy_le16(header.mtime, zip_time);
        copy_le16(header.mdate, zip_date);
 -      set_zip_header_data_desc(&header, size, compressed_size, crc);
 +      if (need_zip64_extra) {
 +              set_zip_header_data_desc(&header, 0xffffffff, 0xffffffff, crc);
 +              header_extra_size += ZIP64_EXTRA_SIZE;
 +      } else {
 +              set_zip_header_data_desc(&header, size, compressed_size, crc);
 +      }
        copy_le16(header.filename_length, pathlen);
 -      copy_le16(header.extra_length, ZIP_EXTRA_MTIME_SIZE);
 +      copy_le16(header.extra_length, header_extra_size);
        write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE);
        zip_offset += ZIP_LOCAL_HEADER_SIZE;
        write_or_die(1, path, pathlen);
        zip_offset += pathlen;
        write_or_die(1, &extra, ZIP_EXTRA_MTIME_SIZE);
        zip_offset += ZIP_EXTRA_MTIME_SIZE;
 +      if (need_zip64_extra) {
 +              copy_le16(extra64.magic, 0x0001);
 +              copy_le16(extra64.extra_size, ZIP64_EXTRA_PAYLOAD_SIZE);
 +              copy_le64(extra64.size, size);
 +              copy_le64(extra64.compressed_size, compressed_size);
 +              write_or_die(1, &extra64, ZIP64_EXTRA_SIZE);
 +              zip_offset += ZIP64_EXTRA_SIZE;
 +      }
 +
        if (stream && method == 0) {
                unsigned char buf[STREAM_BUFFER_SIZE];
                ssize_t readlen;
                zip_offset += compressed_size;
  
                write_zip_data_desc(size, compressed_size, crc);
 -              zip_offset += ZIP_DATA_DESC_SIZE;
 -
 -              set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
        } else if (stream && method == 8) {
                unsigned char buf[STREAM_BUFFER_SIZE];
                ssize_t readlen;
                zip_offset += compressed_size;
  
                write_zip_data_desc(size, compressed_size, crc);
 -              zip_offset += ZIP_DATA_DESC_SIZE;
 -
 -              set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
        } else if (compressed_size > 0) {
                write_or_die(1, out, compressed_size);
                zip_offset += compressed_size;
        free(deflated);
        free(buffer);
  
 -      copy_le16(dirent.attr1, !is_binary);
 +      if (compressed_size > 0xffffffff || size > 0xffffffff ||
 +          offset > 0xffffffff) {
 +              if (compressed_size >= 0xffffffff)
 +                      zip64_dir_extra_payload_size += 8;
 +              if (size >= 0xffffffff)
 +                      zip64_dir_extra_payload_size += 8;
 +              if (offset >= 0xffffffff)
 +                      zip64_dir_extra_payload_size += 8;
 +              zip_dir_extra_size += 2 + 2 + zip64_dir_extra_payload_size;
 +      }
  
 -      memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);
 -      zip_dir_offset += ZIP_DIR_HEADER_SIZE;
 -      memcpy(zip_dir + zip_dir_offset, path, pathlen);
 -      zip_dir_offset += pathlen;
 -      memcpy(zip_dir + zip_dir_offset, &extra, ZIP_EXTRA_MTIME_SIZE);
 -      zip_dir_offset += ZIP_EXTRA_MTIME_SIZE;
 +      strbuf_add_le(&zip_dir, 4, 0x02014b50); /* magic */
 +      strbuf_add_le(&zip_dir, 2, creator_version);
 +      strbuf_add_le(&zip_dir, 2, version_needed);
 +      strbuf_add_le(&zip_dir, 2, flags);
 +      strbuf_add_le(&zip_dir, 2, method);
 +      strbuf_add_le(&zip_dir, 2, zip_time);
 +      strbuf_add_le(&zip_dir, 2, zip_date);
 +      strbuf_add_le(&zip_dir, 4, crc);
 +      strbuf_add_le(&zip_dir, 4, clamp32(compressed_size));
 +      strbuf_add_le(&zip_dir, 4, clamp32(size));
 +      strbuf_add_le(&zip_dir, 2, pathlen);
 +      strbuf_add_le(&zip_dir, 2, zip_dir_extra_size);
 +      strbuf_add_le(&zip_dir, 2, 0);          /* comment length */
 +      strbuf_add_le(&zip_dir, 2, 0);          /* disk */
 +      strbuf_add_le(&zip_dir, 2, !is_binary);
 +      strbuf_add_le(&zip_dir, 4, attr2);
 +      strbuf_add_le(&zip_dir, 4, clamp32(offset));
 +      strbuf_add(&zip_dir, path, pathlen);
 +      strbuf_add(&zip_dir, &extra, ZIP_EXTRA_MTIME_SIZE);
 +      if (zip64_dir_extra_payload_size) {
 +              strbuf_add_le(&zip_dir, 2, 0x0001);     /* magic */
 +              strbuf_add_le(&zip_dir, 2, zip64_dir_extra_payload_size);
 +              if (size >= 0xffffffff)
 +                      strbuf_add_le(&zip_dir, 8, size);
 +              if (compressed_size >= 0xffffffff)
 +                      strbuf_add_le(&zip_dir, 8, compressed_size);
 +              if (offset >= 0xffffffff)
 +                      strbuf_add_le(&zip_dir, 8, offset);
 +      }
        zip_dir_entries++;
  
        return 0;
@@@ -558,12 -510,12 +558,12 @@@ static void write_zip64_trailer(void
        copy_le32(trailer64.directory_start_disk, 0);
        copy_le64(trailer64.entries_on_this_disk, zip_dir_entries);
        copy_le64(trailer64.entries, zip_dir_entries);
 -      copy_le64(trailer64.size, zip_dir_offset);
 +      copy_le64(trailer64.size, zip_dir.len);
        copy_le64(trailer64.offset, zip_offset);
  
        copy_le32(locator64.magic, 0x07064b50);
        copy_le32(locator64.disk, 0);
 -      copy_le64(locator64.offset, zip_offset + zip_dir_offset);
 +      copy_le64(locator64.offset, zip_offset + zip_dir.len);
        copy_le32(locator64.number_of_disks, 1);
  
        write_or_die(1, &trailer64, ZIP64_DIR_TRAILER_SIZE);
@@@ -581,11 -533,11 +581,11 @@@ static void write_zip_trailer(const uns
        copy_le16_clamp(trailer.entries_on_this_disk, zip_dir_entries,
                        &clamped);
        copy_le16_clamp(trailer.entries, zip_dir_entries, &clamped);
 -      copy_le32(trailer.size, zip_dir_offset);
 -      copy_le32(trailer.offset, zip_offset);
 +      copy_le32(trailer.size, zip_dir.len);
 +      copy_le32_clamp(trailer.offset, zip_offset, &clamped);
        copy_le16(trailer.comment_length, sha1 ? GIT_SHA1_HEXSZ : 0);
  
 -      write_or_die(1, zip_dir, zip_dir_offset);
 +      write_or_die(1, zip_dir.buf, zip_dir.len);
        if (clamped)
                write_zip64_trailer();
        write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE);
                write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
  }
  
- static void dos_time(time_t *time, int *dos_date, int *dos_time)
+ static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
  {
-       struct tm *t = localtime(time);
+       time_t time;
+       struct tm *t;
+       if (date_overflows(*timestamp))
+               die("timestamp too large for this system: %"PRItime,
+                   *timestamp);
+       time = (time_t)*timestamp;
+       t = localtime(&time);
+       *timestamp = time;
  
        *dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
                    (t->tm_year + 1900 - 1980) * 512;
@@@ -616,13 -576,14 +624,13 @@@ static int write_zip_archive(const stru
  
        dos_time(&args->time, &zip_date, &zip_time);
  
 -      zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
 -      zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
 +      strbuf_init(&zip_dir, 0);
  
        err = write_archive_entries(args, write_zip_entry);
        if (!err)
                write_zip_trailer(args->commit_sha1);
  
 -      free(zip_dir);
 +      strbuf_release(&zip_dir);
  
        return err;
  }
diff --combined builtin/am.c
index 17c80329c23beb2aa60621aa3dc10d95153c61e3,89914ed875709f0263ff7f7a6c25157d1d30fbd2..a63935cc83409ef99551a69da79d52ac297fe83a
@@@ -134,15 -134,17 +134,15 @@@ struct am_state 
  };
  
  /**
 - * Initializes am_state with the default values. The state directory is set to
 - * dir.
 + * Initializes am_state with the default values.
   */
 -static void am_state_init(struct am_state *state, const char *dir)
 +static void am_state_init(struct am_state *state)
  {
        int gpgsign;
  
        memset(state, 0, sizeof(*state));
  
 -      assert(dir);
 -      state->dir = xstrdup(dir);
 +      state->dir = git_pathdup("rebase-apply");
  
        state->prec = 4;
  
@@@ -760,18 -762,14 +760,18 @@@ static int split_mail_conv(mail_conv_f
                mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
  
                out = fopen(mail, "w");
 -              if (!out)
 +              if (!out) {
 +                      if (in != stdin)
 +                              fclose(in);
                        return error_errno(_("could not open '%s' for writing"),
                                           mail);
 +              }
  
                ret = fn(out, in, keep_cr);
  
                fclose(out);
 -              fclose(in);
 +              if (in != stdin)
 +                      fclose(in);
  
                if (ret)
                        return error(_("could not parse patch '%s'"), *paths);
@@@ -879,12 -877,12 +879,12 @@@ static int hg_patch_to_mail(FILE *out, 
                if (skip_prefix(sb.buf, "# User ", &str))
                        fprintf(out, "From: %s\n", str);
                else if (skip_prefix(sb.buf, "# Date ", &str)) {
-                       unsigned long timestamp;
+                       timestamp_t timestamp;
                        long tz, tz2;
                        char *end;
  
                        errno = 0;
-                       timestamp = strtoul(str, &end, 10);
+                       timestamp = parse_timestamp(str, &end, 10);
                        if (errno)
                                return error(_("invalid timestamp"));
  
@@@ -1183,39 -1181,42 +1183,39 @@@ static void NORETURN die_user_resolve(c
        exit(128);
  }
  
 -static void am_signoff(struct strbuf *sb)
 +/**
 + * Appends signoff to the "msg" field of the am_state.
 + */
 +static void am_append_signoff(struct am_state *state)
  {
        char *cp;
        struct strbuf mine = STRBUF_INIT;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
  
 -      /* Does it end with our own sign-off? */
 +      /* our sign-off */
        strbuf_addf(&mine, "\n%s%s\n",
                    sign_off_header,
                    fmt_name(getenv("GIT_COMMITTER_NAME"),
                             getenv("GIT_COMMITTER_EMAIL")));
 -      if (mine.len < sb->len &&
 -          !strcmp(mine.buf, sb->buf + sb->len - mine.len))
 +
 +      /* Does sb end with it already? */
 +      if (mine.len < sb.len &&
 +          !strcmp(mine.buf, sb.buf + sb.len - mine.len))
                goto exit; /* no need to duplicate */
  
        /* Does it have any Signed-off-by: in the text */
 -      for (cp = sb->buf;
 +      for (cp = sb.buf;
             cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL;
             cp = strchr(cp, '\n')) {
 -              if (sb->buf == cp || cp[-1] == '\n')
 +              if (sb.buf == cp || cp[-1] == '\n')
                        break;
        }
  
 -      strbuf_addstr(sb, mine.buf + !!cp);
 +      strbuf_addstr(&sb, mine.buf + !!cp);
  exit:
        strbuf_release(&mine);
 -}
 -
 -/**
 - * Appends signoff to the "msg" field of the am_state.
 - */
 -static void am_append_signoff(struct am_state *state)
 -{
 -      struct strbuf sb = STRBUF_INIT;
 -
 -      strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
 -      am_signoff(&sb);
        state->msg = strbuf_detach(&sb, &state->msg_len);
  }
  
@@@ -1320,6 -1321,9 +1320,6 @@@ static int parse_mail(struct am_state *
        strbuf_addbuf(&msg, &mi.log_message);
        strbuf_stripspace(&msg, 0);
  
 -      if (state->signoff)
 -              am_signoff(&msg);
 -
        assert(!state->author_name);
        state->author_name = strbuf_detach(&author_name, NULL);
  
@@@ -1372,33 -1376,40 +1372,33 @@@ static int get_mail_commit_oid(struct o
   */
  static void get_commit_info(struct am_state *state, struct commit *commit)
  {
 -      const char *buffer, *ident_line, *author_date, *msg;
 +      const char *buffer, *ident_line, *msg;
        size_t ident_len;
 -      struct ident_split ident_split;
 -      struct strbuf sb = STRBUF_INIT;
 +      struct ident_split id;
  
        buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
  
        ident_line = find_commit_header(buffer, "author", &ident_len);
  
 -      if (split_ident_line(&ident_split, ident_line, ident_len) < 0) {
 -              strbuf_add(&sb, ident_line, ident_len);
 -              die(_("invalid ident line: %s"), sb.buf);
 -      }
 +      if (split_ident_line(&id, ident_line, ident_len) < 0)
 +              die(_("invalid ident line: %.*s"), (int)ident_len, ident_line);
  
        assert(!state->author_name);
 -      if (ident_split.name_begin) {
 -              strbuf_add(&sb, ident_split.name_begin,
 -                      ident_split.name_end - ident_split.name_begin);
 -              state->author_name = strbuf_detach(&sb, NULL);
 -      } else
 +      if (id.name_begin)
 +              state->author_name =
 +                      xmemdupz(id.name_begin, id.name_end - id.name_begin);
 +      else
                state->author_name = xstrdup("");
  
        assert(!state->author_email);
 -      if (ident_split.mail_begin) {
 -              strbuf_add(&sb, ident_split.mail_begin,
 -                      ident_split.mail_end - ident_split.mail_begin);
 -              state->author_email = strbuf_detach(&sb, NULL);
 -      } else
 +      if (id.mail_begin)
 +              state->author_email =
 +                      xmemdupz(id.mail_begin, id.mail_end - id.mail_begin);
 +      else
                state->author_email = xstrdup("");
  
 -      author_date = show_ident_date(&ident_split, DATE_MODE(NORMAL));
 -      strbuf_addstr(&sb, author_date);
        assert(!state->author_date);
 -      state->author_date = strbuf_detach(&sb, NULL);
 +      state->author_date = xstrdup(show_ident_date(&id, DATE_MODE(NORMAL)));
  
        assert(!state->msg);
        msg = strstr(buffer, "\n\n");
                die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
        state->msg = xstrdup(msg + 2);
        state->msg_len = strlen(state->msg);
 +      unuse_commit_buffer(commit, buffer);
  }
  
  /**
@@@ -1838,9 -1848,6 +1838,9 @@@ static void am_run(struct am_state *sta
                        if (skip)
                                goto next; /* mail should be skipped */
  
 +                      if (state->signoff)
 +                              am_append_signoff(state);
 +
                        write_author_script(state);
                        write_commit_msg(state);
                }
@@@ -2315,7 -2322,7 +2315,7 @@@ int cmd_am(int argc, const char **argv
  
        git_config(git_am_config, NULL);
  
 -      am_state_init(&state, git_path("rebase-apply"));
 +      am_state_init(&state);
  
        in_progress = am_in_progress(&state);
        if (in_progress)
diff --combined builtin/fsck.c
index b5e13a45560f9338a65191c22d213f33052bf9b9,ea3a9f8a70a2f87a45c5549e4585cc1f877d8433..32a32e55c8539372c11a36eba85f3bf14f0e221a
@@@ -397,7 -397,7 +397,7 @@@ static int fsck_obj_buffer(const unsign
  static int default_refs;
  
  static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
-       unsigned long timestamp)
+       timestamp_t timestamp)
  {
        struct object *obj;
  
                        if (timestamp && name_objects)
                                add_decoration(fsck_walk_options.object_names,
                                        obj,
-                                       xstrfmt("%s@{%ld}", refname, timestamp));
+                                       xstrfmt("%s@{%"PRItime"}", refname, timestamp));
                        obj->used = 1;
                        mark_object_reachable(obj);
                } else {
  }
  
  static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
-               const char *email, unsigned long timestamp, int tz,
+               const char *email, timestamp_t timestamp, int tz,
                const char *message, void *cb_data)
  {
        const char *refname = cb_data;
@@@ -771,7 -771,6 +771,7 @@@ int cmd_fsck(int argc, const char **arg
        }
  
        if (keep_cache_objects) {
 +              verify_index_checksum = 1;
                read_cache();
                for (i = 0; i < active_nr; i++) {
                        unsigned int mode;
diff --combined builtin/gc.c
index 91f7696a85ec974d0ff5a1591cc404ed99efb7d7,cb1e20aae4adfe89dc2cda2d418f6d9d5d45c899..f484eda43ca06046924931d2055b50c2f89ea8d7
@@@ -33,7 -33,7 +33,7 @@@ static int aggressive_window = 250
  static int gc_auto_threshold = 6700;
  static int gc_auto_pack_limit = 50;
  static int detach_auto = 1;
- static unsigned long gc_log_expire_time;
+ static timestamp_t gc_log_expire_time;
  static const char *gc_log_expire = "1.day.ago";
  static const char *prune_expire = "2.weeks.ago";
  static const char *prune_worktrees_expire = "3.months.ago";
@@@ -232,7 -232,7 +232,7 @@@ static int need_to_gc(void
  static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
  {
        static struct lock_file lock;
 -      char my_host[128];
 +      char my_host[HOST_NAME_MAX + 1];
        struct strbuf sb = STRBUF_INIT;
        struct stat st;
        uintmax_t pid;
                /* already locked */
                return NULL;
  
 -      if (gethostname(my_host, sizeof(my_host)))
 +      if (xgethostname(my_host, sizeof(my_host)))
                xsnprintf(my_host, sizeof(my_host), "unknown");
  
        pidfile_path = git_pathdup("gc.pid");
        fd = hold_lock_file_for_update(&lock, pidfile_path,
                                       LOCK_DIE_ON_ERROR);
        if (!force) {
 -              static char locking_host[128];
 +              static char locking_host[HOST_NAME_MAX + 1];
 +              static char *scan_fmt;
                int should_exit;
 +
 +              if (!scan_fmt)
 +                      scan_fmt = xstrfmt("%s %%%dc", "%"SCNuMAX, HOST_NAME_MAX);
                fp = fopen(pidfile_path, "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =
                         * running.
                         */
                        time(NULL) - st.st_mtime <= 12 * 3600 &&
 -                      fscanf(fp, "%"SCNuMAX" %127c", &pid, locking_host) == 2 &&
 +                      fscanf(fp, scan_fmt, &pid, locking_host) == 2 &&
                        /* be gentle to concurrent "gc" on remote hosts */
                        (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM);
                if (fp != NULL)
diff --combined builtin/receive-pack.c
index f96834f42c9849746b64c20c84869232cbac7367,b4e22f44497fdf076226c2aa4e9f52f5ec907d80..0bb36d584d7e97ef0234c932d27e8312c1461107
@@@ -78,7 -78,7 +78,7 @@@ static const char *NONCE_OK = "OK"
  static const char *NONCE_SLOP = "SLOP";
  static const char *nonce_status;
  static long nonce_stamp_slop;
- static unsigned long nonce_stamp_slop_limit;
+ static timestamp_t nonce_stamp_slop_limit;
  static struct ref_transaction *transaction;
  
  static enum {
@@@ -454,17 -454,17 +454,17 @@@ static void hmac_sha1(unsigned char *ou
        git_SHA1_Final(out, &ctx);
  }
  
- static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+ static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
  {
        struct strbuf buf = STRBUF_INIT;
        unsigned char sha1[20];
  
-       strbuf_addf(&buf, "%s:%lu", path, stamp);
+       strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
        hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
        strbuf_release(&buf);
  
        /* RFC 2104 5. HMAC-SHA1-80 */
-       strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+       strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1));
        return strbuf_detach(&buf, NULL);
  }
  
@@@ -496,7 -496,7 +496,7 @@@ static char *find_header(const char *ms
  static const char *check_nonce(const char *buf, size_t len)
  {
        char *nonce = find_header(buf, len, "nonce");
-       unsigned long stamp, ostamp;
+       timestamp_t stamp, ostamp;
        char *bohmac, *expect = NULL;
        const char *retval = NONCE_BAD;
  
                retval = NONCE_BAD;
                goto leave;
        }
-       stamp = strtoul(nonce, &bohmac, 10);
+       stamp = parse_timestamp(nonce, &bohmac, 10);
        if (bohmac == nonce || bohmac[0] != '-') {
                retval = NONCE_BAD;
                goto leave;
         * would mean it was issued by another server with its clock
         * skewed in the future.
         */
-       ostamp = strtoul(push_cert_nonce, NULL, 10);
+       ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
        nonce_stamp_slop = (long)ostamp - (long)stamp;
  
        if (nonce_stamp_slop_limit &&
@@@ -772,6 -772,7 +772,6 @@@ static int run_update_hook(struct comma
        proc.stdout_to_stderr = 1;
        proc.err = use_sideband ? -1 : 0;
        proc.argv = argv;
 -      proc.env = tmp_objdir_env(tmp_objdir);
  
        code = start_command(&proc);
        if (code)
@@@ -1697,12 -1698,12 +1697,12 @@@ static const char *unpack(int err_fd, s
                if (status)
                        return "unpack-objects abnormal exit";
        } else {
 -              char hostname[256];
 +              char hostname[HOST_NAME_MAX + 1];
  
                argv_array_pushl(&child.args, "index-pack", "--stdin", NULL);
                push_header_arg(&child.args, &hdr);
  
 -              if (gethostname(hostname, sizeof(hostname)))
 +              if (xgethostname(hostname, sizeof(hostname)))
                        xsnprintf(hostname, sizeof(hostname), "localhost");
                argv_array_pushf(&child.args,
                                 "--keep=receive-pack %"PRIuMAX" on %s",
diff --combined builtin/worktree.c
index 1722a9bdc2a07ce3d98921299c6b57d03a50e870,74f9b18d40c507db921efb4196710499b7c7f54a..11f90d6e45f44f96f637cc976163075181c78fac
@@@ -24,14 -24,13 +24,14 @@@ struct add_opts 
        int force;
        int detach;
        int checkout;
 +      int keep_locked;
        const char *new_branch;
        int force_new_branch;
  };
  
  static int show_only;
  static int verbose;
- static unsigned long expire;
+ static timestamp_t expire;
  
  static int prune_worktree(const char *id, struct strbuf *reason)
  {
@@@ -107,7 -106,8 +107,7 @@@ static void prune_worktrees(void
                        printf("%s\n", reason.buf);
                if (show_only)
                        continue;
 -              strbuf_reset(&path);
 -              strbuf_addstr(&path, git_path("worktrees/%s", d->d_name));
 +              git_path_buf(&path, "worktrees/%s", d->d_name);
                ret = remove_dir_recursively(&path, 0);
                if (ret < 0 && errno == ENOTDIR)
                        ret = unlink(path.buf);
@@@ -131,7 -131,7 +131,7 @@@ static int prune(int ac, const char **a
                OPT_END()
        };
  
-       expire = ULONG_MAX;
+       expire = TIME_MAX;
        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
        if (ac)
                usage_with_options(worktree_usage, options);
@@@ -215,7 -215,8 +215,7 @@@ static int add_worktree(const char *pat
        }
  
        name = worktree_basename(path, &len);
 -      strbuf_addstr(&sb_repo,
 -                    git_path("worktrees/%.*s", (int)(path + len - name), name));
 +      git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name);
        len = sb_repo.len;
        if (safe_create_leading_directories_const(sb_repo.buf))
                die_errno(_("could not create leading directories of '%s'"),
         * after the preparation is over.
         */
        strbuf_addf(&sb, "%s/locked", sb_repo.buf);
 -      write_file(sb.buf, "initializing");
 +      if (!opts->keep_locked)
 +              write_file(sb.buf, "initializing");
 +      else
 +              write_file(sb.buf, "added with --lock");
  
        strbuf_addf(&sb_git, "%s/.git", path);
        if (safe_create_leading_directories_const(sb_git.buf))
        junk_git_dir = NULL;
  
  done:
 -      strbuf_reset(&sb);
 -      strbuf_addf(&sb, "%s/locked", sb_repo.buf);
 -      unlink_or_warn(sb.buf);
 +      if (ret || !opts->keep_locked) {
 +              strbuf_reset(&sb);
 +              strbuf_addf(&sb, "%s/locked", sb_repo.buf);
 +              unlink_or_warn(sb.buf);
 +      }
        argv_array_clear(&child_env);
        strbuf_release(&sb);
        strbuf_release(&symref);
@@@ -332,7 -328,6 +332,7 @@@ static int add(int ac, const char **av
                           N_("create or reset a branch")),
                OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
                OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
 +              OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
                OPT_END()
        };
  
diff --combined cache.h
index e1f0e182ad011d5a3b6912f48b597dcc6d1070b0,f3a02993b20346ddaeb87d90cf49e1c1031f92d8..188811920ccf5b389332db252e389ae17e88d59f
+++ b/cache.h
@@@ -599,7 -599,6 +599,7 @@@ extern int write_locked_index(struct in
  extern int discard_index(struct index_state *);
  extern int unmerged_index(const struct index_state *);
  extern int verify_path(const char *path);
 +extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
  extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
  extern void adjust_dirname_case(struct index_state *istate, char *name);
  extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
@@@ -711,8 -710,6 +711,8 @@@ extern void update_index_if_able(struc
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
 +extern int verify_index_checksum;
 +
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
  extern int trust_ctime;
@@@ -1167,7 -1164,7 +1167,7 @@@ typedef int create_file_fn(const char *
  int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
  
  int mkdir_in_gitdir(const char *path);
 -extern char *expand_user_path(const char *path);
 +extern char *expand_user_path(const char *path, int real_home);
  const char *enter_repo(const char *path, int strict);
  static inline int is_absolute_path(const char *path)
  {
@@@ -1479,18 -1476,18 +1479,18 @@@ struct date_mode 
  #define DATE_MODE(t) date_mode_from_type(DATE_##t)
  struct date_mode *date_mode_from_type(enum date_mode_type type);
  
- const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
- void show_date_relative(unsigned long time, int tz, const struct timeval *now,
+ const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
+ void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
                        struct strbuf *timebuf);
  int parse_date(const char *date, struct strbuf *out);
- int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
- int parse_expiry_date(const char *date, unsigned long *timestamp);
+ int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
+ int parse_expiry_date(const char *date, timestamp_t *timestamp);
  void datestamp(struct strbuf *out);
  #define approxidate(s) approxidate_careful((s), NULL)
unsigned long approxidate_careful(const char *, int *);
unsigned long approxidate_relative(const char *date, const struct timeval *now);
timestamp_t approxidate_careful(const char *, int *);
timestamp_t approxidate_relative(const char *date, const struct timeval *now);
  void parse_date_format(const char *format, struct date_mode *mode);
- int date_overflows(unsigned long date);
+ int date_overflows(timestamp_t date);
  
  #define IDENT_STRICT         1
  #define IDENT_NO_DATE        2
@@@ -1892,11 -1889,6 +1892,11 @@@ enum config_origin_type 
        CONFIG_ORIGIN_CMDLINE
  };
  
 +struct config_options {
 +      unsigned int respect_includes : 1;
 +      const char *git_dir;
 +};
 +
  typedef int (*config_fn_t)(const char *, const char *, void *);
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
@@@ -1910,13 -1902,12 +1910,13 @@@ extern void read_early_config(config_fn
  extern void git_config(config_fn_t fn, void *);
  extern int git_config_with_options(config_fn_t fn, void *,
                                   struct git_config_source *config_source,
 -                                 int respect_includes);
 +                                 const struct config_options *opts);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_parse_maybe_bool(const char *);
  extern int git_config_int(const char *, const char *);
  extern int64_t git_config_int64(const char *, const char *);
  extern unsigned long git_config_ulong(const char *, const char *);
 +extern ssize_t git_config_ssize_t(const char *, const char *);
  extern int git_config_bool_or_int(const char *, const char *, int *);
  extern int git_config_bool(const char *, const char *);
  extern int git_config_maybe_bool(const char *, const char *);
@@@ -1963,7 -1954,6 +1963,7 @@@ struct config_include_data 
        int depth;
        config_fn_t fn;
        void *data;
 +      const struct config_options *opts;
  };
  #define CONFIG_INCLUDE_INIT { 0 }
  extern int git_config_include(const char *name, const char *value, void *data);
diff --combined config.c
index b4a3205da32faf43db1ab990f08c0bb941af87d0,3247bfaa0205eaab4ad6282e4661ed1a637215f0..bb4d735701928d7f648c33ab371c04e3b7af12ad
+++ b/config.c
@@@ -135,7 -135,7 +135,7 @@@ static int handle_path_include(const ch
        if (!path)
                return config_error_nonbool("include.path");
  
 -      expanded = expand_user_path(path);
 +      expanded = expand_user_path(path, 0);
        if (!expanded)
                return error("could not expand include path '%s'", path);
        path = expanded;
@@@ -177,7 -177,7 +177,7 @@@ static int prepare_include_condition_pa
        char *expanded;
        int prefix = 0;
  
 -      expanded = expand_user_path(pat->buf);
 +      expanded = expand_user_path(pat->buf, 1);
        if (expanded) {
                strbuf_reset(pat);
                strbuf_addstr(pat, expanded);
                        return error(_("relative config include "
                                       "conditionals must come from files"));
  
 -              strbuf_add_absolute_path(&path, cf->path);
 +              strbuf_realpath(&path, cf->path, 1);
                slash = find_last_dir_sep(path.buf);
                if (!slash)
                        die("BUG: how is this possible?");
        return prefix;
  }
  
 -static int include_by_gitdir(const char *cond, size_t cond_len, int icase)
 +static int include_by_gitdir(const struct config_options *opts,
 +                           const char *cond, size_t cond_len, int icase)
  {
        struct strbuf text = STRBUF_INIT;
        struct strbuf pattern = STRBUF_INIT;
        int ret = 0, prefix;
 +      const char *git_dir;
  
 -      strbuf_add_absolute_path(&text, get_git_dir());
 +      if (opts->git_dir)
 +              git_dir = opts->git_dir;
 +      else if (have_git_dir())
 +              git_dir = get_git_dir();
 +      else
 +              goto done;
 +
 +      strbuf_realpath(&text, git_dir, 1);
        strbuf_add(&pattern, cond, cond_len);
        prefix = prepare_include_condition_pattern(&pattern);
  
@@@ -251,14 -242,13 +251,14 @@@ done
        return ret;
  }
  
 -static int include_condition_is_true(const char *cond, size_t cond_len)
 +static int include_condition_is_true(const struct config_options *opts,
 +                                   const char *cond, size_t cond_len)
  {
  
        if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
 -              return include_by_gitdir(cond, cond_len, 0);
 +              return include_by_gitdir(opts, cond, cond_len, 0);
        else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
 -              return include_by_gitdir(cond, cond_len, 1);
 +              return include_by_gitdir(opts, cond, cond_len, 1);
  
        /* unknown conditionals are always false */
        return 0;
@@@ -283,7 -273,7 +283,7 @@@ int git_config_include(const char *var
                ret = handle_path_include(value, inc);
  
        if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
 -          (cond && include_condition_is_true(cond, cond_len)) &&
 +          (cond && include_condition_is_true(inc->opts, cond, cond_len)) &&
            !strcmp(key, "path"))
                ret = handle_path_include(value, inc);
  
@@@ -844,15 -834,6 +844,15 @@@ int git_parse_ulong(const char *value, 
        return 1;
  }
  
 +static int git_parse_ssize_t(const char *value, ssize_t *ret)
 +{
 +      intmax_t tmp;
 +      if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
 +              return 0;
 +      *ret = tmp;
 +      return 1;
 +}
 +
  NORETURN
  static void die_bad_number(const char *name, const char *value)
  {
@@@ -911,14 -892,6 +911,14 @@@ unsigned long git_config_ulong(const ch
        return ret;
  }
  
 +ssize_t git_config_ssize_t(const char *name, const char *value)
 +{
 +      ssize_t ret;
 +      if (!git_parse_ssize_t(value, &ret))
 +              die_bad_number(name, value);
 +      return ret;
 +}
 +
  int git_parse_maybe_bool(const char *value)
  {
        if (!value)
@@@ -975,7 -948,7 +975,7 @@@ int git_config_pathname(const char **de
  {
        if (!value)
                return config_error_nonbool(var);
 -      *dest = expand_user_path(value);
 +      *dest = expand_user_path(value, 0);
        if (!*dest)
                die(_("failed to expand user dir in: '%s'"), value);
        return 0;
@@@ -1521,20 -1494,12 +1521,20 @@@ int git_config_system(void
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
  }
  
 -static int do_git_config_sequence(config_fn_t fn, void *data)
 +static int do_git_config_sequence(const struct config_options *opts,
 +                                config_fn_t fn, void *data)
  {
        int ret = 0;
        char *xdg_config = xdg_config_home("config");
 -      char *user_config = expand_user_path("~/.gitconfig");
 -      char *repo_config = have_git_dir() ? git_pathdup("config") : NULL;
 +      char *user_config = expand_user_path("~/.gitconfig", 0);
 +      char *repo_config;
 +
 +      if (opts->git_dir)
 +              repo_config = mkpathdup("%s/config", opts->git_dir);
 +      else if (have_git_dir())
 +              repo_config = git_pathdup("config");
 +      else
 +              repo_config = NULL;
  
        current_parsing_scope = CONFIG_SCOPE_SYSTEM;
        if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
  
  int git_config_with_options(config_fn_t fn, void *data,
                            struct git_config_source *config_source,
 -                          int respect_includes)
 +                          const struct config_options *opts)
  {
        struct config_include_data inc = CONFIG_INCLUDE_INIT;
  
 -      if (respect_includes) {
 +      if (opts->respect_includes) {
                inc.fn = fn;
                inc.data = data;
 +              inc.opts = opts;
                fn = git_config_include;
                data = &inc;
        }
        else if (config_source && config_source->blob)
                return git_config_from_blob_ref(fn, config_source->blob, data);
  
 -      return do_git_config_sequence(fn, data);
 +      return do_git_config_sequence(opts, fn, data);
  }
  
  static void git_config_raw(config_fn_t fn, void *data)
  {
 -      if (git_config_with_options(fn, data, NULL, 1) < 0)
 +      struct config_options opts = {0};
 +
 +      opts.respect_includes = 1;
 +      if (git_config_with_options(fn, data, NULL, &opts) < 0)
                /*
                 * git_config_with_options() normally returns only
                 * zero, as most errors are fatal, and
@@@ -1636,13 -1597,10 +1636,13 @@@ static void configset_iter(struct confi
  
  void read_early_config(config_fn_t cb, void *data)
  {
 +      struct config_options opts = {0};
        struct strbuf buf = STRBUF_INIT;
  
 -      git_config_with_options(cb, data, NULL, 1);
 +      opts.respect_includes = 1;
  
 +      if (have_git_dir())
 +              opts.git_dir = get_git_dir();
        /*
         * When setup_git_directory() was not yet asked to discover the
         * GIT_DIR, we ask discover_git_directory() to figure out whether there
         * notably, the current working directory is still the same after the
         * call).
         */
 -      if (!have_git_dir() && discover_git_directory(&buf)) {
 -              struct git_config_source repo_config;
 +      else if (discover_git_directory(&buf))
 +              opts.git_dir = buf.buf;
 +
 +      git_config_with_options(cb, data, NULL, &opts);
  
 -              memset(&repo_config, 0, sizeof(repo_config));
 -              strbuf_addstr(&buf, "/config");
 -              repo_config.file = buf.buf;
 -              git_config_with_options(cb, data, &repo_config, 1);
 -      }
        strbuf_release(&buf);
  }
  
@@@ -1965,7 -1926,7 +1965,7 @@@ int git_config_get_expiry(const char *k
        if (ret)
                return ret;
        if (strcmp(*output, "now")) {
-               unsigned long now = approxidate("now");
+               timestamp_t now = approxidate("now");
                if (approxidate(*output) >= now)
                        git_die_config(key, _("Invalid %s: '%s'"), key, *output);
        }
diff --combined fetch-pack.c
index afb8b05024823981be9bedfaa70f7301c6f7076c,fbe52f02346da7463cc74e4cc552e5d217532711..5f15dd2c390a8105da0d84d0dfeda7cb69b111b7
@@@ -276,8 -276,6 +276,8 @@@ static enum ack_type get_ack(int fd, un
                        return ACK;
                }
        }
 +      if (skip_prefix(line, "ERR ", &arg))
 +              die(_("remote error: %s"), arg);
        die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line);
  }
  
@@@ -394,8 -392,8 +394,8 @@@ static int find_common(struct fetch_pac
        if (args->depth > 0)
                packet_buf_write(&req_buf, "deepen %d", args->depth);
        if (args->deepen_since) {
-               unsigned long max_age = approxidate(args->deepen_since);
-               packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+               timestamp_t max_age = approxidate(args->deepen_since);
+               packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
        }
        if (args->deepen_not) {
                int i;
@@@ -583,7 -581,7 +583,7 @@@ static int mark_complete_oid(const cha
  }
  
  static void mark_recent_complete_commits(struct fetch_pack_args *args,
-                                        unsigned long cutoff)
+                                        timestamp_t cutoff)
  {
        while (complete && cutoff <= complete->item->date) {
                print_verbose(args, _("Marking %s as complete"),
@@@ -670,7 -668,7 +670,7 @@@ static int everything_local(struct fetc
  {
        struct ref *ref;
        int retval;
-       unsigned long cutoff = 0;
+       timestamp_t cutoff = 0;
  
        save_commit_buffer = 0;
  
@@@ -804,8 -802,8 +804,8 @@@ static int get_pack(struct fetch_pack_a
                if (args->use_thin_pack)
                        argv_array_push(&cmd.args, "--fix-thin");
                if (args->lock_pack || unpack_limit) {
 -                      char hostname[256];
 -                      if (gethostname(hostname, sizeof(hostname)))
 +                      char hostname[HOST_NAME_MAX + 1];
 +                      if (xgethostname(hostname, sizeof(hostname)))
                                xsnprintf(hostname, sizeof(hostname), "localhost");
                        argv_array_pushf(&cmd.args,
                                        "--keep=fetch-pack %"PRIuMAX " on %s",
diff --combined git-compat-util.h
index bb4537c0ba0631f94fa1e57c6c5877949e50d750,f8349a03bde5dd984c23bfbc7a053ab4848f8b6e..ab7552a7ce2e292df2875258fef6f8bd0438e115
@@@ -319,6 -319,11 +319,11 @@@ extern char *gitdirname(char *)
  #define PRIo32 "o"
  #endif
  
+ typedef uintmax_t timestamp_t;
+ #define PRItime PRIuMAX
+ #define parse_timestamp strtoumax
+ #define TIME_MAX UINTMAX_MAX
  #ifndef PATH_SEP
  #define PATH_SEP ':'
  #endif
@@@ -616,7 -621,7 +621,7 @@@ extern int git_lstat(const char *, stru
  #endif
  
  #define DEFAULT_PACKED_GIT_LIMIT \
 -      ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? 8192 : 256))
 +      ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? (32 * 1024L * 1024L) : 256))
  
  #ifdef NO_PREAD
  #define pread git_pread
@@@ -884,12 -889,6 +889,12 @@@ static inline size_t xsize_t(off_t len
  __attribute__((format (printf, 3, 4)))
  extern int xsnprintf(char *dst, size_t max, const char *fmt, ...);
  
 +#ifndef HOST_NAME_MAX
 +#define HOST_NAME_MAX 256
 +#endif
 +
 +extern int xgethostname(char *buf, size_t len);
 +
  /* in ctype.c, for kwset users */
  extern const unsigned char tolower_trans_tbl[256];
  
diff --combined refs.c
index fda450118112694ed1fedd1258cea3e283866569,6841bd9f7122c5a866b27e0e66c2c38da9d2990b..26d40f99277f91167a243c19a2380b26075e30d6
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -5,13 -5,11 +5,13 @@@
  #include "cache.h"
  #include "hashmap.h"
  #include "lockfile.h"
 +#include "iterator.h"
  #include "refs.h"
  #include "refs/refs-internal.h"
  #include "object.h"
  #include "tag.h"
  #include "submodule.h"
 +#include "worktree.h"
  
  /*
   * List of all available backends
@@@ -714,7 -712,7 +714,7 @@@ int is_branch(const char *refname
  
  struct read_ref_at_cb {
        const char *refname;
-       unsigned long at_time;
+       timestamp_t at_time;
        int cnt;
        int reccnt;
        unsigned char *sha1;
        unsigned char osha1[20];
        unsigned char nsha1[20];
        int tz;
-       unsigned long date;
+       timestamp_t date;
        char **msg;
-       unsigned long *cutoff_time;
+       timestamp_t *cutoff_time;
        int *cutoff_tz;
        int *cutoff_cnt;
  };
  
  static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
-               const char *email, unsigned long timestamp, int tz,
+               const char *email, timestamp_t timestamp, int tz,
                const char *message, void *cb_data)
  {
        struct read_ref_at_cb *cb = cb_data;
  }
  
  static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
-                                 const char *email, unsigned long timestamp,
+                                 const char *email, timestamp_t timestamp,
                                  int tz, const char *message, void *cb_data)
  {
        struct read_ref_at_cb *cb = cb_data;
        return 1;
  }
  
- int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
+ int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
                unsigned char *sha1, char **msg,
-               unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+               timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
  {
        struct read_ref_at_cb cb;
  
@@@ -1240,18 -1238,6 +1240,18 @@@ int head_ref(each_ref_fn fn, void *cb_d
        return head_ref_submodule(NULL, fn, cb_data);
  }
  
 +struct ref_iterator *refs_ref_iterator_begin(
 +              struct ref_store *refs,
 +              const char *prefix, int trim, int flags)
 +{
 +      struct ref_iterator *iter;
 +
 +      iter = refs->be->iterator_begin(refs, prefix, flags);
 +      iter = prefix_ref_iterator_begin(iter, prefix, trim);
 +
 +      return iter;
 +}
 +
  /*
   * Call fn for each reference in the specified submodule for which the
   * refname begins with prefix. If trim is non-zero, then trim that
@@@ -1269,7 -1255,8 +1269,7 @@@ static int do_for_each_ref(struct ref_s
        if (!refs)
                return 0;
  
 -      iter = refs->be->iterator_begin(refs, prefix, flags);
 -      iter = prefix_ref_iterator_begin(iter, prefix, trim);
 +      iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
  
        return do_for_each_ref_iterator(iter, fn, cb_data);
  }
@@@ -1347,13 -1334,6 +1347,13 @@@ int for_each_rawref(each_ref_fn fn, voi
        return refs_for_each_rawref(get_main_ref_store(), fn, cb_data);
  }
  
 +int refs_read_raw_ref(struct ref_store *ref_store,
 +                    const char *refname, unsigned char *sha1,
 +                    struct strbuf *referent, unsigned int *type)
 +{
 +      return ref_store->be->read_raw_ref(ref_store, refname, sha1, referent, type);
 +}
 +
  /* This function needs to return a meaningful errno on failure */
  const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                                    const char *refname,
        for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
                unsigned int read_flags = 0;
  
 -              if (refs->be->read_raw_ref(refs, refname,
 -                                         sha1, &sb_refname, &read_flags)) {
 +              if (refs_read_raw_ref(refs, refname,
 +                                    sha1, &sb_refname, &read_flags)) {
                        *flags |= read_flags;
                        if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
                                return NULL;
@@@ -1478,32 -1458,32 +1478,32 @@@ int resolve_gitlink_ref(const char *sub
        return 0;
  }
  
 -struct submodule_hash_entry
 +struct ref_store_hash_entry
  {
        struct hashmap_entry ent; /* must be the first member! */
  
        struct ref_store *refs;
  
 -      /* NUL-terminated name of submodule: */
 -      char submodule[FLEX_ARRAY];
 +      /* NUL-terminated identifier of the ref store: */
 +      char name[FLEX_ARRAY];
  };
  
 -static int submodule_hash_cmp(const void *entry, const void *entry_or_key,
 +static int ref_store_hash_cmp(const void *entry, const void *entry_or_key,
                              const void *keydata)
  {
 -      const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key;
 -      const char *submodule = keydata ? keydata : e2->submodule;
 +      const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;
 +      const char *name = keydata ? keydata : e2->name;
  
 -      return strcmp(e1->submodule, submodule);
 +      return strcmp(e1->name, name);
  }
  
 -static struct submodule_hash_entry *alloc_submodule_hash_entry(
 -              const char *submodule, struct ref_store *refs)
 +static struct ref_store_hash_entry *alloc_ref_store_hash_entry(
 +              const char *name, struct ref_store *refs)
  {
 -      struct submodule_hash_entry *entry;
 +      struct ref_store_hash_entry *entry;
  
 -      FLEX_ALLOC_STR(entry, submodule, submodule);
 -      hashmap_entry_init(entry, strhash(submodule));
 +      FLEX_ALLOC_STR(entry, name, name);
 +      hashmap_entry_init(entry, strhash(name));
        entry->refs = refs;
        return entry;
  }
@@@ -1514,23 -1494,20 +1514,23 @@@ static struct ref_store *main_ref_store
  /* A hashmap of ref_stores, stored by submodule name: */
  static struct hashmap submodule_ref_stores;
  
 +/* A hashmap of ref_stores, stored by worktree id: */
 +static struct hashmap worktree_ref_stores;
 +
  /*
 - * Return the ref_store instance for the specified submodule. If that
 - * ref_store hasn't been initialized yet, return NULL.
 + * Look up a ref store by name. If that ref_store hasn't been
 + * registered yet, return NULL.
   */
 -static struct ref_store *lookup_submodule_ref_store(const char *submodule)
 +static struct ref_store *lookup_ref_store_map(struct hashmap *map,
 +                                            const char *name)
  {
 -      struct submodule_hash_entry *entry;
 +      struct ref_store_hash_entry *entry;
  
 -      if (!submodule_ref_stores.tablesize)
 +      if (!map->tablesize)
                /* It's initialized on demand in register_ref_store(). */
                return NULL;
  
 -      entry = hashmap_get_from_hash(&submodule_ref_stores,
 -                                    strhash(submodule), submodule);
 +      entry = hashmap_get_from_hash(map, strhash(name), name);
        return entry ? entry->refs : NULL;
  }
  
@@@ -1557,24 -1534,29 +1557,24 @@@ struct ref_store *get_main_ref_store(vo
        if (main_ref_store)
                return main_ref_store;
  
 -      main_ref_store = ref_store_init(get_git_dir(),
 -                                      (REF_STORE_READ |
 -                                       REF_STORE_WRITE |
 -                                       REF_STORE_ODB |
 -                                       REF_STORE_MAIN));
 +      main_ref_store = ref_store_init(get_git_dir(), REF_STORE_ALL_CAPS);
        return main_ref_store;
  }
  
  /*
 - * Register the specified ref_store to be the one that should be used
 - * for submodule. It is a fatal error to call this function twice for
 - * the same submodule.
 + * Associate a ref store with a name. It is a fatal error to call this
 + * function twice for the same name.
   */
 -static void register_submodule_ref_store(struct ref_store *refs,
 -                                       const char *submodule)
 +static void register_ref_store_map(struct hashmap *map,
 +                                 const char *type,
 +                                 struct ref_store *refs,
 +                                 const char *name)
  {
 -      if (!submodule_ref_stores.tablesize)
 -              hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);
 +      if (!map->tablesize)
 +              hashmap_init(map, ref_store_hash_cmp, 0);
  
 -      if (hashmap_put(&submodule_ref_stores,
 -                      alloc_submodule_hash_entry(submodule, refs)))
 -              die("BUG: ref_store for submodule '%s' initialized twice",
 -                  submodule);
 +      if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))
 +              die("BUG: %s ref_store '%s' initialized twice", type, name);
  }
  
  struct ref_store *get_submodule_ref_store(const char *submodule)
                return get_main_ref_store();
        }
  
 -      refs = lookup_submodule_ref_store(submodule);
 +      refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
        if (refs)
                return refs;
  
        /* assume that add_submodule_odb() has been called */
        refs = ref_store_init(submodule_sb.buf,
                              REF_STORE_READ | REF_STORE_ODB);
 -      register_submodule_ref_store(refs, submodule);
 +      register_ref_store_map(&submodule_ref_stores, "submodule",
 +                             refs, submodule);
  
        strbuf_release(&submodule_sb);
        return refs;
  }
  
 +struct ref_store *get_worktree_ref_store(const struct worktree *wt)
 +{
 +      struct ref_store *refs;
 +      const char *id;
 +
 +      if (wt->is_current)
 +              return get_main_ref_store();
 +
 +      id = wt->id ? wt->id : "/";
 +      refs = lookup_ref_store_map(&worktree_ref_stores, id);
 +      if (refs)
 +              return refs;
 +
 +      if (wt->id)
 +              refs = ref_store_init(git_common_path("worktrees/%s", wt->id),
 +                                    REF_STORE_ALL_CAPS);
 +      else
 +              refs = ref_store_init(get_git_common_dir(),
 +                                    REF_STORE_ALL_CAPS);
 +
 +      if (refs)
 +              register_ref_store_map(&worktree_ref_stores, "worktree",
 +                                     refs, id);
 +      return refs;
 +}
 +
  void base_ref_store_init(struct ref_store *refs,
                         const struct ref_storage_be *be)
  {
@@@ -1688,102 -1643,16 +1688,102 @@@ int ref_transaction_commit(struct ref_t
  {
        struct ref_store *refs = transaction->ref_store;
  
 +      if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
 +              strbuf_addstr(err,
 +                            _("ref updates forbidden inside quarantine environment"));
 +              return -1;
 +      }
 +
        return refs->be->transaction_commit(refs, transaction, err);
  }
  
  int refs_verify_refname_available(struct ref_store *refs,
                                  const char *refname,
 -                                const struct string_list *extra,
 +                                const struct string_list *extras,
                                  const struct string_list *skip,
                                  struct strbuf *err)
  {
 -      return refs->be->verify_refname_available(refs, refname, extra, skip, err);
 +      const char *slash;
 +      const char *extra_refname;
 +      struct strbuf dirname = STRBUF_INIT;
 +      struct strbuf referent = STRBUF_INIT;
 +      struct object_id oid;
 +      unsigned int type;
 +      struct ref_iterator *iter;
 +      int ok;
 +      int ret = -1;
 +
 +      /*
 +       * For the sake of comments in this function, suppose that
 +       * refname is "refs/foo/bar".
 +       */
 +
 +      assert(err);
 +
 +      strbuf_grow(&dirname, strlen(refname) + 1);
 +      for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
 +              /* Expand dirname to the new prefix, not including the trailing slash: */
 +              strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
 +
 +              /*
 +               * We are still at a leading dir of the refname (e.g.,
 +               * "refs/foo"; if there is a reference with that name,
 +               * it is a conflict, *unless* it is in skip.
 +               */
 +              if (skip && string_list_has_string(skip, dirname.buf))
 +                      continue;
 +
 +              if (!refs_read_raw_ref(refs, dirname.buf, oid.hash, &referent, &type)) {
 +                      strbuf_addf(err, "'%s' exists; cannot create '%s'",
 +                                  dirname.buf, refname);
 +                      goto cleanup;
 +              }
 +
 +              if (extras && string_list_has_string(extras, dirname.buf)) {
 +                      strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
 +                                  refname, dirname.buf);
 +                      goto cleanup;
 +              }
 +      }
 +
 +      /*
 +       * We are at the leaf of our refname (e.g., "refs/foo/bar").
 +       * There is no point in searching for a reference with that
 +       * name, because a refname isn't considered to conflict with
 +       * itself. But we still need to check for references whose
 +       * names are in the "refs/foo/bar/" namespace, because they
 +       * *do* conflict.
 +       */
 +      strbuf_addstr(&dirname, refname + dirname.len);
 +      strbuf_addch(&dirname, '/');
 +
 +      iter = refs_ref_iterator_begin(refs, dirname.buf, 0,
 +                                     DO_FOR_EACH_INCLUDE_BROKEN);
 +      while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
 +              if (skip &&
 +                  string_list_has_string(skip, iter->refname))
 +                      continue;
 +
 +              strbuf_addf(err, "'%s' exists; cannot create '%s'",
 +                          iter->refname, refname);
 +              ref_iterator_abort(iter);
 +              goto cleanup;
 +      }
 +
 +      if (ok != ITER_DONE)
 +              die("BUG: error while iterating over references");
 +
 +      extra_refname = find_descendant_ref(dirname.buf, extras, skip);
 +      if (extra_refname)
 +              strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
 +                          refname, extra_refname);
 +      else
 +              ret = 0;
 +
 +cleanup:
 +      strbuf_release(&referent);
 +      strbuf_release(&dirname);
 +      return ret;
  }
  
  int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
diff --combined refs.h
index aca805168abd98149bc1725721fe2d53b6ed5802,2b80d37fd34641b9128ad1792a93781e998973ff..d18ef47128688b6bee35b4bd5ae5c42bc3e6690e
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -5,7 -5,6 +5,7 @@@ struct object_id
  struct ref_store;
  struct strbuf;
  struct string_list;
 +struct worktree;
  
  /*
   * Resolve a reference, recursively following symbolic refererences.
@@@ -98,7 -97,7 +98,7 @@@ int read_ref(const char *refname, unsig
  
  int refs_verify_refname_available(struct ref_store *refs,
                                  const char *refname,
 -                                const struct string_list *extra,
 +                                const struct string_list *extras,
                                  const struct string_list *skip,
                                  struct strbuf *err);
  
@@@ -318,9 -317,9 +318,9 @@@ int safe_create_reflog(const char *refn
  
  /** Reads log for the value of ref during at_time. **/
  int read_ref_at(const char *refname, unsigned int flags,
-               unsigned long at_time, int cnt,
+               timestamp_t at_time, int cnt,
                unsigned char *sha1, char **msg,
-               unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
+               timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
  
  /** Check if a particular reflog exists */
  int refs_reflog_exists(struct ref_store *refs, const char *refname);
@@@ -357,7 -356,7 +357,7 @@@ int delete_reflog(const char *refname)
  /* iterate over reflog entries */
  typedef int each_reflog_ent_fn(
                struct object_id *old_oid, struct object_id *new_oid,
-               const char *committer, unsigned long timestamp,
+               const char *committer, timestamp_t timestamp,
                int tz, const char *msg, void *cb_data);
  
  int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
@@@ -402,6 -401,16 +402,6 @@@ int refs_create_symref(struct ref_stor
                       const char *target, const char *logmsg);
  int create_symref(const char *refname, const char *target, const char *logmsg);
  
 -/*
 - * Update HEAD of the specified gitdir.
 - * Similar to create_symref("relative-git-dir/HEAD", target, NULL), but
 - * this can update the main working tree's HEAD regardless of where
 - * $GIT_DIR points to.
 - * Return 0 if successful, non-zero otherwise.
 - * */
 -int set_worktree_head_symref(const char *gitdir, const char *target,
 -                           const char *logmsg);
 -
  enum action_on_err {
        UPDATE_REFS_MSG_ON_ERR,
        UPDATE_REFS_DIE_ON_ERR,
@@@ -607,7 -616,7 +607,7 @@@ typedef void reflog_expiry_prepare_fn(c
  typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
                                          unsigned char *nsha1,
                                          const char *email,
-                                         unsigned long timestamp, int tz,
+                                         timestamp_t timestamp, int tz,
                                          const char *message, void *cb_data);
  typedef void reflog_expiry_cleanup_fn(void *cb_data);
  
@@@ -646,6 -655,5 +646,6 @@@ struct ref_store *get_main_ref_store(vo
   * submodule==NULL.
   */
  struct ref_store *get_submodule_ref_store(const char *submodule);
 +struct ref_store *get_worktree_ref_store(const struct worktree *wt);
  
  #endif /* REFS_H */
diff --combined refs/files-backend.c
index 9d08e84ded1e93c3f43b74f4dd92934d190fabf1,6646dcfc838e9d88296e989180e5184730e49134..4925e698d84b46e425c158e28709c22c339803c4
@@@ -1,7 -1,6 +1,7 @@@
  #include "../cache.h"
  #include "../refs.h"
  #include "refs-internal.h"
 +#include "ref-cache.h"
  #include "../iterator.h"
  #include "../dir-iterator.h"
  #include "../lockfile.h"
@@@ -14,6 -13,511 +14,6 @@@ struct ref_lock 
        struct object_id old_oid;
  };
  
 -struct ref_entry;
 -
 -/*
 - * Information used (along with the information in ref_entry) to
 - * describe a single cached reference.  This data structure only
 - * occurs embedded in a union in struct ref_entry, and only when
 - * (ref_entry->flag & REF_DIR) is zero.
 - */
 -struct ref_value {
 -      /*
 -       * The name of the object to which this reference resolves
 -       * (which may be a tag object).  If REF_ISBROKEN, this is
 -       * null.  If REF_ISSYMREF, then this is the name of the object
 -       * referred to by the last reference in the symlink chain.
 -       */
 -      struct object_id oid;
 -
 -      /*
 -       * If REF_KNOWS_PEELED, then this field holds the peeled value
 -       * of this reference, or null if the reference is known not to
 -       * be peelable.  See the documentation for peel_ref() for an
 -       * exact definition of "peelable".
 -       */
 -      struct object_id peeled;
 -};
 -
 -struct files_ref_store;
 -
 -/*
 - * Information used (along with the information in ref_entry) to
 - * describe a level in the hierarchy of references.  This data
 - * structure only occurs embedded in a union in struct ref_entry, and
 - * only when (ref_entry.flag & REF_DIR) is set.  In that case,
 - * (ref_entry.flag & REF_INCOMPLETE) determines whether the references
 - * in the directory have already been read:
 - *
 - *     (ref_entry.flag & REF_INCOMPLETE) unset -- a directory of loose
 - *         or packed references, already read.
 - *
 - *     (ref_entry.flag & REF_INCOMPLETE) set -- a directory of loose
 - *         references that hasn't been read yet (nor has any of its
 - *         subdirectories).
 - *
 - * Entries within a directory are stored within a growable array of
 - * pointers to ref_entries (entries, nr, alloc).  Entries 0 <= i <
 - * sorted are sorted by their component name in strcmp() order and the
 - * remaining entries are unsorted.
 - *
 - * Loose references are read lazily, one directory at a time.  When a
 - * directory of loose references is read, then all of the references
 - * in that directory are stored, and REF_INCOMPLETE stubs are created
 - * for any subdirectories, but the subdirectories themselves are not
 - * read.  The reading is triggered by get_ref_dir().
 - */
 -struct ref_dir {
 -      int nr, alloc;
 -
 -      /*
 -       * Entries with index 0 <= i < sorted are sorted by name.  New
 -       * entries are appended to the list unsorted, and are sorted
 -       * only when required; thus we avoid the need to sort the list
 -       * after the addition of every reference.
 -       */
 -      int sorted;
 -
 -      /* A pointer to the files_ref_store that contains this ref_dir. */
 -      struct files_ref_store *ref_store;
 -
 -      struct ref_entry **entries;
 -};
 -
 -/*
 - * Bit values for ref_entry::flag.  REF_ISSYMREF=0x01,
 - * REF_ISPACKED=0x02, REF_ISBROKEN=0x04 and REF_BAD_NAME=0x08 are
 - * public values; see refs.h.
 - */
 -
 -/*
 - * The field ref_entry->u.value.peeled of this value entry contains
 - * the correct peeled value for the reference, which might be
 - * null_sha1 if the reference is not a tag or if it is broken.
 - */
 -#define REF_KNOWS_PEELED 0x10
 -
 -/* ref_entry represents a directory of references */
 -#define REF_DIR 0x20
 -
 -/*
 - * Entry has not yet been read from disk (used only for REF_DIR
 - * entries representing loose references)
 - */
 -#define REF_INCOMPLETE 0x40
 -
 -/*
 - * A ref_entry represents either a reference or a "subdirectory" of
 - * references.
 - *
 - * Each directory in the reference namespace is represented by a
 - * ref_entry with (flags & REF_DIR) set and containing a subdir member
 - * that holds the entries in that directory that have been read so
 - * far.  If (flags & REF_INCOMPLETE) is set, then the directory and
 - * its subdirectories haven't been read yet.  REF_INCOMPLETE is only
 - * used for loose reference directories.
 - *
 - * References are represented by a ref_entry with (flags & REF_DIR)
 - * unset and a value member that describes the reference's value.  The
 - * flag member is at the ref_entry level, but it is also needed to
 - * interpret the contents of the value field (in other words, a
 - * ref_value object is not very much use without the enclosing
 - * ref_entry).
 - *
 - * Reference names cannot end with slash and directories' names are
 - * always stored with a trailing slash (except for the top-level
 - * directory, which is always denoted by "").  This has two nice
 - * consequences: (1) when the entries in each subdir are sorted
 - * lexicographically by name (as they usually are), the references in
 - * a whole tree can be generated in lexicographic order by traversing
 - * the tree in left-to-right, depth-first order; (2) the names of
 - * references and subdirectories cannot conflict, and therefore the
 - * presence of an empty subdirectory does not block the creation of a
 - * similarly-named reference.  (The fact that reference names with the
 - * same leading components can conflict *with each other* is a
 - * separate issue that is regulated by verify_refname_available().)
 - *
 - * Please note that the name field contains the fully-qualified
 - * reference (or subdirectory) name.  Space could be saved by only
 - * storing the relative names.  But that would require the full names
 - * to be generated on the fly when iterating in do_for_each_ref(), and
 - * would break callback functions, who have always been able to assume
 - * that the name strings that they are passed will not be freed during
 - * the iteration.
 - */
 -struct ref_entry {
 -      unsigned char flag; /* ISSYMREF? ISPACKED? */
 -      union {
 -              struct ref_value value; /* if not (flags&REF_DIR) */
 -              struct ref_dir subdir; /* if (flags&REF_DIR) */
 -      } u;
 -      /*
 -       * The full name of the reference (e.g., "refs/heads/master")
 -       * or the full name of the directory with a trailing slash
 -       * (e.g., "refs/heads/"):
 -       */
 -      char name[FLEX_ARRAY];
 -};
 -
 -static void read_loose_refs(const char *dirname, struct ref_dir *dir);
 -static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len);
 -static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,
 -                                        const char *dirname, size_t len,
 -                                        int incomplete);
 -static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
 -static int files_log_ref_write(struct files_ref_store *refs,
 -                             const char *refname, const unsigned char *old_sha1,
 -                             const unsigned char *new_sha1, const char *msg,
 -                             int flags, struct strbuf *err);
 -
 -static struct ref_dir *get_ref_dir(struct ref_entry *entry)
 -{
 -      struct ref_dir *dir;
 -      assert(entry->flag & REF_DIR);
 -      dir = &entry->u.subdir;
 -      if (entry->flag & REF_INCOMPLETE) {
 -              read_loose_refs(entry->name, dir);
 -
 -              /*
 -               * Manually add refs/bisect, which, being
 -               * per-worktree, might not appear in the directory
 -               * listing for refs/ in the main repo.
 -               */
 -              if (!strcmp(entry->name, "refs/")) {
 -                      int pos = search_ref_dir(dir, "refs/bisect/", 12);
 -                      if (pos < 0) {
 -                              struct ref_entry *child_entry;
 -                              child_entry = create_dir_entry(dir->ref_store,
 -                                                             "refs/bisect/",
 -                                                             12, 1);
 -                              add_entry_to_dir(dir, child_entry);
 -                              read_loose_refs("refs/bisect",
 -                                              &child_entry->u.subdir);
 -                      }
 -              }
 -              entry->flag &= ~REF_INCOMPLETE;
 -      }
 -      return dir;
 -}
 -
 -static struct ref_entry *create_ref_entry(const char *refname,
 -                                        const unsigned char *sha1, int flag,
 -                                        int check_name)
 -{
 -      struct ref_entry *ref;
 -
 -      if (check_name &&
 -          check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
 -              die("Reference has invalid format: '%s'", refname);
 -      FLEX_ALLOC_STR(ref, name, refname);
 -      hashcpy(ref->u.value.oid.hash, sha1);
 -      oidclr(&ref->u.value.peeled);
 -      ref->flag = flag;
 -      return ref;
 -}
 -
 -static void clear_ref_dir(struct ref_dir *dir);
 -
 -static void free_ref_entry(struct ref_entry *entry)
 -{
 -      if (entry->flag & REF_DIR) {
 -              /*
 -               * Do not use get_ref_dir() here, as that might
 -               * trigger the reading of loose refs.
 -               */
 -              clear_ref_dir(&entry->u.subdir);
 -      }
 -      free(entry);
 -}
 -
 -/*
 - * Add a ref_entry to the end of dir (unsorted).  Entry is always
 - * stored directly in dir; no recursion into subdirectories is
 - * done.
 - */
 -static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)
 -{
 -      ALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc);
 -      dir->entries[dir->nr++] = entry;
 -      /* optimize for the case that entries are added in order */
 -      if (dir->nr == 1 ||
 -          (dir->nr == dir->sorted + 1 &&
 -           strcmp(dir->entries[dir->nr - 2]->name,
 -                  dir->entries[dir->nr - 1]->name) < 0))
 -              dir->sorted = dir->nr;
 -}
 -
 -/*
 - * Clear and free all entries in dir, recursively.
 - */
 -static void clear_ref_dir(struct ref_dir *dir)
 -{
 -      int i;
 -      for (i = 0; i < dir->nr; i++)
 -              free_ref_entry(dir->entries[i]);
 -      free(dir->entries);
 -      dir->sorted = dir->nr = dir->alloc = 0;
 -      dir->entries = NULL;
 -}
 -
 -/*
 - * Create a struct ref_entry object for the specified dirname.
 - * dirname is the name of the directory with a trailing slash (e.g.,
 - * "refs/heads/") or "" for the top-level directory.
 - */
 -static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,
 -                                        const char *dirname, size_t len,
 -                                        int incomplete)
 -{
 -      struct ref_entry *direntry;
 -      FLEX_ALLOC_MEM(direntry, name, dirname, len);
 -      direntry->u.subdir.ref_store = ref_store;
 -      direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
 -      return direntry;
 -}
 -
 -static int ref_entry_cmp(const void *a, const void *b)
 -{
 -      struct ref_entry *one = *(struct ref_entry **)a;
 -      struct ref_entry *two = *(struct ref_entry **)b;
 -      return strcmp(one->name, two->name);
 -}
 -
 -static void sort_ref_dir(struct ref_dir *dir);
 -
 -struct string_slice {
 -      size_t len;
 -      const char *str;
 -};
 -
 -static int ref_entry_cmp_sslice(const void *key_, const void *ent_)
 -{
 -      const struct string_slice *key = key_;
 -      const struct ref_entry *ent = *(const struct ref_entry * const *)ent_;
 -      int cmp = strncmp(key->str, ent->name, key->len);
 -      if (cmp)
 -              return cmp;
 -      return '\0' - (unsigned char)ent->name[key->len];
 -}
 -
 -/*
 - * Return the index of the entry with the given refname from the
 - * ref_dir (non-recursively), sorting dir if necessary.  Return -1 if
 - * no such entry is found.  dir must already be complete.
 - */
 -static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len)
 -{
 -      struct ref_entry **r;
 -      struct string_slice key;
 -
 -      if (refname == NULL || !dir->nr)
 -              return -1;
 -
 -      sort_ref_dir(dir);
 -      key.len = len;
 -      key.str = refname;
 -      r = bsearch(&key, dir->entries, dir->nr, sizeof(*dir->entries),
 -                  ref_entry_cmp_sslice);
 -
 -      if (r == NULL)
 -              return -1;
 -
 -      return r - dir->entries;
 -}
 -
 -/*
 - * Search for a directory entry directly within dir (without
 - * recursing).  Sort dir if necessary.  subdirname must be a directory
 - * name (i.e., end in '/').  If mkdir is set, then create the
 - * directory if it is missing; otherwise, return NULL if the desired
 - * directory cannot be found.  dir must already be complete.
 - */
 -static struct ref_dir *search_for_subdir(struct ref_dir *dir,
 -                                       const char *subdirname, size_t len,
 -                                       int mkdir)
 -{
 -      int entry_index = search_ref_dir(dir, subdirname, len);
 -      struct ref_entry *entry;
 -      if (entry_index == -1) {
 -              if (!mkdir)
 -                      return NULL;
 -              /*
 -               * Since dir is complete, the absence of a subdir
 -               * means that the subdir really doesn't exist;
 -               * therefore, create an empty record for it but mark
 -               * the record complete.
 -               */
 -              entry = create_dir_entry(dir->ref_store, subdirname, len, 0);
 -              add_entry_to_dir(dir, entry);
 -      } else {
 -              entry = dir->entries[entry_index];
 -      }
 -      return get_ref_dir(entry);
 -}
 -
 -/*
 - * If refname is a reference name, find the ref_dir within the dir
 - * tree that should hold refname.  If refname is a directory name
 - * (i.e., ends in '/'), then return that ref_dir itself.  dir must
 - * represent the top-level directory and must already be complete.
 - * Sort ref_dirs and recurse into subdirectories as necessary.  If
 - * mkdir is set, then create any missing directories; otherwise,
 - * return NULL if the desired directory cannot be found.
 - */
 -static struct ref_dir *find_containing_dir(struct ref_dir *dir,
 -                                         const char *refname, int mkdir)
 -{
 -      const char *slash;
 -      for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
 -              size_t dirnamelen = slash - refname + 1;
 -              struct ref_dir *subdir;
 -              subdir = search_for_subdir(dir, refname, dirnamelen, mkdir);
 -              if (!subdir) {
 -                      dir = NULL;
 -                      break;
 -              }
 -              dir = subdir;
 -      }
 -
 -      return dir;
 -}
 -
 -/*
 - * Find the value entry with the given name in dir, sorting ref_dirs
 - * and recursing into subdirectories as necessary.  If the name is not
 - * found or it corresponds to a directory entry, return NULL.
 - */
 -static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)
 -{
 -      int entry_index;
 -      struct ref_entry *entry;
 -      dir = find_containing_dir(dir, refname, 0);
 -      if (!dir)
 -              return NULL;
 -      entry_index = search_ref_dir(dir, refname, strlen(refname));
 -      if (entry_index == -1)
 -              return NULL;
 -      entry = dir->entries[entry_index];
 -      return (entry->flag & REF_DIR) ? NULL : entry;
 -}
 -
 -/*
 - * Remove the entry with the given name from dir, recursing into
 - * subdirectories as necessary.  If refname is the name of a directory
 - * (i.e., ends with '/'), then remove the directory and its contents.
 - * If the removal was successful, return the number of entries
 - * remaining in the directory entry that contained the deleted entry.
 - * If the name was not found, return -1.  Please note that this
 - * function only deletes the entry from the cache; it does not delete
 - * it from the filesystem or ensure that other cache entries (which
 - * might be symbolic references to the removed entry) are updated.
 - * Nor does it remove any containing dir entries that might be made
 - * empty by the removal.  dir must represent the top-level directory
 - * and must already be complete.
 - */
 -static int remove_entry(struct ref_dir *dir, const char *refname)
 -{
 -      int refname_len = strlen(refname);
 -      int entry_index;
 -      struct ref_entry *entry;
 -      int is_dir = refname[refname_len - 1] == '/';
 -      if (is_dir) {
 -              /*
 -               * refname represents a reference directory.  Remove
 -               * the trailing slash; otherwise we will get the
 -               * directory *representing* refname rather than the
 -               * one *containing* it.
 -               */
 -              char *dirname = xmemdupz(refname, refname_len - 1);
 -              dir = find_containing_dir(dir, dirname, 0);
 -              free(dirname);
 -      } else {
 -              dir = find_containing_dir(dir, refname, 0);
 -      }
 -      if (!dir)
 -              return -1;
 -      entry_index = search_ref_dir(dir, refname, refname_len);
 -      if (entry_index == -1)
 -              return -1;
 -      entry = dir->entries[entry_index];
 -
 -      memmove(&dir->entries[entry_index],
 -              &dir->entries[entry_index + 1],
 -              (dir->nr - entry_index - 1) * sizeof(*dir->entries)
 -              );
 -      dir->nr--;
 -      if (dir->sorted > entry_index)
 -              dir->sorted--;
 -      free_ref_entry(entry);
 -      return dir->nr;
 -}
 -
 -/*
 - * Add a ref_entry to the ref_dir (unsorted), recursing into
 - * subdirectories as necessary.  dir must represent the top-level
 - * directory.  Return 0 on success.
 - */
 -static int add_ref(struct ref_dir *dir, struct ref_entry *ref)
 -{
 -      dir = find_containing_dir(dir, ref->name, 1);
 -      if (!dir)
 -              return -1;
 -      add_entry_to_dir(dir, ref);
 -      return 0;
 -}
 -
 -/*
 - * Emit a warning and return true iff ref1 and ref2 have the same name
 - * and the same sha1.  Die if they have the same name but different
 - * sha1s.
 - */
 -static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2)
 -{
 -      if (strcmp(ref1->name, ref2->name))
 -              return 0;
 -
 -      /* Duplicate name; make sure that they don't conflict: */
 -
 -      if ((ref1->flag & REF_DIR) || (ref2->flag & REF_DIR))
 -              /* This is impossible by construction */
 -              die("Reference directory conflict: %s", ref1->name);
 -
 -      if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid))
 -              die("Duplicated ref, and SHA1s don't match: %s", ref1->name);
 -
 -      warning("Duplicated ref: %s", ref1->name);
 -      return 1;
 -}
 -
 -/*
 - * Sort the entries in dir non-recursively (if they are not already
 - * sorted) and remove any duplicate entries.
 - */
 -static void sort_ref_dir(struct ref_dir *dir)
 -{
 -      int i, j;
 -      struct ref_entry *last = NULL;
 -
 -      /*
 -       * This check also prevents passing a zero-length array to qsort(),
 -       * which is a problem on some platforms.
 -       */
 -      if (dir->sorted == dir->nr)
 -              return;
 -
 -      QSORT(dir->entries, dir->nr, ref_entry_cmp);
 -
 -      /* Remove any duplicates: */
 -      for (i = 0, j = 0; j < dir->nr; j++) {
 -              struct ref_entry *entry = dir->entries[j];
 -              if (last && is_dup_ref(last, entry))
 -                      free_ref_entry(entry);
 -              else
 -                      last = dir->entries[i++] = entry;
 -      }
 -      dir->sorted = dir->nr = i;
 -}
 -
  /*
   * Return true if refname, which has the specified oid and flags, can
   * be resolved to an object in the database. If the referred-to object
@@@ -32,8 -536,358 +32,8 @@@ static int ref_resolves_to_object(cons
        return 1;
  }
  
 -/*
 - * Return true if the reference described by entry can be resolved to
 - * an object in the database; otherwise, emit a warning and return
 - * false.
 - */
 -static int entry_resolves_to_object(struct ref_entry *entry)
 -{
 -      return ref_resolves_to_object(entry->name,
 -                                    &entry->u.value.oid, entry->flag);
 -}
 -
 -typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data);
 -
 -/*
 - * Call fn for each reference in dir that has index in the range
 - * offset <= index < dir->nr.  Recurse into subdirectories that are in
 - * that index range, sorting them before iterating.  This function
 - * does not sort dir itself; it should be sorted beforehand.  fn is
 - * called for all references, including broken ones.
 - */
 -static int do_for_each_entry_in_dir(struct ref_dir *dir, int offset,
 -                                  each_ref_entry_fn fn, void *cb_data)
 -{
 -      int i;
 -      assert(dir->sorted == dir->nr);
 -      for (i = offset; i < dir->nr; i++) {
 -              struct ref_entry *entry = dir->entries[i];
 -              int retval;
 -              if (entry->flag & REF_DIR) {
 -                      struct ref_dir *subdir = get_ref_dir(entry);
 -                      sort_ref_dir(subdir);
 -                      retval = do_for_each_entry_in_dir(subdir, 0, fn, cb_data);
 -              } else {
 -                      retval = fn(entry, cb_data);
 -              }
 -              if (retval)
 -                      return retval;
 -      }
 -      return 0;
 -}
 -
 -/*
 - * Load all of the refs from the dir into our in-memory cache. The hard work
 - * of loading loose refs is done by get_ref_dir(), so we just need to recurse
 - * through all of the sub-directories. We do not even need to care about
 - * sorting, as traversal order does not matter to us.
 - */
 -static void prime_ref_dir(struct ref_dir *dir)
 -{
 -      int i;
 -      for (i = 0; i < dir->nr; i++) {
 -              struct ref_entry *entry = dir->entries[i];
 -              if (entry->flag & REF_DIR)
 -                      prime_ref_dir(get_ref_dir(entry));
 -      }
 -}
 -
 -/*
 - * A level in the reference hierarchy that is currently being iterated
 - * through.
 - */
 -struct cache_ref_iterator_level {
 -      /*
 -       * The ref_dir being iterated over at this level. The ref_dir
 -       * is sorted before being stored here.
 -       */
 -      struct ref_dir *dir;
 -
 -      /*
 -       * The index of the current entry within dir (which might
 -       * itself be a directory). If index == -1, then the iteration
 -       * hasn't yet begun. If index == dir->nr, then the iteration
 -       * through this level is over.
 -       */
 -      int index;
 -};
 -
 -/*
 - * Represent an iteration through a ref_dir in the memory cache. The
 - * iteration recurses through subdirectories.
 - */
 -struct cache_ref_iterator {
 -      struct ref_iterator base;
 -
 -      /*
 -       * The number of levels currently on the stack. This is always
 -       * at least 1, because when it becomes zero the iteration is
 -       * ended and this struct is freed.
 -       */
 -      size_t levels_nr;
 -
 -      /* The number of levels that have been allocated on the stack */
 -      size_t levels_alloc;
 -
 -      /*
 -       * A stack of levels. levels[0] is the uppermost level that is
 -       * being iterated over in this iteration. (This is not
 -       * necessary the top level in the references hierarchy. If we
 -       * are iterating through a subtree, then levels[0] will hold
 -       * the ref_dir for that subtree, and subsequent levels will go
 -       * on from there.)
 -       */
 -      struct cache_ref_iterator_level *levels;
 -};
 -
 -static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
 -{
 -      struct cache_ref_iterator *iter =
 -              (struct cache_ref_iterator *)ref_iterator;
 -
 -      while (1) {
 -              struct cache_ref_iterator_level *level =
 -                      &iter->levels[iter->levels_nr - 1];
 -              struct ref_dir *dir = level->dir;
 -              struct ref_entry *entry;
 -
 -              if (level->index == -1)
 -                      sort_ref_dir(dir);
 -
 -              if (++level->index == level->dir->nr) {
 -                      /* This level is exhausted; pop up a level */
 -                      if (--iter->levels_nr == 0)
 -                              return ref_iterator_abort(ref_iterator);
 -
 -                      continue;
 -              }
 -
 -              entry = dir->entries[level->index];
 -
 -              if (entry->flag & REF_DIR) {
 -                      /* push down a level */
 -                      ALLOC_GROW(iter->levels, iter->levels_nr + 1,
 -                                 iter->levels_alloc);
 -
 -                      level = &iter->levels[iter->levels_nr++];
 -                      level->dir = get_ref_dir(entry);
 -                      level->index = -1;
 -              } else {
 -                      iter->base.refname = entry->name;
 -                      iter->base.oid = &entry->u.value.oid;
 -                      iter->base.flags = entry->flag;
 -                      return ITER_OK;
 -              }
 -      }
 -}
 -
 -static enum peel_status peel_entry(struct ref_entry *entry, int repeel);
 -
 -static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
 -                                 struct object_id *peeled)
 -{
 -      struct cache_ref_iterator *iter =
 -              (struct cache_ref_iterator *)ref_iterator;
 -      struct cache_ref_iterator_level *level;
 -      struct ref_entry *entry;
 -
 -      level = &iter->levels[iter->levels_nr - 1];
 -
 -      if (level->index == -1)
 -              die("BUG: peel called before advance for cache iterator");
 -
 -      entry = level->dir->entries[level->index];
 -
 -      if (peel_entry(entry, 0))
 -              return -1;
 -      oidcpy(peeled, &entry->u.value.peeled);
 -      return 0;
 -}
 -
 -static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
 -{
 -      struct cache_ref_iterator *iter =
 -              (struct cache_ref_iterator *)ref_iterator;
 -
 -      free(iter->levels);
 -      base_ref_iterator_free(ref_iterator);
 -      return ITER_DONE;
 -}
 -
 -static struct ref_iterator_vtable cache_ref_iterator_vtable = {
 -      cache_ref_iterator_advance,
 -      cache_ref_iterator_peel,
 -      cache_ref_iterator_abort
 -};
 -
 -static struct ref_iterator *cache_ref_iterator_begin(struct ref_dir *dir)
 -{
 -      struct cache_ref_iterator *iter;
 -      struct ref_iterator *ref_iterator;
 -      struct cache_ref_iterator_level *level;
 -
 -      iter = xcalloc(1, sizeof(*iter));
 -      ref_iterator = &iter->base;
 -      base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
 -      ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
 -
 -      iter->levels_nr = 1;
 -      level = &iter->levels[0];
 -      level->index = -1;
 -      level->dir = dir;
 -
 -      return ref_iterator;
 -}
 -
 -struct nonmatching_ref_data {
 -      const struct string_list *skip;
 -      const char *conflicting_refname;
 -};
 -
 -static int nonmatching_ref_fn(struct ref_entry *entry, void *vdata)
 -{
 -      struct nonmatching_ref_data *data = vdata;
 -
 -      if (data->skip && string_list_has_string(data->skip, entry->name))
 -              return 0;
 -
 -      data->conflicting_refname = entry->name;
 -      return 1;
 -}
 -
 -/*
 - * Return 0 if a reference named refname could be created without
 - * conflicting with the name of an existing reference in dir.
 - * See verify_refname_available for more information.
 - */
 -static int verify_refname_available_dir(const char *refname,
 -                                      const struct string_list *extras,
 -                                      const struct string_list *skip,
 -                                      struct ref_dir *dir,
 -                                      struct strbuf *err)
 -{
 -      const char *slash;
 -      const char *extra_refname;
 -      int pos;
 -      struct strbuf dirname = STRBUF_INIT;
 -      int ret = -1;
 -
 -      /*
 -       * For the sake of comments in this function, suppose that
 -       * refname is "refs/foo/bar".
 -       */
 -
 -      assert(err);
 -
 -      strbuf_grow(&dirname, strlen(refname) + 1);
 -      for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
 -              /* Expand dirname to the new prefix, not including the trailing slash: */
 -              strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
 -
 -              /*
 -               * We are still at a leading dir of the refname (e.g.,
 -               * "refs/foo"; if there is a reference with that name,
 -               * it is a conflict, *unless* it is in skip.
 -               */
 -              if (dir) {
 -                      pos = search_ref_dir(dir, dirname.buf, dirname.len);
 -                      if (pos >= 0 &&
 -                          (!skip || !string_list_has_string(skip, dirname.buf))) {
 -                              /*
 -                               * We found a reference whose name is
 -                               * a proper prefix of refname; e.g.,
 -                               * "refs/foo", and is not in skip.
 -                               */
 -                              strbuf_addf(err, "'%s' exists; cannot create '%s'",
 -                                          dirname.buf, refname);
 -                              goto cleanup;
 -                      }
 -              }
 -
 -              if (extras && string_list_has_string(extras, dirname.buf) &&
 -                  (!skip || !string_list_has_string(skip, dirname.buf))) {
 -                      strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
 -                                  refname, dirname.buf);
 -                      goto cleanup;
 -              }
 -
 -              /*
 -               * Otherwise, we can try to continue our search with
 -               * the next component. So try to look up the
 -               * directory, e.g., "refs/foo/". If we come up empty,
 -               * we know there is nothing under this whole prefix,
 -               * but even in that case we still have to continue the
 -               * search for conflicts with extras.
 -               */
 -              strbuf_addch(&dirname, '/');
 -              if (dir) {
 -                      pos = search_ref_dir(dir, dirname.buf, dirname.len);
 -                      if (pos < 0) {
 -                              /*
 -                               * There was no directory "refs/foo/",
 -                               * so there is nothing under this
 -                               * whole prefix. So there is no need
 -                               * to continue looking for conflicting
 -                               * references. But we need to continue
 -                               * looking for conflicting extras.
 -                               */
 -                              dir = NULL;
 -                      } else {
 -                              dir = get_ref_dir(dir->entries[pos]);
 -                      }
 -              }
 -      }
 -
 -      /*
 -       * We are at the leaf of our refname (e.g., "refs/foo/bar").
 -       * There is no point in searching for a reference with that
 -       * name, because a refname isn't considered to conflict with
 -       * itself. But we still need to check for references whose
 -       * names are in the "refs/foo/bar/" namespace, because they
 -       * *do* conflict.
 -       */
 -      strbuf_addstr(&dirname, refname + dirname.len);
 -      strbuf_addch(&dirname, '/');
 -
 -      if (dir) {
 -              pos = search_ref_dir(dir, dirname.buf, dirname.len);
 -
 -              if (pos >= 0) {
 -                      /*
 -                       * We found a directory named "$refname/"
 -                       * (e.g., "refs/foo/bar/"). It is a problem
 -                       * iff it contains any ref that is not in
 -                       * "skip".
 -                       */
 -                      struct nonmatching_ref_data data;
 -
 -                      data.skip = skip;
 -                      data.conflicting_refname = NULL;
 -                      dir = get_ref_dir(dir->entries[pos]);
 -                      sort_ref_dir(dir);
 -                      if (do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data)) {
 -                              strbuf_addf(err, "'%s' exists; cannot create '%s'",
 -                                          data.conflicting_refname, refname);
 -                              goto cleanup;
 -                      }
 -              }
 -      }
 -
 -      extra_refname = find_descendant_ref(dirname.buf, extras, skip);
 -      if (extra_refname)
 -              strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
 -                          refname, extra_refname);
 -      else
 -              ret = 0;
 -
 -cleanup:
 -      strbuf_release(&dirname);
 -      return ret;
 -}
 -
  struct packed_ref_cache {
 -      struct ref_entry *root;
 +      struct ref_cache *cache;
  
        /*
         * Count of references to the data structure in this instance,
@@@ -68,7 -922,7 +68,7 @@@ struct files_ref_store 
        char *gitcommondir;
        char *packed_refs_path;
  
 -      struct ref_entry *loose;
 +      struct ref_cache *loose;
        struct packed_ref_cache *packed;
  };
  
@@@ -90,7 -944,7 +90,7 @@@ static void acquire_packed_ref_cache(st
  static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
  {
        if (!--packed_refs->referrers) {
 -              free_ref_entry(packed_refs->root);
 +              free_ref_cache(packed_refs->cache);
                stat_validity_clear(&packed_refs->validity);
                free(packed_refs);
                return 1;
@@@ -114,7 -968,7 +114,7 @@@ static void clear_packed_ref_cache(stru
  static void clear_loose_ref_cache(struct files_ref_store *refs)
  {
        if (refs->loose) {
 -              free_ref_entry(refs->loose);
 +              free_ref_cache(refs->loose);
                refs->loose = NULL;
        }
  }
@@@ -287,7 -1141,7 +287,7 @@@ static void read_packed_refs(FILE *f, s
                        if (peeled == PEELED_FULLY ||
                            (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
                                last->flag |= REF_KNOWS_PEELED;
 -                      add_ref(dir, last);
 +                      add_ref_entry(dir, last);
                        continue;
                }
                if (last &&
@@@ -375,12 -1229,11 +375,12 @@@ static struct packed_ref_cache *get_pac
  
                refs->packed = xcalloc(1, sizeof(*refs->packed));
                acquire_packed_ref_cache(refs->packed);
 -              refs->packed->root = create_dir_entry(refs, "", 0, 0);
 +              refs->packed->cache = create_ref_cache(&refs->base, NULL);
 +              refs->packed->cache->root->flag &= ~REF_INCOMPLETE;
                f = fopen(packed_refs_file, "r");
                if (f) {
                        stat_validity_update(&refs->packed->validity, fileno(f));
 -                      read_packed_refs(f, get_ref_dir(refs->packed->root));
 +                      read_packed_refs(f, get_ref_dir(refs->packed->cache->root));
                        fclose(f);
                }
        }
  
  static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
  {
 -      return get_ref_dir(packed_ref_cache->root);
 +      return get_ref_dir(packed_ref_cache->cache->root);
  }
  
  static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
@@@ -410,8 -1263,8 +410,8 @@@ static void add_packed_ref(struct files
  
        if (!packed_ref_cache->lock)
                die("internal error: packed refs not locked");
 -      add_ref(get_packed_ref_dir(packed_ref_cache),
 -              create_ref_entry(refname, sha1, REF_ISPACKED, 1));
 +      add_ref_entry(get_packed_ref_dir(packed_ref_cache),
 +                    create_ref_entry(refname, sha1, REF_ISPACKED, 1));
  }
  
  /*
   * (without recursing).  dirname must end with '/'.  dir must be the
   * directory entry corresponding to dirname.
   */
 -static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 +static void loose_fill_ref_dir(struct ref_store *ref_store,
 +                             struct ref_dir *dir, const char *dirname)
  {
 -      struct files_ref_store *refs = dir->ref_store;
 +      struct files_ref_store *refs =
 +              files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir");
        DIR *d;
        struct dirent *de;
        int dirnamelen = strlen(dirname);
                } else if (S_ISDIR(st.st_mode)) {
                        strbuf_addch(&refname, '/');
                        add_entry_to_dir(dir,
 -                                       create_dir_entry(refs, refname.buf,
 +                                       create_dir_entry(dir->cache, refname.buf,
                                                          refname.len, 1));
                } else {
                        if (!refs_resolve_ref_unsafe(&refs->base,
        strbuf_release(&refname);
        strbuf_release(&path);
        closedir(d);
 +
 +      /*
 +       * Manually add refs/bisect, which, being per-worktree, might
 +       * not appear in the directory listing for refs/ in the main
 +       * repo.
 +       */
 +      if (!strcmp(dirname, "refs/")) {
 +              int pos = search_ref_dir(dir, "refs/bisect/", 12);
 +
 +              if (pos < 0) {
 +                      struct ref_entry *child_entry = create_dir_entry(
 +                                      dir->cache, "refs/bisect/", 12, 1);
 +                      add_entry_to_dir(dir, child_entry);
 +              }
 +      }
  }
  
 -static struct ref_dir *get_loose_refs(struct files_ref_store *refs)
 +static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
  {
        if (!refs->loose) {
                /*
                 * are about to read the only subdirectory that can
                 * hold references:
                 */
 -              refs->loose = create_dir_entry(refs, "", 0, 0);
 +              refs->loose = create_ref_cache(&refs->base, loose_fill_ref_dir);
 +
 +              /* We're going to fill the top level ourselves: */
 +              refs->loose->root->flag &= ~REF_INCOMPLETE;
 +
                /*
 -               * Create an incomplete entry for "refs/":
 +               * Add an incomplete entry for "refs/" (to be filled
 +               * lazily):
                 */
 -              add_entry_to_dir(get_ref_dir(refs->loose),
 -                               create_dir_entry(refs, "refs/", 5, 1));
 +              add_entry_to_dir(get_ref_dir(refs->loose->root),
 +                               create_dir_entry(refs->loose, "refs/", 5, 1));
        }
 -      return get_ref_dir(refs->loose);
 +      return refs->loose;
  }
  
  /*
  static struct ref_entry *get_packed_ref(struct files_ref_store *refs,
                                        const char *refname)
  {
 -      return find_ref(get_packed_refs(refs), refname);
 +      return find_ref_entry(get_packed_refs(refs), refname);
  }
  
  /*
@@@ -733,7 -1564,7 +733,7 @@@ static void unlock_ref(struct ref_lock 
   *
   * If the reference doesn't already exist, verify that refname doesn't
   * have a D/F conflict with any existing references. extras and skip
 - * are passed to verify_refname_available_dir() for this check.
 + * are passed to refs_verify_refname_available() for this check.
   *
   * If mustexist is not set and the reference is not found or is
   * broken, lock the reference anyway but clear sha1.
   *
   * but it includes a lot more code to
   * - Deal with possible races with other processes
 - * - Avoid calling verify_refname_available_dir() when it can be
 + * - Avoid calling refs_verify_refname_available() when it can be
   *   avoided, namely if we were successfully able to read the ref
   * - Generate informative error messages in the case of failure
   */
@@@ -805,8 -1636,7 +805,8 @@@ retry
                        } else {
                                /*
                                 * The error message set by
 -                               * verify_refname_available_dir() is OK.
 +                               * refs_verify_refname_available() is
 +                               * OK.
                                 */
                                ret = TRANSACTION_NAME_CONFLICT;
                        }
                                goto error_return;
                        } else if (remove_dir_recursively(&ref_file,
                                                          REMOVE_DIR_EMPTY_ONLY)) {
 -                              if (verify_refname_available_dir(
 -                                                  refname, extras, skip,
 -                                                  get_loose_refs(refs),
 -                                                  err)) {
 +                              if (refs_verify_refname_available(
 +                                                  &refs->base, refname,
 +                                                  extras, skip, err)) {
                                        /*
                                         * The error message set by
                                         * verify_refname_available() is OK.
  
                /*
                 * If the ref did not exist and we are creating it,
 -               * make sure there is no existing packed ref whose
 -               * name begins with our refname, nor a packed ref
 -               * whose name is a proper prefix of our refname.
 +               * make sure there is no existing ref that conflicts
 +               * with refname:
                 */
 -              if (verify_refname_available_dir(
 -                                  refname, extras, skip,
 -                                  get_packed_refs(refs),
 -                                  err)) {
 +              if (refs_verify_refname_available(
 +                                  &refs->base, refname,
 +                                  extras, skip, err))
                        goto error_return;
 -              }
        }
  
        ret = 0;
@@@ -951,6 -1785,41 +951,6 @@@ out
        return ret;
  }
  
 -/*
 - * Peel the entry (if possible) and return its new peel_status.  If
 - * repeel is true, re-peel the entry even if there is an old peeled
 - * value that is already stored in it.
 - *
 - * It is OK to call this function with a packed reference entry that
 - * might be stale and might even refer to an object that has since
 - * been garbage-collected.  In such a case, if the entry has
 - * REF_KNOWS_PEELED then leave the status unchanged and return
 - * PEEL_PEELED or PEEL_NON_TAG; otherwise, return PEEL_INVALID.
 - */
 -static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
 -{
 -      enum peel_status status;
 -
 -      if (entry->flag & REF_KNOWS_PEELED) {
 -              if (repeel) {
 -                      entry->flag &= ~REF_KNOWS_PEELED;
 -                      oidclr(&entry->u.value.peeled);
 -              } else {
 -                      return is_null_oid(&entry->u.value.peeled) ?
 -                              PEEL_NON_TAG : PEEL_PEELED;
 -              }
 -      }
 -      if (entry->flag & REF_ISBROKEN)
 -              return PEEL_BROKEN;
 -      if (entry->flag & REF_ISSYMREF)
 -              return PEEL_IS_SYMREF;
 -
 -      status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash);
 -      if (status == PEEL_PEELED || status == PEEL_NON_TAG)
 -              entry->flag |= REF_KNOWS_PEELED;
 -      return status;
 -}
 -
  static int files_peel_ref(struct ref_store *ref_store,
                          const char *refname, unsigned char *sha1)
  {
@@@ -1066,6 -1935,7 +1066,6 @@@ static struct ref_iterator *files_ref_i
                const char *prefix, unsigned int flags)
  {
        struct files_ref_store *refs;
 -      struct ref_dir *loose_dir, *packed_dir;
        struct ref_iterator *loose_iter, *packed_iter;
        struct files_ref_iterator *iter;
        struct ref_iterator *ref_iterator;
         * condition if loose refs are migrated to the packed-refs
         * file by a simultaneous process, but our in-memory view is
         * from before the migration. We ensure this as follows:
 -       * First, we call prime_ref_dir(), which pre-reads the loose
 -       * references for the subtree into the cache. (If they've
 -       * already been read, that's OK; we only need to guarantee
 -       * that they're read before the packed refs, not *how much*
 -       * before.) After that, we call get_packed_ref_cache(), which
 -       * internally checks whether the packed-ref cache is up to
 -       * date with what is on disk, and re-reads it if not.
 +       * First, we call start the loose refs iteration with its
 +       * `prime_ref` argument set to true. This causes the loose
 +       * references in the subtree to be pre-read into the cache.
 +       * (If they've already been read, that's OK; we only need to
 +       * guarantee that they're read before the packed refs, not
 +       * *how much* before.) After that, we call
 +       * get_packed_ref_cache(), which internally checks whether the
 +       * packed-ref cache is up to date with what is on disk, and
 +       * re-reads it if not.
         */
  
 -      loose_dir = get_loose_refs(refs);
 -
 -      if (prefix && *prefix)
 -              loose_dir = find_containing_dir(loose_dir, prefix, 0);
 -
 -      if (loose_dir) {
 -              prime_ref_dir(loose_dir);
 -              loose_iter = cache_ref_iterator_begin(loose_dir);
 -      } else {
 -              /* There's nothing to iterate over. */
 -              loose_iter = empty_ref_iterator_begin();
 -      }
 +      loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
 +                                            prefix, 1);
  
        iter->packed_ref_cache = get_packed_ref_cache(refs);
        acquire_packed_ref_cache(iter->packed_ref_cache);
 -      packed_dir = get_packed_ref_dir(iter->packed_ref_cache);
 -
 -      if (prefix && *prefix)
 -              packed_dir = find_containing_dir(packed_dir, prefix, 0);
 -
 -      if (packed_dir) {
 -              packed_iter = cache_ref_iterator_begin(packed_dir);
 -      } else {
 -              /* There's nothing to iterate over. */
 -              packed_iter = empty_ref_iterator_begin();
 -      }
 +      packed_iter = cache_ref_iterator_begin(iter->packed_ref_cache->cache,
 +                                             prefix, 0);
  
        iter->iter0 = overlay_ref_iterator_begin(loose_iter, packed_iter);
        iter->flags = flags;
@@@ -1209,9 -2096,9 +1209,9 @@@ static struct ref_lock *lock_ref_sha1_b
                 */
                if (remove_empty_directories(&ref_file)) {
                        last_errno = errno;
 -                      if (!verify_refname_available_dir(
 -                                          refname, extras, skip,
 -                                          get_loose_refs(refs), err))
 +                      if (!refs_verify_refname_available(
 +                                          &refs->base,
 +                                          refname, extras, skip, err))
                                strbuf_addf(err, "there are still refs under '%s'",
                                            refname);
                        goto error_return;
        if (!resolved) {
                last_errno = errno;
                if (last_errno != ENOTDIR ||
 -                  !verify_refname_available_dir(
 -                                  refname, extras, skip,
 -                                  get_loose_refs(refs), err))
 +                  !refs_verify_refname_available(&refs->base, refname,
 +                                                 extras, skip, err))
                        strbuf_addf(err, "unable to resolve reference '%s': %s",
                                    refname, strerror(last_errno));
  
         * our refname.
         */
        if (is_null_oid(&lock->old_oid) &&
 -          verify_refname_available_dir(refname, extras, skip,
 -                                       get_packed_refs(refs),
 -                                       err)) {
 +          refs_verify_refname_available(&refs->base, refname,
 +                                        extras, skip, err)) {
                last_errno = ENOTDIR;
                goto error_return;
        }
   * Write an entry to the packed-refs file for the specified refname.
   * If peeled is non-NULL, write it as the entry's peeled value.
   */
 -static void write_packed_entry(FILE *fh, char *refname, unsigned char *sha1,
 -                             unsigned char *peeled)
 +static void write_packed_entry(FILE *fh, const char *refname,
 +                             const unsigned char *sha1,
 +                             const unsigned char *peeled)
  {
        fprintf_or_die(fh, "%s %s\n", sha1_to_hex(sha1), refname);
        if (peeled)
                fprintf_or_die(fh, "^%s\n", sha1_to_hex(peeled));
  }
  
 -/*
 - * An each_ref_entry_fn that writes the entry to a packed-refs file.
 - */
 -static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
 -{
 -      enum peel_status peel_status = peel_entry(entry, 0);
 -
 -      if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
 -              error("internal error: %s is not a valid packed reference!",
 -                    entry->name);
 -      write_packed_entry(cb_data, entry->name, entry->u.value.oid.hash,
 -                         peel_status == PEEL_PEELED ?
 -                         entry->u.value.peeled.hash : NULL);
 -      return 0;
 -}
 -
  /*
   * Lock the packed-refs file for writing. Flags is passed to
   * hold_lock_file_for_update(). Return 0 on success. On errors, set
@@@ -1328,10 -2232,9 +1328,10 @@@ static int commit_packed_refs(struct fi
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(refs);
 -      int error = 0;
 +      int ok, error = 0;
        int save_errno = 0;
        FILE *out;
 +      struct ref_iterator *iter;
  
        files_assert_main_repository(refs, "commit_packed_refs");
  
                die_errno("unable to fdopen packed-refs descriptor");
  
        fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
 -      do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
 -                               0, write_packed_entry_fn, out);
 +
 +      iter = cache_ref_iterator_begin(packed_ref_cache->cache, NULL, 0);
 +      while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
 +              struct object_id peeled;
 +              int peel_error = ref_iterator_peel(iter, &peeled);
 +
 +              write_packed_entry(out, iter->refname, iter->oid->hash,
 +                                 peel_error ? NULL : peeled.hash);
 +      }
 +
 +      if (ok != ITER_DONE)
 +              die("error while iterating over references");
  
        if (commit_lock_file(packed_ref_cache->lock)) {
                save_errno = errno;
@@@ -1392,6 -2285,65 +1392,6 @@@ struct ref_to_prune 
        char name[FLEX_ARRAY];
  };
  
 -struct pack_refs_cb_data {
 -      unsigned int flags;
 -      struct ref_dir *packed_refs;
 -      struct ref_to_prune *ref_to_prune;
 -};
 -
 -/*
 - * An each_ref_entry_fn that is run over loose references only.  If
 - * the loose reference can be packed, add an entry in the packed ref
 - * cache.  If the reference should be pruned, also add it to
 - * ref_to_prune in the pack_refs_cb_data.
 - */
 -static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
 -{
 -      struct pack_refs_cb_data *cb = cb_data;
 -      enum peel_status peel_status;
 -      struct ref_entry *packed_entry;
 -      int is_tag_ref = starts_with(entry->name, "refs/tags/");
 -
 -      /* Do not pack per-worktree refs: */
 -      if (ref_type(entry->name) != REF_TYPE_NORMAL)
 -              return 0;
 -
 -      /* ALWAYS pack tags */
 -      if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
 -              return 0;
 -
 -      /* Do not pack symbolic or broken refs: */
 -      if ((entry->flag & REF_ISSYMREF) || !entry_resolves_to_object(entry))
 -              return 0;
 -
 -      /* Add a packed ref cache entry equivalent to the loose entry. */
 -      peel_status = peel_entry(entry, 1);
 -      if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
 -              die("internal error peeling reference %s (%s)",
 -                  entry->name, oid_to_hex(&entry->u.value.oid));
 -      packed_entry = find_ref(cb->packed_refs, entry->name);
 -      if (packed_entry) {
 -              /* Overwrite existing packed entry with info from loose entry */
 -              packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
 -              oidcpy(&packed_entry->u.value.oid, &entry->u.value.oid);
 -      } else {
 -              packed_entry = create_ref_entry(entry->name, entry->u.value.oid.hash,
 -                                              REF_ISPACKED | REF_KNOWS_PEELED, 0);
 -              add_ref(cb->packed_refs, packed_entry);
 -      }
 -      oidcpy(&packed_entry->u.value.peeled, &entry->u.value.peeled);
 -
 -      /* Schedule the loose reference for pruning if requested. */
 -      if ((cb->flags & PACK_REFS_PRUNE)) {
 -              struct ref_to_prune *n;
 -              FLEX_ALLOC_STR(n, name, entry->name);
 -              hashcpy(n->sha1, entry->u.value.oid.hash);
 -              n->next = cb->ref_to_prune;
 -              cb->ref_to_prune = n;
 -      }
 -      return 0;
 -}
 -
  enum {
        REMOVE_EMPTY_PARENTS_REF = 0x01,
        REMOVE_EMPTY_PARENTS_REFLOG = 0x02
@@@ -1481,73 -2433,21 +1481,73 @@@ static int files_pack_refs(struct ref_s
        struct files_ref_store *refs =
                files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
                               "pack_refs");
 -      struct pack_refs_cb_data cbdata;
 -
 -      memset(&cbdata, 0, sizeof(cbdata));
 -      cbdata.flags = flags;
 +      struct ref_iterator *iter;
 +      struct ref_dir *packed_refs;
 +      int ok;
 +      struct ref_to_prune *refs_to_prune = NULL;
  
        lock_packed_refs(refs, LOCK_DIE_ON_ERROR);
 -      cbdata.packed_refs = get_packed_refs(refs);
 +      packed_refs = get_packed_refs(refs);
 +
 +      iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0);
 +      while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
 +              /*
 +               * If the loose reference can be packed, add an entry
 +               * in the packed ref cache. If the reference should be
 +               * pruned, also add it to refs_to_prune.
 +               */
 +              struct ref_entry *packed_entry;
 +              int is_tag_ref = starts_with(iter->refname, "refs/tags/");
 +
 +              /* Do not pack per-worktree refs: */
 +              if (ref_type(iter->refname) != REF_TYPE_NORMAL)
 +                      continue;
  
 -      do_for_each_entry_in_dir(get_loose_refs(refs), 0,
 -                               pack_if_possible_fn, &cbdata);
 +              /* ALWAYS pack tags */
 +              if (!(flags & PACK_REFS_ALL) && !is_tag_ref)
 +                      continue;
 +
 +              /* Do not pack symbolic or broken refs: */
 +              if (iter->flags & REF_ISSYMREF)
 +                      continue;
 +
 +              if (!ref_resolves_to_object(iter->refname, iter->oid, iter->flags))
 +                      continue;
 +
 +              /*
 +               * Create an entry in the packed-refs cache equivalent
 +               * to the one from the loose ref cache, except that
 +               * we don't copy the peeled status, because we want it
 +               * to be re-peeled.
 +               */
 +              packed_entry = find_ref_entry(packed_refs, iter->refname);
 +              if (packed_entry) {
 +                      /* Overwrite existing packed entry with info from loose entry */
 +                      packed_entry->flag = REF_ISPACKED;
 +                      oidcpy(&packed_entry->u.value.oid, iter->oid);
 +              } else {
 +                      packed_entry = create_ref_entry(iter->refname, iter->oid->hash,
 +                                                      REF_ISPACKED, 0);
 +                      add_ref_entry(packed_refs, packed_entry);
 +              }
 +              oidclr(&packed_entry->u.value.peeled);
 +
 +              /* Schedule the loose reference for pruning if requested. */
 +              if ((flags & PACK_REFS_PRUNE)) {
 +                      struct ref_to_prune *n;
 +                      FLEX_ALLOC_STR(n, name, iter->refname);
 +                      hashcpy(n->sha1, iter->oid->hash);
 +                      n->next = refs_to_prune;
 +                      refs_to_prune = n;
 +              }
 +      }
 +      if (ok != ITER_DONE)
 +              die("error while iterating over references");
  
        if (commit_packed_refs(refs))
                die_errno("unable to overwrite old ref-pack file");
  
 -      prune_refs(refs, cbdata.ref_to_prune);
 +      prune_refs(refs, refs_to_prune);
        return 0;
  }
  
@@@ -1588,7 -2488,7 +1588,7 @@@ static int repack_without_refs(struct f
  
        /* Remove refnames from the cache */
        for_each_string_list_item(refname, refnames)
 -              if (remove_entry(packed, refname->string) != -1)
 +              if (remove_entry_from_dir(packed, refname->string) != -1)
                        removed = 1;
        if (!removed) {
                /*
@@@ -1708,6 -2608,26 +1708,6 @@@ static int rename_tmp_log(struct files_
        return ret;
  }
  
 -static int files_verify_refname_available(struct ref_store *ref_store,
 -                                        const char *newname,
 -                                        const struct string_list *extras,
 -                                        const struct string_list *skip,
 -                                        struct strbuf *err)
 -{
 -      struct files_ref_store *refs =
 -              files_downcast(ref_store, REF_STORE_READ, "verify_refname_available");
 -      struct ref_dir *packed_refs = get_packed_refs(refs);
 -      struct ref_dir *loose_refs = get_loose_refs(refs);
 -
 -      if (verify_refname_available_dir(newname, extras, skip,
 -                                       packed_refs, err) ||
 -          verify_refname_available_dir(newname, extras, skip,
 -                                       loose_refs, err))
 -              return -1;
 -
 -      return 0;
 -}
 -
  static int write_ref_to_lockfile(struct ref_lock *lock,
                                 const unsigned char *sha1, struct strbuf *err);
  static int commit_ref_update(struct files_ref_store *refs,
@@@ -2240,6 -3160,50 +2240,6 @@@ static int files_create_symref(struct r
        return ret;
  }
  
 -int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg)
 -{
 -      /*
 -       * FIXME: this obviously will not work well for future refs
 -       * backends. This function needs to die.
 -       */
 -      struct files_ref_store *refs =
 -              files_downcast(get_main_ref_store(),
 -                             REF_STORE_WRITE,
 -                             "set_head_symref");
 -
 -      static struct lock_file head_lock;
 -      struct ref_lock *lock;
 -      struct strbuf head_path = STRBUF_INIT;
 -      const char *head_rel;
 -      int ret;
 -
 -      strbuf_addf(&head_path, "%s/HEAD", absolute_path(gitdir));
 -      if (hold_lock_file_for_update(&head_lock, head_path.buf,
 -                                    LOCK_NO_DEREF) < 0) {
 -              struct strbuf err = STRBUF_INIT;
 -              unable_to_lock_message(head_path.buf, errno, &err);
 -              error("%s", err.buf);
 -              strbuf_release(&err);
 -              strbuf_release(&head_path);
 -              return -1;
 -      }
 -
 -      /* head_rel will be "HEAD" for the main tree, "worktrees/wt/HEAD" for
 -         linked trees */
 -      head_rel = remove_leading_path(head_path.buf,
 -                                     absolute_path(get_git_common_dir()));
 -      /* to make use of create_symref_locked(), initialize ref_lock */
 -      lock = xcalloc(1, sizeof(struct ref_lock));
 -      lock->lk = &head_lock;
 -      lock->ref_name = xstrdup(head_rel);
 -
 -      ret = create_symref_locked(refs, lock, head_rel, target, logmsg);
 -
 -      unlock_ref(lock); /* will free lock */
 -      strbuf_release(&head_path);
 -      return ret;
 -}
 -
  static int files_reflog_exists(struct ref_store *ref_store,
                               const char *refname)
  {
@@@ -2273,7 -3237,7 +2273,7 @@@ static int show_one_reflog_ent(struct s
  {
        struct object_id ooid, noid;
        char *email_end, *message;
-       unsigned long timestamp;
+       timestamp_t timestamp;
        int tz;
        const char *p = sb->buf;
  
            parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
            !(email_end = strchr(p, '>')) ||
            email_end[1] != ' ' ||
-           !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+           !(timestamp = parse_timestamp(email_end + 2, &message, 10)) ||
            !message || message[0] != ' ' ||
            (message[1] != '+' && message[1] != '-') ||
            !isdigit(message[2]) || !isdigit(message[3]) ||
@@@ -2330,8 -3294,8 +2330,8 @@@ static int files_for_each_reflog_ent_re
  
        /* Jump to the end */
        if (fseek(logfp, 0, SEEK_END) < 0)
 -              return error("cannot seek back reflog for %s: %s",
 -                           refname, strerror(errno));
 +              ret = error("cannot seek back reflog for %s: %s",
 +                          refname, strerror(errno));
        pos = ftell(logfp);
        while (!ret && 0 < pos) {
                int cnt;
  
                /* Fill next block from the end */
                cnt = (sizeof(buf) < pos) ? sizeof(buf) : pos;
 -              if (fseek(logfp, pos - cnt, SEEK_SET))
 -                      return error("cannot seek back reflog for %s: %s",
 -                                   refname, strerror(errno));
 +              if (fseek(logfp, pos - cnt, SEEK_SET)) {
 +                      ret = error("cannot seek back reflog for %s: %s",
 +                                  refname, strerror(errno));
 +                      break;
 +              }
                nread = fread(buf, cnt, 1, logfp);
 -              if (nread != 1)
 -                      return error("cannot read %d bytes from reflog for %s: %s",
 -                                   cnt, refname, strerror(errno));
 +              if (nread != 1) {
 +                      ret = error("cannot read %d bytes from reflog for %s: %s",
 +                                  cnt, refname, strerror(errno));
 +                      break;
 +              }
                pos -= cnt;
  
                scanp = endp = buf + cnt;
@@@ -3154,7 -4114,7 +3154,7 @@@ struct expire_reflog_cb 
  };
  
  static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
-                            const char *email, unsigned long timestamp, int tz,
+                            const char *email, timestamp_t timestamp, int tz,
                             const char *message, void *cb_data)
  {
        struct expire_reflog_cb *cb = cb_data;
                        printf("prune %s", message);
        } else {
                if (cb->newlog) {
-                       fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
+                       fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s",
                                oid_to_hex(ooid), oid_to_hex(noid),
                                email, timestamp, tz, message);
                        oidcpy(&cb->last_kept_oid, noid);
@@@ -3330,6 -4290,7 +3330,6 @@@ struct ref_storage_be refs_be_files = 
  
        files_ref_iterator_begin,
        files_read_raw_ref,
 -      files_verify_refname_available,
  
        files_reflog_iterator_begin,
        files_for_each_reflog_ent,
index 4a487c014e08794a818704735101e85a6781c14a,9077ec2c330e216ee23f84de77e03008a8d874db..fba85e7da58fb124b409de502e9bb4f9a7d8f5b5
@@@ -1,6 -1,5 +1,6 @@@
  #include "cache.h"
  #include "refs.h"
 +#include "worktree.h"
  
  static const char *notnull(const char *arg, const char *name)
  {
@@@ -33,23 -32,6 +33,23 @@@ static const char **get_store(const cha
                strbuf_release(&sb);
  
                *refs = get_submodule_ref_store(gitdir);
 +      } else if (skip_prefix(argv[0], "worktree:", &gitdir)) {
 +              struct worktree **p, **worktrees = get_worktrees(0);
 +
 +              for (p = worktrees; *p; p++) {
 +                      struct worktree *wt = *p;
 +
 +                      if (!wt->id) {
 +                              /* special case for main worktree */
 +                              if (!strcmp(gitdir, "main"))
 +                                      break;
 +                      } else if (!strcmp(gitdir, wt->id))
 +                              break;
 +              }
 +              if (!*p)
 +                      die("no such worktree: %s", gitdir);
 +
 +              *refs = get_worktree_ref_store(*p);
        } else
                die("unknown backend %s", argv[0]);
  
@@@ -156,10 -138,10 +156,10 @@@ static int cmd_for_each_reflog(struct r
  }
  
  static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
-                      const char *committer, unsigned long timestamp,
+                      const char *committer, timestamp_t timestamp,
                       int tz, const char *msg, void *cb_data)
  {
-       printf("%s %s %s %lu %d %s\n",
+       printf("%s %s %s %"PRItime" %d %s\n",
               oid_to_hex(old_oid), oid_to_hex(new_oid),
               committer, timestamp, tz, msg);
        return 0;
diff --combined t/test-lib.sh
index 014136fb06dc689a4a2a2744cedcd0f2fda37d6d,8d25cb7c18343232eee39b54b55e890b682af2d9..26b3edfb2e4e725f589fc99986587aa3ecb48b75
@@@ -761,15 -761,10 +761,15 @@@ test_done () 
                        say "1..$test_count$skip_all"
                fi
  
 -              test -d "$remove_trash" &&
 -              cd "$(dirname "$remove_trash")" &&
 -              rm -rf "$(basename "$remove_trash")"
 +              if test -z "$debug"
 +              then
 +                      test -d "$TRASH_DIRECTORY" ||
 +                      error "Tests passed but trash directory already removed before test cleanup; aborting"
  
 +                      cd "$TRASH_DIRECTORY/.." &&
 +                      rm -fr "$TRASH_DIRECTORY" ||
 +                      error "Tests passed but test cleanup failed; aborting"
 +              fi
                test_at_end_hook_
  
                exit 0 ;;
@@@ -924,6 -919,7 +924,6 @@@ case "$TRASH_DIRECTORY" i
  /*) ;; # absolute path is good
   *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;;
  esac
 -test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
  rm -fr "$TRASH_DIRECTORY" || {
        GIT_EXIT_OK=t
        echo >&5 "FATAL: Cannot prepare test area"
@@@ -1168,3 -1164,6 +1168,6 @@@ build_option () 
  test_lazy_prereq LONG_IS_64BIT '
        test 8 -le "$(build_option sizeof-long)"
  '
+ test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
+ test_lazy_prereq TIME_T_IS_64BIT 'test-date time_t-is64bit'
diff --combined wt-status.c
index b7ade902fb4ef0cf30b1933a32433ab355b21395,1b1d644b85f7ee33bc0cb66bb87a09678b5e8433..7daa5320ac7538835285495782e74be5423ed76b
@@@ -1002,7 -1002,7 +1002,7 @@@ static void wt_longstatus_print_trackin
                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
                                 comment_line_char);
        else
 -              fputs("", s->fp);
 +              fputs("\n", s->fp);
  }
  
  static int has_unmerged(struct wt_status *s)
@@@ -1387,7 -1387,7 +1387,7 @@@ struct grab_1st_switch_cbdata 
  };
  
  static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
-                          const char *email, unsigned long timestamp, int tz,
+                          const char *email, timestamp_t timestamp, int tz,
                           const char *message, void *cb_data)
  {
        struct grab_1st_switch_cbdata *cb = cb_data;