files-backend: cheapen refname_available check when locking refs
[gitweb.git] / refs / ref-cache.c
index bf911028c8079df82edef0ff3e3a64fcd0c5608d..af2fcb2c1217bc89e095515f6dcfcfb1c0e2befa 100644 (file)
@@ -4,9 +4,6 @@
 #include "ref-cache.h"
 #include "../iterator.h"
 
-/* FIXME: This declaration shouldn't be here */
-void read_loose_refs(const char *dirname, struct ref_dir *dir);
-
 void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)
 {
        ALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc);
@@ -25,49 +22,35 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry)
        assert(entry->flag & REF_DIR);
        dir = &entry->u.subdir;
        if (entry->flag & REF_INCOMPLETE) {
-               read_loose_refs(entry->name, dir);
+               if (!dir->cache->fill_ref_dir)
+                       die("BUG: incomplete ref_store without fill_ref_dir function");
 
-               /*
-                * 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);
-                       }
-               }
+               dir->cache->fill_ref_dir(dir->cache->ref_store, dir, entry->name);
                entry->flag &= ~REF_INCOMPLETE;
        }
        return dir;
 }
 
 struct ref_entry *create_ref_entry(const char *refname,
-                                  const unsigned char *sha1, int flag,
-                                  int check_name)
+                                  const struct object_id *oid, int flag)
 {
        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);
+       oidcpy(&ref->u.value.oid, oid);
        oidclr(&ref->u.value.peeled);
        ref->flag = flag;
        return ref;
 }
 
-struct ref_cache *create_ref_cache(struct files_ref_store *refs)
+struct ref_cache *create_ref_cache(struct ref_store *refs,
+                                  fill_ref_dir_fn *fill_ref_dir)
 {
        struct ref_cache *ret = xcalloc(1, sizeof(*ret));
 
-       ret->root = create_dir_entry(refs, "", 0, 1);
+       ret->ref_store = refs;
+       ret->fill_ref_dir = fill_ref_dir;
+       ret->root = create_dir_entry(ret, "", 0, 1);
        return ret;
 }
 
@@ -104,13 +87,14 @@ static void clear_ref_dir(struct ref_dir *dir)
        dir->entries = NULL;
 }
 
-struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,
+struct ref_entry *create_dir_entry(struct ref_cache *cache,
                                   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->u.subdir.cache = cache;
        direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
        return direntry;
 }
@@ -181,7 +165,7 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir,
                 * therefore, create an empty record for it but mark
                 * the record complete.
                 */
-               entry = create_dir_entry(dir->ref_store, subdirname, len, 0);
+               entry = create_dir_entry(dir->cache, subdirname, len, 0);
                add_entry_to_dir(dir, entry);
        } else {
                entry = dir->entries[entry_index];
@@ -189,8 +173,17 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir,
        return get_ref_dir(entry);
 }
 
-struct ref_dir *find_containing_dir(struct ref_dir *dir,
-                                   const char *refname, int mkdir)
+/*
+ * 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., it 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, '/')) {
@@ -319,28 +312,42 @@ static void sort_ref_dir(struct ref_dir *dir)
        dir->sorted = dir->nr = i;
 }
 
-int do_for_each_entry_in_dir(struct ref_dir *dir, int offset,
-                            each_ref_entry_fn fn, void *cb_data)
+enum prefix_state {
+       /* All refs within the directory would match prefix: */
+       PREFIX_CONTAINS_DIR,
+
+       /* Some, but not all, refs within the directory might match prefix: */
+       PREFIX_WITHIN_DIR,
+
+       /* No refs within the directory could possibly match prefix: */
+       PREFIX_EXCLUDES_DIR
+};
+
+/*
+ * Return a `prefix_state` constant describing the relationship
+ * between the directory with the specified `dirname` and `prefix`.
+ */
+static enum prefix_state overlaps_prefix(const char *dirname,
+                                        const char *prefix)
 {
-       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;
+       while (*prefix && *dirname == *prefix) {
+               dirname++;
+               prefix++;
        }
-       return 0;
+       if (!*prefix)
+               return PREFIX_CONTAINS_DIR;
+       else if (!*dirname)
+               return PREFIX_WITHIN_DIR;
+       else
+               return PREFIX_EXCLUDES_DIR;
 }
 
