Merge branch 'mh/packed-ref-store'
authorJunio C Hamano <gitster@pobox.com>
Tue, 22 Aug 2017 17:29:16 +0000 (10:29 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 22 Aug 2017 17:29:16 +0000 (10:29 -0700)
The "ref-store" code reorganization continues.

* mh/packed-ref-store: (32 commits)
  files-backend: cheapen refname_available check when locking refs
  packed_ref_store: handle a packed-refs file that is a symlink
  read_packed_refs(): die if `packed-refs` contains bogus data
  t3210: add some tests of bogus packed-refs file contents
  repack_without_refs(): don't lock or unlock the packed refs
  commit_packed_refs(): remove call to `packed_refs_unlock()`
  clear_packed_ref_cache(): don't protest if the lock is held
  packed_refs_unlock(), packed_refs_is_locked(): new functions
  packed_refs_lock(): report errors via a `struct strbuf *err`
  packed_refs_lock(): function renamed from lock_packed_refs()
  commit_packed_refs(): use a staging file separate from the lockfile
  commit_packed_refs(): report errors rather than dying
  packed_ref_store: make class into a subclass of `ref_store`
  packed-backend: new module for handling packed references
  packed_read_raw_ref(): new function, replacing `resolve_packed_ref()`
  packed_ref_store: support iteration
  packed_peel_ref(): new function, extracted from `files_peel_ref()`
  repack_without_refs(): take a `packed_ref_store *` parameter
  get_packed_ref(): take a `packed_ref_store *` parameter
  rollback_packed_refs(): take a `packed_ref_store *` parameter
  ...

1  2 
Makefile
refs.c
refs/files-backend.c
refs/packed-backend.c

diff --combined Makefile
+++ b/Makefile
@@@ -19,34 -19,16 +19,34 @@@ all:
  # have been written to the final string if enough space had been available.
  #
  # Define FREAD_READS_DIRECTORIES if you are on a system which succeeds
 -# when attempting to read from an fopen'ed directory.
 +# when attempting to read from an fopen'ed directory (or even to fopen
 +# it at all).
  #
  # Define NO_OPENSSL environment variable if you do not have OpenSSL.
  # This also implies BLK_SHA1.
  #
 -# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
 -# able to use Perl-compatible regular expressions.
 -#
 -# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
 -# /foo/bar/include and /foo/bar/lib directories.
 +# Define USE_LIBPCRE if you have and want to use libpcre. Various
 +# commands such as log and grep offer runtime options to use
 +# Perl-compatible regular expressions instead of standard or extended
 +# POSIX regular expressions.
 +#
 +# Currently USE_LIBPCRE is a synonym for USE_LIBPCRE1, define
 +# USE_LIBPCRE2 instead if you'd like to use version 2 of the PCRE
 +# library. The USE_LIBPCRE flag will likely be changed to mean v2 by
 +# default in future releases.
 +#
 +# When using USE_LIBPCRE1, define NO_LIBPCRE1_JIT if the PCRE v1
 +# library is compiled without --enable-jit. We will auto-detect
 +# whether the version of the PCRE v1 library in use has JIT support at
 +# all, but we unfortunately can't auto-detect whether JIT support
 +# hasn't been compiled in in an otherwise JIT-supporting version. If
 +# you have link-time errors about a missing `pcre_jit_exec` define
 +# this, or recompile PCRE v1 with --enable-jit.
 +#
 +# Define LIBPCREDIR=/foo/bar if your PCRE header and library files are
 +# in /foo/bar/include and /foo/bar/lib directories. Which version of
 +# PCRE this points to determined by the USE_LIBPCRE1 and USE_LIBPCRE2
 +# variables.
  #
  # Define HAVE_ALLOCA_H if you have working alloca(3) defined in that header.
  #
  # algorithm. This is slower, but may detect attempted collision attacks.
  # Takes priority over other *_SHA1 knobs.
  #
 +# Define DC_SHA1_SUBMODULE in addition to DC_SHA1 to use the
 +# sha1collisiondetection shipped as a submodule instead of the
 +# non-submodule copy in sha1dc/. This is an experimental option used
 +# by the git project to migrate to using sha1collisiondetection as a
 +# submodule.
 +#
  # Define OPENSSL_SHA1 environment variable when running make to link
  # with the SHA1 routine from openssl library.
  #
@@@ -742,7 -718,6 +742,7 @@@ LIB_OBJS += argv-array.
  LIB_OBJS += attr.o
  LIB_OBJS += base85.o
  LIB_OBJS += bisect.o
 +LIB_OBJS += blame.o
  LIB_OBJS += blob.o
  LIB_OBJS += branch.o
  LIB_OBJS += bulk-checkin.o
@@@ -842,11 -817,11 +842,12 @@@ LIB_OBJS += reflog-walk.
  LIB_OBJS += refs.o
  LIB_OBJS += refs/files-backend.o
  LIB_OBJS += refs/iterator.o
+ LIB_OBJS += refs/packed-backend.o
  LIB_OBJS += refs/ref-cache.o
  LIB_OBJS += ref-filter.o
  LIB_OBJS += remote.o
  LIB_OBJS += replace_object.o
 +LIB_OBJS += repository.o
  LIB_OBJS += rerere.o
  LIB_OBJS += resolve-undo.o
  LIB_OBJS += revision.o
@@@ -868,7 -843,6 +869,7 @@@ LIB_OBJS += streaming.
  LIB_OBJS += string-list.o
  LIB_OBJS += submodule.o
  LIB_OBJS += submodule-config.o
 +LIB_OBJS += sub-process.o
  LIB_OBJS += symlinks.o
  LIB_OBJS += tag.o
  LIB_OBJS += tempfile.o
@@@ -1010,10 -984,6 +1011,10 @@@ EXTLIBS 
  
  GIT_USER_AGENT = git/$(GIT_VERSION)
  
 +ifeq ($(wildcard sha1collisiondetection/lib/sha1.h),sha1collisiondetection/lib/sha1.h)
 +DC_SHA1_SUBMODULE = auto
 +endif
 +
  include config.mak.uname
  -include config.mak.autogen
  -include config.mak
@@@ -1022,19 -992,6 +1023,19 @@@ ifdef DEVELOPE
  CFLAGS += $(DEVELOPER_CFLAGS)
  endif
  
 +comma := ,
 +empty :=
 +space := $(empty) $(empty)
 +
 +ifdef SANITIZE
 +SANITIZERS := $(foreach flag,$(subst $(comma),$(space),$(SANITIZE)),$(flag))
 +BASIC_CFLAGS += -fsanitize=$(SANITIZE) -fno-sanitize-recover=$(SANITIZE)
 +BASIC_CFLAGS += -fno-omit-frame-pointer
 +ifneq ($(filter undefined,$(SANITIZERS)),)
 +BASIC_CFLAGS += -DNO_UNALIGNED_LOADS
 +endif
 +endif
 +
  ifndef sysconfdir
  ifeq ($(prefix),/usr)
  sysconfdir = /etc
@@@ -1129,29 -1086,13 +1130,29 @@@ ifdef NO_LIBGEN_
        COMPAT_OBJS += compat/basename.o
  endif
  
 -ifdef USE_LIBPCRE
 -      BASIC_CFLAGS += -DUSE_LIBPCRE
 -      ifdef LIBPCREDIR
 -              BASIC_CFLAGS += -I$(LIBPCREDIR)/include
 -              EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
 +USE_LIBPCRE1 ?= $(USE_LIBPCRE)
 +
 +ifneq (,$(USE_LIBPCRE1))
 +      ifdef USE_LIBPCRE2
 +$(error Only set USE_LIBPCRE1 (or its alias USE_LIBPCRE) or USE_LIBPCRE2, not both!)
        endif
 +
 +      BASIC_CFLAGS += -DUSE_LIBPCRE1
        EXTLIBS += -lpcre
 +
 +ifdef NO_LIBPCRE1_JIT
 +      BASIC_CFLAGS += -DNO_LIBPCRE1_JIT
 +endif
 +endif
 +
 +ifdef USE_LIBPCRE2
 +      BASIC_CFLAGS += -DUSE_LIBPCRE2
 +      EXTLIBS += -lpcre2-8
 +endif
 +
 +ifdef LIBPCREDIR
 +      BASIC_CFLAGS += -I$(LIBPCREDIR)/include
 +      EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
  endif
  
  ifdef HAVE_ALLOCA_H
@@@ -1472,22 -1413,9 +1473,22 @@@ ifdef APPLE_COMMON_CRYPT
        BASIC_CFLAGS += -DSHA1_APPLE
  else
        DC_SHA1 := YesPlease
 +ifdef DC_SHA1_SUBMODULE
 +      LIB_OBJS += sha1collisiondetection/lib/sha1.o
 +      LIB_OBJS += sha1collisiondetection/lib/ubc_check.o
 +      BASIC_CFLAGS += -DDC_SHA1_SUBMODULE
 +else
        LIB_OBJS += sha1dc/sha1.o
        LIB_OBJS += sha1dc/ubc_check.o
 -      BASIC_CFLAGS += -DSHA1_DC
 +endif
 +      BASIC_CFLAGS += \
 +              -DSHA1_DC \
 +              -DSHA1DC_NO_STANDARD_INCLUDES \
 +              -DSHA1DC_INIT_SAFE_HASH_DEFAULT=0 \
 +              -DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"cache.h\"" \
 +              -DSHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C="\"sha1dc_git.c\"" \
 +              -DSHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H="\"sha1dc_git.h\"" \
 +              -DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="\"git-compat-util.h\""
  endif
  endif
  endif
@@@ -2221,33 -2149,12 +2222,33 @@@ LOCALIZED_SH += t/t0200/test.s
  LOCALIZED_PERL += t/t0200/test.perl
  endif
  
 +## Note that this is meant to be run only by the localization coordinator
 +## under a very controlled condition, i.e. (1) it is to be run in a
 +## Git repository (not a tarball extract), (2) any local modifications
 +## will be lost.
 +## Gettext tools cannot work with our own custom PRItime type, so
 +## we replace PRItime with PRIuMAX.  We need to update this to
 +## PRIdMAX if we switch to a signed type later.
 +
  po/git.pot: $(GENERATED_H) FORCE
 +      # All modifications will be reverted at the end, so we do not
 +      # want to have any local change.
 +      git diff --quiet HEAD && git diff --quiet --cached
 +
 +      @for s in $(LOCALIZED_C) $(LOCALIZED_SH) $(LOCALIZED_PERL); \
 +      do \
 +              sed -e 's|PRItime|PRIuMAX|g' <"$$s" >"$$s+" && \
 +              cat "$$s+" >"$$s" && rm "$$s+"; \
 +      done
 +
        $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ $(XGETTEXT_FLAGS_C) $(LOCALIZED_C)
        $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ --join-existing $(XGETTEXT_FLAGS_SH) \
                $(LOCALIZED_SH)
        $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ --join-existing $(XGETTEXT_FLAGS_PERL) \
                $(LOCALIZED_PERL)
 +
 +      # Reverting the munged source, leaving only the updated $@
 +      git reset --hard
        mv $@+ $@
  
  .PHONY: pot
@@@ -2332,11 -2239,8 +2333,11 @@@ GIT-BUILD-OPTIONS: FORC
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@+
        @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+
        @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+
 -      @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+
 +      @echo USE_LIBPCRE1=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE1)))'\' >>$@+
 +      @echo USE_LIBPCRE2=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE2)))'\' >>$@+
 +      @echo NO_LIBPCRE1_JIT=\''$(subst ','\'',$(subst ','\'',$(NO_LIBPCRE1_JIT)))'\' >>$@+
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
 +      @echo NO_PTHREADS=\''$(subst ','\'',$(subst ','\'',$(NO_PTHREADS)))'\' >>$@+
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
        @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
        @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
