Merge branch 'pw/rebase-i-regression-fix' into maint
[gitweb.git] / refs / files-backend.c
index 4d705b4037e6c1c0c1f2e277e9a5ef5bce695e61..83ea080e013f90e95d1897258dd80b44ac9b5db6 100644 (file)
@@ -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"
@@ -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
@@ -536,358 +32,8 @@ static int ref_resolves_to_object(const char *refname,
        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,
@@ -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;
 };
 
@@ -944,7 +90,7 @@ static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
 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;
@@ -968,7 +114,7 @@ static void clear_packed_ref_cache(struct files_ref_store *refs)
 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;
        }
 }
@@ -1141,7 +287,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                        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 &&
@@ -1229,11 +375,12 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref
 
                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);
                }
        }
@@ -1242,7 +389,7 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref
 
 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)
@@ -1263,8 +410,8 @@ static void add_packed_ref(struct files_ref_store *refs,
 
        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));
 }
 
 /*
@@ -1272,9 +419,11 @@ static void add_packed_ref(struct files_ref_store *refs,
  * (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);
@@ -1310,7 +459,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
                } 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,
@@ -1347,9 +496,24 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
        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) {
                /*
@@ -1357,14 +521,19 @@ static struct ref_dir *get_loose_refs(struct files_ref_store *refs)
                 * 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;
 }
 
 /*
@@ -1374,7 +543,7 @@ static struct ref_dir *get_loose_refs(struct files_ref_store *refs)
 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);
 }
 
 /*
@@ -1564,7 +733,7 @@ static void unlock_ref(struct ref_lock *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.
@@ -1579,7 +748,7 @@ static void unlock_ref(struct ref_lock *lock)
  *
  * 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
  */
@@ -1636,7 +805,8 @@ static int lock_raw_ref(struct files_ref_store *refs,
                        } else {
                                /*
                                 * The error message set by
-                                * verify_refname_available_dir() is OK.
+                                * refs_verify_refname_available() is
+                                * OK.
                                 */
                                ret = TRANSACTION_NAME_CONFLICT;
                        }
@@ -1726,10 +896,9 @@ static int lock_raw_ref(struct files_ref_store *refs,
                                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.
@@ -1761,16 +930,13 @@ static int lock_raw_ref(struct files_ref_store *refs,
 
                /*
                 * 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;
@@ -1785,41 +951,6 @@ static int lock_raw_ref(struct files_ref_store *refs,
        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)
 {
@@ -1935,7 +1066,6 @@ static struct ref_iterator *files_ref_iterator_begin(
                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;
@@ -1959,41 +1089,24 @@ static struct ref_iterator *files_ref_iterator_begin(
         * 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;
@@ -2096,9 +1209,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
                 */
                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;
@@ -2110,9 +1223,8 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
        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));
 
@@ -2126,9 +1238,8 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
         * 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;
        }
@@ -2163,30 +1274,15 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
  * 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
@@ -2232,9 +1328,10 @@ static int commit_packed_refs(struct files_ref_store *refs)
 {
        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");
 
@@ -2246,8 +1343,18 @@ static int commit_packed_refs(struct files_ref_store *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;
@@ -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
@@ -2433,21 +1481,73 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
        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_for_each_entry_in_dir(get_loose_refs(refs), 0,
-                                pack_if_possible_fn, &cbdata);
+               /* Do not pack per-worktree refs: */
+               if (ref_type(iter->refname) != REF_TYPE_NORMAL)
+                       continue;
+
+               /* 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;
 }
 
@@ -2488,7 +1588,7 @@ static int repack_without_refs(struct files_ref_store *refs,
 
        /* 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) {
                /*
@@ -2608,26 +1708,6 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
        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,
@@ -3294,8 +2374,8 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,
 
        /* 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;
@@ -3305,13 +2385,17 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,
 
                /* 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;
@@ -4290,7 +3374,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,