aboutsummaryrefslogtreecommitdiffstats
path: root/refs
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2025-03-29 16:39:07 +0900
committerJunio C Hamano <gitster@pobox.com>2025-03-29 16:39:07 +0900
commit8d6413a1bef7876b9c17a79358bd70b764ffacba (patch)
treeae6597f607ea6f315a9356180d96281bd14cf9e5 /refs
parent01d17c05305edefbbe62926f5a5425207324a87f (diff)
parent87d297f48367737444810f8c3e76ef88cb6aa4e3 (diff)
downloadgit-8d6413a1bef7876b9c17a79358bd70b764ffacba.tar.gz
Merge branch 'ps/refname-avail-check-optim'
The code paths to check whether a refname X is available (by seeing if another ref X/Y exists, etc.) have been optimized. * ps/refname-avail-check-optim: refs: reuse iterators when determining refname availability refs/iterator: implement seeking for files iterators refs/iterator: implement seeking for packed-ref iterators refs/iterator: implement seeking for ref-cache iterators refs/iterator: implement seeking for reftable iterators refs/iterator: implement seeking for merged iterators refs/iterator: provide infrastructure to re-seek iterators refs/iterator: separate lifecycle from iteration refs: stop re-verifying common prefixes for availability refs/files: batch refname availability checks for initial transactions refs/files: batch refname availability checks for normal transactions refs/reftable: batch refname availability checks refs: introduce function to batch refname availability checks builtin/update-ref: skip ambiguity checks when parsing object IDs object-name: allow skipping ambiguity checks in `get_oid()` family object-name: introduce `repo_get_oid_with_flags()`
Diffstat (limited to 'refs')
-rw-r--r--refs/debug.c20
-rw-r--r--refs/files-backend.c117
-rw-r--r--refs/iterator.c150
-rw-r--r--refs/packed-backend.c92
-rw-r--r--refs/ref-cache.c88
-rw-r--r--refs/refs-internal.h53
-rw-r--r--refs/reftable-backend.c84
7 files changed, 359 insertions, 245 deletions
diff --git a/refs/debug.c b/refs/debug.c
index fbc4df08b4..5390fa9c18 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -169,6 +169,16 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
return res;
}
+static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ 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);
+ return res;
+}
+
static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -179,19 +189,19 @@ static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
return res;
}
-static int debug_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct debug_ref_iterator *diter =
(struct debug_ref_iterator *)ref_iterator;
- int res = diter->iter->vtable->abort(diter->iter);
- trace_printf_key(&trace_refs, "iterator_abort: %d\n", res);
- return res;
+ diter->iter->vtable->release(diter->iter);
+ trace_printf_key(&trace_refs, "iterator_abort\n");
}
static struct ref_iterator_vtable debug_ref_iterator_vtable = {
.advance = debug_ref_iterator_advance,
+ .seek = debug_ref_iterator_seek,
.peel = debug_ref_iterator_peel,
- .abort = debug_ref_iterator_abort,
+ .release = debug_ref_iterator_release,
};
static struct ref_iterator *
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 6c6e67dc1c..ff54a4bb7e 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -678,6 +678,7 @@ static void unlock_ref(struct ref_lock *lock)
*/
static int lock_raw_ref(struct files_ref_store *refs,
const char *refname, int mustexist,
+ struct string_list *refnames_to_check,
const struct string_list *extras,
struct ref_lock **lock_p,
struct strbuf *referent,
@@ -855,16 +856,11 @@ retry:
}
/*
- * If the ref did not exist and we are creating it,
- * make sure there is no existing packed ref that
- * conflicts with refname:
+ * If the ref did not exist and we are creating it, we have to
+ * make sure there is no existing packed ref that conflicts
+ * with refname. This check is deferred so that we can batch it.
*/
- if (refs_verify_refname_available(
- refs->packed_ref_store, refname,
- extras, NULL, 0, err)) {
- ret = TRANSACTION_NAME_CONFLICT;
- goto error_return;
- }
+ string_list_append(refnames_to_check, refname);
}
ret = 0;
@@ -919,13 +915,17 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK;
}
- iter->iter0 = NULL;
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- ok = ITER_ERROR;
-
return ok;
}
+static int files_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct files_ref_iterator *iter =
+ (struct files_ref_iterator *)ref_iterator;
+ return ref_iterator_seek(iter->iter0, prefix);
+}
+
static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -935,23 +935,18 @@ static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
return ref_iterator_peel(iter->iter0, peeled);
}
-static int files_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_ref_iterator *iter =
(struct files_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->iter0)
- ok = ref_iterator_abort(iter->iter0);
-
- base_ref_iterator_free(ref_iterator);
- return ok;
+ ref_iterator_free(iter->iter0);
}
static struct ref_iterator_vtable files_ref_iterator_vtable = {
.advance = files_ref_iterator_advance,
+ .seek = files_ref_iterator_seek,
.peel = files_ref_iterator_peel,
- .abort = files_ref_iterator_abort,
+ .release = files_ref_iterator_release,
};
static struct ref_iterator *files_ref_iterator_begin(
@@ -1382,7 +1377,7 @@ static int should_pack_refs(struct files_ref_store *refs,
iter->flags, opts))
refcount++;
if (refcount >= limit) {
- ref_iterator_abort(iter);
+ ref_iterator_free(iter);
return 1;
}
}
@@ -1390,6 +1385,7 @@ static int should_pack_refs(struct files_ref_store *refs,
if (ret != ITER_DONE)
die("error while iterating over references");
+ ref_iterator_free(iter);
return 0;
}
@@ -1456,6 +1452,7 @@ static int files_pack_refs(struct ref_store *ref_store,
packed_refs_unlock(refs->packed_ref_store);
prune_refs(refs, &refs_to_prune);
+ ref_iterator_free(iter);
strbuf_release(&err);
return 0;
}
@@ -2303,35 +2300,33 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK;
}
- iter->dir_iterator = NULL;
- if (ref_iterator_abort(ref_iterator) == ITER_ERROR)
- ok = ITER_ERROR;
return ok;
}
+static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
+ const char *prefix UNUSED)
+{
+ BUG("ref_iterator_seek() called for reflog_iterator");
+}
+
static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
struct object_id *peeled UNUSED)
{
BUG("ref_iterator_peel() called for reflog_iterator");
}
-static int files_reflog_iterator_abort(struct ref_iterator *ref_iterator)
+static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_reflog_iterator *iter =
(struct files_reflog_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->dir_iterator)
- ok = dir_iterator_abort(iter->dir_iterator);
-
- base_ref_iterator_free(ref_iterator);
- return ok;
+ dir_iterator_free(iter->dir_iterator);
}
static struct ref_iterator_vtable files_reflog_iterator_vtable = {
.advance = files_reflog_iterator_advance,
+ .seek = files_reflog_iterator_seek,
.peel = files_reflog_iterator_peel,
- .abort = files_reflog_iterator_abort,
+ .release = files_reflog_iterator_release,
};
static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
@@ -2569,6 +2564,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
struct ref_update *update,
struct ref_transaction *transaction,
const char *head_ref,
+ struct string_list *refnames_to_check,
struct string_list *affected_refnames,
struct strbuf *err)
{
@@ -2597,7 +2593,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
lock->count++;
} else {
ret = lock_raw_ref(refs, update->refname, mustexist,
- affected_refnames,
+ refnames_to_check, affected_refnames,
&lock, &referent,
&update->type, err);
if (ret) {
@@ -2811,6 +2807,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
size_t i;
int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
char *head_ref = NULL;
int head_type;
struct files_transaction_backend_data *backend_data;
@@ -2898,7 +2895,8 @@ static int files_transaction_prepare(struct ref_store *ref_store,
struct ref_update *update = transaction->updates[i];
ret = lock_ref_for_update(refs, update, transaction,
- head_ref, &affected_refnames, err);
+ head_ref, &refnames_to_check,
+ &affected_refnames, err);
if (ret)
goto cleanup;
@@ -2930,6 +2928,26 @@ static int files_transaction_prepare(struct ref_store *ref_store,
}
}
+ /*
+ * Verify that none of the loose reference that we're about to write
+ * conflict with any existing packed references. Ideally, we'd do this
+ * check after the packed-refs are locked so that the file cannot
+ * change underneath our feet. But introducing such a lock now would
+ * probably do more harm than good as users rely on there not being a
+ * global lock with the "files" backend.
+ *
+ * Another alternative would be to do the check after the (optional)
+ * lock, but that would extend the time we spend in the globally-locked
+ * state.
+ *
+ * So instead, we accept the race for now.
+ */
+ if (refs_verify_refnames_available(refs->packed_ref_store, &refnames_to_check,
+ &affected_refnames, NULL, 0, err)) {
+ ret = TRANSACTION_NAME_CONFLICT;
+ goto cleanup;
+ }
+
if (packed_transaction) {
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
ret = TRANSACTION_GENERIC_ERROR;
@@ -2972,6 +2990,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
cleanup:
free(head_ref);
string_list_clear(&affected_refnames, 0);
+ string_list_clear(&refnames_to_check, 0);
if (ret)
files_transaction_cleanup(refs, transaction);
@@ -3036,6 +3055,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
size_t i;
int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
struct ref_transaction *packed_transaction = NULL;
struct ref_transaction *loose_transaction = NULL;
@@ -3085,11 +3105,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
!is_null_oid(&update->old_oid))
BUG("initial ref transaction with old_sha1 set");
- if (refs_verify_refname_available(&refs->base, update->refname,
- &affected_refnames, NULL, 1, err)) {
- ret = TRANSACTION_NAME_CONFLICT;
- goto cleanup;
- }
+ string_list_append(&refnames_to_check, update->refname);
/*
* packed-refs don't support symbolic refs, root refs and reflogs,
@@ -3125,8 +3141,19 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
}
}
- if (packed_refs_lock(refs->packed_ref_store, 0, err) ||
- ref_transaction_commit(packed_transaction, err)) {
+ if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
+ if (refs_verify_refnames_available(&refs->base, &refnames_to_check,
+ &affected_refnames, NULL, 1, err)) {
+ packed_refs_unlock(refs->packed_ref_store);
+ ret = TRANSACTION_NAME_CONFLICT;
+ goto cleanup;
+ }
+
+ if (ref_transaction_commit(packed_transaction, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
@@ -3147,6 +3174,7 @@ cleanup:
ref_transaction_free(packed_transaction);
transaction->state = REF_TRANSACTION_CLOSED;
string_list_clear(&affected_refnames, 0);
+ string_list_clear(&refnames_to_check, 0);
return ret;
}
@@ -3808,6 +3836,7 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
ret = error(_("failed to iterate over '%s'"), sb.buf);
out:
+ dir_iterator_free(iter);
strbuf_release(&sb);
strbuf_release(&refname);
return ret;
diff --git a/refs/iterator.c b/refs/iterator.c
index d25e568bf0..766d96e795 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -15,15 +15,26 @@ 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)
+{
+ return ref_iterator->vtable->seek(ref_iterator, prefix);
+}
+
int ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
return ref_iterator->vtable->peel(ref_iterator, peeled);
}
-int ref_iterator_abort(struct ref_iterator *ref_iterator)
+void ref_iterator_free(struct ref_iterator *ref_iterator)
{
- return ref_iterator->vtable->abort(ref_iterator);
+ if (ref_iterator) {
+ ref_iterator->vtable->release(ref_iterator);
+ /* Help make use-after-free bugs fail quickly: */
+ ref_iterator->vtable = NULL;
+ free(ref_iterator);
+ }
}
void base_ref_iterator_init(struct ref_iterator *iter,
@@ -36,20 +47,19 @@ void base_ref_iterator_init(struct ref_iterator *iter,
iter->flags = 0;
}
-void base_ref_iterator_free(struct ref_iterator *iter)
-{
- /* Help make use-after-free bugs fail quickly: */
- iter->vtable = NULL;
- free(iter);
-}
-
struct empty_ref_iterator {
struct ref_iterator base;
};
-static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator)
+static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator UNUSED)
+{
+ return ITER_DONE;
+}
+
+static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
+ const char *prefix UNUSED)
{
- return ref_iterator_abort(ref_iterator);
+ return 0;
}
static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
@@ -58,16 +68,15 @@ static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
BUG("peel called for empty iterator");
}
-static int empty_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
{
- base_ref_iterator_free(ref_iterator);
- return ITER_DONE;
}
static struct ref_iterator_vtable empty_ref_iterator_vtable = {
.advance = empty_ref_iterator_advance,
+ .seek = empty_ref_iterator_seek,
.peel = empty_ref_iterator_peel,
- .abort = empty_ref_iterator_abort,
+ .release = empty_ref_iterator_release,
};
struct ref_iterator *empty_ref_iterator_begin(void)
@@ -87,7 +96,8 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator)
struct merge_ref_iterator {
struct ref_iterator base;
- struct ref_iterator *iter0, *iter1;
+ struct ref_iterator *iter0, *iter0_owned;
+ struct ref_iterator *iter1, *iter1_owned;
ref_iterator_select_fn *select;
void *cb_data;
@@ -179,9 +189,8 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
iter->select(iter->iter0, iter->iter1, iter->cb_data);
if (selection == ITER_SELECT_DONE) {
- return ref_iterator_abort(ref_iterator);
+ return ITER_DONE;
} else if (selection == ITER_SELECT_ERROR) {
- ref_iterator_abort(ref_iterator);
return ITER_ERROR;
}
@@ -211,10 +220,31 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
error:
- ref_iterator_abort(ref_iterator);
return ITER_ERROR;
}
+static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct merge_ref_iterator *iter =
+ (struct merge_ref_iterator *)ref_iterator;
+ int ret;
+
+ iter->current = NULL;
+ iter->iter0 = iter->iter0_owned;
+ iter->iter1 = iter->iter1_owned;
+
+ ret = ref_iterator_seek(iter->iter0, prefix);
+ if (ret < 0)
+ return ret;
+
+ ret = ref_iterator_seek(iter->iter1, prefix);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -227,28 +257,19 @@ static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
return ref_iterator_peel(*iter->current, peeled);
}
-static int merge_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
(struct merge_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->iter0) {
- if (ref_iterator_abort(iter->iter0) != ITER_DONE)
- ok = ITER_ERROR;
- }
- if (iter->iter1) {
- if (ref_iterator_abort(iter->iter1) != ITER_DONE)
- ok = ITER_ERROR;
- }
- base_ref_iterator_free(ref_iterator);
- return ok;
+ ref_iterator_free(iter->iter0_owned);
+ ref_iterator_free(iter->iter1_owned);
}
static struct ref_iterator_vtable merge_ref_iterator_vtable = {
.advance = merge_ref_iterator_advance,
+ .seek = merge_ref_iterator_seek,
.peel = merge_ref_iterator_peel,
- .abort = merge_ref_iterator_abort,
+ .release = merge_ref_iterator_release,
};
struct ref_iterator *merge_ref_iterator_begin(
@@ -267,8 +288,8 @@ struct ref_iterator *merge_ref_iterator_begin(
*/
base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable);
- iter->iter0 = iter0;
- iter->iter1 = iter1;
+ iter->iter0 = iter->iter0_owned = iter0;
+ iter->iter1 = iter->iter1_owned = iter1;
iter->select = select;
iter->cb_data = cb_data;
iter->current = NULL;
@@ -310,10 +331,10 @@ struct ref_iterator *overlay_ref_iterator_begin(
* them.
*/
if (is_empty_ref_iterator(front)) {
- ref_iterator_abort(front);
+ ref_iterator_free(front);
return back;
} else if (is_empty_ref_iterator(back)) {
- ref_iterator_abort(back);
+ ref_iterator_free(back);
return front;
}
@@ -350,19 +371,15 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
-
if (cmp < 0)
continue;
-
- if (cmp > 0) {
- /*
- * As the source iterator is ordered, we
- * can stop the iteration as soon as we see a
- * refname that comes after the prefix:
- */
- ok = ref_iterator_abort(iter->iter0);
- break;
- }
+ /*
+ * As the source iterator is ordered, we
+ * can stop the iteration as soon as we see a
+ * refname that comes after the prefix:
+ */
+ if (cmp > 0)
+ return ITER_DONE;
if (iter->trim) {
/*
@@ -386,12 +403,19 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK;
}
- iter->iter0 = NULL;
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- return ITER_ERROR;
return ok;
}
+static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ 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);
+}
+
static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -401,23 +425,19 @@ static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
return ref_iterator_peel(iter->iter0, peeled);
}
-static int prefix_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct prefix_ref_iterator *iter =
(struct prefix_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->iter0)
- ok = ref_iterator_abort(iter->iter0);
+ ref_iterator_free(iter->iter0);
free(iter->prefix);
- base_ref_iterator_free(ref_iterator);
- return ok;
}
static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
.advance = prefix_ref_iterator_advance,
+ .seek = prefix_ref_iterator_seek,
.peel = prefix_ref_iterator_peel,
- .abort = prefix_ref_iterator_abort,
+ .release = prefix_ref_iterator_release,
};
struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
@@ -453,20 +473,14 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
retval = fn(iter->refname, iter->referent, iter->oid, iter->flags, cb_data);
- if (retval) {
- /*
- * If ref_iterator_abort() returns ITER_ERROR,
- * we ignore that error in deference to the
- * callback function's return value.
- */
- ref_iterator_abort(iter);
+ if (retval)
goto out;
- }
}
out:
current_ref_iter = old_ref_iter;
if (ok == ITER_ERROR)
- return -1;
+ retval = -1;
+ ref_iterator_free(iter);
return retval;
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 813e5020e4..b4289a7d9c 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -841,6 +841,8 @@ struct packed_ref_iterator {
struct snapshot *snapshot;
+ char *prefix;
+
/* The current position in the snapshot's buffer: */
const char *pos;
@@ -863,11 +865,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)
{
@@ -967,6 +967,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;
@@ -976,15 +979,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;
}
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- ok = ITER_ERROR;
-
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)
{
@@ -1001,23 +1030,21 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
}
}
-static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
strbuf_release(&iter->refname_buf);
free(iter->jump);
+ free(iter->prefix);
release_snapshot(iter->snapshot);
- base_ref_iterator_free(ref_iterator);
- return ok;
}
static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.advance = packed_ref_iterator_advance,
+ .seek = packed_ref_iterator_seek,
.peel = packed_ref_iterator_peel,
- .abort = packed_ref_iterator_abort
+ .release = packed_ref_iterator_release,
};
static int jump_list_entry_cmp(const void *va, const void *vb)
@@ -1129,7 +1156,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;
@@ -1145,14 +1171,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);
@@ -1162,19 +1180,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;
}
@@ -1387,8 +1401,10 @@ static int write_with_updates(struct packed_ref_store *refs,
*/
iter = packed_ref_iterator_begin(&refs->base, "", NULL,
DO_FOR_EACH_INCLUDE_BROKEN);
- if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+ if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
+ ref_iterator_free(iter);
iter = NULL;
+ }
i = 0;
@@ -1436,8 +1452,10 @@ static int write_with_updates(struct packed_ref_store *refs,
* the iterator over the unneeded
* value.
*/
- if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+ if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
+ ref_iterator_free(iter);
iter = NULL;
+ }
cmp = +1;
} else {
/*
@@ -1474,8 +1492,10 @@ static int write_with_updates(struct packed_ref_store *refs,
peel_error ? NULL : &peeled))
goto write_error;
- if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+ if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
+ ref_iterator_free(iter);
iter = NULL;
+ }
} else if (is_null_oid(&update->new_oid)) {
/*
* The update wants to delete the reference,
@@ -1524,9 +1544,7 @@ write_error:
get_tempfile_path(refs->tempfile), strerror(errno));
error:
- if (iter)
- ref_iterator_abort(iter);
-
+ ref_iterator_free(iter);
delete_tempfile(&refs->tempfile);
return -1;
}
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 02f09e4df8..c1f1bab1d5 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -362,9 +362,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 +374,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 +387,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 +397,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 +413,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 +448,41 @@ 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)
+{
+ struct cache_ref_iterator *iter =
+ (struct cache_ref_iterator *)ref_iterator;
+ 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_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -452,21 +491,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 +511,22 @@ 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) < 0) {
+ ref_iterator_free(&iter->base);
+ return NULL;
}
- iter->repo = repo;
-
return ref_iterator;
}
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 8894b43d1d..e5862757a7 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -273,11 +273,11 @@ enum do_for_each_ref_flags {
* the next reference and returns ITER_OK. The data pointed at by
* refname and oid belong to the iterator; if you want to retain them
* after calling ref_iterator_advance() again or calling
- * ref_iterator_abort(), you must make a copy. When the iteration has
+ * ref_iterator_free(), you must make a copy. When the iteration has
* been exhausted, ref_iterator_advance() releases any resources
* associated with the iteration, frees the ref_iterator object, and
* returns ITER_DONE. If you want to abort the iteration early, call
- * ref_iterator_abort(), which also frees the ref_iterator object and
+ * ref_iterator_free(), which also frees the ref_iterator object and
* any associated resources. If there was an internal error advancing
* to the next entry, ref_iterator_advance() aborts the iteration,
* frees the ref_iterator, and returns ITER_ERROR.
@@ -293,7 +293,7 @@ enum do_for_each_ref_flags {
*
* while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
* if (want_to_stop_iteration()) {
- * ok = ref_iterator_abort(iter);
+ * ok = ITER_DONE;
* break;
* }
*
@@ -307,6 +307,7 @@ enum do_for_each_ref_flags {
*
* if (ok != ITER_DONE)
* handle_error();
+ * ref_iterator_free(iter);
*/
struct ref_iterator {
struct ref_iterator_vtable *vtable;
@@ -327,18 +328,30 @@ struct ref_iterator {
int ref_iterator_advance(struct ref_iterator *ref_iterator);
/*
+ * 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
+ * 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.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix);
+
+/*
* If possible, peel the reference currently being viewed by the
* iterator. Return 0 on success.
*/
int ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled);
-/*
- * End the iteration before it has been exhausted, freeing the
- * reference iterator and any associated resources and returning
- * ITER_DONE. If the abort itself failed, return ITER_ERROR.
- */
-int ref_iterator_abort(struct ref_iterator *ref_iterator);
+/* Free the reference iterator and any associated resources. */
+void ref_iterator_free(struct ref_iterator *ref_iterator);
/*
* An iterator over nothing (its first ref_iterator_advance() call
@@ -438,13 +451,6 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
void base_ref_iterator_init(struct ref_iterator *iter,
struct ref_iterator_vtable *vtable);
-/*
- * Base class destructor for ref_iterators. Destroy the ref_iterator
- * part of iter and shallow-free the object. This is meant to be
- * called only by the destructors of derived classes.
- */
-void base_ref_iterator_free(struct ref_iterator *iter);
-
/* Virtual function declarations for ref_iterators: */
/*
@@ -456,6 +462,13 @@ void base_ref_iterator_free(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.
+ */
+typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator,
+ const char *prefix);
+
+/*
* Peels the current ref, returning 0 for success or -1 for failure.
*/
typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
@@ -463,15 +476,15 @@ typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
/*
* Implementations of this function should free any resources specific
- * to the derived class, then call base_ref_iterator_free() to clean
- * up and free the ref_iterator object.
+ * to the derived class.
*/
-typedef int ref_iterator_abort_fn(struct ref_iterator *ref_iterator);
+typedef void ref_iterator_release_fn(struct ref_iterator *ref_iterator);
struct ref_iterator_vtable {
ref_iterator_advance_fn *advance;
+ ref_iterator_seek_fn *seek;
ref_iterator_peel_fn *peel;
- ref_iterator_abort_fn *abort;
+ ref_iterator_release_fn *release;
};
/*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 771f50df38..ae434cd248 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -547,7 +547,7 @@ struct reftable_ref_iterator {
struct reftable_ref_record ref;
struct object_id oid;
- const char *prefix;
+ char *prefix;
size_t prefix_len;
char **exclude_patterns;
size_t exclude_patterns_index;
@@ -711,20 +711,27 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
break;
}
- if (iter->err > 0) {
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- return ITER_ERROR;
+ if (iter->err > 0)
return ITER_DONE;
- }
-
- if (iter->err < 0) {
- ref_iterator_abort(ref_iterator);
+ if (iter->err < 0)
return ITER_ERROR;
- }
-
return ITER_OK;
}
+static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ 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);
+
+ return iter->err;
+}
+
static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -740,7 +747,7 @@ static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
return -1;
}
-static int reftable_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_ref_iterator *iter =
(struct reftable_ref_iterator *)ref_iterator;
@@ -751,14 +758,14 @@ static int reftable_ref_iterator_abort(struct ref_iterator *ref_iterator)
free(iter->exclude_patterns[i]);
free(iter->exclude_patterns);
}
- free(iter);
- return ITER_DONE;
+ free(iter->prefix);
}
static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
.advance = reftable_ref_iterator_advance,
+ .seek = reftable_ref_iterator_seek,
.peel = reftable_ref_iterator_peel,
- .abort = reftable_ref_iterator_abort
+ .release = reftable_ref_iterator_release,
};
static int qsort_strcmp(const void *va, const void *vb)
@@ -815,8 +822,6 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
iter = xcalloc(1, sizeof(*iter));
base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
- iter->prefix = prefix;
- iter->prefix_len = prefix ? strlen(prefix) : 0;
iter->base.oid = &iter->oid;
iter->flags = flags;
iter->refs = refs;
@@ -830,8 +835,11 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
if (ret)
goto done;
- reftable_stack_init_ref_iterator(stack, &iter->iter);
- ret = reftable_iterator_seek_ref(&iter->iter, prefix);
+ ret = reftable_stack_init_ref_iterator(stack, &iter->iter);
+ if (ret)
+ goto done;
+
+ ret = reftable_ref_iterator_seek(&iter->base, prefix);
if (ret)
goto done;
@@ -1069,6 +1077,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
reftable_be_downcast(ref_store, REF_STORE_WRITE|REF_STORE_MAIN, "ref_transaction_prepare");
struct strbuf referent = STRBUF_INIT, head_referent = STRBUF_INIT;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
struct reftable_transaction_data *tx_data = NULL;
struct reftable_backend *be;
struct object_id head_oid;
@@ -1224,12 +1233,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
* can output a proper error message instead of failing
* at a later point.
*/
- ret = refs_verify_refname_available(ref_store, u->refname,
- &affected_refnames, NULL,
- transaction->flags & REF_TRANSACTION_FLAG_INITIAL,
- err);
- if (ret < 0)
- goto done;
+ string_list_append(&refnames_to_check, u->refname);
/*
* There is no need to write the reference deletion
@@ -1379,6 +1383,12 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
}
}
+ ret = refs_verify_refnames_available(ref_store, &refnames_to_check, &affected_refnames, NULL,
+ transaction->flags & REF_TRANSACTION_FLAG_INITIAL,
+ err);
+ if (ret < 0)
+ goto done;
+
transaction->backend_data = tx_data;
transaction->state = REF_TRANSACTION_PREPARED;
@@ -1394,6 +1404,7 @@ done:
string_list_clear(&affected_refnames, 0);
strbuf_release(&referent);
strbuf_release(&head_referent);
+ string_list_clear(&refnames_to_check, 0);
return ret;
}
@@ -2017,20 +2028,20 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
break;
}
- if (iter->err > 0) {
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- return ITER_ERROR;
+ if (iter->err > 0)
return ITER_DONE;
- }
-
- if (iter->err < 0) {
- ref_iterator_abort(ref_iterator);
+ if (iter->err < 0)
return ITER_ERROR;
- }
-
return ITER_OK;
}
+static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
+ const char *prefix UNUSED)
+{
+ BUG("reftable reflog iterator cannot be seeked");
+ return -1;
+}
+
static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
struct object_id *peeled UNUSED)
{
@@ -2038,21 +2049,20 @@ static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSE
return -1;
}
-static int reftable_reflog_iterator_abort(struct ref_iterator *ref_iterator)
+static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_reflog_iterator *iter =
(struct reftable_reflog_iterator *)ref_iterator;
reftable_log_record_release(&iter->log);
reftable_iterator_destroy(&iter->iter);
strbuf_release(&iter->last_name);
- free(iter);
- return ITER_DONE;
}
static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
.advance = reftable_reflog_iterator_advance,
+ .seek = reftable_reflog_iterator_seek,
.peel = reftable_reflog_iterator_peel,
- .abort = reftable_reflog_iterator_abort
+ .release = reftable_reflog_iterator_release,
};
static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftable_ref_store *refs,