@@@ -2367,9 -2271,6 +2368,9 @@@ endi
  ifdef GIT_PERF_MAKE_OPTS
        @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+
  endif
 +ifdef GIT_PERF_MAKE_COMMAND
 +      @echo GIT_PERF_MAKE_COMMAND=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_COMMAND)))'\' >>$@+
 +endif
  ifdef GIT_INTEROP_MAKE_OPTS
        @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
  endif
diff --combined refs.c
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -3,7 -3,6 +3,7 @@@
   */
  
  #include "cache.h"
 +#include "config.h"
  #include "hashmap.h"
  #include "lockfile.h"
  #include "iterator.h"
@@@ -174,6 -173,24 +174,24 @@@ int refname_is_safe(const char *refname
        return 1;
  }
  
+ /*
+  * 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
+  * does not exist, emit a warning and return false.
+  */
+ int ref_resolves_to_object(const char *refname,
+                          const struct object_id *oid,
+                          unsigned int flags)
+ {
+       if (flags & REF_ISBROKEN)
+               return 0;
+       if (!has_sha1_file(oid->hash)) {
+               error("%s does not point to a valid object!", refname);
+               return 0;
+       }
+       return 1;
+ }
  char *refs_resolve_refdup(struct ref_store *refs,
                          const char *refname, int resolve_flags,
                          unsigned char *sha1, int *flags)
