refs: selectively set prefix in the seek functions
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>
This commit is contained in:
committed by
Junio C Hamano
parent
883a7ea054
commit
2b4648b919
6
refs.c
6
refs.c
@@ -2666,12 +2666,12 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
|
|||||||
if (!initial_transaction) {
|
if (!initial_transaction) {
|
||||||
int ok;
|
int ok;
|
||||||
|
|
||||||
if (!iter) {
|
if (!iter)
|
||||||
iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
|
iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
|
||||||
DO_FOR_EACH_INCLUDE_BROKEN);
|
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;
|
goto cleanup;
|
||||||
}
|
|
||||||
|
|
||||||
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
|
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
|
||||||
if (skip &&
|
if (skip &&
|
||||||
|
|||||||
26
refs.h
26
refs.h
@@ -1299,21 +1299,29 @@ struct ref_iterator *refs_ref_iterator_begin(
|
|||||||
*/
|
*/
|
||||||
int ref_iterator_advance(struct ref_iterator *ref_iterator);
|
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.
|
* Seek the iterator to the first reference matching the given seek string.
|
||||||
* The prefix is matched as a literal string, without regard for path
|
* The seek string is matched as a literal string, without regard for path
|
||||||
* separators. If prefix is NULL or the empty string, seek the iterator to the
|
* separators. If seek is NULL or the empty string, seek the iterator to the
|
||||||
* first reference again.
|
* first reference again.
|
||||||
*
|
*
|
||||||
* This function is expected to behave as if a new ref iterator with the same
|
* This function is expected to behave as if a new ref iterator has been
|
||||||
* prefix had been created, but allows reuse of iterators and thus may allow
|
* created, but allows reuse of existing iterators for optimization.
|
||||||
* the backend to optimize. Parameters other than the prefix that have been
|
|
||||||
* passed when creating the iterator will remain unchanged.
|
|
||||||
*
|
*
|
||||||
* Returns 0 on success, a negative error code otherwise.
|
* Returns 0 on success, a negative error code otherwise.
|
||||||
*/
|
*/
|
||||||
int ref_iterator_seek(struct ref_iterator *ref_iterator,
|
int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
|
||||||
const char *prefix);
|
unsigned int flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If possible, peel the reference currently being viewed by the
|
* If possible, peel the reference currently being viewed by the
|
||||||
|
|||||||
@@ -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,
|
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 *diter =
|
||||||
(struct debug_ref_iterator *)ref_iterator;
|
(struct debug_ref_iterator *)ref_iterator;
|
||||||
int res = diter->iter->vtable->seek(diter->iter, prefix);
|
int res = diter->iter->vtable->seek(diter->iter, refname, flags);
|
||||||
trace_printf_key(&trace_refs, "iterator_seek: %s: %d\n", prefix ? prefix : "", res);
|
trace_printf_key(&trace_refs, "iterator_seek: %s flags: %d: %d\n",
|
||||||
|
refname ? refname : "", flags, res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
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 *iter =
|
||||||
(struct files_ref_iterator *)ref_iterator;
|
(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,
|
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,
|
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");
|
BUG("ref_iterator_seek() called for reflog_iterator");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ int ref_iterator_advance(struct ref_iterator *ref_iterator)
|
|||||||
return ref_iterator->vtable->advance(ref_iterator);
|
return ref_iterator->vtable->advance(ref_iterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ref_iterator_seek(struct ref_iterator *ref_iterator,
|
int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname,
|
||||||
const char *prefix)
|
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,
|
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,
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -224,7 +225,7 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
|
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 *iter =
|
||||||
(struct merge_ref_iterator *)ref_iterator;
|
(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->iter0 = iter->iter0_owned;
|
||||||
iter->iter1 = iter->iter1_owned;
|
iter->iter1 = iter->iter1_owned;
|
||||||
|
|
||||||
ret = ref_iterator_seek(iter->iter0, prefix);
|
ret = ref_iterator_seek(iter->iter0, refname, flags);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = ref_iterator_seek(iter->iter1, prefix);
|
ret = ref_iterator_seek(iter->iter1, refname, flags);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
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,
|
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 *iter =
|
||||||
(struct prefix_ref_iterator *)ref_iterator;
|
(struct prefix_ref_iterator *)ref_iterator;
|
||||||
free(iter->prefix);
|
|
||||||
iter->prefix = xstrdup_or_null(prefix);
|
if (flags & REF_ITERATOR_SEEK_SET_PREFIX) {
|
||||||
return ref_iterator_seek(iter->iter0, 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,
|
static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
|
||||||
|
|||||||
@@ -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,
|
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 *iter =
|
||||||
(struct packed_ref_iterator *)ref_iterator;
|
(struct packed_ref_iterator *)ref_iterator;
|
||||||
const char *start;
|
const char *start;
|
||||||
|
|
||||||
if (prefix && *prefix)
|
if (refname && *refname)
|
||||||
start = find_reference_location(iter->snapshot, prefix, 0);
|
start = find_reference_location(iter->snapshot, refname, 0);
|
||||||
else
|
else
|
||||||
start = iter->snapshot->start;
|
start = iter->snapshot->start;
|
||||||
|
|
||||||
free(iter->prefix);
|
/* Unset any previously set prefix */
|
||||||
iter->prefix = xstrdup_or_null(prefix);
|
FREE_AND_NULL(iter->prefix);
|
||||||
|
|
||||||
|
if (flags & REF_ITERATOR_SEEK_SET_PREFIX)
|
||||||
|
iter->prefix = xstrdup_or_null(refname);
|
||||||
|
|
||||||
iter->pos = start;
|
iter->pos = start;
|
||||||
iter->eof = iter->snapshot->eof;
|
iter->eof = iter->snapshot->eof;
|
||||||
|
|
||||||
@@ -1194,7 +1198,8 @@ static struct ref_iterator *packed_ref_iterator_begin(
|
|||||||
iter->repo = ref_store->repo;
|
iter->repo = ref_store->repo;
|
||||||
iter->flags = flags;
|
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);
|
ref_iterator_free(&iter->base);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
static int cache_ref_iterator_set_prefix(struct cache_ref_iterator *iter,
|
||||||
const char *prefix)
|
const char *prefix)
|
||||||
{
|
{
|
||||||
struct cache_ref_iterator *iter =
|
|
||||||
(struct cache_ref_iterator *)ref_iterator;
|
|
||||||
struct cache_ref_iterator_level *level;
|
struct cache_ref_iterator_level *level;
|
||||||
struct ref_dir *dir;
|
struct ref_dir *dir;
|
||||||
|
|
||||||
@@ -469,6 +467,82 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
|
|||||||
return 0;
|
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,
|
static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
|
||||||
struct object_id *peeled)
|
struct object_id *peeled)
|
||||||
{
|
{
|
||||||
@@ -509,7 +583,8 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
|
|||||||
iter->cache = cache;
|
iter->cache = cache;
|
||||||
iter->prime_dir = prime_dir;
|
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);
|
ref_iterator_free(&iter->base);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -353,11 +353,12 @@ void base_ref_iterator_init(struct ref_iterator *iter,
|
|||||||
typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
|
typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Seek the iterator to the first reference matching the given prefix. Should
|
* Seek the iterator to the first matching reference. If the
|
||||||
* behave the same as if a new iterator was created with the same prefix.
|
* 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,
|
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.
|
* Peels the current ref, returning 0 for success or -1 for failure.
|
||||||
|
|||||||
@@ -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,
|
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 *iter =
|
||||||
(struct reftable_ref_iterator *)ref_iterator;
|
(struct reftable_ref_iterator *)ref_iterator;
|
||||||
|
|
||||||
free(iter->prefix);
|
/* Unset any previously set prefix */
|
||||||
iter->prefix = xstrdup_or_null(prefix);
|
FREE_AND_NULL(iter->prefix);
|
||||||
iter->prefix_len = prefix ? strlen(prefix) : 0;
|
iter->prefix_len = 0;
|
||||||
iter->err = reftable_iterator_seek_ref(&iter->iter, prefix);
|
|
||||||
|
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;
|
return iter->err;
|
||||||
}
|
}
|
||||||
@@ -839,7 +844,8 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto done;
|
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)
|
if (ret)
|
||||||
goto done;
|
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,
|
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");
|
BUG("reftable reflog iterator cannot be seeked");
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
Reference in New Issue
Block a user