]> git.ipfire.org Git - thirdparty/git.git/commitdiff
refs: selectively set prefix in the seek functions
authorKarthik Nayak <karthik.188@gmail.com>
Tue, 15 Jul 2025 11:28:28 +0000 (13:28 +0200)
committerJunio C Hamano <gitster@pobox.com>
Tue, 15 Jul 2025 18:54:20 +0000 (11:54 -0700)
The ref iterator exposes a `ref_iterator_seek()` function. The name
suggests that this would seek the iterator to a specific reference in
some ways similar to how `fseek()` works for the filesystem.

However, the function actually sets the prefix for refs iteration. So
further iteration would only yield references which match the particular
prefix. This is a bit confusing.

Let's add a 'flags' field to the function, which when set with the
'REF_ITERATOR_SEEK_SET_PREFIX' flag, will set the prefix for the
iteration in-line with the existing behavior. Otherwise, the reference
backends will simply seek to the specified reference and clears any
previously set prefix. This allows users to start iteration from a
specific reference.

In the packed and reftable backend, since references are available in a
sorted list, the changes are simply setting the prefix if needed. The
changes on the files-backend are a little more involved, since the files
backend uses the 'ref-cache' mechanism. We move out the existing logic
within `cache_ref_iterator_seek()` to `cache_ref_iterator_set_prefix()`
which is called when the 'REF_ITERATOR_SEEK_SET_PREFIX' flag is set. We
then parse the provided seek string and set the required levels and
their indexes to ensure that seeking is possible.

Helped-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
refs.c
refs.h
refs/debug.c
refs/files-backend.c
refs/iterator.c
refs/packed-backend.c
refs/ref-cache.c
refs/refs-internal.h
refs/reftable-backend.c