-void prime_ref_dir(struct ref_dir *dir)
+/*
+ * Load all of the refs from `dir` (recursively) that could possibly
+ * contain references matching `prefix` into our in-memory cache. If
+ * `prefix` is NULL, prime unconditionally.
+ */
+static void prime_ref_dir(struct ref_dir *dir, const char *prefix)
 {
        /*
         * The hard work of loading loose refs is done by get_ref_dir(), so we
@@ -351,8 +358,29 @@ 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));
+               if (!(entry->flag & REF_DIR)) {
+                       /* Not a directory; no need to recurse. */
+               } else if (!prefix) {
+                       /* Recurse in any case: */
+                       prime_ref_dir(get_ref_dir(entry), NULL);
+               } else {
+                       switch (overlaps_prefix(entry->name, prefix)) {
+                       case PREFIX_CONTAINS_DIR:
+                               /*
+                                * Recurse, and from here down we
+                                * don't have to check the prefix
+                                * anymore:
+                                */
+                               prime_ref_dir(get_ref_dir(entry), NULL);
+                               break;
+                       case PREFIX_WITHIN_DIR:
+                               prime_ref_dir(get_ref_dir(entry), prefix);
+                               break;
+                       case PREFIX_EXCLUDES_DIR:
+                               /* No need to prime this directory. */
+                               break;
+                       }
+               }
        }
 }
 
@@ -367,6 +395,8 @@ struct cache_ref_iterator_level {
         */
        struct ref_dir *dir;
 
+       enum prefix_state prefix_state;
+
        /*
         * The index of the current entry within dir (which might
         * itself be a directory). If index == -1, then the iteration
@@ -393,6 +423,13 @@ struct cache_ref_iterator {
        /* The number of levels that have been allocated on the stack */
        size_t levels_alloc;
 
+       /*
+        * Only include references with this prefix in the iteration.
+        * The prefix is matched textually, without regard for path
+        * component boundaries.
+        */
+       const char *prefix;
+
        /*
         * A stack of levels. levels[0] is the uppermost level that is
         * being iterated over in this iteration. (This is not
@@ -414,6 +451,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
                        &iter->levels[iter->levels_nr - 1];
                struct ref_dir *dir = level->dir;
                struct ref_entry *entry;
+               enum prefix_state entry_prefix_state;
 
                if (level->index == -1)
                        sort_ref_dir(dir);
@@ -428,6 +466,14 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
                entry = dir->entries[level->index];
 
+               if (level->prefix_state == PREFIX_WITHIN_DIR) {
+                       entry_prefix_state = overlaps_prefix(entry->name, iter->prefix);
+                       if (entry_prefix_state == PREFIX_EXCLUDES_DIR)
+                               continue;
+               } else {
+                       entry_prefix_state = level->prefix_state;
+               }
+
                if (entry->flag & REF_DIR) {
                        /* push down a level */
                        ALLOC_GROW(iter->levels, iter->levels_nr + 1,
@@ -435,6 +481,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
                        level = &iter->levels[iter->levels_nr++];
                        level->dir = get_ref_dir(entry);
+                       level->prefix_state = entry_prefix_state;
                        level->index = -1;
                } else {
                        iter->base.refname = entry->name;
@@ -495,6 +542,7 @@ static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
        struct cache_ref_iterator *iter =
                (struct cache_ref_iterator *)ref_iterator;
 
+       free((char *)iter->prefix);
        free(iter->levels);
        base_ref_iterator_free(ref_iterator);
        return ITER_DONE;
@@ -506,12 +554,25 @@ static struct ref_iterator_vtable cache_ref_iterator_vtable = {
        cache_ref_iterator_abort
 };
 
-struct ref_iterator *cache_ref_iterator_begin(struct ref_dir *dir)
+struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
+                                             const char *prefix,
+                                             int prime_dir)
 {
+       struct ref_dir *dir;
        struct cache_ref_iterator *iter;
        struct ref_iterator *ref_iterator;
        struct cache_ref_iterator_level *level;
 
+       dir = get_ref_dir(cache->root);
+       if (prefix && *prefix)
+               dir = find_containing_dir(dir, prefix, 0);
+       if (!dir)
+               /* There's nothing to iterate over. */
+               return empty_ref_iterator_begin();
+
+       if (prime_dir)
+               prime_ref_dir(dir, prefix);
+
        iter = xcalloc(1, sizeof(*iter));
        ref_iterator = &iter->base;
        base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
@@ -522,5 +583,12 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_dir *dir)
        level->index = -1;
        level->dir = dir;
 
+       if (prefix && *prefix) {
+               iter->prefix = xstrdup(prefix);
+               level->prefix_state = PREFIX_WITHIN_DIR;
+       } else {
+               level->prefix_state = PREFIX_CONTAINS_DIR;
+       }
+
        return ref_iterator;
 }