@@@ -230,7 -247,7 +248,7 @@@ static int filter_refs(const char *refn
  {
        struct ref_filter *filter = (struct ref_filter *)data;
  
 -      if (wildmatch(filter->pattern, refname, 0, NULL))
 +      if (wildmatch(filter->pattern, refname, 0))
                return 0;
        return filter->fn(refname, oid, flags, filter->cb_data);
  }
@@@ -818,7 -835,7 +836,7 @@@ int read_ref_at(const char *refname, un
        for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
  
        if (!cb.reccnt) {
 -              if (flags & GET_SHA1_QUIETLY)
 +              if (flags & GET_OID_QUIETLY)
                        exit(128);
                else
                        die("Log for %s is empty.", refname);
@@@ -1160,7 -1177,7 +1178,7 @@@ int ref_is_hidden(const char *refname, 
                const char *match = hide_refs->items[i].string;
                const char *subject;
                int neg = 0;
 -              int len;
 +              const char *p;
  
                if (*match == '!') {
                        neg = 1;
                }
  
                /* refname can be NULL when namespaces are used. */
 -              if (!subject || !starts_with(subject, match))
 -                      continue;
 -              len = strlen(match);
 -              if (!subject[len] || subject[len] == '/')
 +              if (subject &&
 +                  skip_prefix(subject, match, &p) &&
 +                  (!*p || *p == '/'))
                        return !neg;
        }
        return 0;
@@@ -1524,8 -1542,7 +1542,8 @@@ struct ref_store_hash_entr
        char name[FLEX_ARRAY];
  };
  
 -static int ref_store_hash_cmp(const void *entry, const void *entry_or_key,
 +static int ref_store_hash_cmp(const void *unused_cmp_data,
 +                            const void *entry, const void *entry_or_key,
                              const void *keydata)
  {
        const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;
@@@ -1608,7 -1625,7 +1626,7 @@@ static void register_ref_store_map(stru
                                   const char *name)
  {
        if (!map->tablesize)
 -              hashmap_init(map, ref_store_hash_cmp, 0);
 +              hashmap_init(map, ref_store_hash_cmp, NULL, 0);
  
        if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))
                die("BUG: %s ref_store '%s' initialized twice", type, name);
diff --combined refs/files-backend.c
@@@ -1,8 -1,8 +1,9 @@@
  #include "../cache.h"
 +#include "../config.h"
  #include "../refs.h"
  #include "refs-internal.h"
  #include "ref-cache.h"
+ #include "packed-backend.h"
  #include "../iterator.h"
  #include "../dir-iterator.h"
  #include "../lockfile.h"
@@@ -15,39 -15,6 +16,6 @@@ struct ref_lock 
        struct object_id old_oid;
  };
  
- /*
-  * 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
-  * does not exist, emit a warning and return false.
-  */
- static int ref_resolves_to_object(const char *refname,
-                                 const struct object_id *oid,
-                                 unsigned int flags)
- {
-       if (flags & REF_ISBROKEN)
-               return 0;
-       if (!has_sha1_file(oid->hash)) {
-               error("%s does not point to a valid object!", refname);
-               return 0;
-       }
-       return 1;
- }
- struct packed_ref_cache {
-       struct ref_cache *cache;
-       /*
-        * Count of references to the data structure in this instance,
-        * including the pointer from files_ref_store::packed if any.
-        * The data will not be freed as long as the reference count
-        * is nonzero.
-        */
-       unsigned int referrers;
-       /* The metadata from when this packed-refs cache was read */
-       struct stat_validity validity;
- };
  /*
   * Future: need to be in "struct repository"
   * when doing a full libification.
@@@ -58,54 -25,12 +26,12 @@@ struct files_ref_store 
  
        char *gitdir;
        char *gitcommondir;
-       char *packed_refs_path;
  
        struct ref_cache *loose;
-       struct packed_ref_cache *packed;
  
-       /*
-        * Lock used for the "packed-refs" file. Note that this (and
-        * thus the enclosing `files_ref_store`) must not be freed.
-        */
-       struct lock_file packed_refs_lock;
+       struct ref_store *packed_ref_store;
  };
  
- /*
-  * Increment the reference count of *packed_refs.
-  */
- static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
- {
-       packed_refs->referrers++;
- }
- /*
-  * Decrease the reference count of *packed_refs.  If it goes to zero,
-  * free *packed_refs and return true; otherwise return false.
-  */
- static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
- {
-       if (!--packed_refs->referrers) {
-               free_ref_cache(packed_refs->cache);
-               stat_validity_clear(&packed_refs->validity);
-               free(packed_refs);
-               return 1;
-       } else {
-               return 0;
-       }
- }
- static void clear_packed_ref_cache(struct files_ref_store *refs)
- {
-       if (refs->packed) {
-               struct packed_ref_cache *packed_refs = refs->packed;
-               if (is_lock_file_locked(&refs->packed_refs_lock))
-                       die("BUG: packed-ref cache cleared while locked");
-               refs->packed = NULL;
-               release_packed_ref_cache(packed_refs);
-       }
- }
  static void clear_loose_ref_cache(struct files_ref_store *refs)
  {
        if (refs->loose) {
@@@ -132,7 -57,8 +58,8 @@@ static struct ref_store *files_ref_stor
        get_common_dir_noenv(&sb, gitdir);
        refs->gitcommondir = strbuf_detach(&sb, NULL);
        strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
-       refs->packed_refs_path = strbuf_detach(&sb, NULL);
+       refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
+       strbuf_release(&sb);
  
        return ref_store;
  }
@@@ -175,156 -101,6 +102,6 @@@ static struct files_ref_store *files_do
        return refs;
  }
  
- /* The length of a peeled reference line in packed-refs, including EOL: */
- #define PEELED_LINE_LENGTH 42
- /*
-  * The packed-refs header line that we write out.  Perhaps other
-  * traits will be added later.  The trailing space is required.
-  */
- static const char PACKED_REFS_HEADER[] =
-       "# pack-refs with: peeled fully-peeled \n";
- /*
-  * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
-  * Return a pointer to the refname within the line (null-terminated),
-  * or NULL if there was a problem.
-  */
- static const char *parse_ref_line(struct strbuf *line, struct object_id *oid)
- {
-       const char *ref;
-       if (parse_oid_hex(line->buf, oid, &ref) < 0)
-               return NULL;
-       if (!isspace(*ref++))
-               return NULL;
-       if (isspace(*ref))
-               return NULL;
-       if (line->buf[line->len - 1] != '\n')
-               return NULL;
-       line->buf[--line->len] = 0;
-       return ref;
- }
- /*
-  * Read from `packed_refs_file` into a newly-allocated
-  * `packed_ref_cache` and return it. The return value will already
-  * have its reference count incremented.
-  *
-  * A comment line of the form "# pack-refs with: " may contain zero or
-  * more traits. We interpret the traits as follows:
-  *
-  *   No traits:
-  *
-  *      Probably no references are peeled. But if the file contains a
-  *      peeled value for a reference, we will use it.
-  *
-  *   peeled:
-  *
-  *      References under "refs/tags/", if they *can* be peeled, *are*
-  *      peeled in this file. References outside of "refs/tags/" are
-  *      probably not peeled even if they could have been, but if we find
-  *      a peeled value for such a reference we will use it.
-  *
-  *   fully-peeled:
-  *
-  *      All references in the file that can be peeled are peeled.
-  *      Inversely (and this is more important), any references in the
-  *      file for which no peeled value is recorded is not peelable. This
-  *      trait should typically be written alongside "peeled" for
-  *      compatibility with older clients, but we do not require it
-  *      (i.e., "peeled" is a no-op if "fully-peeled" is set).
-  */
- static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file)
- {
-       FILE *f;
-       struct packed_ref_cache *packed_refs = xcalloc(1, sizeof(*packed_refs));
-       struct ref_entry *last = NULL;
-       struct strbuf line = STRBUF_INIT;
-       enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
-       struct ref_dir *dir;
-       acquire_packed_ref_cache(packed_refs);
-       packed_refs->cache = create_ref_cache(NULL, NULL);
-       packed_refs->cache->root->flag &= ~REF_INCOMPLETE;
-       f = fopen(packed_refs_file, "r");
-       if (!f) {
-               if (errno == ENOENT) {
-                       /*
-                        * This is OK; it just means that no
-                        * "packed-refs" file has been written yet,
-                        * which is equivalent to it being empty.
-                        */
-                       return packed_refs;
-               } else {
-                       die_errno("couldn't read %s", packed_refs_file);
-               }
-       }
-       stat_validity_update(&packed_refs->validity, fileno(f));
-       dir = get_ref_dir(packed_refs->cache->root);
-       while (strbuf_getwholeline(&line, f, '\n') != EOF) {
-               struct object_id oid;
-               const char *refname;
-               const char *traits;
-               if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
-                       if (strstr(traits, " fully-peeled "))
-                               peeled = PEELED_FULLY;
-                       else if (strstr(traits, " peeled "))
-                               peeled = PEELED_TAGS;
-                       /* perhaps other traits later as well */
-                       continue;
-               }
-               refname = parse_ref_line(&line, &oid);
-               if (refname) {
-                       int flag = REF_ISPACKED;
-                       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
-                               if (!refname_is_safe(refname))
-                                       die("packed refname is dangerous: %s", refname);
-                               oidclr(&oid);
-                               flag |= REF_BAD_NAME | REF_ISBROKEN;
-                       }
-                       last = create_ref_entry(refname, &oid, flag);
-                       if (peeled == PEELED_FULLY ||
-                           (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
-                               last->flag |= REF_KNOWS_PEELED;
-                       add_ref_entry(dir, last);
-                       continue;
-               }
-               if (last &&
-                   line.buf[0] == '^' &&
-                   line.len == PEELED_LINE_LENGTH &&
-                   line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
-                   !get_oid_hex(line.buf + 1, &oid)) {
-                       oidcpy(&last->u.value.peeled, &oid);
-                       /*
-                        * Regardless of what the file header said,
-                        * we definitely know the value of *this*
-                        * reference:
-                        */
-                       last->flag |= REF_KNOWS_PEELED;
-               }
-       }
-       fclose(f);
-       strbuf_release(&line);
-       return packed_refs;
- }
- static const char *files_packed_refs_path(struct files_ref_store *refs)
- {
-       return refs->packed_refs_path;
- }
  static void files_reflog_path(struct files_ref_store *refs,
                              struct strbuf *sb,
                              const char *refname)
@@@ -370,70 -146,6 +147,6 @@@ static void files_ref_path(struct files
        }
  }
  
- /*
-  * Check that the packed refs cache (if any) still reflects the
-  * contents of the file. If not, clear the cache.
-  */
- static void validate_packed_ref_cache(struct files_ref_store *refs)
- {
-       if (refs->packed &&
-           !stat_validity_check(&refs->packed->validity,
-                                files_packed_refs_path(refs)))
-               clear_packed_ref_cache(refs);
- }
- /*
-  * Get the packed_ref_cache for the specified files_ref_store,
-  * creating and populating it if it hasn't been read before or if the
-  * file has been changed (according to its `validity` field) since it
-  * was last read. On the other hand, if we hold the lock, then assume
-  * that the file hasn't been changed out from under us, so skip the
-  * extra `stat()` call in `stat_validity_check()`.
-  */
- static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)
- {
-       const char *packed_refs_file = files_packed_refs_path(refs);
-       if (!is_lock_file_locked(&refs->packed_refs_lock))
-               validate_packed_ref_cache(refs);
-       if (!refs->packed)
-               refs->packed = read_packed_refs(packed_refs_file);
-       return refs->packed;
- }
- static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
- {
-       return get_ref_dir(packed_ref_cache->cache->root);
- }
- static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
- {
-       return get_packed_ref_dir(get_packed_ref_cache(refs));
- }
- /*
-  * Add a reference to the in-memory packed reference cache.  This may
-  * only be called while the packed-refs file is locked (see
-  * lock_packed_refs()).  To actually write the packed-refs file, call
-  * commit_packed_refs().
-  */
- static void add_packed_ref(struct files_ref_store *refs,
-                          const char *refname, const struct object_id *oid)
- {
-       struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs);
-       if (!is_lock_file_locked(&refs->packed_refs_lock))
-               die("BUG: packed refs not locked");
-       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
-               die("Reference has invalid format: '%s'", refname);
-       add_ref_entry(get_packed_ref_dir(packed_ref_cache),
-                     create_ref_entry(refname, oid, REF_ISPACKED));
- }
  /*
   * Read the loose references from the namespace dirname into dir
   * (without recursing).  dirname must end with '/'.  dir must be the
@@@ -556,39 -268,6 +269,6 @@@ static struct ref_cache *get_loose_ref_
        return refs->loose;
  }
  
- /*
-  * Return the ref_entry for the given refname from the packed
-  * references.  If it does not exist, return NULL.
-  */
- static struct ref_entry *get_packed_ref(struct files_ref_store *refs,
-                                       const char *refname)
- {
-       return find_ref_entry(get_packed_refs(refs), refname);
- }
- /*
-  * A loose ref file doesn't exist; check for a packed ref.
-  */
- static int resolve_packed_ref(struct files_ref_store *refs,
-                             const char *refname,
-                             unsigned char *sha1, unsigned int *flags)
- {
-       struct ref_entry *entry;
-       /*
-        * The loose reference file does not exist; check for a packed
-        * reference.
-        */
-       entry = get_packed_ref(refs, refname);
-       if (entry) {
-               hashcpy(sha1, entry->u.value.oid.hash);
-               *flags |= REF_ISPACKED;
-               return 0;
-       }
-       /* refname is not a packed reference. */
-       return -1;
- }
  static int files_read_raw_ref(struct ref_store *ref_store,
                              const char *refname, unsigned char *sha1,
                              struct strbuf *referent, unsigned int *type)
