refs/iterator: implement seeking for packed-ref iterators

Implement seeking of `packed-ref` iterators. The implementation is again
straight forward, except that we cannot continue to use the prefix
iterator as we would otherwise not be able to reseek the iterator
anymore in case one first asks for an empty and then for a non-empty
prefix. Instead, we open-code the logic to in `advance()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
main
Patrick Steinhardt 2025-03-12 16:56:20 +01:00 committed by Junio C Hamano
parent 84e656919c
commit 22600c0452
1 changed files with 43 additions and 22 deletions

View File

@ -819,6 +819,8 @@ struct packed_ref_iterator {

struct snapshot *snapshot;

char *prefix;

/* The current position in the snapshot's buffer: */
const char *pos;

@ -841,11 +843,9 @@ struct packed_ref_iterator {
};

/*
* Move the iterator to the next record in the snapshot, without
* respect for whether the record is actually required by the current
* iteration. Adjust the fields in `iter` and return `ITER_OK` or
* `ITER_DONE`. This function does not free the iterator in the case
* of `ITER_DONE`.
* Move the iterator to the next record in the snapshot. Adjust the fields in
* `iter` and return `ITER_OK` or `ITER_DONE`. This function does not free the
* iterator in the case of `ITER_DONE`.
*/
static int next_record(struct packed_ref_iterator *iter)
{
@ -942,6 +942,9 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;

while ((ok = next_record(iter)) == ITER_OK) {
const char *refname = iter->base.refname;
const char *prefix = iter->prefix;

if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
!is_per_worktree_ref(iter->base.refname))
continue;
@ -951,12 +954,41 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, iter->flags))
continue;

while (prefix && *prefix) {
if (*refname < *prefix)
BUG("packed-refs backend yielded reference preceding its prefix");
else if (*refname > *prefix)
return ITER_DONE;
prefix++;
refname++;
}

return ITER_OK;
}

return ok;
}

static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator,
const char *prefix)
{
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);
else
start = iter->snapshot->start;

free(iter->prefix);
iter->prefix = xstrdup_or_null(prefix);
iter->pos = start;
iter->eof = iter->snapshot->eof;

return 0;
}

static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@ -979,11 +1011,13 @@ static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
(struct packed_ref_iterator *)ref_iterator;
strbuf_release(&iter->refname_buf);
free(iter->jump);
free(iter->prefix);
release_snapshot(iter->snapshot);
}

static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.advance = packed_ref_iterator_advance,
.seek = packed_ref_iterator_seek,
.peel = packed_ref_iterator_peel,
.release = packed_ref_iterator_release,
};
@ -1097,7 +1131,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
{
struct packed_ref_store *refs;
struct snapshot *snapshot;
const char *start;
struct packed_ref_iterator *iter;
struct ref_iterator *ref_iterator;
unsigned int required_flags = REF_STORE_READ;
@ -1113,14 +1146,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
*/
snapshot = get_snapshot(refs);

if (prefix && *prefix)
start = find_reference_location(snapshot, prefix, 0);
else
start = snapshot->start;

if (start == snapshot->eof)
return empty_ref_iterator_begin();

CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable);
@ -1130,19 +1155,15 @@ static struct ref_iterator *packed_ref_iterator_begin(

iter->snapshot = snapshot;
acquire_snapshot(snapshot);

iter->pos = start;
iter->eof = snapshot->eof;
strbuf_init(&iter->refname_buf, 0);

iter->base.oid = &iter->oid;

iter->repo = ref_store->repo;
iter->flags = flags;

if (prefix && *prefix)
/* Stop iteration after we've gone *past* prefix: */
ref_iterator = prefix_ref_iterator_begin(ref_iterator, prefix, 0);
if (packed_ref_iterator_seek(&iter->base, prefix) < 0) {
ref_iterator_free(&iter->base);
return NULL;
}

return ref_iterator;
}