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()`main
commit
8d6413a1be
|
@ -342,6 +342,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
|
|||
strbuf_setlen(src, src_len);
|
||||
die(_("failed to iterate over '%s'"), src->buf);
|
||||
}
|
||||
|
||||
dir_iterator_free(iter);
|
||||
}
|
||||
|
||||
static void clone_local(const char *src_repo, const char *dest_repo)
|
||||
|
|
|
@ -179,7 +179,8 @@ static int parse_next_oid(const char **next, const char *end,
|
|||
(*next)++;
|
||||
*next = parse_arg(*next, &arg);
|
||||
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;
|
||||
} else {
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
} else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
|
||||
have_old_oid = 1;
|
||||
|
@ -772,7 +775,8 @@ int cmd_update_ref(int argc,
|
|||
refname = argv[0];
|
||||
value = argv[1];
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -783,7 +787,8 @@ int cmd_update_ref(int argc,
|
|||
* must not already exist:
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
|
||||
goto error_out;
|
||||
return ITER_ERROR;
|
||||
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. */
|
||||
|
@ -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);
|
||||
if (ret < 0) {
|
||||
if (iter->flags & DIR_ITERATOR_PEDANTIC)
|
||||
goto error_out;
|
||||
return ITER_ERROR;
|
||||
continue;
|
||||
} else if (ret > 0) {
|
||||
if (pop_level(iter) == 0)
|
||||
return dir_iterator_abort(dir_iterator);
|
||||
return ITER_DONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,7 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
|
|||
} else {
|
||||
if (level->entries_idx >= level->entries.nr) {
|
||||
if (pop_level(iter) == 0)
|
||||
return dir_iterator_abort(dir_iterator);
|
||||
return ITER_DONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -232,22 +232,21 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
|
|||
|
||||
if (prepare_next_entry_data(iter, name)) {
|
||||
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
|
||||
goto error_out;
|
||||
return ITER_ERROR;
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!iter)
|
||||
return;
|
||||
|
||||
for (; iter->levels_nr; iter->levels_nr--) {
|
||||
struct dir_iterator_level *level =
|
||||
&iter->levels[iter->levels_nr - 1];
|
||||
|
@ -266,7 +265,6 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
|
|||
free(iter->levels);
|
||||
strbuf_release(&iter->base.path);
|
||||
free(iter);
|
||||
return ITER_DONE;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
error_out:
|
||||
dir_iterator_abort(dir_iterator);
|
||||
dir_iterator_free(dir_iterator);
|
||||
errno = saved_errno;
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
*
|
||||
* while ((ok = dir_iterator_advance(iter)) == ITER_OK) {
|
||||
* if (want_to_stop_iteration()) {
|
||||
* ok = dir_iterator_abort(iter);
|
||||
* ok = ITER_DONE;
|
||||
* break;
|
||||
* }
|
||||
*
|
||||
|
@ -39,6 +39,7 @@
|
|||
*
|
||||
* if (ok != ITER_DONE)
|
||||
* handle_error();
|
||||
* dir_iterator_free(iter);
|
||||
*
|
||||
* Callers are allowed to modify iter->path while they are working,
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* End the iteration before it has been exhausted. Free the
|
||||
* 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);
|
||||
/* Free the dir_iterator and any associated resources. */
|
||||
void dir_iterator_free(struct dir_iterator *iterator);
|
||||
|
||||
#endif
|
||||
|
|
23
hash.h
23
hash.h
|
@ -193,17 +193,18 @@ struct object_id {
|
|||
int algo; /* XXX requires 4-byte alignment */
|
||||
};
|
||||
|
||||
#define GET_OID_QUIETLY 01
|
||||
#define GET_OID_COMMIT 02
|
||||
#define GET_OID_COMMITTISH 04
|
||||
#define GET_OID_TREE 010
|
||||
#define GET_OID_TREEISH 020
|
||||
#define GET_OID_BLOB 040
|
||||
#define GET_OID_FOLLOW_SYMLINKS 0100
|
||||
#define GET_OID_RECORD_PATH 0200
|
||||
#define GET_OID_ONLY_TO_DIE 04000
|
||||
#define GET_OID_REQUIRE_PATH 010000
|
||||
#define GET_OID_HASH_ANY 020000
|
||||
#define GET_OID_QUIETLY 01
|
||||
#define GET_OID_COMMIT 02
|
||||
#define GET_OID_COMMITTISH 04
|
||||
#define GET_OID_TREE 010
|
||||
#define GET_OID_TREEISH 020
|
||||
#define GET_OID_BLOB 040
|
||||
#define GET_OID_FOLLOW_SYMLINKS 0100
|
||||
#define GET_OID_RECORD_PATH 0200
|
||||
#define GET_OID_ONLY_TO_DIE 04000
|
||||
#define GET_OID_REQUIRE_PATH 010000
|
||||
#define GET_OID_HASH_ANY 020000
|
||||
#define GET_OID_SKIP_AMBIGUITY_CHECK 040000
|
||||
|
||||
#define GET_OID_DISAMBIGUATORS \
|
||||
(GET_OID_COMMIT | GET_OID_COMMITTISH | \
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#define ITER_OK 0
|
||||
|
||||
/*
|
||||
* The iterator is exhausted and has been freed.
|
||||
* The iterator is exhausted.
|
||||
*/
|
||||
#define ITER_DONE -1
|
||||
|
||||
|
|
|
@ -961,7 +961,9 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
|
|||
int fatal = !(flags & GET_OID_QUIETLY);
|
||||
|
||||
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);
|
||||
if (refs_found > 0) {
|
||||
warning(warn_msg, len, str);
|
||||
|
@ -1794,18 +1796,20 @@ void object_context_release(struct object_context *ctx)
|
|||
strbuf_release(&ctx->symlink_path);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is like "get_oid_basic()", except it allows "object ID expressions",
|
||||
* notably "xyz^" for "parent of xyz"
|
||||
*/
|
||||
int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
|
||||
int repo_get_oid_with_flags(struct repository *r, const char *name,
|
||||
struct object_id *oid, unsigned flags)
|
||||
{
|
||||
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);
|
||||
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
|
||||
* format and the given arguments) is not a valid object.
|
||||
|
|
|
@ -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,
|
||||
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);
|
||||
__attribute__((format (printf, 2, 3)))
|
||||
int get_oidf(struct object_id *oid, const char *fmt, ...);
|
||||
|
|
187
refs.c
187
refs.c
|
@ -2495,19 +2495,18 @@ int ref_transaction_commit(struct ref_transaction *transaction,
|
|||
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)
|
||||
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)
|
||||
{
|
||||
const char *slash;
|
||||
const char *extra_refname;
|
||||
struct strbuf dirname = STRBUF_INIT;
|
||||
struct strbuf referent = STRBUF_INIT;
|
||||
struct object_id oid;
|
||||
unsigned int type;
|
||||
struct string_list_item *item;
|
||||
struct ref_iterator *iter = NULL;
|
||||
struct strset dirnames;
|
||||
int ret = -1;
|
||||
|
||||
/*
|
||||
|
@ -2517,86 +2516,130 @@ int refs_verify_refname_available(struct ref_store *refs,
|
|||
|
||||
assert(err);
|
||||
|
||||
strbuf_grow(&dirname, strlen(refname) + 1);
|
||||
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);
|
||||
strset_init(&dirnames);
|
||||
|
||||
/*
|
||||
* We are still at a leading dir of the refname (e.g.,
|
||||
* "refs/foo"; if there is a reference with that name,
|
||||
* it is a conflict, *unless* it is in skip.
|
||||
*/
|
||||
if (skip && string_list_has_string(skip, dirname.buf))
|
||||
continue;
|
||||
for_each_string_list_item(item, refnames) {
|
||||
const char *refname = item->string;
|
||||
const char *extra_refname;
|
||||
struct object_id oid;
|
||||
unsigned int type;
|
||||
const char *slash;
|
||||
|
||||
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;
|
||||
}
|
||||
strbuf_reset(&dirname);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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, '/');
|
||||
/* Expand dirname to the new prefix, not including the trailing slash: */
|
||||
strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
|
||||
|
||||
if (!initial_transaction) {
|
||||
struct ref_iterator *iter;
|
||||
int ok;
|
||||
|
||||
iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
|
||||
DO_FOR_EACH_INCLUDE_BROKEN);
|
||||
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
|
||||
if (skip &&
|
||||
string_list_has_string(skip, iter->refname))
|
||||
/*
|
||||
* We are still at a leading dir of the refname (e.g.,
|
||||
* "refs/foo"; if there is a reference with that name,
|
||||
* it is a conflict, *unless* it is in skip.
|
||||
*/
|
||||
if (skip && string_list_has_string(skip, dirname.buf))
|
||||
continue;
|
||||
|
||||
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
|
||||
iter->refname, refname);
|
||||
ref_iterator_abort(iter);
|
||||
goto cleanup;
|
||||
/*
|
||||
* If we've already seen the directory we don't need to
|
||||
* process it again. Skip it to avoid checking checking
|
||||
* 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);
|
||||
if (extra_refname)
|
||||
strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
|
||||
refname, extra_refname);
|
||||
else
|
||||
ret = 0;
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
strbuf_release(&referent);
|
||||
strbuf_release(&dirname);
|
||||
strset_clear(&dirnames);
|
||||
ref_iterator_free(iter);
|
||||
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 {
|
||||
each_reflog_fn *fn;
|
||||
void *cb_data;
|
||||
|
|
12
refs.h
12
refs.h
|
@ -124,6 +124,18 @@ int refs_verify_refname_available(struct ref_store *refs,
|
|||
unsigned int initial_transaction,
|
||||
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 should_autocreate_reflog(enum log_refs_config log_all_ref_updates,
|
||||
|
|
20
refs/debug.c
20
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 *
|
||||
|
|
|
@ -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;
|
||||
|
|
150
refs/iterator.c
150
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 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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
if (prefix && *prefix) {
|
||||
iter->prefix = xstrdup(prefix);
|
||||
level->prefix_state = PREFIX_WITHIN_DIR;
|
||||
} else {
|
||||
level->prefix_state = PREFIX_CONTAINS_DIR;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -326,6 +327,22 @@ 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.
|
||||
|
@ -333,12 +350,8 @@ int ref_iterator_advance(struct ref_iterator *ref_iterator);
|
|||
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: */
|
||||
|
||||
/*
|
||||
|
@ -455,6 +461,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.
|
||||
*/
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -53,6 +53,7 @@ int cmd__dir_iterator(int argc, const char **argv)
|
|||
printf("(%s) [%s] %s\n", diter->relative_path, diter->basename,
|
||||
diter->path.buf);
|
||||
}
|
||||
dir_iterator_free(diter);
|
||||
|
||||
if (iter_status != ITER_DONE) {
|
||||
printf("dir_iterator_advance failure\n");
|
||||
|
|
Loading…
Reference in New Issue