@@@ -632,7 -311,8 +312,8 @@@ stat_ref
        if (lstat(path, &st) < 0) {
                if (errno != ENOENT)
                        goto out;
-               if (resolve_packed_ref(refs, refname, sha1, type)) {
+               if (refs_read_raw_ref(refs->packed_ref_store, refname,
+                                     sha1, referent, type)) {
                        errno = ENOENT;
                        goto out;
                }
                 * ref is supposed to be, there could still be a
                 * packed ref:
                 */
-               if (resolve_packed_ref(refs, refname, sha1, type)) {
+               if (refs_read_raw_ref(refs->packed_ref_store, refname,
+                                     sha1, referent, type)) {
                        errno = EISDIR;
                        goto out;
                }
@@@ -950,11 -631,11 +632,11 @@@ retry
  
                /*
                 * If the ref did not exist and we are creating it,
-                * make sure there is no existing ref that conflicts
-                * with refname:
+                * make sure there is no existing packed ref that
+                * conflicts with refname:
                 */
                if (refs_verify_refname_available(
-                                   &refs->base, refname,
+                                   refs->packed_ref_store, refname,
                                    extras, skip, err))
                        goto error_return;
        }
@@@ -1001,15 -682,9 +683,9 @@@ static int files_peel_ref(struct ref_st
         * be expensive and (b) loose references anyway usually do not
         * have REF_KNOWS_PEELED.
         */
-       if (flag & REF_ISPACKED) {
-               struct ref_entry *r = get_packed_ref(refs, refname);
-               if (r) {
-                       if (peel_entry(r, 0))
-                               return -1;
-                       hashcpy(sha1, r->u.value.peeled.hash);
-                       return 0;
-               }
-       }
+       if (flag & REF_ISPACKED &&
+           !refs_peel_ref(refs->packed_ref_store, refname, sha1))
+               return 0;
  
        return peel_object(base, sha1);
  }
  struct files_ref_iterator {
        struct ref_iterator base;
  
-       struct packed_ref_cache *packed_ref_cache;
        struct ref_iterator *iter0;
        unsigned int flags;
  };
@@@ -1070,7 -744,6 +745,6 @@@ static int files_ref_iterator_abort(str
        if (iter->iter0)
                ok = ref_iterator_abort(iter->iter0);
  
-       release_packed_ref_cache(iter->packed_ref_cache);
        base_ref_iterator_free(ref_iterator);
        return ok;
  }
@@@ -1112,18 -785,28 +786,28 @@@ static struct ref_iterator *files_ref_i
         * (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.
+        * packed_ref_iterator_begin(), which internally checks
+        * whether the packed-ref cache is up to date with what is on
+        * disk, and re-reads it if not.
         */
  
        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_iter = cache_ref_iterator_begin(iter->packed_ref_cache->cache,
-                                              prefix, 0);
+       /*
+        * The packed-refs file might contain broken references, for
+        * example an old version of a reference that points at an
+        * object that has since been garbage-collected. This is OK as
+        * long as there is a corresponding loose reference that
+        * overrides it, and we don't want to emit an error message in
+        * this case. So ask the packed_ref_store for all of its
+        * references, and (if needed) do our own check for broken
+        * ones in files_ref_iterator_advance(), after we have merged
+        * the packed and loose references.
+        */
+       packed_iter = refs_ref_iterator_begin(
+                       refs->packed_ref_store, prefix, 0,
+                       DO_FOR_EACH_INCLUDE_BROKEN);
  
        iter->iter0 = overlay_ref_iterator_begin(loose_iter, packed_iter);
        iter->flags = flags;
@@@ -1255,7 -938,7 +939,7 @@@ static struct ref_lock *lock_ref_sha1_b
         * our refname.
         */
        if (is_null_oid(&lock->old_oid) &&
-           refs_verify_refname_available(&refs->base, refname,
+           refs_verify_refname_available(refs->packed_ref_store, refname,
                                          extras, skip, err)) {
                last_errno = ENOTDIR;
                goto error_return;
        return lock;
  }
  
- /*
-  * 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, 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));
- }
- /*
-  * Lock the packed-refs file for writing. Flags is passed to
-  * hold_lock_file_for_update(). Return 0 on success. On errors, set
-  * errno appropriately and return a nonzero value.
-  */
- static int lock_packed_refs(struct files_ref_store *refs, int flags)
- {
-       static int timeout_configured = 0;
-       static int timeout_value = 1000;
-       struct packed_ref_cache *packed_ref_cache;
-       files_assert_main_repository(refs, "lock_packed_refs");
-       if (!timeout_configured) {
-               git_config_get_int("core.packedrefstimeout", &timeout_value);
-               timeout_configured = 1;
-       }
-       if (hold_lock_file_for_update_timeout(
-                           &refs->packed_refs_lock, files_packed_refs_path(refs),
-                           flags, timeout_value) < 0)
-               return -1;
-       /*
-        * Now that we hold the `packed-refs` lock, make sure that our
-        * cache matches the current version of the file. Normally
-        * `get_packed_ref_cache()` does that for us, but that
-        * function assumes that when the file is locked, any existing
-        * cache is still valid. We've just locked the file, but it
-        * might have changed the moment *before* we locked it.
-        */
-       validate_packed_ref_cache(refs);
-       packed_ref_cache = get_packed_ref_cache(refs);
-       /* Increment the reference count to prevent it from being freed: */
-       acquire_packed_ref_cache(packed_ref_cache);
-       return 0;
- }
- /*
-  * Write the current version of the packed refs cache from memory to
-  * disk. The packed-refs file must already be locked for writing (see
-  * lock_packed_refs()). Return zero on success. On errors, set errno
-  * and return a nonzero value
-  */
- static int commit_packed_refs(struct files_ref_store *refs)
- {
-       struct packed_ref_cache *packed_ref_cache =
-               get_packed_ref_cache(refs);
-       int ok, error = 0;
-       int save_errno = 0;
-       FILE *out;
-       struct ref_iterator *iter;
-       files_assert_main_repository(refs, "commit_packed_refs");
-       if (!is_lock_file_locked(&refs->packed_refs_lock))
-               die("BUG: packed-refs not locked");
-       out = fdopen_lock_file(&refs->packed_refs_lock, "w");
-       if (!out)
-               die_errno("unable to fdopen packed-refs descriptor");
-       fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
-       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(&refs->packed_refs_lock)) {
-               save_errno = errno;
-               error = -1;
-       }
-       release_packed_ref_cache(packed_ref_cache);
-       errno = save_errno;
-       return error;
- }
- /*
-  * Rollback the lockfile for the packed-refs file, and discard the
-  * in-memory packed reference cache.  (The packed-refs file will be
-  * read anew if it is needed again after this function is called.)
-  */
- static void rollback_packed_refs(struct files_ref_store *refs)
- {
-       struct packed_ref_cache *packed_ref_cache =
-               get_packed_ref_cache(refs);
-       files_assert_main_repository(refs, "rollback_packed_refs");
-       if (!is_lock_file_locked(&refs->packed_refs_lock))
-               die("BUG: packed-refs not locked");
-       rollback_lock_file(&refs->packed_refs_lock);
-       release_packed_ref_cache(packed_ref_cache);
-       clear_packed_ref_cache(refs);
- }
  struct ref_to_prune {
        struct ref_to_prune *next;
        unsigned char sha1[20];
@@@ -1527,12 -1092,11 +1093,11 @@@ static int files_pack_refs(struct ref_s
                files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
                               "pack_refs");
        struct ref_iterator *iter;
-       struct ref_dir *packed_refs;
        int ok;
        struct ref_to_prune *refs_to_prune = NULL;
+       struct strbuf err = STRBUF_INIT;
  
-       lock_packed_refs(refs, LOCK_DIE_ON_ERROR);
-       packed_refs = get_packed_refs(refs);
+       packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
  
        iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0);
        while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
                 * in the packed ref cache. If the reference should be
                 * pruned, also add it to refs_to_prune.
                 */
-               struct ref_entry *packed_entry;
                if (!should_pack_ref(iter->refname, iter->oid, iter->flags,
                                     flags))
                        continue;
                 * 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,
-                                                       REF_ISPACKED);
-                       add_ref_entry(packed_refs, packed_entry);
-               }
-               oidclr(&packed_entry->u.value.peeled);
+               add_packed_ref(refs->packed_ref_store, iter->refname, iter->oid);
  
                /* Schedule the loose reference for pruning if requested. */
                if ((flags & PACK_REFS_PRUNE)) {
        if (ok != ITER_DONE)
                die("error while iterating over references");
  
-       if (commit_packed_refs(refs))
-               die_errno("unable to overwrite old ref-pack file");
+       if (commit_packed_refs(refs->packed_ref_store, &err))
+               die("unable to overwrite old ref-pack file: %s", err.buf);
+       packed_refs_unlock(refs->packed_ref_store);
  
        prune_refs(refs, refs_to_prune);
+       strbuf_release(&err);
        return 0;
  }
  
- /*
-  * Rewrite the packed-refs file, omitting any refs listed in
-  * 'refnames'. On error, leave packed-refs unchanged, write an error
-  * message to 'err', and return a nonzero value.
-  *
-  * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
-  */
- static int repack_without_refs(struct files_ref_store *refs,
-                              struct string_list *refnames, struct strbuf *err)
- {
-       struct ref_dir *packed;
-       struct string_list_item *refname;
-       int ret, needs_repacking = 0, removed = 0;
-       files_assert_main_repository(refs, "repack_without_refs");
-       assert(err);
-       /* Look for a packed ref */
-       for_each_string_list_item(refname, refnames) {
-               if (get_packed_ref(refs, refname->string)) {
-                       needs_repacking = 1;
-                       break;
-               }
-       }
-       /* Avoid locking if we have nothing to do */
-       if (!needs_repacking)
-               return 0; /* no refname exists in packed refs */
-       if (lock_packed_refs(refs, 0)) {
-               unable_to_lock_message(files_packed_refs_path(refs), errno, err);
-               return -1;
-       }
-       packed = get_packed_refs(refs);
-       /* Remove refnames from the cache */
-       for_each_string_list_item(refname, refnames)
-               if (remove_entry_from_dir(packed, refname->string) != -1)
-                       removed = 1;
-       if (!removed) {
-               /*
-                * All packed entries disappeared while we were
-                * acquiring the lock.
-                */
-               rollback_packed_refs(refs);
-               return 0;
-       }
-       /* Write what remains */
-       ret = commit_packed_refs(refs);
-       if (ret)
-               strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
-                           strerror(errno));
-       return ret;
- }
  static int files_delete_refs(struct ref_store *ref_store, const char *msg,
                             struct string_list *refnames, unsigned int flags)
  {
        if (!refnames->nr)
                return 0;
  
-       result = repack_without_refs(refs, refnames, &err);
-       if (result) {
-               /*
-                * If we failed to rewrite the packed-refs file, then
-                * it is unsafe to try to remove loose refs, because
-                * doing so might expose an obsolete packed value for
-                * a reference that might even point at an object that
-                * has been garbage collected.
-                */
-               if (refnames->nr == 1)
-                       error(_("could not delete reference %s: %s"),
-                             refnames->items[0].string, err.buf);
-               else
-                       error(_("could not delete references: %s"), err.buf);
+       if (packed_refs_lock(refs->packed_ref_store, 0, &err))
+               goto error;
  
-               goto out;
+       if (repack_without_refs(refs->packed_ref_store, refnames, &err)) {
+               packed_refs_unlock(refs->packed_ref_store);
+               goto error;
        }
  
+       packed_refs_unlock(refs->packed_ref_store);
        for (i = 0; i < refnames->nr; i++) {
                const char *refname = refnames->items[i].string;
  
                        result |= error(_("could not remove reference %s"), refname);
        }
  
- out:
        strbuf_release(&err);
        return result;
+ error:
+       /*
+        * If we failed to rewrite the packed-refs file, then it is
+        * unsafe to try to remove loose refs, because doing so might
+        * expose an obsolete packed value for a reference that might
+        * even point at an object that has been garbage collected.
+        */
+       if (refnames->nr == 1)
+               error(_("could not delete reference %s: %s"),
+                     refnames->items[0].string, err.buf);
+       else
+               error(_("could not delete references: %s"), err.buf);
+       strbuf_release(&err);
+       return -1;
  }
  
  /*
@@@ -2959,7 -2464,8 +2465,7 @@@ static int files_transaction_prepare(st
                                       head_oid.hash, &head_type);
  
        if (head_ref && !(head_type & REF_ISSYMREF)) {
 -              free(head_ref);
 -              head_ref = NULL;
 +              FREE_AND_NULL(head_ref);
        }
  
        /*
@@@ -3070,11 -2576,19 +2576,19 @@@ static int files_transaction_finish(str
                }
        }
  
-       if (repack_without_refs(refs, &refs_to_delete, err)) {
+       if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
  
+       if (repack_without_refs(refs->packed_ref_store, &refs_to_delete, err)) {
+               ret = TRANSACTION_GENERIC_ERROR;
+               packed_refs_unlock(refs->packed_ref_store);
+               goto cleanup;
+       }
+       packed_refs_unlock(refs->packed_ref_store);
        /* Delete the reflogs of any references that were deleted: */
        for_each_string_list_item(ref_to_delete, &refs_to_delete) {
                strbuf_reset(&sb);
@@@ -3181,9 -2695,7 +2695,7 @@@ static int files_initial_transaction_co
                }
        }
  
