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:
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
23
hash.h
@@ -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 | \
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
187
refs.c
@@ -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
12
refs.h
@@ -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,
|
||||||
|
|||||||
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;
|
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 *
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
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);
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user