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()`
This commit is contained in:
Junio C Hamano
2025-03-29 16:39:07 +09:00
18 changed files with 545 additions and 362 deletions

View File

@@ -342,6 +342,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
strbuf_setlen(src, src_len); strbuf_setlen(src, src_len);
die(_("failed to iterate over '%s'"), src->buf); die(_("failed to iterate over '%s'"), src->buf);
} }
dir_iterator_free(iter);
} }
static void clone_local(const char *src_repo, const char *dest_repo) static void clone_local(const char *src_repo, const char *dest_repo)

View File

@@ -179,7 +179,8 @@ static int parse_next_oid(const char **next, const char *end,
(*next)++; (*next)++;
*next = parse_arg(*next, &arg); *next = parse_arg(*next, &arg);
if (arg.len) { if (arg.len) {
if (repo_get_oid(the_repository, arg.buf, oid)) if (repo_get_oid_with_flags(the_repository, arg.buf, oid,
GET_OID_SKIP_AMBIGUITY_CHECK))
goto invalid; goto invalid;
} else { } else {
/* Without -z, an empty value means all zeros: */ /* Without -z, an empty value means all zeros: */
@@ -197,7 +198,8 @@ static int parse_next_oid(const char **next, const char *end,
*next += arg.len; *next += arg.len;
if (arg.len) { if (arg.len) {
if (repo_get_oid(the_repository, arg.buf, oid)) if (repo_get_oid_with_flags(the_repository, arg.buf, oid,
GET_OID_SKIP_AMBIGUITY_CHECK))
goto invalid; goto invalid;
} else if (flags & PARSE_SHA1_ALLOW_EMPTY) { } else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
/* With -z, treat an empty value as all zeros: */ /* With -z, treat an empty value as all zeros: */
@@ -299,7 +301,8 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
die("symref-update %s: expected old value", refname); die("symref-update %s: expected old value", refname);
if (!strcmp(old_arg, "oid")) { if (!strcmp(old_arg, "oid")) {
if (repo_get_oid(the_repository, old_target, &old_oid)) if (repo_get_oid_with_flags(the_repository, old_target, &old_oid,
GET_OID_SKIP_AMBIGUITY_CHECK))
die("symref-update %s: invalid oid: %s", refname, old_target); die("symref-update %s: invalid oid: %s", refname, old_target);
have_old_oid = 1; have_old_oid = 1;
@@ -772,7 +775,8 @@ int cmd_update_ref(int argc,
refname = argv[0]; refname = argv[0];
value = argv[1]; value = argv[1];
oldval = argv[2]; oldval = argv[2];
if (repo_get_oid(the_repository, value, &oid)) if (repo_get_oid_with_flags(the_repository, value, &oid,
GET_OID_SKIP_AMBIGUITY_CHECK))
die("%s: not a valid SHA1", value); die("%s: not a valid SHA1", value);
} }
@@ -783,7 +787,8 @@ int cmd_update_ref(int argc,
* must not already exist: * must not already exist:
*/ */
oidclr(&oldoid, the_repository->hash_algo); oidclr(&oldoid, the_repository->hash_algo);
else if (repo_get_oid(the_repository, oldval, &oldoid)) else if (repo_get_oid_with_flags(the_repository, oldval, &oldoid,
GET_OID_SKIP_AMBIGUITY_CHECK))
die("%s: not a valid old SHA1", oldval); die("%s: not a valid old SHA1", oldval);
} }

View File

@@ -193,9 +193,9 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
if (S_ISDIR(iter->base.st.st_mode) && push_level(iter)) { if (S_ISDIR(iter->base.st.st_mode) && push_level(iter)) {
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC) if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
goto error_out; return ITER_ERROR;
if (iter->levels_nr == 0) if (iter->levels_nr == 0)
goto error_out; return ITER_ERROR;
} }
/* Loop until we find an entry that we can give back to the caller. */ /* Loop until we find an entry that we can give back to the caller. */
@@ -211,11 +211,11 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
int ret = next_directory_entry(level->dir, iter->base.path.buf, &de); int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
if (ret < 0) { if (ret < 0) {
if (iter->flags & DIR_ITERATOR_PEDANTIC) if (iter->flags & DIR_ITERATOR_PEDANTIC)
goto error_out; return ITER_ERROR;
continue; continue;
} else if (ret > 0) { } else if (ret > 0) {
if (pop_level(iter) == 0) if (pop_level(iter) == 0)
return dir_iterator_abort(dir_iterator); return ITER_DONE;
continue; continue;
} }
@@ -223,7 +223,7 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
} else { } else {
if (level->entries_idx >= level->entries.nr) { if (level->entries_idx >= level->entries.nr) {
if (pop_level(iter) == 0) if (pop_level(iter) == 0)
return dir_iterator_abort(dir_iterator); return ITER_DONE;
continue; continue;
} }
@@ -232,22 +232,21 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
if (prepare_next_entry_data(iter, name)) { if (prepare_next_entry_data(iter, name)) {
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC) if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
goto error_out; return ITER_ERROR;
continue; continue;
} }
return ITER_OK; return ITER_OK;
} }
error_out:
dir_iterator_abort(dir_iterator);
return ITER_ERROR;
} }
int dir_iterator_abort(struct dir_iterator *dir_iterator) void dir_iterator_free(struct dir_iterator *dir_iterator)
{ {
struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator; struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator;
if (!iter)
return;
for (; iter->levels_nr; iter->levels_nr--) { for (; iter->levels_nr; iter->levels_nr--) {
struct dir_iterator_level *level = struct dir_iterator_level *level =
&iter->levels[iter->levels_nr - 1]; &iter->levels[iter->levels_nr - 1];
@@ -266,7 +265,6 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
free(iter->levels); free(iter->levels);
strbuf_release(&iter->base.path); strbuf_release(&iter->base.path);
free(iter); free(iter);
return ITER_DONE;
} }
struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags) struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags)
@@ -301,7 +299,7 @@ struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags)
return dir_iterator; return dir_iterator;
error_out: error_out:
dir_iterator_abort(dir_iterator); dir_iterator_free(dir_iterator);
errno = saved_errno; errno = saved_errno;
return NULL; return NULL;
} }

View File

@@ -28,7 +28,7 @@
* *
* while ((ok = dir_iterator_advance(iter)) == ITER_OK) { * while ((ok = dir_iterator_advance(iter)) == ITER_OK) {
* if (want_to_stop_iteration()) { * if (want_to_stop_iteration()) {
* ok = dir_iterator_abort(iter); * ok = ITER_DONE;
* break; * break;
* } * }
* *
@@ -39,6 +39,7 @@
* *
* if (ok != ITER_DONE) * if (ok != ITER_DONE)
* handle_error(); * handle_error();
* dir_iterator_free(iter);
* *
* Callers are allowed to modify iter->path while they are working, * Callers are allowed to modify iter->path while they are working,
* but they must restore it to its original contents before calling * but they must restore it to its original contents before calling
@@ -107,11 +108,7 @@ struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags);
*/ */
int dir_iterator_advance(struct dir_iterator *iterator); int dir_iterator_advance(struct dir_iterator *iterator);
/* /* Free the dir_iterator and any associated resources. */
* End the iteration before it has been exhausted. Free the void dir_iterator_free(struct dir_iterator *iterator);
* dir_iterator and any associated resources and return ITER_DONE. On
* error, free the dir_iterator and return ITER_ERROR.
*/
int dir_iterator_abort(struct dir_iterator *iterator);
#endif #endif

23
hash.h
View File

@@ -193,17 +193,18 @@ struct object_id {
int algo; /* XXX requires 4-byte alignment */ int algo; /* XXX requires 4-byte alignment */
}; };
#define GET_OID_QUIETLY 01 #define GET_OID_QUIETLY 01
#define GET_OID_COMMIT 02 #define GET_OID_COMMIT 02
#define GET_OID_COMMITTISH 04 #define GET_OID_COMMITTISH 04
#define GET_OID_TREE 010 #define GET_OID_TREE 010
#define GET_OID_TREEISH 020 #define GET_OID_TREEISH 020
#define GET_OID_BLOB 040 #define GET_OID_BLOB 040
#define GET_OID_FOLLOW_SYMLINKS 0100 #define GET_OID_FOLLOW_SYMLINKS 0100
#define GET_OID_RECORD_PATH 0200 #define GET_OID_RECORD_PATH 0200
#define GET_OID_ONLY_TO_DIE 04000 #define GET_OID_ONLY_TO_DIE 04000
#define GET_OID_REQUIRE_PATH 010000 #define GET_OID_REQUIRE_PATH 010000
#define GET_OID_HASH_ANY 020000 #define GET_OID_HASH_ANY 020000
#define GET_OID_SKIP_AMBIGUITY_CHECK 040000
#define GET_OID_DISAMBIGUATORS \ #define GET_OID_DISAMBIGUATORS \
(GET_OID_COMMIT | GET_OID_COMMITTISH | \ (GET_OID_COMMIT | GET_OID_COMMITTISH | \

View File

@@ -12,7 +12,7 @@
#define ITER_OK 0 #define ITER_OK 0
/* /*
* The iterator is exhausted and has been freed. * The iterator is exhausted.
*/ */
#define ITER_DONE -1 #define ITER_DONE -1

View File

@@ -961,7 +961,9 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
int fatal = !(flags & GET_OID_QUIETLY); int fatal = !(flags & GET_OID_QUIETLY);
if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) { if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
if (repo_settings_get_warn_ambiguous_refs(r) && warn_on_object_refname_ambiguity) { if (!(flags & GET_OID_SKIP_AMBIGUITY_CHECK) &&
repo_settings_get_warn_ambiguous_refs(r) &&
warn_on_object_refname_ambiguity) {
refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref, 0); refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref, 0);
if (refs_found > 0) { if (refs_found > 0) {
warning(warn_msg, len, str); warning(warn_msg, len, str);
@@ -1794,18 +1796,20 @@ void object_context_release(struct object_context *ctx)
strbuf_release(&ctx->symlink_path); strbuf_release(&ctx->symlink_path);
} }
/* int repo_get_oid_with_flags(struct repository *r, const char *name,
* This is like "get_oid_basic()", except it allows "object ID expressions", struct object_id *oid, unsigned flags)
* notably "xyz^" for "parent of xyz"
*/
int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
{ {
struct object_context unused; struct object_context unused;
int ret = get_oid_with_context(r, name, 0, oid, &unused); int ret = get_oid_with_context(r, name, flags, oid, &unused);
object_context_release(&unused); object_context_release(&unused);
return ret; return ret;
} }
int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
{
return repo_get_oid_with_flags(r, name, oid, 0);
}
/* /*
* This returns a non-zero value if the string (built using printf * This returns a non-zero value if the string (built using printf
* format and the given arguments) is not a valid object. * format and the given arguments) is not a valid object.

View File

@@ -51,6 +51,12 @@ void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid, void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
int abbrev_len); int abbrev_len);
/*
* This is like "get_oid_basic()", except it allows "object ID expressions",
* notably "xyz^" for "parent of xyz". Accepts GET_OID_* flags.
*/
int repo_get_oid_with_flags(struct repository *r, const char *str,
struct object_id *oid, unsigned flags);
int repo_get_oid(struct repository *r, const char *str, struct object_id *oid); int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
__attribute__((format (printf, 2, 3))) __attribute__((format (printf, 2, 3)))
int get_oidf(struct object_id *oid, const char *fmt, ...); int get_oidf(struct object_id *oid, const char *fmt, ...);

187
refs.c
View File

@@ -2495,19 +2495,18 @@ int ref_transaction_commit(struct ref_transaction *transaction,
return ret; return ret;
} }
int refs_verify_refname_available(struct ref_store *refs, int refs_verify_refnames_available(struct ref_store *refs,
const char *refname, const struct string_list *refnames,
const struct string_list *extras, const struct string_list *extras,
const struct string_list *skip, const struct string_list *skip,
unsigned int initial_transaction, unsigned int initial_transaction,
struct strbuf *err) struct strbuf *err)
{ {
const char *slash;
const char *extra_refname;
struct strbuf dirname = STRBUF_INIT; struct strbuf dirname = STRBUF_INIT;
struct strbuf referent = STRBUF_INIT; struct strbuf referent = STRBUF_INIT;
struct object_id oid; struct string_list_item *item;
unsigned int type; struct ref_iterator *iter = NULL;
struct strset dirnames;
int ret = -1; int ret = -1;
/* /*
@@ -2517,86 +2516,130 @@ int refs_verify_refname_available(struct ref_store *refs,
assert(err); assert(err);
strbuf_grow(&dirname, strlen(refname) + 1); strset_init(&dirnames);
for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
/*
* Just saying "Is a directory" when we e.g. can't
* lock some multi-level ref isn't very informative,
* the user won't be told *what* is a directory, so
* let's not use strerror() below.
*/
int ignore_errno;
/* Expand dirname to the new prefix, not including the trailing slash: */
strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
/* for_each_string_list_item(item, refnames) {
* We are still at a leading dir of the refname (e.g., const char *refname = item->string;
* "refs/foo"; if there is a reference with that name, const char *extra_refname;
* it is a conflict, *unless* it is in skip. struct object_id oid;
*/ unsigned int type;
if (skip && string_list_has_string(skip, dirname.buf)) const char *slash;
continue;
if (!initial_transaction && strbuf_reset(&dirname);
!refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
&type, &ignore_errno)) {
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
dirname.buf, refname);
goto cleanup;
}
if (extras && string_list_has_string(extras, dirname.buf)) { for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"), /*
refname, dirname.buf); * Just saying "Is a directory" when we e.g. can't
goto cleanup; * lock some multi-level ref isn't very informative,
} * the user won't be told *what* is a directory, so
} * let's not use strerror() below.
*/
int ignore_errno;
/* /* Expand dirname to the new prefix, not including the trailing slash: */
* We are at the leaf of our refname (e.g., "refs/foo/bar"). strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
* There is no point in searching for a reference with that
* name, because a refname isn't considered to conflict with
* itself. But we still need to check for references whose
* names are in the "refs/foo/bar/" namespace, because they
* *do* conflict.
*/
strbuf_addstr(&dirname, refname + dirname.len);
strbuf_addch(&dirname, '/');
if (!initial_transaction) { /*
struct ref_iterator *iter; * We are still at a leading dir of the refname (e.g.,
int ok; * "refs/foo"; if there is a reference with that name,
* it is a conflict, *unless* it is in skip.
iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0, */
DO_FOR_EACH_INCLUDE_BROKEN); if (skip && string_list_has_string(skip, dirname.buf))
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
if (skip &&
string_list_has_string(skip, iter->refname))
continue; continue;
strbuf_addf(err, _("'%s' exists; cannot create '%s'"), /*
iter->refname, refname); * If we've already seen the directory we don't need to
ref_iterator_abort(iter); * process it again. Skip it to avoid checking checking
goto cleanup; * common prefixes like "refs/heads/" repeatedly.
*/
if (!strset_add(&dirnames, dirname.buf))
continue;
if (!initial_transaction &&
!refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
&type, &ignore_errno)) {
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
dirname.buf, refname);
goto cleanup;
}
if (extras && string_list_has_string(extras, dirname.buf)) {
strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
refname, dirname.buf);
goto cleanup;
}
} }
if (ok != ITER_DONE) /*
BUG("error while iterating over references"); * We are at the leaf of our refname (e.g., "refs/foo/bar").
* There is no point in searching for a reference with that
* name, because a refname isn't considered to conflict with
* itself. But we still need to check for references whose
* names are in the "refs/foo/bar/" namespace, because they
* *do* conflict.
*/
strbuf_addstr(&dirname, refname + dirname.len);
strbuf_addch(&dirname, '/');
if (!initial_transaction) {
int ok;
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) {
goto cleanup;
}
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
if (skip &&
string_list_has_string(skip, iter->refname))
continue;
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
iter->refname, refname);
goto cleanup;
}
if (ok != ITER_DONE)
BUG("error while iterating over references");
}
extra_refname = find_descendant_ref(dirname.buf, extras, skip);
if (extra_refname) {
strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
refname, extra_refname);
goto cleanup;
}
} }
extra_refname = find_descendant_ref(dirname.buf, extras, skip); ret = 0;
if (extra_refname)
strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
refname, extra_refname);
else
ret = 0;
cleanup: cleanup:
strbuf_release(&referent); strbuf_release(&referent);
strbuf_release(&dirname); strbuf_release(&dirname);
strset_clear(&dirnames);
ref_iterator_free(iter);
return ret; return ret;
} }
int refs_verify_refname_available(struct ref_store *refs,
const char *refname,
const struct string_list *extras,
const struct string_list *skip,
unsigned int initial_transaction,
struct strbuf *err)
{
struct string_list_item item = { .string = (char *) refname };
struct string_list refnames = {
.items = &item,
.nr = 1,
};
return refs_verify_refnames_available(refs, &refnames, extras, skip,
initial_transaction, err);
}
struct do_for_each_reflog_help { struct do_for_each_reflog_help {
each_reflog_fn *fn; each_reflog_fn *fn;
void *cb_data; void *cb_data;

12
refs.h
View File

@@ -124,6 +124,18 @@ int refs_verify_refname_available(struct ref_store *refs,
unsigned int initial_transaction, unsigned int initial_transaction,
struct strbuf *err); struct strbuf *err);
/*
* Same as `refs_verify_refname_available()`, but checking for a list of
* refnames instead of only a single item. This is more efficient in the case
* where one needs to check multiple refnames.
*/
int refs_verify_refnames_available(struct ref_store *refs,
const struct string_list *refnames,
const struct string_list *extras,
const struct string_list *skip,
unsigned int initial_transaction,
struct strbuf *err);
int refs_ref_exists(struct ref_store *refs, const char *refname); int refs_ref_exists(struct ref_store *refs, const char *refname);
int should_autocreate_reflog(enum log_refs_config log_all_ref_updates, int should_autocreate_reflog(enum log_refs_config log_all_ref_updates,

View File

@@ -169,6 +169,16 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
return res; 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, static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled) struct object_id *peeled)
{ {
@@ -179,19 +189,19 @@ static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
return res; 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 *diter =
(struct debug_ref_iterator *)ref_iterator; (struct debug_ref_iterator *)ref_iterator;
int res = diter->iter->vtable->abort(diter->iter); diter->iter->vtable->release(diter->iter);
trace_printf_key(&trace_refs, "iterator_abort: %d\n", res); trace_printf_key(&trace_refs, "iterator_abort\n");
return res;
} }
static struct ref_iterator_vtable debug_ref_iterator_vtable = { static struct ref_iterator_vtable debug_ref_iterator_vtable = {
.advance = debug_ref_iterator_advance, .advance = debug_ref_iterator_advance,
.seek = debug_ref_iterator_seek,
.peel = debug_ref_iterator_peel, .peel = debug_ref_iterator_peel,
.abort = debug_ref_iterator_abort, .release = debug_ref_iterator_release,
}; };
static struct ref_iterator * static struct ref_iterator *

View File

@@ -678,6 +678,7 @@ static void unlock_ref(struct ref_lock *lock)
*/ */
static int lock_raw_ref(struct files_ref_store *refs, static int lock_raw_ref(struct files_ref_store *refs,
const char *refname, int mustexist, const char *refname, int mustexist,
struct string_list *refnames_to_check,
const struct string_list *extras, const struct string_list *extras,
struct ref_lock **lock_p, struct ref_lock **lock_p,
struct strbuf *referent, struct strbuf *referent,
@@ -855,16 +856,11 @@ retry:
} }
/* /*
* If the ref did not exist and we are creating it, * If the ref did not exist and we are creating it, we have to
* make sure there is no existing packed ref that * make sure there is no existing packed ref that conflicts
* conflicts with refname: * with refname. This check is deferred so that we can batch it.
*/ */
if (refs_verify_refname_available( string_list_append(refnames_to_check, refname);
refs->packed_ref_store, refname,
extras, NULL, 0, err)) {
ret = TRANSACTION_NAME_CONFLICT;
goto error_return;
}
} }
ret = 0; ret = 0;
@@ -919,13 +915,17 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK; return ITER_OK;
} }
iter->iter0 = NULL;
if (ref_iterator_abort(ref_iterator) != ITER_DONE)
ok = ITER_ERROR;
return ok; 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, static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled) 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); 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 *iter =
(struct files_ref_iterator *)ref_iterator; (struct files_ref_iterator *)ref_iterator;
int ok = ITER_DONE; ref_iterator_free(iter->iter0);
if (iter->iter0)
ok = ref_iterator_abort(iter->iter0);
base_ref_iterator_free(ref_iterator);
return ok;
} }
static struct ref_iterator_vtable files_ref_iterator_vtable = { static struct ref_iterator_vtable files_ref_iterator_vtable = {
.advance = files_ref_iterator_advance, .advance = files_ref_iterator_advance,
.seek = files_ref_iterator_seek,
.peel = files_ref_iterator_peel, .peel = files_ref_iterator_peel,
.abort = files_ref_iterator_abort, .release = files_ref_iterator_release,
}; };
static struct ref_iterator *files_ref_iterator_begin( 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)) iter->flags, opts))
refcount++; refcount++;
if (refcount >= limit) { if (refcount >= limit) {
ref_iterator_abort(iter); ref_iterator_free(iter);
return 1; return 1;
} }
} }
@@ -1390,6 +1385,7 @@ static int should_pack_refs(struct files_ref_store *refs,
if (ret != ITER_DONE) if (ret != ITER_DONE)
die("error while iterating over references"); die("error while iterating over references");
ref_iterator_free(iter);
return 0; return 0;
} }
@@ -1456,6 +1452,7 @@ static int files_pack_refs(struct ref_store *ref_store,
packed_refs_unlock(refs->packed_ref_store); packed_refs_unlock(refs->packed_ref_store);
prune_refs(refs, &refs_to_prune); prune_refs(refs, &refs_to_prune);
ref_iterator_free(iter);
strbuf_release(&err); strbuf_release(&err);
return 0; return 0;
} }
@@ -2303,35 +2300,33 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK; return ITER_OK;
} }
iter->dir_iterator = NULL;
if (ref_iterator_abort(ref_iterator) == ITER_ERROR)
ok = ITER_ERROR;
return ok; 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, static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
struct object_id *peeled UNUSED) struct object_id *peeled UNUSED)
{ {
BUG("ref_iterator_peel() called for reflog_iterator"); 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 *iter =
(struct files_reflog_iterator *)ref_iterator; (struct files_reflog_iterator *)ref_iterator;
int ok = ITER_DONE; dir_iterator_free(iter->dir_iterator);
if (iter->dir_iterator)
ok = dir_iterator_abort(iter->dir_iterator);
base_ref_iterator_free(ref_iterator);
return ok;
} }
static struct ref_iterator_vtable files_reflog_iterator_vtable = { static struct ref_iterator_vtable files_reflog_iterator_vtable = {
.advance = files_reflog_iterator_advance, .advance = files_reflog_iterator_advance,
.seek = files_reflog_iterator_seek,
.peel = files_reflog_iterator_peel, .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, 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_update *update,
struct ref_transaction *transaction, struct ref_transaction *transaction,
const char *head_ref, const char *head_ref,
struct string_list *refnames_to_check,
struct string_list *affected_refnames, struct string_list *affected_refnames,
struct strbuf *err) struct strbuf *err)
{ {
@@ -2597,7 +2593,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
lock->count++; lock->count++;
} else { } else {
ret = lock_raw_ref(refs, update->refname, mustexist, ret = lock_raw_ref(refs, update->refname, mustexist,
affected_refnames, refnames_to_check, affected_refnames,
&lock, &referent, &lock, &referent,
&update->type, err); &update->type, err);
if (ret) { if (ret) {
@@ -2811,6 +2807,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
size_t i; size_t i;
int ret = 0; int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP; struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
char *head_ref = NULL; char *head_ref = NULL;
int head_type; int head_type;
struct files_transaction_backend_data *backend_data; 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]; struct ref_update *update = transaction->updates[i];
ret = lock_ref_for_update(refs, update, transaction, ret = lock_ref_for_update(refs, update, transaction,
head_ref, &affected_refnames, err); head_ref, &refnames_to_check,
&affected_refnames, err);
if (ret) if (ret)
goto cleanup; 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_transaction) {
if (packed_refs_lock(refs->packed_ref_store, 0, err)) { if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
ret = TRANSACTION_GENERIC_ERROR; ret = TRANSACTION_GENERIC_ERROR;
@@ -2972,6 +2990,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
cleanup: cleanup:
free(head_ref); free(head_ref);
string_list_clear(&affected_refnames, 0); string_list_clear(&affected_refnames, 0);
string_list_clear(&refnames_to_check, 0);
if (ret) if (ret)
files_transaction_cleanup(refs, transaction); files_transaction_cleanup(refs, transaction);
@@ -3036,6 +3055,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
size_t i; size_t i;
int ret = 0; int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP; 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 *packed_transaction = NULL;
struct ref_transaction *loose_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)) !is_null_oid(&update->old_oid))
BUG("initial ref transaction with old_sha1 set"); BUG("initial ref transaction with old_sha1 set");
if (refs_verify_refname_available(&refs->base, update->refname, string_list_append(&refnames_to_check, update->refname);
&affected_refnames, NULL, 1, err)) {
ret = TRANSACTION_NAME_CONFLICT;
goto cleanup;
}
/* /*
* packed-refs don't support symbolic refs, root refs and reflogs, * 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) || if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
ref_transaction_commit(packed_transaction, 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; ret = TRANSACTION_GENERIC_ERROR;
goto cleanup; goto cleanup;
} }
@@ -3147,6 +3174,7 @@ cleanup:
ref_transaction_free(packed_transaction); ref_transaction_free(packed_transaction);
transaction->state = REF_TRANSACTION_CLOSED; transaction->state = REF_TRANSACTION_CLOSED;
string_list_clear(&affected_refnames, 0); string_list_clear(&affected_refnames, 0);
string_list_clear(&refnames_to_check, 0);
return ret; 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); ret = error(_("failed to iterate over '%s'"), sb.buf);
out: out:
dir_iterator_free(iter);
strbuf_release(&sb); strbuf_release(&sb);
strbuf_release(&refname); strbuf_release(&refname);
return ret; return ret;

View File

@@ -15,15 +15,26 @@ 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,
const char *prefix)
{
return ref_iterator->vtable->seek(ref_iterator, prefix);
}
int ref_iterator_peel(struct ref_iterator *ref_iterator, int ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled) struct object_id *peeled)
{ {
return ref_iterator->vtable->peel(ref_iterator, 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, 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; 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 empty_ref_iterator {
struct ref_iterator base; 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 ref_iterator_abort(ref_iterator); return ITER_DONE;
}
static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
const char *prefix UNUSED)
{
return 0;
} }
static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED, 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"); 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 = { static struct ref_iterator_vtable empty_ref_iterator_vtable = {
.advance = empty_ref_iterator_advance, .advance = empty_ref_iterator_advance,
.seek = empty_ref_iterator_seek,
.peel = empty_ref_iterator_peel, .peel = empty_ref_iterator_peel,
.abort = empty_ref_iterator_abort, .release = empty_ref_iterator_release,
}; };
struct ref_iterator *empty_ref_iterator_begin(void) 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 merge_ref_iterator {
struct ref_iterator base; 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; ref_iterator_select_fn *select;
void *cb_data; 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); iter->select(iter->iter0, iter->iter1, iter->cb_data);
if (selection == ITER_SELECT_DONE) { if (selection == ITER_SELECT_DONE) {
return ref_iterator_abort(ref_iterator); return ITER_DONE;
} else if (selection == ITER_SELECT_ERROR) { } else if (selection == ITER_SELECT_ERROR) {
ref_iterator_abort(ref_iterator);
return ITER_ERROR; return ITER_ERROR;
} }
@@ -211,10 +220,31 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
} }
error: error:
ref_iterator_abort(ref_iterator);
return ITER_ERROR; 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, static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled) 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); 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 *iter =
(struct merge_ref_iterator *)ref_iterator; (struct merge_ref_iterator *)ref_iterator;
int ok = ITER_DONE; ref_iterator_free(iter->iter0_owned);
ref_iterator_free(iter->iter1_owned);
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;
} }
static struct ref_iterator_vtable merge_ref_iterator_vtable = { static struct ref_iterator_vtable merge_ref_iterator_vtable = {
.advance = merge_ref_iterator_advance, .advance = merge_ref_iterator_advance,
.seek = merge_ref_iterator_seek,
.peel = merge_ref_iterator_peel, .peel = merge_ref_iterator_peel,
.abort = merge_ref_iterator_abort, .release = merge_ref_iterator_release,
}; };
struct ref_iterator *merge_ref_iterator_begin( 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); base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable);
iter->iter0 = iter0; iter->iter0 = iter->iter0_owned = iter0;
iter->iter1 = iter1; iter->iter1 = iter->iter1_owned = iter1;
iter->select = select; iter->select = select;
iter->cb_data = cb_data; iter->cb_data = cb_data;
iter->current = NULL; iter->current = NULL;
@@ -310,10 +331,10 @@ struct ref_iterator *overlay_ref_iterator_begin(
* them. * them.
*/ */
if (is_empty_ref_iterator(front)) { if (is_empty_ref_iterator(front)) {
ref_iterator_abort(front); ref_iterator_free(front);
return back; return back;
} else if (is_empty_ref_iterator(back)) { } else if (is_empty_ref_iterator(back)) {
ref_iterator_abort(back); ref_iterator_free(back);
return front; 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) { while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
int cmp = compare_prefix(iter->iter0->refname, iter->prefix); int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
if (cmp < 0) if (cmp < 0)
continue; continue;
/*
if (cmp > 0) { * As the source iterator is ordered, we
/* * can stop the iteration as soon as we see a
* As the source iterator is ordered, we * refname that comes after the prefix:
* can stop the iteration as soon as we see a */
* refname that comes after the prefix: if (cmp > 0)
*/ return ITER_DONE;
ok = ref_iterator_abort(iter->iter0);
break;
}
if (iter->trim) { if (iter->trim) {
/* /*
@@ -386,12 +403,19 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK; return ITER_OK;
} }
iter->iter0 = NULL;
if (ref_iterator_abort(ref_iterator) != ITER_DONE)
return ITER_ERROR;
return ok; 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, static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled) 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); 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 *iter =
(struct prefix_ref_iterator *)ref_iterator; (struct prefix_ref_iterator *)ref_iterator;
int ok = ITER_DONE; ref_iterator_free(iter->iter0);
if (iter->iter0)
ok = ref_iterator_abort(iter->iter0);
free(iter->prefix); free(iter->prefix);
base_ref_iterator_free(ref_iterator);
return ok;
} }
static struct ref_iterator_vtable prefix_ref_iterator_vtable = { static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
.advance = prefix_ref_iterator_advance, .advance = prefix_ref_iterator_advance,
.seek = prefix_ref_iterator_seek,
.peel = prefix_ref_iterator_peel, .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, 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; current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) { while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
retval = fn(iter->refname, iter->referent, iter->oid, iter->flags, cb_data); retval = fn(iter->refname, iter->referent, iter->oid, iter->flags, cb_data);
if (retval) { 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);
goto out; goto out;
}
} }
out: out:
current_ref_iter = old_ref_iter; current_ref_iter = old_ref_iter;
if (ok == ITER_ERROR) if (ok == ITER_ERROR)
return -1; retval = -1;
ref_iterator_free(iter);
return retval; return retval;
} }

View File

@@ -841,6 +841,8 @@ struct packed_ref_iterator {
struct snapshot *snapshot; struct snapshot *snapshot;
char *prefix;
/* The current position in the snapshot's buffer: */ /* The current position in the snapshot's buffer: */
const char *pos; const char *pos;
@@ -863,11 +865,9 @@ struct packed_ref_iterator {
}; };
/* /*
* Move the iterator to the next record in the snapshot, without * Move the iterator to the next record in the snapshot. Adjust the fields in
* respect for whether the record is actually required by the current * `iter` and return `ITER_OK` or `ITER_DONE`. This function does not free the
* iteration. Adjust the fields in `iter` and return `ITER_OK` or * iterator in the case of `ITER_DONE`.
* `ITER_DONE`. This function does not free the iterator in the case
* of `ITER_DONE`.
*/ */
static int next_record(struct packed_ref_iterator *iter) 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; int ok;
while ((ok = next_record(iter)) == ITER_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 && if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
!is_per_worktree_ref(iter->base.refname)) !is_per_worktree_ref(iter->base.refname))
continue; continue;
@@ -976,15 +979,41 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, iter->flags)) &iter->oid, iter->flags))
continue; 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 ITER_OK;
} }
if (ref_iterator_abort(ref_iterator) != ITER_DONE)
ok = ITER_ERROR;
return 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, static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled) 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 *iter =
(struct packed_ref_iterator *)ref_iterator; (struct packed_ref_iterator *)ref_iterator;
int ok = ITER_DONE;
strbuf_release(&iter->refname_buf); strbuf_release(&iter->refname_buf);
free(iter->jump); free(iter->jump);
free(iter->prefix);
release_snapshot(iter->snapshot); release_snapshot(iter->snapshot);
base_ref_iterator_free(ref_iterator);
return ok;
} }
static struct ref_iterator_vtable packed_ref_iterator_vtable = { static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.advance = packed_ref_iterator_advance, .advance = packed_ref_iterator_advance,
.seek = packed_ref_iterator_seek,
.peel = packed_ref_iterator_peel, .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) 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 packed_ref_store *refs;
struct snapshot *snapshot; struct snapshot *snapshot;
const char *start;
struct packed_ref_iterator *iter; struct packed_ref_iterator *iter;
struct ref_iterator *ref_iterator; struct ref_iterator *ref_iterator;
unsigned int required_flags = REF_STORE_READ; unsigned int required_flags = REF_STORE_READ;
@@ -1145,14 +1171,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
*/ */
snapshot = get_snapshot(refs); 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); CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base; ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable); 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; iter->snapshot = snapshot;
acquire_snapshot(snapshot); acquire_snapshot(snapshot);
iter->pos = start;
iter->eof = snapshot->eof;
strbuf_init(&iter->refname_buf, 0); strbuf_init(&iter->refname_buf, 0);
iter->base.oid = &iter->oid; iter->base.oid = &iter->oid;
iter->repo = ref_store->repo; iter->repo = ref_store->repo;
iter->flags = flags; iter->flags = flags;
if (prefix && *prefix) if (packed_ref_iterator_seek(&iter->base, prefix) < 0) {
/* Stop iteration after we've gone *past* prefix: */ ref_iterator_free(&iter->base);
ref_iterator = prefix_ref_iterator_begin(ref_iterator, prefix, 0); return NULL;
}
return ref_iterator; 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, iter = packed_ref_iterator_begin(&refs->base, "", NULL,
DO_FOR_EACH_INCLUDE_BROKEN); 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; iter = NULL;
}
i = 0; i = 0;
@@ -1436,8 +1452,10 @@ static int write_with_updates(struct packed_ref_store *refs,
* the iterator over the unneeded * the iterator over the unneeded
* value. * value.
*/ */
if ((ok = ref_iterator_advance(iter)) != ITER_OK) if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
ref_iterator_free(iter);
iter = NULL; iter = NULL;
}
cmp = +1; cmp = +1;
} else { } else {
/* /*
@@ -1474,8 +1492,10 @@ static int write_with_updates(struct packed_ref_store *refs,
peel_error ? NULL : &peeled)) peel_error ? NULL : &peeled))
goto write_error; 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; iter = NULL;
}
} else if (is_null_oid(&update->new_oid)) { } else if (is_null_oid(&update->new_oid)) {
/* /*
* The update wants to delete the reference, * The update wants to delete the reference,
@@ -1524,9 +1544,7 @@ write_error:
get_tempfile_path(refs->tempfile), strerror(errno)); get_tempfile_path(refs->tempfile), strerror(errno));
error: error:
if (iter) ref_iterator_free(iter);
ref_iterator_abort(iter);
delete_tempfile(&refs->tempfile); delete_tempfile(&refs->tempfile);
return -1; return -1;
} }

View File

@@ -362,9 +362,7 @@ struct cache_ref_iterator {
struct ref_iterator base; struct ref_iterator base;
/* /*
* The number of levels currently on the stack. This is always * The number of levels currently on the stack.
* at least 1, because when it becomes zero the iteration is
* ended and this struct is freed.
*/ */
size_t levels_nr; size_t levels_nr;
@@ -376,7 +374,7 @@ struct cache_ref_iterator {
* The prefix is matched textually, without regard for path * The prefix is matched textually, without regard for path
* component boundaries. * component boundaries.
*/ */
const char *prefix; char *prefix;
/* /*
* A stack of levels. levels[0] is the uppermost level that is * 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 cache_ref_iterator_level *levels;
struct repository *repo; struct repository *repo;
struct ref_cache *cache;
int prime_dir;
}; };
static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) 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 *iter =
(struct cache_ref_iterator *)ref_iterator; (struct cache_ref_iterator *)ref_iterator;
if (!iter->levels_nr)
return ITER_DONE;
while (1) { while (1) {
struct cache_ref_iterator_level *level = struct cache_ref_iterator_level *level =
&iter->levels[iter->levels_nr - 1]; &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) { if (++level->index == level->dir->nr) {
/* This level is exhausted; pop up a level */ /* This level is exhausted; pop up a level */
if (--iter->levels_nr == 0) if (--iter->levels_nr == 0)
return ref_iterator_abort(ref_iterator); return ITER_DONE;
continue; 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, static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled) 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; 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 *iter =
(struct cache_ref_iterator *)ref_iterator; (struct cache_ref_iterator *)ref_iterator;
free(iter->prefix);
free((char *)iter->prefix);
free(iter->levels); free(iter->levels);
base_ref_iterator_free(ref_iterator);
return ITER_DONE;
} }
static struct ref_iterator_vtable cache_ref_iterator_vtable = { static struct ref_iterator_vtable cache_ref_iterator_vtable = {
.advance = cache_ref_iterator_advance, .advance = cache_ref_iterator_advance,
.seek = cache_ref_iterator_seek,
.peel = cache_ref_iterator_peel, .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, 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, struct repository *repo,
int prime_dir) int prime_dir)
{ {
struct ref_dir *dir;
struct cache_ref_iterator *iter; struct cache_ref_iterator *iter;
struct ref_iterator *ref_iterator; 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); CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base; ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable); base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
ALLOC_GROW(iter->levels, 10, iter->levels_alloc); ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
iter->levels_nr = 1;
level = &iter->levels[0];
level->index = -1;
level->dir = dir;
if (prefix && *prefix) {
iter->prefix = xstrdup(prefix);
level->prefix_state = PREFIX_WITHIN_DIR;
} else {
level->prefix_state = PREFIX_CONTAINS_DIR;
}
iter->repo = repo; iter->repo = repo;
iter->cache = cache;
iter->prime_dir = prime_dir;
if (cache_ref_iterator_seek(&iter->base, prefix) < 0) {
ref_iterator_free(&iter->base);
return NULL;
}
return ref_iterator; return ref_iterator;
} }

View File

@@ -273,11 +273,11 @@ enum do_for_each_ref_flags {
* the next reference and returns ITER_OK. The data pointed at by * 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 * refname and oid belong to the iterator; if you want to retain them
* after calling ref_iterator_advance() again or calling * 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 * been exhausted, ref_iterator_advance() releases any resources
* associated with the iteration, frees the ref_iterator object, and * associated with the iteration, frees the ref_iterator object, and
* returns ITER_DONE. If you want to abort the iteration early, call * 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 * any associated resources. If there was an internal error advancing
* to the next entry, ref_iterator_advance() aborts the iteration, * to the next entry, ref_iterator_advance() aborts the iteration,
* frees the ref_iterator, and returns ITER_ERROR. * 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) { * while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
* if (want_to_stop_iteration()) { * if (want_to_stop_iteration()) {
* ok = ref_iterator_abort(iter); * ok = ITER_DONE;
* break; * break;
* } * }
* *
@@ -307,6 +307,7 @@ enum do_for_each_ref_flags {
* *
* if (ok != ITER_DONE) * if (ok != ITER_DONE)
* handle_error(); * handle_error();
* ref_iterator_free(iter);
*/ */
struct ref_iterator { struct ref_iterator {
struct ref_iterator_vtable *vtable; struct ref_iterator_vtable *vtable;
@@ -326,6 +327,22 @@ struct ref_iterator {
*/ */
int ref_iterator_advance(struct ref_iterator *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 * If possible, peel the reference currently being viewed by the
* iterator. Return 0 on success. * iterator. Return 0 on success.
@@ -333,12 +350,8 @@ int ref_iterator_advance(struct ref_iterator *ref_iterator);
int ref_iterator_peel(struct ref_iterator *ref_iterator, int ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled); struct object_id *peeled);
/* /* Free the reference iterator and any associated resources. */
* End the iteration before it has been exhausted, freeing the void ref_iterator_free(struct ref_iterator *ref_iterator);
* 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);
/* /*
* An iterator over nothing (its first ref_iterator_advance() call * 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, void base_ref_iterator_init(struct ref_iterator *iter,
struct ref_iterator_vtable *vtable); 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: */ /* Virtual function declarations for ref_iterators: */
/* /*
@@ -455,6 +461,13 @@ void base_ref_iterator_free(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
* 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. * Peels the current ref, returning 0 for success or -1 for failure.
*/ */
@@ -463,15 +476,15 @@ typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
/* /*
* Implementations of this function should free any resources specific * Implementations of this function should free any resources specific
* to the derived class, then call base_ref_iterator_free() to clean * to the derived class.
* up and free the ref_iterator object.
*/ */
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 { struct ref_iterator_vtable {
ref_iterator_advance_fn *advance; ref_iterator_advance_fn *advance;
ref_iterator_seek_fn *seek;
ref_iterator_peel_fn *peel; ref_iterator_peel_fn *peel;
ref_iterator_abort_fn *abort; ref_iterator_release_fn *release;
}; };
/* /*

View File

@@ -547,7 +547,7 @@ struct reftable_ref_iterator {
struct reftable_ref_record ref; struct reftable_ref_record ref;
struct object_id oid; struct object_id oid;
const char *prefix; char *prefix;
size_t prefix_len; size_t prefix_len;
char **exclude_patterns; char **exclude_patterns;
size_t exclude_patterns_index; size_t exclude_patterns_index;
@@ -711,20 +711,27 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
break; break;
} }
if (iter->err > 0) { if (iter->err > 0)
if (ref_iterator_abort(ref_iterator) != ITER_DONE)
return ITER_ERROR;
return ITER_DONE; return ITER_DONE;
} if (iter->err < 0)
if (iter->err < 0) {
ref_iterator_abort(ref_iterator);
return ITER_ERROR; return ITER_ERROR;
}
return ITER_OK; 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, static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled) struct object_id *peeled)
{ {
@@ -740,7 +747,7 @@ static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
return -1; 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 *iter =
(struct reftable_ref_iterator *)ref_iterator; (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[i]);
free(iter->exclude_patterns); free(iter->exclude_patterns);
} }
free(iter); free(iter->prefix);
return ITER_DONE;
} }
static struct ref_iterator_vtable reftable_ref_iterator_vtable = { static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
.advance = reftable_ref_iterator_advance, .advance = reftable_ref_iterator_advance,
.seek = reftable_ref_iterator_seek,
.peel = reftable_ref_iterator_peel, .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) 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)); iter = xcalloc(1, sizeof(*iter));
base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable); 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->base.oid = &iter->oid;
iter->flags = flags; iter->flags = flags;
iter->refs = refs; iter->refs = refs;
@@ -830,8 +835,11 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
if (ret) if (ret)
goto done; goto done;
reftable_stack_init_ref_iterator(stack, &iter->iter); ret = reftable_stack_init_ref_iterator(stack, &iter->iter);
ret = reftable_iterator_seek_ref(&iter->iter, prefix); if (ret)
goto done;
ret = reftable_ref_iterator_seek(&iter->base, prefix);
if (ret) if (ret)
goto done; 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"); reftable_be_downcast(ref_store, REF_STORE_WRITE|REF_STORE_MAIN, "ref_transaction_prepare");
struct strbuf referent = STRBUF_INIT, head_referent = STRBUF_INIT; struct strbuf referent = STRBUF_INIT, head_referent = STRBUF_INIT;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP; 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_transaction_data *tx_data = NULL;
struct reftable_backend *be; struct reftable_backend *be;
struct object_id head_oid; 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 * can output a proper error message instead of failing
* at a later point. * at a later point.
*/ */
ret = refs_verify_refname_available(ref_store, u->refname, string_list_append(&refnames_to_check, u->refname);
&affected_refnames, NULL,
transaction->flags & REF_TRANSACTION_FLAG_INITIAL,
err);
if (ret < 0)
goto done;
/* /*
* There is no need to write the reference deletion * 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->backend_data = tx_data;
transaction->state = REF_TRANSACTION_PREPARED; transaction->state = REF_TRANSACTION_PREPARED;
@@ -1394,6 +1404,7 @@ done:
string_list_clear(&affected_refnames, 0); string_list_clear(&affected_refnames, 0);
strbuf_release(&referent); strbuf_release(&referent);
strbuf_release(&head_referent); strbuf_release(&head_referent);
string_list_clear(&refnames_to_check, 0);
return ret; return ret;
} }
@@ -2017,20 +2028,20 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
break; break;
} }
if (iter->err > 0) { if (iter->err > 0)
if (ref_iterator_abort(ref_iterator) != ITER_DONE)
return ITER_ERROR;
return ITER_DONE; return ITER_DONE;
} if (iter->err < 0)
if (iter->err < 0) {
ref_iterator_abort(ref_iterator);
return ITER_ERROR; return ITER_ERROR;
}
return ITER_OK; 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, static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
struct object_id *peeled UNUSED) struct object_id *peeled UNUSED)
{ {
@@ -2038,21 +2049,20 @@ static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSE
return -1; 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 *iter =
(struct reftable_reflog_iterator *)ref_iterator; (struct reftable_reflog_iterator *)ref_iterator;
reftable_log_record_release(&iter->log); reftable_log_record_release(&iter->log);
reftable_iterator_destroy(&iter->iter); reftable_iterator_destroy(&iter->iter);
strbuf_release(&iter->last_name); strbuf_release(&iter->last_name);
free(iter);
return ITER_DONE;
} }
static struct ref_iterator_vtable reftable_reflog_iterator_vtable = { static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
.advance = reftable_reflog_iterator_advance, .advance = reftable_reflog_iterator_advance,
.seek = reftable_reflog_iterator_seek,
.peel = reftable_reflog_iterator_peel, .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, static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftable_ref_store *refs,

View File

@@ -53,6 +53,7 @@ int cmd__dir_iterator(int argc, const char **argv)
printf("(%s) [%s] %s\n", diter->relative_path, diter->basename, printf("(%s) [%s] %s\n", diter->relative_path, diter->basename,
diter->path.buf); diter->path.buf);
} }
dir_iterator_free(diter);
if (iter_status != ITER_DONE) { if (iter_status != ITER_DONE) {
printf("dir_iterator_advance failure\n"); printf("dir_iterator_advance failure\n");