diff --git a/refs.c b/refs.c
index dce5c49ca2ba65fd6a2974e38f67134215bee369..243e6898b80a9d8b6eef0a11f399f52b57cd3de8 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -2666,12 +2666,12 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
                if (!initial_transaction) {
                        int ok;
 
-                       if (!iter) {
+                       if (!iter)
                                iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
                                                               DO_FOR_EACH_INCLUDE_BROKEN);
-                       } else if (ref_iterator_seek(iter, dirname.buf) < 0) {
+                       else if (ref_iterator_seek(iter, dirname.buf,
+                                                  REF_ITERATOR_SEEK_SET_PREFIX) < 0)
                                goto cleanup;
-                       }
 
                        while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
                                if (skip &&
diff --git a/refs.h b/refs.h
index 7c21aaef3db414ea257a8d4a03ef8d46979d584c..e6780a8848bfc05adbf4c5e7c8d743473044d9db 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -1299,21 +1299,29 @@ struct ref_iterator *refs_ref_iterator_begin(
  */
 int ref_iterator_advance(struct ref_iterator *ref_iterator);
 
+enum ref_iterator_seek_flag {
+       /*
+        * When the REF_ITERATOR_SEEK_SET_PREFIX flag is set, the iterator's prefix is
+        * updated to match the provided string, affecting all subsequent iterations. If
+        * not, the iterator seeks to the specified reference and clears any previously
+        * set prefix.
+        */
+       REF_ITERATOR_SEEK_SET_PREFIX = (1 << 0),
+};
+
 /*
- * Seek the iterator to the first reference with the given prefix.
- * The prefix is matched as a literal string, without regard for path
- * separators. If prefix is NULL or the empty string, seek the iterator to the
+ * Seek the iterator to the first reference matching the given seek string.
+ * The seek string is matched as a literal string, without regard for path
+ * separators. If seek is NULL or the empty string, seek the iterator to the
  * first reference again.
  *
- * This function is expected to behave as if a new ref iterator with the same
- * prefix had been created, but allows reuse of iterators and thus may allow
- * the backend to optimize. Parameters other than the prefix that have been
- * passed when creating the iterator will remain unchanged.
+ * This function is expected to behave as if a new ref iterator has been
+ * created, but allows reuse of existing iterators for optimization.
  *
  * Returns 0 on success, a negative error code otherwise.
  */
-int ref_iterator_seek(struct ref_iterator *ref_iterator,
-                     const char *prefix);
+int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
+                     unsigned int flags);
 
 /*
  * If possible, peel the reference currently being viewed by the
index 485e3079d7a3a7b70899b3d88ffa765dc8740498..da300efaf30973f01c99aeb930b4ee5a499ce467 100644 (file)
@@ -170,12 +170,13 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
 }
 
 static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator,
-                                  const char *prefix)
+                                  const char *refname, unsigned int flags)
 {
        struct debug_ref_iterator *diter =
                (struct debug_ref_iterator *)ref_iterator;
-       int res = diter->iter->vtable->seek(diter->iter, prefix);
-       trace_printf_key(&trace_refs, "iterator_seek: %s: %d\n", prefix ? prefix : "", res);
+       int res = diter->iter->vtable->seek(diter->iter, refname, flags);
+       trace_printf_key(&trace_refs, "iterator_seek: %s flags: %d: %d\n",
+                        refname ? refname : "", flags, res);
        return res;
 }
 
index bf6f89b1d1938b6fae0c4125839c188b0d65fc86..8b282f2a60a466fa783405d8b197526560a58129 100644 (file)
@@ -929,11 +929,11 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
 }
 
 static int files_ref_iterator_seek(struct ref_iterator *ref_iterator,
-                                  const char *prefix)
+                                  const char *refname, unsigned int flags)
 {
        struct files_ref_iterator *iter =
                (struct files_ref_iterator *)ref_iterator;
-       return ref_iterator_seek(iter->iter0, prefix);
+       return ref_iterator_seek(iter->iter0, refname, flags);
 }
 
 static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
@@ -2316,7 +2316,8 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
 }
 
 static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
-                                     const char *prefix UNUSED)
+                                     const char *refname UNUSED,
+                                     unsigned int flags UNUSED)
 {
        BUG("ref_iterator_seek() called for reflog_iterator");
 }
index 766d96e795c9b933923786fb5d6f4257aa6449b7..17ef841d8a3013c2a93ac97a1dfe0aa421e3e364 100644 (file)
@@ -15,10 +15,10 @@ int ref_iterator_advance(struct ref_iterator *ref_iterator)
        return ref_iterator->vtable->advance(ref_iterator);
 }
 
-int ref_iterator_seek(struct ref_iterator *ref_iterator,
-                     const char *prefix)
+int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
+                     unsigned int flags)
 {
-       return ref_iterator->vtable->seek(ref_iterator, prefix);
+       return ref_iterator->vtable->seek(ref_iterator, refname, flags);
 }
 
 int ref_iterator_peel(struct ref_iterator *ref_iterator,
@@ -57,7 +57,8 @@ static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator UNUSED)
 }
 
 static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
-                                  const char *prefix UNUSED)
+                                  const char *refname UNUSED,
+                                  unsigned int flags UNUSED)
 {
        return 0;
 }
@@ -224,7 +225,7 @@ error:
 }
 
 static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
-                                  const char *prefix)
+                                  const char *refname, unsigned int flags)
 {
        struct merge_ref_iterator *iter =
                (struct merge_ref_iterator *)ref_iterator;
@@ -234,11 +235,11 @@ static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
        iter->iter0 = iter->iter0_owned;
        iter->iter1 = iter->iter1_owned;
 
-       ret = ref_iterator_seek(iter->iter0, prefix);
+       ret = ref_iterator_seek(iter->iter0, refname, flags);
        if (ret < 0)
                return ret;
 
-       ret = ref_iterator_seek(iter->iter1, prefix);
+       ret = ref_iterator_seek(iter->iter1, refname, flags);
        if (ret < 0)
                return ret;
 
@@ -407,13 +408,16 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
 }
 
 static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator,
-                                   const char *prefix)
+                                   const char *refname, unsigned int flags)
 {
        struct prefix_ref_iterator *iter =
                (struct prefix_ref_iterator *)ref_iterator;
-       free(iter->prefix);
-       iter->prefix = xstrdup_or_null(prefix);
-       return ref_iterator_seek(iter->iter0, prefix);
+
+       if (flags & REF_ITERATOR_SEEK_SET_PREFIX) {
+               free(iter->prefix);
+               iter->prefix = xstrdup_or_null(refname);
+       }
+       return ref_iterator_seek(iter->iter0, refname, flags);
 }
 
 static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
index 7fd73a0e6da3b5a4db630730f6e50c55e38747eb..5fa4ae6655ea0f91fe1729c06ead2a9025987aff 100644 (file)
@@ -1004,19 +1004,23 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
 }
 
 static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator,
-                                   const char *prefix)
+                                   const char *refname, unsigned int flags)
 {
        struct packed_ref_iterator *iter =
                (struct packed_ref_iterator *)ref_iterator;
        const char *start;
 
-       if (prefix && *prefix)
-               start = find_reference_location(iter->snapshot, prefix, 0);
+       if (refname && *refname)
+               start = find_reference_location(iter->snapshot, refname, 0);
        else
                start = iter->snapshot->start;
 
-       free(iter->prefix);
-       iter->prefix = xstrdup_or_null(prefix);
+       /* Unset any previously set prefix */
+       FREE_AND_NULL(iter->prefix);
+
+       if (flags & REF_ITERATOR_SEEK_SET_PREFIX)
+               iter->prefix = xstrdup_or_null(refname);
+
        iter->pos = start;
        iter->eof = iter->snapshot->eof;
 
@@ -1194,7 +1198,8 @@ static struct ref_iterator *packed_ref_iterator_begin(
        iter->repo = ref_store->repo;
        iter->flags = flags;
 
-       if (packed_ref_iterator_seek(&iter->base, prefix) < 0) {
+       if (packed_ref_iterator_seek(&iter->base, prefix,
+                                    REF_ITERATOR_SEEK_SET_PREFIX) < 0) {
                ref_iterator_free(&iter->base);
                return NULL;
        }
index 8aaffa8c6b81f084cd93fadcf85adf38a4edebe5..1d95b56d4000bce609561650e6caba33e06c5ab0 100644 (file)
@@ -434,11 +434,9 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
        }
 }
 
-static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
-                                  const char *prefix)
+static int cache_ref_iterator_set_prefix(struct cache_ref_iterator *iter,
+                                        const char *prefix)
 {
-       struct cache_ref_iterator *iter =
-               (struct cache_ref_iterator *)ref_iterator;
        struct cache_ref_iterator_level *level;
        struct ref_dir *dir;
 
@@ -469,6 +467,82 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
        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 len, idx;
+                       int cmp = 0;
+
+                       sort_ref_dir(dir);
+
+                       slash = strchr(slash, '/');
+                       len = slash ? slash - refname : (int)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;
+                       } 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)
 {
@@ -509,7 +583,8 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
        iter->cache = cache;
        iter->prime_dir = prime_dir;
 
-       if (cache_ref_iterator_seek(&iter->base, prefix) < 0) {
+       if (cache_ref_iterator_seek(&iter->base, prefix,
+                                   REF_ITERATOR_SEEK_SET_PREFIX) < 0) {
                ref_iterator_free(&iter->base);
                return NULL;
        }
index 03f5df04d590a2a23122de13afa3b80a48ea603f..40c1c0f93dc8be72087be123bd33b924279c27b3 100644 (file)
@@ -353,11 +353,12 @@ void base_ref_iterator_init(struct ref_iterator *iter,
 typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
 
 /*
- * Seek the iterator to the first reference matching the given prefix. Should
- * behave the same as if a new iterator was created with the same prefix.
+ * Seek the iterator to the first matching reference. If the
+ * REF_ITERATOR_SEEK_SET_PREFIX flag is set, it would behave the same as if a
+ * new iterator was created with the provided refname as prefix.
  */
 typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator,
-                                const char *prefix);
+                                const char *refname, unsigned int flags);
 
 /*
  * Peels the current ref, returning 0 for success or -1 for failure.
index 4c3817f4ec1a887aa6e6feed2a1336ed2b29cbe2..c3d48cc4120fe2239c0e6515b62587a215c474f6 100644 (file)
@@ -719,15 +719,20 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
 }
 
 static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator,
-                                     const char *prefix)
+                                     const char *refname, unsigned int flags)
 {
        struct reftable_ref_iterator *iter =
                (struct reftable_ref_iterator *)ref_iterator;
 
-       free(iter->prefix);
-       iter->prefix = xstrdup_or_null(prefix);
-       iter->prefix_len = prefix ? strlen(prefix) : 0;
-       iter->err = reftable_iterator_seek_ref(&iter->iter, prefix);
+       /* Unset any previously set prefix */
+       FREE_AND_NULL(iter->prefix);
+       iter->prefix_len = 0;
+
+       if (flags & REF_ITERATOR_SEEK_SET_PREFIX) {
+               iter->prefix = xstrdup_or_null(refname);
+               iter->prefix_len = refname ? strlen(refname) : 0;
+       }
+       iter->err = reftable_iterator_seek_ref(&iter->iter, refname);
 
        return iter->err;
 }
@@ -839,7 +844,8 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
        if (ret)
                goto done;
 
-       ret = reftable_ref_iterator_seek(&iter->base, prefix);
+       ret = reftable_ref_iterator_seek(&iter->base, prefix,
+                                        REF_ITERATOR_SEEK_SET_PREFIX);
        if (ret)
                goto done;
 
@@ -2042,7 +2048,8 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
 }
 
 static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
-                                        const char *prefix UNUSED)
+                                        const char *refname UNUSED,
+                                        unsigned int flags UNUSED)
 {
        BUG("reftable reflog iterator cannot be seeked");
        return -1;