Imported Upstream version 2.17.0
[platform/upstream/git.git] / refs / ref-cache.c
index 6059362..e90bd3e 100644 (file)
@@ -32,17 +32,12 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry)
 }
 
 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);
-       oidclr(&ref->u.value.peeled);
+       oidcpy(&ref->u.value.oid, oid);
        ref->flag = flag;
        return ref;
 }
@@ -86,9 +81,8 @@ 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);
+       FREE_AND_NULL(dir->entries);
        dir->sorted = dir->nr = dir->alloc = 0;
-       dir->entries = NULL;
 }
 
 struct ref_entry *create_dir_entry(struct ref_cache *cache,
@@ -244,10 +238,8 @@ int remove_entry_from_dir(struct ref_dir *dir, const char *refname)
                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)
-               );
+       MOVE_ARRAY(&dir->entries[entry_index],
+                  &dir->entries[entry_index + 1], dir->nr - entry_index - 1);
        dir->nr--;
        if (dir->sorted > entry_index)
                dir->sorted--;
@@ -266,8 +258,8 @@ int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref)
 
 /*
  * 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.
+ * and the same oid. Die if they have the same name but different
+ * oids.
  */
 static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2)
 {
@@ -316,11 +308,42 @@ static void sort_ref_dir(struct ref_dir *dir)
        dir->sorted = dir->nr = i;
 }
 
+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)
+{
+       while (*prefix && *dirname == *prefix) {
+               dirname++;
+               prefix++;
+       }
+       if (!*prefix)
+               return PREFIX_CONTAINS_DIR;
+       else if (!*dirname)
+               return PREFIX_WITHIN_DIR;
+       else
+               return PREFIX_EXCLUDES_DIR;
+}
+
 /*
- * Load all of the refs from `dir` (recursively) into our in-memory
- * cache.
+ * 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)
+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
@@ -331,8 +354,29 @@ 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));
+               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;
+                       }
+               }
        }
 }
 
@@ -347,6 +391,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
@@ -374,6 +420,13 @@ struct cache_ref_iterator {
        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
         * necessary the top level in the references hierarchy. If we
@@ -394,6 +447,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);
@@ -408,6 +462,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,
@@ -415,6 +477,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;
@@ -425,49 +488,10 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
        }
 }
 
-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 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;
+       return peel_object(ref_iterator->oid, peeled);
 }
 
 static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
@@ -475,6 +499,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;
@@ -500,14 +525,14 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
                dir = find_containing_dir(dir, prefix, 0);
        if (!dir)
                /* There's nothing to iterate over. */
-               return  empty_ref_iterator_begin();
+               return empty_ref_iterator_begin();
 
        if (prime_dir)
-               prime_ref_dir(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);
+       base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable, 1);
        ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
 
        iter->levels_nr = 1;
@@ -515,9 +540,12 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
        level->index = -1;
        level->dir = dir;
 
-       if (prefix && *prefix)
-               ref_iterator = prefix_ref_iterator_begin(ref_iterator,
-                                                        prefix, 0);
+       if (prefix && *prefix) {
+               iter->prefix = xstrdup(prefix);
+               level->prefix_state = PREFIX_WITHIN_DIR;
+       } else {
+               level->prefix_state = PREFIX_CONTAINS_DIR;
+       }
 
        return ref_iterator;
 }