-       if (lock_packed_refs(refs, 0)) {
-               strbuf_addf(err, "unable to lock packed-refs file: %s",
-                           strerror(errno));
+       if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
  
                if ((update->flags & REF_HAVE_NEW) &&
                    !is_null_oid(&update->new_oid))
-                       add_packed_ref(refs, update->refname,
+                       add_packed_ref(refs->packed_ref_store, update->refname,
                                       &update->new_oid);
        }
  
-       if (commit_packed_refs(refs)) {
-               strbuf_addf(err, "unable to commit packed-refs file: %s",
-                           strerror(errno));
+       if (commit_packed_refs(refs->packed_ref_store, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
  
  cleanup:
+       packed_refs_unlock(refs->packed_ref_store);
        transaction->state = REF_TRANSACTION_CLOSED;
        string_list_clear(&affected_refnames, 0);
        return ret;
diff --combined refs/packed-backend.c
index 0000000,59e7d1a..412c850
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,884 +1,885 @@@
+ #include "../cache.h"
++#include "../config.h"
+ #include "../refs.h"
+ #include "refs-internal.h"
+ #include "ref-cache.h"
+ #include "packed-backend.h"
+ #include "../iterator.h"
+ #include "../lockfile.h"
+ struct packed_ref_cache {
+       struct ref_cache *cache;
+       /*
+        * Count of references to the data structure in this instance,
+        * including the pointer from files_ref_store::packed if any.
+        * The data will not be freed as long as the reference count
+        * is nonzero.
+        */
+       unsigned int referrers;
+       /* The metadata from when this packed-refs cache was read */
+       struct stat_validity validity;
+ };
+ /*
+  * Increment the reference count of *packed_refs.
+  */
+ static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
+ {
+       packed_refs->referrers++;
+ }
+ /*
+  * Decrease the reference count of *packed_refs.  If it goes to zero,
+  * free *packed_refs and return true; otherwise return false.
+  */
+ static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
+ {
+       if (!--packed_refs->referrers) {
+               free_ref_cache(packed_refs->cache);
+               stat_validity_clear(&packed_refs->validity);
+               free(packed_refs);
+               return 1;
+       } else {
+               return 0;
+       }
+ }
+ /*
+  * A container for `packed-refs`-related data. It is not (yet) a
+  * `ref_store`.
+  */
+ struct packed_ref_store {
+       struct ref_store base;
+       unsigned int store_flags;
+       /* The path of the "packed-refs" file: */
+       char *path;
+       /*
+        * A cache of the values read from the `packed-refs` file, if
+        * it might still be current; otherwise, NULL.
+        */
+       struct packed_ref_cache *cache;
+       /*
+        * Lock used for the "packed-refs" file. Note that this (and
+        * thus the enclosing `packed_ref_store`) must not be freed.
+        */
+       struct lock_file lock;
+       /*
+        * Temporary file used when rewriting new contents to the
+        * "packed-refs" file. Note that this (and thus the enclosing
+        * `packed_ref_store`) must not be freed.
+        */
+       struct tempfile tempfile;
+ };
+ struct ref_store *packed_ref_store_create(const char *path,
+                                         unsigned int store_flags)
+ {
+       struct packed_ref_store *refs = xcalloc(1, sizeof(*refs));
+       struct ref_store *ref_store = (struct ref_store *)refs;
+       base_ref_store_init(ref_store, &refs_be_packed);
+       refs->store_flags = store_flags;
+       refs->path = xstrdup(path);
+       return ref_store;
+ }
+ /*
+  * Die if refs is not the main ref store. caller is used in any
+  * necessary error messages.
+  */
+ static void packed_assert_main_repository(struct packed_ref_store *refs,
+                                         const char *caller)
+ {
+       if (refs->store_flags & REF_STORE_MAIN)
+               return;
+       die("BUG: operation %s only allowed for main ref store", caller);
+ }
+ /*
+  * Downcast `ref_store` to `packed_ref_store`. Die if `ref_store` is
+  * not a `packed_ref_store`. Also die if `packed_ref_store` doesn't
+  * support at least the flags specified in `required_flags`. `caller`
+  * is used in any necessary error messages.
+  */
+ static struct packed_ref_store *packed_downcast(struct ref_store *ref_store,
+                                               unsigned int required_flags,
+                                               const char *caller)
+ {
+       struct packed_ref_store *refs;
+       if (ref_store->be != &refs_be_packed)
+               die("BUG: ref_store is type \"%s\" not \"packed\" in %s",
+                   ref_store->be->name, caller);
+       refs = (struct packed_ref_store *)ref_store;
+       if ((refs->store_flags & required_flags) != required_flags)
+               die("BUG: unallowed operation (%s), requires %x, has %x\n",
+                   caller, required_flags, refs->store_flags);
+       return refs;
+ }
+ static void clear_packed_ref_cache(struct packed_ref_store *refs)
+ {
+       if (refs->cache) {
+               struct packed_ref_cache *cache = refs->cache;
+               refs->cache = NULL;
+               release_packed_ref_cache(cache);
+       }
+ }
+ /* The length of a peeled reference line in packed-refs, including EOL: */
+ #define PEELED_LINE_LENGTH 42
+ /*
+  * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
+  * Return a pointer to the refname within the line (null-terminated),
+  * or NULL if there was a problem.
+  */
+ static const char *parse_ref_line(struct strbuf *line, struct object_id *oid)
+ {
+       const char *ref;
+       if (parse_oid_hex(line->buf, oid, &ref) < 0)
+               return NULL;
+       if (!isspace(*ref++))
+               return NULL;
+       if (isspace(*ref))
+               return NULL;
+       if (line->buf[line->len - 1] != '\n')
+               return NULL;
+       line->buf[--line->len] = 0;
+       return ref;
+ }
+ /*
+  * Read from `packed_refs_file` into a newly-allocated
+  * `packed_ref_cache` and return it. The return value will already
+  * have its reference count incremented.
+  *
+  * A comment line of the form "# pack-refs with: " may contain zero or
+  * more traits. We interpret the traits as follows:
+  *
+  *   No traits:
+  *
+  *      Probably no references are peeled. But if the file contains a
+  *      peeled value for a reference, we will use it.
+  *
+  *   peeled:
+  *
+  *      References under "refs/tags/", if they *can* be peeled, *are*
+  *      peeled in this file. References outside of "refs/tags/" are
+  *      probably not peeled even if they could have been, but if we find
+  *      a peeled value for such a reference we will use it.
+  *
+  *   fully-peeled:
+  *
+  *      All references in the file that can be peeled are peeled.
+  *      Inversely (and this is more important), any references in the
+  *      file for which no peeled value is recorded is not peelable. This
+  *      trait should typically be written alongside "peeled" for
+  *      compatibility with older clients, but we do not require it
+  *      (i.e., "peeled" is a no-op if "fully-peeled" is set).
+  */
+ static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file)
+ {
+       FILE *f;
+       struct packed_ref_cache *packed_refs = xcalloc(1, sizeof(*packed_refs));
+       struct ref_entry *last = NULL;
+       struct strbuf line = STRBUF_INIT;
+       enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
+       struct ref_dir *dir;
+       acquire_packed_ref_cache(packed_refs);
+       packed_refs->cache = create_ref_cache(NULL, NULL);
+       packed_refs->cache->root->flag &= ~REF_INCOMPLETE;
+       f = fopen(packed_refs_file, "r");
+       if (!f) {
+               if (errno == ENOENT) {
+                       /*
+                        * This is OK; it just means that no
+                        * "packed-refs" file has been written yet,
+                        * which is equivalent to it being empty.
+                        */
+                       return packed_refs;
+               } else {
+                       die_errno("couldn't read %s", packed_refs_file);
+               }
+       }
+       stat_validity_update(&packed_refs->validity, fileno(f));
+       dir = get_ref_dir(packed_refs->cache->root);
+       while (strbuf_getwholeline(&line, f, '\n') != EOF) {
+               struct object_id oid;
+               const char *refname;
+               const char *traits;
+               if (!line.len || line.buf[line.len - 1] != '\n')
+                       die("unterminated line in %s: %s", packed_refs_file, line.buf);
+               if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
+                       if (strstr(traits, " fully-peeled "))
+                               peeled = PEELED_FULLY;
+                       else if (strstr(traits, " peeled "))
+                               peeled = PEELED_TAGS;
+                       /* perhaps other traits later as well */
+                       continue;
+               }
+               refname = parse_ref_line(&line, &oid);
+               if (refname) {
+                       int flag = REF_ISPACKED;
+                       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+                               if (!refname_is_safe(refname))
+                                       die("packed refname is dangerous: %s", refname);
+                               oidclr(&oid);
+                               flag |= REF_BAD_NAME | REF_ISBROKEN;
+                       }
+                       last = create_ref_entry(refname, &oid, flag);
+                       if (peeled == PEELED_FULLY ||
+                           (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
+                               last->flag |= REF_KNOWS_PEELED;
+                       add_ref_entry(dir, last);
+               } else if (last &&
+                   line.buf[0] == '^' &&
+                   line.len == PEELED_LINE_LENGTH &&
+                   line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
+                   !get_oid_hex(line.buf + 1, &oid)) {
+                       oidcpy(&last->u.value.peeled, &oid);
+                       /*
+                        * Regardless of what the file header said,
+                        * we definitely know the value of *this*
+                        * reference:
+                        */
+                       last->flag |= REF_KNOWS_PEELED;
+               } else {
+                       strbuf_setlen(&line, line.len - 1);
+                       die("unexpected line in %s: %s", packed_refs_file, line.buf);
+               }
+       }
+       fclose(f);
+       strbuf_release(&line);
+       return packed_refs;
+ }
+ /*
+  * Check that the packed refs cache (if any) still reflects the
+  * contents of the file. If not, clear the cache.
+  */
+ static void validate_packed_ref_cache(struct packed_ref_store *refs)
+ {
+       if (refs->cache &&
+           !stat_validity_check(&refs->cache->validity, refs->path))
+               clear_packed_ref_cache(refs);
+ }
+ /*
+  * Get the packed_ref_cache for the specified packed_ref_store,
+  * creating and populating it if it hasn't been read before or if the
+  * file has been changed (according to its `validity` field) since it
+  * was last read. On the other hand, if we hold the lock, then assume
+  * that the file hasn't been changed out from under us, so skip the
+  * extra `stat()` call in `stat_validity_check()`.
+  */
+ static struct packed_ref_cache *get_packed_ref_cache(struct packed_ref_store *refs)
+ {
+       if (!is_lock_file_locked(&refs->lock))
+               validate_packed_ref_cache(refs);
+       if (!refs->cache)
+               refs->cache = read_packed_refs(refs->path);
+       return refs->cache;
+ }
+ static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
+ {
+       return get_ref_dir(packed_ref_cache->cache->root);
+ }
+ static struct ref_dir *get_packed_refs(struct packed_ref_store *refs)
+ {
+       return get_packed_ref_dir(get_packed_ref_cache(refs));
+ }
+ /*
+  * Add or overwrite a reference in the in-memory packed reference
+  * cache. This may only be called while the packed-refs file is locked
+  * (see packed_refs_lock()). To actually write the packed-refs file,
+  * call commit_packed_refs().
+  */
+ void add_packed_ref(struct ref_store *ref_store,
+                   const char *refname, const struct object_id *oid)
+ {
+       struct packed_ref_store *refs =
+               packed_downcast(ref_store, REF_STORE_WRITE,
+                               "add_packed_ref");
+       struct ref_dir *packed_refs;
+       struct ref_entry *packed_entry;
+       if (!is_lock_file_locked(&refs->lock))
+               die("BUG: packed refs not locked");
+       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
+               die("Reference has invalid format: '%s'", refname);
+       packed_refs = get_packed_refs(refs);
+       packed_entry = find_ref_entry(packed_refs, refname);
+       if (packed_entry) {
+               /* Overwrite the existing entry: */
+               oidcpy(&packed_entry->u.value.oid, oid);
+               packed_entry->flag = REF_ISPACKED;
+               oidclr(&packed_entry->u.value.peeled);
+       } else {
+               packed_entry = create_ref_entry(refname, oid, REF_ISPACKED);
+               add_ref_entry(packed_refs, packed_entry);
+       }
+ }
+ /*
+  * Return the ref_entry for the given refname from the packed
+  * references.  If it does not exist, return NULL.
+  */
+ static struct ref_entry *get_packed_ref(struct packed_ref_store *refs,
+                                       const char *refname)
+ {
+       return find_ref_entry(get_packed_refs(refs), refname);
+ }
+ static int packed_read_raw_ref(struct ref_store *ref_store,
+                              const char *refname, unsigned char *sha1,
+                              struct strbuf *referent, unsigned int *type)
+ {
+       struct packed_ref_store *refs =
+               packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
+       struct ref_entry *entry;
+       *type = 0;
+       entry = get_packed_ref(refs, refname);
+       if (!entry) {
+               errno = ENOENT;
+               return -1;
+       }
+       hashcpy(sha1, entry->u.value.oid.hash);
+       *type = REF_ISPACKED;
+       return 0;
+ }
+ static int packed_peel_ref(struct ref_store *ref_store,
+                          const char *refname, unsigned char *sha1)
+ {
+       struct packed_ref_store *refs =
+               packed_downcast(ref_store, REF_STORE_READ | REF_STORE_ODB,
+                               "peel_ref");
+       struct ref_entry *r = get_packed_ref(refs, refname);
+       if (!r || peel_entry(r, 0))
+               return -1;
+       hashcpy(sha1, r->u.value.peeled.hash);
+       return 0;
+ }
+ struct packed_ref_iterator {
+       struct ref_iterator base;
+       struct packed_ref_cache *cache;
+       struct ref_iterator *iter0;
+       unsigned int flags;
+ };
+ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
+ {
+       struct packed_ref_iterator *iter =
+               (struct packed_ref_iterator *)ref_iterator;
+       int ok;
+       while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
+               if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
+                   ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
+                       continue;
+               if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
+                   !ref_resolves_to_object(iter->iter0->refname,
+                                           iter->iter0->oid,
+                                           iter->iter0->flags))
+                       continue;
+               iter->base.refname = iter->iter0->refname;
+               iter->base.oid = iter->iter0->oid;
+               iter->base.flags = iter->iter0->flags;
+               return ITER_OK;
+       }
+       iter->iter0 = NULL;
+       if (ref_iterator_abort(ref_iterator) != ITER_DONE)
+               ok = ITER_ERROR;
+       return ok;
+ }
+ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
+                                  struct object_id *peeled)
+ {
+       struct packed_ref_iterator *iter =
+               (struct packed_ref_iterator *)ref_iterator;
+       return ref_iterator_peel(iter->iter0, peeled);
+ }
+ static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator)
+ {
+       struct packed_ref_iterator *iter =
+               (struct packed_ref_iterator *)ref_iterator;
+       int ok = ITER_DONE;
+       if (iter->iter0)
+               ok = ref_iterator_abort(iter->iter0);
+       release_packed_ref_cache(iter->cache);
+       base_ref_iterator_free(ref_iterator);
+       return ok;
+ }
+ static struct ref_iterator_vtable packed_ref_iterator_vtable = {
+       packed_ref_iterator_advance,
+       packed_ref_iterator_peel,
+       packed_ref_iterator_abort
+ };
+ static struct ref_iterator *packed_ref_iterator_begin(
+               struct ref_store *ref_store,
+               const char *prefix, unsigned int flags)
+ {
+       struct packed_ref_store *refs;
+       struct packed_ref_iterator *iter;
+       struct ref_iterator *ref_iterator;
+       unsigned int required_flags = REF_STORE_READ;
+       if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN))
+               required_flags |= REF_STORE_ODB;
+       refs = packed_downcast(ref_store, required_flags, "ref_iterator_begin");
+       iter = xcalloc(1, sizeof(*iter));
+       ref_iterator = &iter->base;
+       base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable);
+       /*
+        * Note that get_packed_ref_cache() internally checks whether
+        * the packed-ref cache is up to date with what is on disk,
+        * and re-reads it if not.
+        */
+       iter->cache = get_packed_ref_cache(refs);
+       acquire_packed_ref_cache(iter->cache);
+       iter->iter0 = cache_ref_iterator_begin(iter->cache->cache, prefix, 0);
+       iter->flags = flags;
+       return ref_iterator;
+ }
+ /*
+  * 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. On
+  * error, return a nonzero value and leave errno set at the value left
+  * by the failing call to `fprintf()`.
+  */
+ static int write_packed_entry(FILE *fh, const char *refname,
+                             const unsigned char *sha1,
+                             const unsigned char *peeled)
+ {
+       if (fprintf(fh, "%s %s\n", sha1_to_hex(sha1), refname) < 0 ||
+           (peeled && fprintf(fh, "^%s\n", sha1_to_hex(peeled)) < 0))
+               return -1;
+       return 0;
+ }
+ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
+ {
+       struct packed_ref_store *refs =
+               packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN,
+                               "packed_refs_lock");
+       static int timeout_configured = 0;
+       static int timeout_value = 1000;
+       struct packed_ref_cache *packed_ref_cache;
+       if (!timeout_configured) {
+               git_config_get_int("core.packedrefstimeout", &timeout_value);
+               timeout_configured = 1;
+       }
+       /*
+        * Note that we close the lockfile immediately because we
+        * don't write new content to it, but rather to a separate
+        * tempfile.
+        */
+       if (hold_lock_file_for_update_timeout(
+                           &refs->lock,
+                           refs->path,
+                           flags, timeout_value) < 0) {
+               unable_to_lock_message(refs->path, errno, err);
+               return -1;
+       }
+       if (close_lock_file(&refs->lock)) {
+               strbuf_addf(err, "unable to close %s: %s", refs->path, strerror(errno));
+               return -1;
+       }
+       /*
+        * Now that we hold the `packed-refs` lock, make sure that our
+        * cache matches the current version of the file. Normally
+        * `get_packed_ref_cache()` does that for us, but that
+        * function assumes that when the file is locked, any existing
+        * cache is still valid. We've just locked the file, but it
+        * might have changed the moment *before* we locked it.
+        */
+       validate_packed_ref_cache(refs);
+       packed_ref_cache = get_packed_ref_cache(refs);
+       /* Increment the reference count to prevent it from being freed: */
+       acquire_packed_ref_cache(packed_ref_cache);
+       return 0;
+ }
+ void packed_refs_unlock(struct ref_store *ref_store)
+ {
+       struct packed_ref_store *refs = packed_downcast(
+                       ref_store,
+                       REF_STORE_READ | REF_STORE_WRITE,
+                       "packed_refs_unlock");
+       if (!is_lock_file_locked(&refs->lock))
+               die("BUG: packed_refs_unlock() called when not locked");
+       rollback_lock_file(&refs->lock);
+       release_packed_ref_cache(refs->cache);
+ }
+ int packed_refs_is_locked(struct ref_store *ref_store)
+ {
+       struct packed_ref_store *refs = packed_downcast(
+                       ref_store,
+                       REF_STORE_READ | REF_STORE_WRITE,
+                       "packed_refs_is_locked");
+       return is_lock_file_locked(&refs->lock);
+ }
+ /*
+  * The packed-refs header line that we write out.  Perhaps other
+  * traits will be added later.  The trailing space is required.
+  */
+ static const char PACKED_REFS_HEADER[] =
+       "# pack-refs with: peeled fully-peeled \n";
+ /*
+  * Write the current version of the packed refs cache from memory to
+  * disk. The packed-refs file must already be locked for writing (see
+  * packed_refs_lock()). Return zero on success. On errors, rollback
+  * the lockfile, write an error message to `err`, and return a nonzero
+  * value.
+  */
+ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
+ {
+       struct packed_ref_store *refs =
+               packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN,
+                               "commit_packed_refs");
+       struct packed_ref_cache *packed_ref_cache =
+               get_packed_ref_cache(refs);
+       int ok;
+       int ret = -1;
+       struct strbuf sb = STRBUF_INIT;
+       FILE *out;
+       struct ref_iterator *iter;
+       char *packed_refs_path;
+       if (!is_lock_file_locked(&refs->lock))
+               die("BUG: commit_packed_refs() called when unlocked");
+       /*
+        * If packed-refs is a symlink, we want to overwrite the
+        * symlinked-to file, not the symlink itself. Also, put the
+        * staging file next to it:
+        */
+       packed_refs_path = get_locked_file_path(&refs->lock);
+       strbuf_addf(&sb, "%s.new", packed_refs_path);
+       if (create_tempfile(&refs->tempfile, sb.buf) < 0) {
+               strbuf_addf(err, "unable to create file %s: %s",
+                           sb.buf, strerror(errno));
+               strbuf_release(&sb);
+               goto out;
+       }
+       strbuf_release(&sb);
+       out = fdopen_tempfile(&refs->tempfile, "w");
+       if (!out) {
+               strbuf_addf(err, "unable to fdopen packed-refs tempfile: %s",
+                           strerror(errno));
+               goto error;
+       }
+       if (fprintf(out, "%s", PACKED_REFS_HEADER) < 0) {
+               strbuf_addf(err, "error writing to %s: %s",
+                           get_tempfile_path(&refs->tempfile), strerror(errno));
+               goto error;
+       }
+       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);
+               if (write_packed_entry(out, iter->refname, iter->oid->hash,
+                                      peel_error ? NULL : peeled.hash)) {
+                       strbuf_addf(err, "error writing to %s: %s",
+                                   get_tempfile_path(&refs->tempfile),
+                                   strerror(errno));
+                       ref_iterator_abort(iter);
+                       goto error;
+               }
+       }
+       if (ok != ITER_DONE) {
+               strbuf_addf(err, "unable to rewrite packed-refs file: "
+                           "error iterating over old contents");
+               goto error;
+       }
+       if (rename_tempfile(&refs->tempfile, packed_refs_path)) {
+               strbuf_addf(err, "error replacing %s: %s",
+                           refs->path, strerror(errno));
+               goto out;
+       }
+       ret = 0;
+       goto out;
+ error:
+       delete_tempfile(&refs->tempfile);
+ out:
+       free(packed_refs_path);
+       return ret;
+ }
+ /*
+  * Rewrite the packed-refs file, omitting any refs listed in
+  * 'refnames'. On error, leave packed-refs unchanged, write an error
+  * message to 'err', and return a nonzero value. The packed refs lock
+  * must be held when calling this function; it will still be held when
+  * the function returns.
+  *
+  * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
+  */
+ int repack_without_refs(struct ref_store *ref_store,
+                       struct string_list *refnames, struct strbuf *err)
+ {
+       struct packed_ref_store *refs =
+               packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN,
+                               "repack_without_refs");
+       struct ref_dir *packed;
+       struct string_list_item *refname;
+       int needs_repacking = 0, removed = 0;
+       packed_assert_main_repository(refs, "repack_without_refs");
+       assert(err);
+       if (!is_lock_file_locked(&refs->lock))
+               die("BUG: repack_without_refs called without holding lock");
+       /* Look for a packed ref */
+       for_each_string_list_item(refname, refnames) {
+               if (get_packed_ref(refs, refname->string)) {
+                       needs_repacking = 1;
+                       break;
+               }
+       }
+       /* Avoid locking if we have nothing to do */
+       if (!needs_repacking)
+               return 0; /* no refname exists in packed refs */
+       packed = get_packed_refs(refs);
+       /* Remove refnames from the cache */
+       for_each_string_list_item(refname, refnames)
+               if (remove_entry_from_dir(packed, refname->string) != -1)
+                       removed = 1;
+       if (!removed) {
+               /*
+                * All packed entries disappeared while we were
+                * acquiring the lock.
+                */
+               clear_packed_ref_cache(refs);
+               return 0;
+       }
+       /* Write what remains */
+       return commit_packed_refs(&refs->base, err);
+ }
+ static int packed_init_db(struct ref_store *ref_store, struct strbuf *err)
+ {
+       /* Nothing to do. */
+       return 0;
+ }
+ static int packed_transaction_prepare(struct ref_store *ref_store,
+                                     struct ref_transaction *transaction,
+                                     struct strbuf *err)
+ {
+       die("BUG: not implemented yet");
+ }
+ static int packed_transaction_abort(struct ref_store *ref_store,
+                                   struct ref_transaction *transaction,
+                                   struct strbuf *err)
+ {
+       die("BUG: not implemented yet");
+ }
+ static int packed_transaction_finish(struct ref_store *ref_store,
+                                    struct ref_transaction *transaction,
+                                    struct strbuf *err)
+ {
+       die("BUG: not implemented yet");
+ }
+ static int packed_initial_transaction_commit(struct ref_store *ref_store,
+                                           struct ref_transaction *transaction,
+                                           struct strbuf *err)
+ {
+       return ref_transaction_commit(transaction, err);
+ }
+ static int packed_delete_refs(struct ref_store *ref_store, const char *msg,
+                            struct string_list *refnames, unsigned int flags)
+ {
+       die("BUG: not implemented yet");
+ }
+ static int packed_pack_refs(struct ref_store *ref_store, unsigned int flags)
+ {
+       /*
+        * Packed refs are already packed. It might be that loose refs
+        * are packed *into* a packed refs store, but that is done by
+        * updating the packed references via a transaction.
+        */
+       return 0;
+ }
+ static int packed_create_symref(struct ref_store *ref_store,
+                              const char *refname, const char *target,
+                              const char *logmsg)
+ {
+       die("BUG: packed reference store does not support symrefs");
+ }
+ static int packed_rename_ref(struct ref_store *ref_store,
+                           const char *oldrefname, const char *newrefname,
+                           const char *logmsg)
+ {
+       die("BUG: packed reference store does not support renaming references");
+ }
+ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store)
+ {
+       return empty_ref_iterator_begin();
+ }
+ static int packed_for_each_reflog_ent(struct ref_store *ref_store,
+                                     const char *refname,
+                                     each_reflog_ent_fn fn, void *cb_data)
+ {
+       return 0;
+ }
+ static int packed_for_each_reflog_ent_reverse(struct ref_store *ref_store,
+                                             const char *refname,
+                                             each_reflog_ent_fn fn,
+                                             void *cb_data)
+ {
+       return 0;
+ }
+ static int packed_reflog_exists(struct ref_store *ref_store,
+                              const char *refname)
+ {
+       return 0;
+ }
+ static int packed_create_reflog(struct ref_store *ref_store,
+                              const char *refname, int force_create,
+                              struct strbuf *err)
+ {
+       die("BUG: packed reference store does not support reflogs");
+ }
+ static int packed_delete_reflog(struct ref_store *ref_store,
+                              const char *refname)
+ {
+       return 0;
+ }
+ static int packed_reflog_expire(struct ref_store *ref_store,
+                               const char *refname, const unsigned char *sha1,
+                               unsigned int flags,
+                               reflog_expiry_prepare_fn prepare_fn,
+                               reflog_expiry_should_prune_fn should_prune_fn,
+                               reflog_expiry_cleanup_fn cleanup_fn,
+                               void *policy_cb_data)
+ {
+       return 0;
+ }
+ struct ref_storage_be refs_be_packed = {
+       NULL,
+       "packed",
+       packed_ref_store_create,
+       packed_init_db,
+       packed_transaction_prepare,
+       packed_transaction_finish,
+       packed_transaction_abort,
+       packed_initial_transaction_commit,
+       packed_pack_refs,
+       packed_peel_ref,
+       packed_create_symref,
+       packed_delete_refs,
+       packed_rename_ref,
+       packed_ref_iterator_begin,
+       packed_read_raw_ref,
+       packed_reflog_iterator_begin,
+       packed_for_each_reflog_ent,
+       packed_for_each_reflog_ent_reverse,
+       packed_reflog_exists,
+       packed_create_reflog,
+       packed_delete_reflog,
+       packed_reflog_expire
+ };