summaryrefslogtreecommitdiff
path: root/refs/ref-cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'refs/ref-cache.c')
-rw-r--r--refs/ref-cache.c179
1 files changed, 131 insertions, 48 deletions
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 02f09e4df8..c180e0aad7 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -194,20 +194,6 @@ static struct ref_dir *find_containing_dir(struct ref_dir *dir,
return dir;
}
-struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname)
-{
- int entry_index;
- struct ref_entry *entry;
- dir = find_containing_dir(dir, refname);
- 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;
-}
-
/*
* Emit a warning and return true iff ref1 and ref2 have the same name
* and the same oid. Die if they have the same name but different
@@ -362,9 +348,7 @@ 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.
+ * The number of levels currently on the stack.
*/
size_t levels_nr;
@@ -376,7 +360,7 @@ struct cache_ref_iterator {
* The prefix is matched textually, without regard for path
* component boundaries.
*/
- const char *prefix;
+ char *prefix;
/*
* A stack of levels. levels[0] is the uppermost level that is
@@ -389,6 +373,9 @@ struct cache_ref_iterator {
struct cache_ref_iterator_level *levels;
struct repository *repo;
+ struct ref_cache *cache;
+
+ int prime_dir;
};
static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
@@ -396,6 +383,9 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
+ if (!iter->levels_nr)
+ return ITER_DONE;
+
while (1) {
struct cache_ref_iterator_level *level =
&iter->levels[iter->levels_nr - 1];
@@ -409,7 +399,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
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);
+ return ITER_DONE;
continue;
}
@@ -444,6 +434,117 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
}
+static int cache_ref_iterator_set_prefix(struct cache_ref_iterator *iter,
+ const char *prefix)
+{
+ struct cache_ref_iterator_level *level;
+ struct ref_dir *dir;
+
+ dir = get_ref_dir(iter->cache->root);
+ if (prefix && *prefix)
+ dir = find_containing_dir(dir, prefix);
+ if (!dir) {
+ iter->levels_nr = 0;
+ return 0;
+ }
+
+ if (iter->prime_dir)
+ prime_ref_dir(dir, prefix);
+ iter->levels_nr = 1;
+ level = &iter->levels[0];
+ level->index = -1;
+ level->dir = dir;
+
+ if (prefix && *prefix) {
+ free(iter->prefix);
+ iter->prefix = xstrdup(prefix);
+ level->prefix_state = PREFIX_WITHIN_DIR;
+ } else {
+ FREE_AND_NULL(iter->prefix);
+ level->prefix_state = PREFIX_CONTAINS_DIR;
+ }
+
+ return 0;
+}
+
+static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *refname, unsigned int flags)
+{
+ struct cache_ref_iterator *iter =
+ (struct cache_ref_iterator *)ref_iterator;
+
+ if (flags & REF_ITERATOR_SEEK_SET_PREFIX) {
+ return cache_ref_iterator_set_prefix(iter, refname);
+ } else if (refname && *refname) {
+ struct cache_ref_iterator_level *level;
+ const char *slash = refname;
+ struct ref_dir *dir;
+
+ dir = get_ref_dir(iter->cache->root);
+
+ if (iter->prime_dir)
+ prime_ref_dir(dir, refname);
+
+ iter->levels_nr = 1;
+ level = &iter->levels[0];
+ level->index = -1;
+ level->dir = dir;
+
+ /* Unset any previously set prefix */
+ FREE_AND_NULL(iter->prefix);
+
+ /*
+ * Breakdown the provided seek path and assign the correct
+ * indexing to each level as needed.
+ */
+ do {
+ int idx;
+ size_t len;
+ int cmp = 0;
+
+ sort_ref_dir(dir);
+
+ slash = strchr(slash, '/');
+ len = slash ? (size_t)(slash - refname) : strlen(refname);
+
+ for (idx = 0; idx < dir->nr; idx++) {
+ cmp = strncmp(refname, dir->entries[idx]->name, len);
+ if (cmp <= 0)
+ break;
+ }
+ /* don't overflow the index */
+ idx = idx >= dir->nr ? dir->nr - 1 : idx;
+
+ if (slash)
+ slash = slash + 1;
+
+ level->index = idx;
+ if (dir->entries[idx]->flag & REF_DIR) {
+ /* push down a level */
+ dir = get_ref_dir(dir->entries[idx]);
+
+ ALLOC_GROW(iter->levels, iter->levels_nr + 1,
+ iter->levels_alloc);
+ level = &iter->levels[iter->levels_nr++];
+ level->dir = dir;
+ level->index = -1;
+ level->prefix_state = PREFIX_CONTAINS_DIR;
+ } else {
+ /* reduce the index so the leaf node is iterated over */
+ if (cmp <= 0 && !slash)
+ level->index = idx - 1;
+ /*
+ * while the seek path may not be exhausted, our
+ * match is exhausted at a leaf node.
+ */
+ break;
+ }
+ } while (slash);
+ }
+
+ return 0;
+}
+
static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -452,21 +553,19 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
return peel_object(iter->repo, ref_iterator->oid, peeled) ? -1 : 0;
}
-static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
-
- free((char *)iter->prefix);
+ free(iter->prefix);
free(iter->levels);
- base_ref_iterator_free(ref_iterator);
- return ITER_DONE;
}
static struct ref_iterator_vtable cache_ref_iterator_vtable = {
.advance = cache_ref_iterator_advance,
+ .seek = cache_ref_iterator_seek,
.peel = cache_ref_iterator_peel,
- .abort = cache_ref_iterator_abort
+ .release = cache_ref_iterator_release,
};
struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
@@ -474,39 +573,23 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
struct repository *repo,
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);
- if (!dir)
- /* There's nothing to iterate over. */
- return empty_ref_iterator_begin();
-
- if (prime_dir)
- prime_ref_dir(dir, prefix);
CALLOC_ARRAY(iter, 1);
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;
+ iter->repo = repo;
+ iter->cache = cache;
+ iter->prime_dir = prime_dir;
- if (prefix && *prefix) {
- iter->prefix = xstrdup(prefix);
- level->prefix_state = PREFIX_WITHIN_DIR;
- } else {
- level->prefix_state = PREFIX_CONTAINS_DIR;
+ if (cache_ref_iterator_seek(&iter->base, prefix,
+ REF_ITERATOR_SEEK_SET_PREFIX) < 0) {
+ ref_iterator_free(&iter->base);
+ return NULL;
}
- iter->repo = repo;
-
return ref_iterator;
}