aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2026-03-09 14:36:55 -0700
committerJunio C Hamano <gitster@pobox.com>2026-03-09 14:36:55 -0700
commitd445aecfb013ae7b45e946f9aea06464aee69ed8 (patch)
tree14885f56e1e54ecbbf7d8350c1f5e4b753642f0d
parent5c56c725f104ce278fe1ec0ea0fce0ccfb245aea (diff)
parent1dd4f1e43f8f11ebb13c1b9edbd91219a134443d (diff)
downloadgit-d445aecfb013ae7b45e946f9aea06464aee69ed8.tar.xz
Merge branch 'ps/refs-for-each'
Code refactoring around refs-for-each-* API functions. * ps/refs-for-each: refs: replace `refs_for_each_fullref_in()` refs: replace `refs_for_each_namespaced_ref()` refs: replace `refs_for_each_glob_ref()` refs: replace `refs_for_each_glob_ref_in()` refs: replace `refs_for_each_rawref_in()` refs: replace `refs_for_each_rawref()` refs: replace `refs_for_each_ref_in()` refs: improve verification for-each-ref options refs: generalize `refs_for_each_fullref_in_prefixes()` refs: generalize `refs_for_each_namespaced_ref()` refs: speed up `refs_for_each_glob_ref_in()` refs: introduce `refs_for_each_ref_ext` refs: rename `each_ref_fn` refs: rename `do_for_each_ref_flags` refs: move `do_for_each_ref_flags` further up refs: move `refs_head_ref_namespaced()` refs: remove unused `refs_for_each_include_root_ref()`
-rw-r--r--bisect.c16
-rw-r--r--builtin/bisect.c37
-rw-r--r--builtin/describe.c7
-rw-r--r--builtin/fetch.c7
-rw-r--r--builtin/fsck.c7
-rw-r--r--builtin/receive-pack.c8
-rw-r--r--builtin/remote.c8
-rw-r--r--builtin/rev-parse.c38
-rw-r--r--builtin/show-ref.c21
-rw-r--r--fetch-pack.c15
-rw-r--r--http-backend.c8
-rw-r--r--ls-refs.c11
-rw-r--r--notes.c7
-rw-r--r--pack-bitmap.c15
-rw-r--r--pack-bitmap.h2
-rw-r--r--ref-filter.c19
-rw-r--r--refs.c271
-rw-r--r--refs.h199
-rw-r--r--refs/files-backend.c19
-rw-r--r--refs/iterator.c2
-rw-r--r--refs/packed-backend.c8
-rw-r--r--refs/reftable-backend.c10
-rw-r--r--revision.c49
-rw-r--r--submodule.c2
-rw-r--r--t/helper/test-ref-store.c15
-rw-r--r--upload-pack.c13
-rw-r--r--worktree.c2
-rw-r--r--worktree.h2
28 files changed, 457 insertions, 361 deletions
diff --git a/bisect.c b/bisect.c
index 2bdad4ee42..ef17a442e5 100644
--- a/bisect.c
+++ b/bisect.c
@@ -473,8 +473,12 @@ static int register_ref(const struct reference *ref, void *cb_data UNUSED)
static int read_bisect_refs(void)
{
- return refs_for_each_ref_in(get_main_ref_store(the_repository),
- "refs/bisect/", register_ref, NULL);
+ struct refs_for_each_ref_options opts = {
+ .prefix = "refs/bisect/",
+ .trim_prefix = strlen("refs/bisect/"),
+ };
+ return refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ register_ref, NULL, &opts);
}
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
@@ -1186,13 +1190,15 @@ static int mark_for_removal(const struct reference *ref, void *cb_data)
int bisect_clean_state(void)
{
+ struct refs_for_each_ref_options opts = {
+ .prefix = "refs/bisect/",
+ };
int result = 0;
/* There may be some refs packed during bisection */
struct string_list refs_for_removal = STRING_LIST_INIT_DUP;
- refs_for_each_fullref_in(get_main_ref_store(the_repository),
- "refs/bisect/", NULL, mark_for_removal,
- &refs_for_removal);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ mark_for_removal, &refs_for_removal, &opts);
string_list_append(&refs_for_removal, "BISECT_HEAD");
string_list_append(&refs_for_removal, "BISECT_EXPECTED_REV");
result = refs_delete_refs(get_main_ref_store(the_repository),
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 4cc118fb57..4520e585d0 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -422,13 +422,17 @@ static void bisect_status(struct bisect_state *state,
{
char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
char *good_glob = xstrfmt("%s-*", terms->term_good);
+ struct refs_for_each_ref_options opts = {
+ .pattern = good_glob,
+ .prefix = "refs/bisect/",
+ .trim_prefix = strlen("refs/bisect/"),
+ };
if (refs_ref_exists(get_main_ref_store(the_repository), bad_ref))
state->nr_bad = 1;
- refs_for_each_glob_ref_in(get_main_ref_store(the_repository), inc_nr,
- good_glob, "refs/bisect/",
- (void *) &state->nr_good);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ inc_nr, &state->nr_good, &opts);
free(good_glob);
free(bad_ref);
@@ -562,6 +566,10 @@ static int add_bisect_ref(const struct reference *ref, void *cb)
static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
{
+ struct refs_for_each_ref_options opts = {
+ .prefix = "refs/bisect/",
+ .trim_prefix = strlen("refs/bisect/"),
+ };
int res = 0;
struct add_bisect_ref_data cb = { revs };
char *good = xstrfmt("%s-*", terms->term_good);
@@ -581,11 +589,16 @@ static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
reset_revision_walk();
repo_init_revisions(the_repository, revs, NULL);
setup_revisions(0, NULL, revs, NULL);
- refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
- add_bisect_ref, bad, "refs/bisect/", &cb);
+
+ opts.pattern = bad;
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ add_bisect_ref, &cb, &opts);
+
cb.object_flags = UNINTERESTING;
- refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
- add_bisect_ref, good, "refs/bisect/", &cb);
+ opts.pattern = good;
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ add_bisect_ref, &cb, &opts);
+
if (prepare_revision_walk(revs))
res = error(_("revision walk setup failed"));
@@ -1191,10 +1204,14 @@ static int verify_good(const struct bisect_terms *terms, const char *command)
char *good_glob = xstrfmt("%s-*", terms->term_good);
int no_checkout = refs_ref_exists(get_main_ref_store(the_repository),
"BISECT_HEAD");
+ struct refs_for_each_ref_options opts = {
+ .pattern = good_glob,
+ .prefix = "refs/bisect/",
+ .trim_prefix = strlen("refs/bisect/"),
+ };
- refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
- get_first_good, good_glob, "refs/bisect/",
- &good_rev);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ get_first_good, &good_rev, &opts);
free(good_glob);
if (refs_read_ref(get_main_ref_store(the_repository), no_checkout ? "BISECT_HEAD" : "HEAD", &current_rev))
diff --git a/builtin/describe.c b/builtin/describe.c
index abfe3525a5..bffeed13a3 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -641,6 +641,9 @@ int cmd_describe(int argc,
const char *prefix,
struct repository *repo UNUSED )
{
+ struct refs_for_each_ref_options for_each_ref_opts = {
+ .flags = REFS_FOR_EACH_INCLUDE_BROKEN,
+ };
int contains = 0;
struct option options[] = {
OPT_BOOL(0, "contains", &contains, N_("find the tag that comes after the commit")),
@@ -738,8 +741,8 @@ int cmd_describe(int argc,
}
hashmap_init(&names, commit_name_neq, NULL, 0);
- refs_for_each_rawref(get_main_ref_store(the_repository), get_name,
- NULL);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ get_name, NULL, &for_each_ref_opts);
if (!hashmap_get_size(&names) && !always)
die(_("No names found, cannot describe anything."));
diff --git a/builtin/fetch.c b/builtin/fetch.c
index ef071e1e09..8a36cf67b5 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1541,6 +1541,9 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
for (i = 0; i < negotiation_tip.nr; i++) {
const char *s = negotiation_tip.items[i].string;
+ struct refs_for_each_ref_options opts = {
+ .pattern = s,
+ };
int old_nr;
if (!has_glob_specials(s)) {
struct object_id oid;
@@ -1552,8 +1555,8 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
continue;
}
old_nr = oids->nr;
- refs_for_each_glob_ref(get_main_ref_store(the_repository),
- add_oid, s, oids);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ add_oid, oids, &opts);
if (old_nr == oids->nr)
warning("ignoring --negotiation-tip=%s because it does not match any refs",
s);
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 51de6a6395..9bab32effe 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -582,6 +582,9 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
{
+ struct refs_for_each_ref_options opts = {
+ .flags = REFS_FOR_EACH_INCLUDE_BROKEN,
+ };
struct worktree **worktrees, **p;
const char *head_points_at;
struct object_id head_oid;
@@ -607,8 +610,8 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
return;
}
- refs_for_each_rawref(get_main_ref_store(the_repository),
- snapshot_ref, snap);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ snapshot_ref, snap, &opts);
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index f3e3cdeef6..55d39daa62 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -343,9 +343,9 @@ static void show_one_alternate_ref(const struct object_id *oid,
static void write_head_info(void)
{
+ struct refs_for_each_ref_options opts = { 0 };
static struct oidset seen = OIDSET_INIT;
struct strvec excludes_vector = STRVEC_INIT;
- const char **exclude_patterns;
/*
* We need access to the reference names both with and without their
@@ -353,12 +353,12 @@ static void write_head_info(void)
* thus have to adapt exclude patterns to carry the namespace prefix
* ourselves.
*/
- exclude_patterns = get_namespaced_exclude_patterns(
+ opts.exclude_patterns = get_namespaced_exclude_patterns(
hidden_refs_to_excludes(&hidden_refs),
get_git_namespace(), &excludes_vector);
- refs_for_each_fullref_in(get_main_ref_store(the_repository), "",
- exclude_patterns, show_ref_cb, &seen);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ show_ref_cb, &seen, &opts);
odb_for_each_alternate_ref(the_repository->objects,
show_one_alternate_ref, &seen);
diff --git a/builtin/remote.c b/builtin/remote.c
index ace390c671..0fddaa1773 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -912,6 +912,9 @@ static int mv(int argc, const char **argv, const char *prefix,
old_remote_context.buf);
if (refspecs_need_update) {
+ struct refs_for_each_ref_options opts = {
+ .flags = REFS_FOR_EACH_INCLUDE_BROKEN,
+ };
rename.transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
0, &err);
if (!rename.transaction)
@@ -923,9 +926,10 @@ static int mv(int argc, const char **argv, const char *prefix,
strbuf_reset(&buf);
strbuf_addf(&buf, "refs/remotes/%s/", rename.old_name);
+ opts.prefix = buf.buf;
- result = refs_for_each_rawref_in(get_main_ref_store(the_repository), buf.buf,
- rename_one_ref, &rename);
+ result = refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ rename_one_ref, &rename, &opts);
if (result < 0)
die(_("queueing remote ref renames failed: %s"), rename.err->buf);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 9032cc6327..01a62800e8 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -613,13 +613,22 @@ static int opt_with_value(const char *arg, const char *opt, const char **value)
static void handle_ref_opt(const char *pattern, const char *prefix)
{
- if (pattern)
- refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
- show_reference, pattern, prefix,
- NULL);
- else
- refs_for_each_ref_in(get_main_ref_store(the_repository),
- prefix, show_reference, NULL);
+ if (pattern) {
+ struct refs_for_each_ref_options opts = {
+ .pattern = pattern,
+ .prefix = prefix,
+ .trim_prefix = prefix ? strlen(prefix) : 0,
+ };
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ show_reference, NULL, &opts);
+ } else {
+ struct refs_for_each_ref_options opts = {
+ .prefix = prefix,
+ .trim_prefix = strlen(prefix),
+ };
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ show_reference, NULL, &opts);
+ }
clear_ref_exclusions(&ref_excludes);
}
@@ -931,14 +940,13 @@ int cmd_rev_parse(int argc,
continue;
}
if (!strcmp(arg, "--bisect")) {
- refs_for_each_fullref_in(get_main_ref_store(the_repository),
- "refs/bisect/bad",
- NULL, show_reference,
- NULL);
- refs_for_each_fullref_in(get_main_ref_store(the_repository),
- "refs/bisect/good",
- NULL, anti_reference,
- NULL);
+ struct refs_for_each_ref_options opts = { 0 };
+ opts.prefix = "refs/bisect/bad";
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ show_reference, NULL, &opts);
+ opts.prefix = "refs/bisect/good";
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ anti_reference, NULL, &opts);
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 4d4984e4e0..5d31acea7c 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -215,14 +215,19 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts,
refs_head_ref(get_main_ref_store(the_repository), show_ref,
&show_ref_data);
if (opts->branches_only || opts->tags_only) {
- if (opts->branches_only)
- refs_for_each_fullref_in(get_main_ref_store(the_repository),
- "refs/heads/", NULL,
- show_ref, &show_ref_data);
- if (opts->tags_only)
- refs_for_each_fullref_in(get_main_ref_store(the_repository),
- "refs/tags/", NULL, show_ref,
- &show_ref_data);
+ struct refs_for_each_ref_options for_each_ref_opts = { 0 };
+
+ if (opts->branches_only) {
+ for_each_ref_opts.prefix = "refs/heads/";
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ show_ref, &show_ref_data, &for_each_ref_opts);
+ }
+
+ if (opts->tags_only) {
+ for_each_ref_opts.prefix = "refs/tags/";
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ show_ref, &show_ref_data, &for_each_ref_opts);
+ }
} else {
refs_for_each_ref(get_main_ref_store(the_repository),
show_ref, &show_ref_data);
diff --git a/fetch-pack.c b/fetch-pack.c
index 9f8f980516..6ecd468ef7 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -293,11 +293,14 @@ static int next_flush(int stateless_rpc, int count)
static void mark_tips(struct fetch_negotiator *negotiator,
const struct oid_array *negotiation_tips)
{
+ struct refs_for_each_ref_options opts = {
+ .flags = REFS_FOR_EACH_INCLUDE_BROKEN,
+ };
int i;
if (!negotiation_tips) {
- refs_for_each_rawref(get_main_ref_store(the_repository),
- rev_list_insert_ref_oid, negotiator);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ rev_list_insert_ref_oid, negotiator, &opts);
return;
}
@@ -793,8 +796,12 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
*/
trace2_region_enter("fetch-pack", "mark_complete_local_refs", NULL);
if (!args->deepen) {
- refs_for_each_rawref(get_main_ref_store(the_repository),
- mark_complete_oid, NULL);
+ struct refs_for_each_ref_options opts = {
+ .flags = REFS_FOR_EACH_INCLUDE_BROKEN,
+ };
+
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ mark_complete_oid, NULL, &opts);
for_each_cached_alternate(NULL, mark_alternate_complete);
if (cutoff)
mark_recent_complete_commits(args, cutoff);
diff --git a/http-backend.c b/http-backend.c
index 0122146df6..1a171c5c5a 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -565,9 +565,13 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
run_service(argv, 0);
} else {
+ struct refs_for_each_ref_options opts = {
+ .namespace = get_git_namespace(),
+ };
+
select_getanyfile(hdr);
- refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
- NULL, show_text_ref, &buf);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ show_text_ref, &buf, &opts);
send_strbuf(hdr, "text/plain", &buf);
}
strbuf_release(&buf);
diff --git a/ls-refs.c b/ls-refs.c
index 8641281b86..9759826ca7 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -160,6 +160,7 @@ static int ls_refs_config(const char *var, const char *value,
int ls_refs(struct repository *r, struct packet_reader *request)
{
+ struct refs_for_each_ref_options opts = { 0 };
struct ls_refs_data data;
memset(&data, 0, sizeof(data));
@@ -201,10 +202,12 @@ int ls_refs(struct repository *r, struct packet_reader *request)
send_possibly_unborn_head(&data);
if (!data.prefixes.nr)
strvec_push(&data.prefixes, "");
- refs_for_each_fullref_in_prefixes(get_main_ref_store(r),
- get_git_namespace(), data.prefixes.v,
- hidden_refs_to_excludes(&data.hidden_refs),
- send_ref, &data);
+
+ opts.exclude_patterns = hidden_refs_to_excludes(&data.hidden_refs);
+ opts.namespace = get_git_namespace();
+
+ refs_for_each_ref_in_prefixes(get_main_ref_store(r), data.prefixes.v,
+ &opts, send_ref, &data);
packet_fflush(stdout);
strvec_clear(&data.prefixes);
strbuf_release(&data.buf);
diff --git a/notes.c b/notes.c
index 090c48bbd5..51a7ef9f83 100644
--- a/notes.c
+++ b/notes.c
@@ -952,8 +952,11 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
{
assert(list->strdup_strings);
if (has_glob_specials(glob)) {
- refs_for_each_glob_ref(get_main_ref_store(the_repository),
- string_list_add_one_ref, glob, list);
+ struct refs_for_each_ref_options opts = {
+ .pattern = glob,
+ };
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ string_list_add_one_ref, list, &opts);
} else {
struct object_id oid;
if (repo_get_oid(the_repository, glob, &oid))
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 1c93871484..22419bfb33 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -3324,8 +3324,9 @@ static const struct string_list *bitmap_preferred_tips(struct repository *r)
}
void for_each_preferred_bitmap_tip(struct repository *repo,
- each_ref_fn cb, void *cb_data)
+ refs_for_each_cb cb, void *cb_data)
{
+ struct refs_for_each_ref_options opts = { 0 };
struct string_list_item *item;
const struct string_list *preferred_tips;
struct strbuf buf = STRBUF_INIT;
@@ -3335,16 +3336,16 @@ void for_each_preferred_bitmap_tip(struct repository *repo,
return;
for_each_string_list_item(item, preferred_tips) {
- const char *pattern = item->string;
+ opts.prefix = item->string;
- if (!ends_with(pattern, "/")) {
+ if (!ends_with(opts.prefix, "/")) {
strbuf_reset(&buf);
- strbuf_addf(&buf, "%s/", pattern);
- pattern = buf.buf;
+ strbuf_addf(&buf, "%s/", opts.prefix);
+ opts.prefix = buf.buf;
}
- refs_for_each_ref_in(get_main_ref_store(repo),
- pattern, cb, cb_data);
+ refs_for_each_ref_ext(get_main_ref_store(repo),
+ cb, cb_data, &opts);
}
strbuf_release(&buf);
diff --git a/pack-bitmap.h b/pack-bitmap.h
index d0611d0481..a95e1c2d11 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -105,7 +105,7 @@ int for_each_bitmapped_object(struct bitmap_index *bitmap_git,
* "pack.preferBitmapTips" and invoke the callback on each function.
*/
void for_each_preferred_bitmap_tip(struct repository *repo,
- each_ref_fn cb, void *cb_data);
+ refs_for_each_cb cb, void *cb_data);
#define GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL \
"GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL"
diff --git a/ref-filter.c b/ref-filter.c
index 291d94328c..1da4c0e60d 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2762,7 +2762,7 @@ static int start_ref_iterator_after(struct ref_iterator *iter, const char *marke
return ret;
}
-static int for_each_fullref_with_seek(struct ref_filter *filter, each_ref_fn cb,
+static int for_each_fullref_with_seek(struct ref_filter *filter, refs_for_each_cb cb,
void *cb_data, unsigned int flags)
{
struct ref_iterator *iter;
@@ -2785,13 +2785,17 @@ static int for_each_fullref_with_seek(struct ref_filter *filter, each_ref_fn cb,
* pattern match, so the callback still has to match each ref individually.
*/
static int for_each_fullref_in_pattern(struct ref_filter *filter,
- each_ref_fn cb,
+ refs_for_each_cb cb,
void *cb_data)
{
+ struct refs_for_each_ref_options opts = {
+ .exclude_patterns = filter->exclude.v,
+ };
+
if (filter->kind & FILTER_REFS_ROOT_REFS) {
/* In this case, we want to print all refs including root refs. */
return for_each_fullref_with_seek(filter, cb, cb_data,
- DO_FOR_EACH_INCLUDE_ROOT_REFS);
+ REFS_FOR_EACH_INCLUDE_ROOT_REFS);
}
if (!filter->match_as_path) {
@@ -2817,10 +2821,9 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
return for_each_fullref_with_seek(filter, cb, cb_data, 0);
}
- return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),
- NULL, filter->name_patterns,
- filter->exclude.v,
- cb, cb_data);
+ return refs_for_each_ref_in_prefixes(get_main_ref_store(the_repository),
+ filter->name_patterns, &opts,
+ cb, cb_data);
}
/*
@@ -3284,7 +3287,7 @@ void filter_is_base(struct repository *r,
free(bases);
}
-static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref_fn fn, void *cb_data)
+static int do_filter_refs(struct ref_filter *filter, unsigned int type, refs_for_each_cb fn, void *cb_data)
{
const char *prefix = NULL;
int ret = 0;
diff --git a/refs.c b/refs.c
index 7cfb866aab..15069b5d19 100644
--- a/refs.c
+++ b/refs.c
@@ -443,8 +443,8 @@ char *refs_resolve_refdup(struct ref_store *refs,
/* The argument to for_each_filter_refs */
struct for_each_ref_filter {
const char *pattern;
- const char *prefix;
- each_ref_fn *fn;
+ size_t trim_prefix;
+ refs_for_each_cb *fn;
void *cb_data;
};
@@ -474,9 +474,11 @@ static int for_each_filter_refs(const struct reference *ref, void *data)
if (wildmatch(filter->pattern, ref->name, 0))
return 0;
- if (filter->prefix) {
+ if (filter->trim_prefix) {
struct reference skipped = *ref;
- skip_prefix(skipped.name, filter->prefix, &skipped.name);
+ if (strlen(skipped.name) <= filter->trim_prefix)
+ BUG("attempt to trim too many characters");
+ skipped.name += filter->trim_prefix;
return filter->fn(&skipped, filter->cb_data);
} else {
return filter->fn(ref, filter->cb_data);
@@ -523,25 +525,40 @@ void refs_warn_dangling_symrefs(struct ref_store *refs, FILE *fp,
.indent = indent,
.dry_run = dry_run,
};
- refs_for_each_rawref(refs, warn_if_dangling_symref, &data);
+ struct refs_for_each_ref_options opts = {
+ .flags = REFS_FOR_EACH_INCLUDE_BROKEN,
+ };
+ refs_for_each_ref_ext(refs, warn_if_dangling_symref, &data, &opts);
}
-int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+int refs_for_each_tag_ref(struct ref_store *refs, refs_for_each_cb cb, void *cb_data)
{
- return refs_for_each_ref_in(refs, "refs/tags/", fn, cb_data);
+ struct refs_for_each_ref_options opts = {
+ .prefix = "refs/tags/",
+ .trim_prefix = strlen("refs/tags/"),
+ };
+ return refs_for_each_ref_ext(refs, cb, cb_data, &opts);
}
-int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+int refs_for_each_branch_ref(struct ref_store *refs, refs_for_each_cb cb, void *cb_data)
{
- return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
+ struct refs_for_each_ref_options opts = {
+ .prefix = "refs/heads/",
+ .trim_prefix = strlen("refs/heads/"),
+ };
+ return refs_for_each_ref_ext(refs, cb, cb_data, &opts);
}
-int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+int refs_for_each_remote_ref(struct ref_store *refs, refs_for_each_cb cb, void *cb_data)
{
- return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
+ struct refs_for_each_ref_options opts = {
+ .prefix = "refs/remotes/",
+ .trim_prefix = strlen("refs/remotes/"),
+ };
+ return refs_for_each_ref_ext(refs, cb, cb_data, &opts);
}
-int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+int refs_head_ref_namespaced(struct ref_store *refs, refs_for_each_cb fn, void *cb_data)
{
struct strbuf buf = STRBUF_INIT;
int ret = 0;
@@ -589,42 +606,6 @@ void normalize_glob_ref(struct string_list_item *item, const char *prefix,
strbuf_release(&normalized_pattern);
}
-int refs_for_each_glob_ref_in(struct ref_store *refs, each_ref_fn fn,
- const char *pattern, const char *prefix, void *cb_data)
-{
- struct strbuf real_pattern = STRBUF_INIT;
- struct for_each_ref_filter filter;
- int ret;
-
- if (!prefix && !starts_with(pattern, "refs/"))
- strbuf_addstr(&real_pattern, "refs/");
- else if (prefix)
- strbuf_addstr(&real_pattern, prefix);
- strbuf_addstr(&real_pattern, pattern);
-
- if (!has_glob_specials(pattern)) {
- /* Append implied '/' '*' if not present. */
- strbuf_complete(&real_pattern, '/');
- /* No need to check for '*', there is none. */
- strbuf_addch(&real_pattern, '*');
- }
-
- filter.pattern = real_pattern.buf;
- filter.prefix = prefix;
- filter.fn = fn;
- filter.cb_data = cb_data;
- ret = refs_for_each_ref(refs, for_each_filter_refs, &filter);
-
- strbuf_release(&real_pattern);
- return ret;
-}
-
-int refs_for_each_glob_ref(struct ref_store *refs, each_ref_fn fn,
- const char *pattern, void *cb_data)
-{
- return refs_for_each_glob_ref_in(refs, fn, pattern, NULL, cb_data);
-}
-
const char *prettify_refname(const char *name)
{
if (skip_prefix(name, "refs/heads/", &name) ||
@@ -1787,7 +1768,7 @@ const char *find_descendant_ref(const char *dirname,
return NULL;
}
-int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+int refs_head_ref(struct ref_store *refs, refs_for_each_cb fn, void *cb_data)
{
struct object_id oid;
int flag;
@@ -1811,7 +1792,7 @@ struct ref_iterator *refs_ref_iterator_begin(
const char *prefix,
const char **exclude_patterns,
int trim,
- enum do_for_each_ref_flags flags)
+ enum refs_for_each_flag flags)
{
struct ref_iterator *iter;
struct strvec normalized_exclude_patterns = STRVEC_INIT;
@@ -1833,14 +1814,14 @@ struct ref_iterator *refs_ref_iterator_begin(
exclude_patterns = normalized_exclude_patterns.v;
}
- if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
+ if (!(flags & REFS_FOR_EACH_INCLUDE_BROKEN)) {
static int ref_paranoia = -1;
if (ref_paranoia < 0)
ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 1);
if (ref_paranoia) {
- flags |= DO_FOR_EACH_INCLUDE_BROKEN;
- flags |= DO_FOR_EACH_OMIT_DANGLING_SYMREFS;
+ flags |= REFS_FOR_EACH_INCLUDE_BROKEN;
+ flags |= REFS_FOR_EACH_OMIT_DANGLING_SYMREFS;
}
}
@@ -1857,85 +1838,105 @@ struct ref_iterator *refs_ref_iterator_begin(
return iter;
}
-static int do_for_each_ref(struct ref_store *refs, const char *prefix,
- const char **exclude_patterns,
- each_ref_fn fn, int trim,
- enum do_for_each_ref_flags flags, void *cb_data)
+int refs_for_each_ref_ext(struct ref_store *refs,
+ refs_for_each_cb cb, void *cb_data,
+ const struct refs_for_each_ref_options *opts)
{
+ struct strvec namespaced_exclude_patterns = STRVEC_INIT;
+ struct strbuf namespaced_prefix = STRBUF_INIT;
+ struct strbuf real_pattern = STRBUF_INIT;
+ struct for_each_ref_filter filter;
struct ref_iterator *iter;
+ size_t trim_prefix = opts->trim_prefix;
+ const char **exclude_patterns;
+ const char *prefix;
+ int ret;
if (!refs)
- return 0;
+ BUG("no ref store passed");
- iter = refs_ref_iterator_begin(refs, prefix, exclude_patterns, trim,
- flags);
+ if (opts->trim_prefix) {
+ size_t prefix_len;
- return do_for_each_ref_iterator(iter, fn, cb_data);
-}
+ if (!opts->prefix)
+ BUG("trimming only allowed with a prefix");
-int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
-{
- return do_for_each_ref(refs, "", NULL, fn, 0, 0, cb_data);
-}
+ prefix_len = strlen(opts->prefix);
+ if (prefix_len == opts->trim_prefix && opts->prefix[prefix_len - 1] != '/')
+ BUG("ref pattern must end in a trailing slash when trimming");
+ }
-int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
- each_ref_fn fn, void *cb_data)
-{
- return do_for_each_ref(refs, prefix, NULL, fn, strlen(prefix), 0, cb_data);
-}
+ if (opts->pattern) {
+ if (!opts->prefix && !starts_with(opts->pattern, "refs/"))
+ strbuf_addstr(&real_pattern, "refs/");
+ else if (opts->prefix)
+ strbuf_addstr(&real_pattern, opts->prefix);
+ strbuf_addstr(&real_pattern, opts->pattern);
-int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
- const char **exclude_patterns,
- each_ref_fn fn, void *cb_data)
-{
- return do_for_each_ref(refs, prefix, exclude_patterns, fn, 0, 0, cb_data);
-}
+ if (!has_glob_specials(opts->pattern)) {
+ /* Append implied '/' '*' if not present. */
+ strbuf_complete(&real_pattern, '/');
+ /* No need to check for '*', there is none. */
+ strbuf_addch(&real_pattern, '*');
+ }
-int refs_for_each_replace_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
-{
- const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
- return do_for_each_ref(refs, git_replace_ref_base, NULL, fn,
- strlen(git_replace_ref_base),
- DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
-}
+ filter.pattern = real_pattern.buf;
+ filter.trim_prefix = opts->trim_prefix;
+ filter.fn = cb;
+ filter.cb_data = cb_data;
-int refs_for_each_namespaced_ref(struct ref_store *refs,
- const char **exclude_patterns,
- each_ref_fn fn, void *cb_data)
-{
- struct strvec namespaced_exclude_patterns = STRVEC_INIT;
- struct strbuf prefix = STRBUF_INIT;
- int ret;
+ /*
+ * We need to trim the prefix in the callback function as the
+ * pattern is expected to match on the full refname.
+ */
+ trim_prefix = 0;
+
+ cb = for_each_filter_refs;
+ cb_data = &filter;
+ }
+
+ if (opts->namespace) {
+ strbuf_addstr(&namespaced_prefix, opts->namespace);
+ if (opts->prefix)
+ strbuf_addstr(&namespaced_prefix, opts->prefix);
+ else
+ strbuf_addstr(&namespaced_prefix, "refs/");
- exclude_patterns = get_namespaced_exclude_patterns(exclude_patterns,
- get_git_namespace(),
- &namespaced_exclude_patterns);
+ prefix = namespaced_prefix.buf;
+ exclude_patterns = get_namespaced_exclude_patterns(opts->exclude_patterns,
+ opts->namespace,
+ &namespaced_exclude_patterns);
+ } else {
+ prefix = opts->prefix ? opts->prefix : "";
+ exclude_patterns = opts->exclude_patterns;
+ }
- strbuf_addf(&prefix, "%srefs/", get_git_namespace());
- ret = do_for_each_ref(refs, prefix.buf, exclude_patterns, fn, 0, 0, cb_data);
+ iter = refs_ref_iterator_begin(refs, prefix, exclude_patterns,
+ trim_prefix, opts->flags);
+
+ ret = do_for_each_ref_iterator(iter, cb, cb_data);
strvec_clear(&namespaced_exclude_patterns);
- strbuf_release(&prefix);
+ strbuf_release(&namespaced_prefix);
+ strbuf_release(&real_pattern);
return ret;
}
-int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
-{
- return refs_for_each_rawref_in(refs, "", fn, cb_data);
-}
-
-int refs_for_each_rawref_in(struct ref_store *refs, const char *prefix,
- each_ref_fn fn, void *cb_data)
+int refs_for_each_ref(struct ref_store *refs, refs_for_each_cb cb, void *cb_data)
{
- return do_for_each_ref(refs, prefix, NULL, fn, 0,
- DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+ struct refs_for_each_ref_options opts = { 0 };
+ return refs_for_each_ref_ext(refs, cb, cb_data, &opts);
}
-int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
- void *cb_data)
+int refs_for_each_replace_ref(struct ref_store *refs, refs_for_each_cb cb, void *cb_data)
{
- return do_for_each_ref(refs, "", NULL, fn, 0,
- DO_FOR_EACH_INCLUDE_ROOT_REFS, cb_data);
+ const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
+ struct refs_for_each_ref_options opts = {
+ .prefix = git_replace_ref_base,
+ .trim_prefix = strlen(git_replace_ref_base),
+ .flags = REFS_FOR_EACH_INCLUDE_BROKEN,
+ };
+ return refs_for_each_ref_ext(refs, cb, cb_data, &opts);
}
static int qsort_strcmp(const void *va, const void *vb)
@@ -1996,40 +1997,31 @@ static void find_longest_prefixes(struct string_list *out,
strbuf_release(&prefix);
}
-int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
- const char *namespace,
- const char **patterns,
- const char **exclude_patterns,
- each_ref_fn fn, void *cb_data)
+int refs_for_each_ref_in_prefixes(struct ref_store *ref_store,
+ const char **prefixes,
+ const struct refs_for_each_ref_options *opts,
+ refs_for_each_cb cb, void *cb_data)
{
- struct strvec namespaced_exclude_patterns = STRVEC_INIT;
- struct string_list prefixes = STRING_LIST_INIT_DUP;
+ struct string_list longest_prefixes = STRING_LIST_INIT_DUP;
struct string_list_item *prefix;
- struct strbuf buf = STRBUF_INIT;
- int ret = 0, namespace_len;
+ int ret = 0;
- find_longest_prefixes(&prefixes, patterns);
+ if (opts->prefix)
+ BUG("refs_for_each_ref_in_prefixes called with specific prefix");
- if (namespace)
- strbuf_addstr(&buf, namespace);
- namespace_len = buf.len;
+ find_longest_prefixes(&longest_prefixes, prefixes);
- exclude_patterns = get_namespaced_exclude_patterns(exclude_patterns,
- namespace,
- &namespaced_exclude_patterns);
+ for_each_string_list_item(prefix, &longest_prefixes) {
+ struct refs_for_each_ref_options prefix_opts = *opts;
+ prefix_opts.prefix = prefix->string;
- for_each_string_list_item(prefix, &prefixes) {
- strbuf_addstr(&buf, prefix->string);
- ret = refs_for_each_fullref_in(ref_store, buf.buf,
- exclude_patterns, fn, cb_data);
+ ret = refs_for_each_ref_ext(ref_store, cb, cb_data,
+ &prefix_opts);
if (ret)
break;
- strbuf_setlen(&buf, namespace_len);
}
- strvec_clear(&namespaced_exclude_patterns);
- string_list_clear(&prefixes, 0);
- strbuf_release(&buf);
+ string_list_clear(&longest_prefixes, 0);
return ret;
}
@@ -2833,7 +2825,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
if (!iter)
iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
- DO_FOR_EACH_INCLUDE_BROKEN);
+ REFS_FOR_EACH_INCLUDE_BROKEN);
else if (ref_iterator_seek(iter, dirname.buf,
REF_ITERATOR_SEEK_SET_PREFIX) < 0)
goto cleanup;
@@ -3279,6 +3271,9 @@ int repo_migrate_ref_storage_format(struct repository *repo,
struct strbuf *errbuf)
{
struct ref_store *old_refs = NULL, *new_refs = NULL;
+ struct refs_for_each_ref_options for_each_ref_opts = {
+ .flags = REFS_FOR_EACH_INCLUDE_ROOT_REFS | REFS_FOR_EACH_INCLUDE_BROKEN,
+ };
struct ref_transaction *transaction = NULL;
struct strbuf new_gitdir = STRBUF_INIT;
struct migration_data data = {
@@ -3362,7 +3357,7 @@ int repo_migrate_ref_storage_format(struct repository *repo,
data.errbuf = errbuf;
/*
- * We need to use the internal `do_for_each_ref()` here so that we can
+ * We need to use `refs_for_each_ref_ext()` here so that we can
* also include broken refs and symrefs. These would otherwise be
* skipped silently.
*
@@ -3372,9 +3367,7 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* allow for a central lock due to its design. It's thus on the user to
* ensure that there are no concurrent writes.
*/
- ret = do_for_each_ref(old_refs, "", NULL, migrate_one_ref, 0,
- DO_FOR_EACH_INCLUDE_ROOT_REFS | DO_FOR_EACH_INCLUDE_BROKEN,
- &data);
+ ret = refs_for_each_ref_ext(old_refs, migrate_one_ref, &data, &for_each_ref_opts);
if (ret < 0)
goto done;
diff --git a/refs.h b/refs.h
index f3a1d604ad..d98c1fc591 100644
--- a/refs.h
+++ b/refs.h
@@ -170,7 +170,7 @@ int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err);
*
* peel_object(r, oid, &peeled);
*
- * with the "oid" value given to the each_ref_fn callback, except
+ * with the "oid" value given to the refs_for_each_cb callback, except
* that some ref storage may be able to answer the query without
* actually loading the object in memory.
*/
@@ -329,7 +329,7 @@ int check_tag_ref(struct strbuf *sb, const char *name);
struct ref_transaction;
/*
- * Bit values set in the flags argument passed to each_ref_fn() and
+ * Bit values set in the flags argument passed to refs_for_each_cb() and
* stored in ref_iterator::flags. Other bits are for internal use
* only:
*/
@@ -400,7 +400,44 @@ int reference_get_peeled_oid(struct repository *repo,
* argument is only guaranteed to be valid for the duration of a
* single callback invocation.
*/
-typedef int each_ref_fn(const struct reference *ref, void *cb_data);
+typedef int refs_for_each_cb(const struct reference *ref, void *cb_data);
+
+/*
+ * These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(),
+ * which feeds it).
+ */
+enum refs_for_each_flag {
+ /*
+ * Include broken references in a do_for_each_ref*() iteration, which
+ * would normally be omitted. This includes both refs that point to
+ * missing objects (a true repository corruption), ones with illegal
+ * names (which we prefer not to expose to callers), as well as
+ * dangling symbolic refs (i.e., those that point to a non-existent
+ * ref; this is not a corruption, but as they have no valid oid, we
+ * omit them from normal iteration results).
+ */
+ REFS_FOR_EACH_INCLUDE_BROKEN = (1 << 0),
+
+ /*
+ * Only include per-worktree refs in a do_for_each_ref*() iteration.
+ * Normally this will be used with a files ref_store, since that's
+ * where all reference backends will presumably store their
+ * per-worktree refs.
+ */
+ REFS_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1),
+
+ /*
+ * Omit dangling symrefs from output; this only has an effect with
+ * INCLUDE_BROKEN, since they are otherwise not included at all.
+ */
+ REFS_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
+
+ /*
+ * Include root refs i.e. HEAD and pseudorefs along with the regular
+ * refs.
+ */
+ REFS_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3),
+};
/*
* The following functions invoke the specified callback function for
@@ -412,70 +449,75 @@ typedef int each_ref_fn(const struct reference *ref, void *cb_data);
* stop the iteration. Returned references are sorted.
*/
int refs_head_ref(struct ref_store *refs,
- each_ref_fn fn, void *cb_data);
-int refs_for_each_ref(struct ref_store *refs,
- each_ref_fn fn, void *cb_data);
-int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
- each_ref_fn fn, void *cb_data);
-int refs_for_each_tag_ref(struct ref_store *refs,
- each_ref_fn fn, void *cb_data);
-int refs_for_each_branch_ref(struct ref_store *refs,
- each_ref_fn fn, void *cb_data);
-int refs_for_each_remote_ref(struct ref_store *refs,
- each_ref_fn fn, void *cb_data);
-int refs_for_each_replace_ref(struct ref_store *refs,
- each_ref_fn fn, void *cb_data);
+ refs_for_each_cb fn, void *cb_data);
+int refs_head_ref_namespaced(struct ref_store *refs,
+ refs_for_each_cb fn, void *cb_data);
-/*
- * references matching any pattern in "exclude_patterns" are omitted from the
- * result set on a best-effort basis.
- */
-int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
- const char **exclude_patterns,
- each_ref_fn fn, void *cb_data);
-/**
- * iterate all refs in "patterns" by partitioning patterns into disjoint sets
- * and iterating the longest-common prefix of each set.
- *
- * references matching any pattern in "exclude_patterns" are omitted from the
- * result set on a best-effort basis.
- *
- * callers should be prepared to ignore references that they did not ask for.
- */
-int refs_for_each_fullref_in_prefixes(struct ref_store *refs,
- const char *namespace,
- const char **patterns,
- const char **exclude_patterns,
- each_ref_fn fn, void *cb_data);
+struct refs_for_each_ref_options {
+ /* Only iterate over references that have this given prefix. */
+ const char *prefix;
+
+ /*
+ * A globbing pattern that can be used to only yield refs that match.
+ * If given, refs will be matched against the pattern with
+ * `wildmatch()`.
+ *
+ * If the pattern doesn't contain any globbing characters then it is
+ * treated as if it was ending with "/" and "*".
+ */
+ const char *pattern;
-/* iterates all refs that match the specified glob pattern. */
-int refs_for_each_glob_ref(struct ref_store *refs, each_ref_fn fn,
- const char *pattern, void *cb_data);
+ /*
+ * If set, only yield refs part of the configured namespace. Exclude
+ * patterns will be rewritten to apply to the namespace, and the prefix
+ * will be considered relative to the namespace.
+ */
+ const char *namespace;
-int refs_for_each_glob_ref_in(struct ref_store *refs, each_ref_fn fn,
- const char *pattern, const char *prefix, void *cb_data);
+ /*
+ * Exclude any references that match any of these patterns on a
+ * best-effort basis. The caller needs to be prepared for the exclude
+ * patterns to be ignored.
+ *
+ * The array must be terminated with a NULL sentinel value.
+ */
+ const char **exclude_patterns;
-int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_data);
+ /*
+ * The number of bytes to trim from the refname. Note that the trimmed
+ * bytes must not cause the reference to become empty. As such, this
+ * field should typically only be set when one uses a `prefix` ending
+ * in a slash.
+ */
+ size_t trim_prefix;
-/*
- * references matching any pattern in "exclude_patterns" are omitted from the
- * result set on a best-effort basis.
- */
-int refs_for_each_namespaced_ref(struct ref_store *refs,
- const char **exclude_patterns,
- each_ref_fn fn, void *cb_data);
+ /* Flags that change which refs will be included. */
+ enum refs_for_each_flag flags;
+};
-/* can be used to learn about broken ref and symref */
-int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
-int refs_for_each_rawref_in(struct ref_store *refs, const char *prefix,
- each_ref_fn fn, void *cb_data);
+int refs_for_each_ref(struct ref_store *refs,
+ refs_for_each_cb fn, void *cb_data);
+int refs_for_each_ref_ext(struct ref_store *refs,
+ refs_for_each_cb cb, void *cb_data,
+ const struct refs_for_each_ref_options *opts);
+int refs_for_each_tag_ref(struct ref_store *refs,
+ refs_for_each_cb fn, void *cb_data);
+int refs_for_each_branch_ref(struct ref_store *refs,
+ refs_for_each_cb fn, void *cb_data);
+int refs_for_each_remote_ref(struct ref_store *refs,
+ refs_for_each_cb fn, void *cb_data);
+int refs_for_each_replace_ref(struct ref_store *refs,
+ refs_for_each_cb fn, void *cb_data);
-/*
- * Iterates over all refs including root refs, i.e. pseudorefs and HEAD.
+/**
+ * Iterate all refs in "prefixes" by partitioning prefixes into disjoint sets
+ * and iterating the longest-common prefix of each set.
*/
-int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
- void *cb_data);
+int refs_for_each_ref_in_prefixes(struct ref_store *refs,
+ const char **prefixes,
+ const struct refs_for_each_ref_options *opts,
+ refs_for_each_cb cb, void *cb_data);
/*
* Normalizes partial refs to their fully qualified form.
@@ -1332,43 +1374,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
struct ref_iterator;
/*
- * These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(),
- * which feeds it).
- */
-enum do_for_each_ref_flags {
- /*
- * Include broken references in a do_for_each_ref*() iteration, which
- * would normally be omitted. This includes both refs that point to
- * missing objects (a true repository corruption), ones with illegal
- * names (which we prefer not to expose to callers), as well as
- * dangling symbolic refs (i.e., those that point to a non-existent
- * ref; this is not a corruption, but as they have no valid oid, we
- * omit them from normal iteration results).
- */
- DO_FOR_EACH_INCLUDE_BROKEN = (1 << 0),
-
- /*
- * Only include per-worktree refs in a do_for_each_ref*() iteration.
- * Normally this will be used with a files ref_store, since that's
- * where all reference backends will presumably store their
- * per-worktree refs.
- */
- DO_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1),
-
- /*
- * Omit dangling symrefs from output; this only has an effect with
- * INCLUDE_BROKEN, since they are otherwise not included at all.
- */
- DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
-
- /*
- * Include root refs i.e. HEAD and pseudorefs along with the regular
- * refs.
- */
- DO_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3),
-};
-
-/*
* Return an iterator that goes over each reference in `refs` for
* which the refname begins with prefix. If trim is non-zero, then
* trim that many characters off the beginning of each refname.
@@ -1377,7 +1382,7 @@ enum do_for_each_ref_flags {
struct ref_iterator *refs_ref_iterator_begin(
struct ref_store *refs,
const char *prefix, const char **exclude_patterns,
- int trim, enum do_for_each_ref_flags flags);
+ int trim, enum refs_for_each_flag flags);
/*
* Advance the iterator to the first or next item and return ITER_OK.
@@ -1426,7 +1431,7 @@ void ref_iterator_free(struct ref_iterator *ref_iterator);
* iterator style.
*/
int do_for_each_ref_iterator(struct ref_iterator *iter,
- each_ref_fn fn, void *cb_data);
+ refs_for_each_cb fn, void *cb_data);
/*
* Git only recognizes a directory as a repository if it contains:
diff --git a/refs/files-backend.c b/refs/files-backend.c
index d2c4be1653..7ce0d57478 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -446,7 +446,7 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs,
dir = get_ref_dir(refs->loose->root);
- if (flags & DO_FOR_EACH_INCLUDE_ROOT_REFS)
+ if (flags & REFS_FOR_EACH_INCLUDE_ROOT_REFS)
add_root_refs(refs, dir);
/*
@@ -962,17 +962,17 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
- if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
+ if (iter->flags & REFS_FOR_EACH_PER_WORKTREE_ONLY &&
parse_worktree_ref(iter->iter0->ref.name, NULL, NULL,
NULL) != REF_WORKTREE_CURRENT)
continue;
- if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
+ if ((iter->flags & REFS_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
(iter->iter0->ref.flags & REF_ISSYMREF) &&
(iter->iter0->ref.flags & REF_ISBROKEN))
continue;
- if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
+ if (!(iter->flags & REFS_FOR_EACH_INCLUDE_BROKEN) &&
!ref_resolves_to_object(iter->iter0->ref.name,
iter->repo,
iter->iter0->ref.oid,
@@ -1019,7 +1019,7 @@ static struct ref_iterator *files_ref_iterator_begin(
struct ref_iterator *ref_iterator;
unsigned int required_flags = REF_STORE_READ;
- if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN))
+ if (!(flags & REFS_FOR_EACH_INCLUDE_BROKEN))
required_flags |= REF_STORE_ODB;
refs = files_downcast(ref_store, required_flags, "ref_iterator_begin");
@@ -1057,7 +1057,7 @@ static struct ref_iterator *files_ref_iterator_begin(
*/
packed_iter = refs_ref_iterator_begin(
refs->packed_ref_store, prefix, exclude_patterns, 0,
- DO_FOR_EACH_INCLUDE_BROKEN);
+ REFS_FOR_EACH_INCLUDE_BROKEN);
overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter);
@@ -3156,6 +3156,9 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err)
{
+ struct refs_for_each_ref_options opts = {
+ .flags = REFS_FOR_EACH_INCLUDE_BROKEN,
+ };
size_t i;
int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
@@ -3180,8 +3183,8 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
* so here we really only check that none of the references
* that we are creating already exists.
*/
- if (refs_for_each_rawref(&refs->base, ref_present,
- &transaction->refnames))
+ if (refs_for_each_ref_ext(&refs->base, ref_present,
+ &transaction->refnames, &opts))
BUG("initial ref transaction called with existing refs");
packed_transaction = ref_store_transaction_begin(refs->packed_ref_store,
diff --git a/refs/iterator.c b/refs/iterator.c
index d79aa5ec82..d5cacde51b 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -423,7 +423,7 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
}
int do_for_each_ref_iterator(struct ref_iterator *iter,
- each_ref_fn fn, void *cb_data)
+ refs_for_each_cb fn, void *cb_data)
{
int retval = 0, ok;
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 8872a7fe18..23ed62984b 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -987,11 +987,11 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
const char *refname = iter->base.ref.name;
const char *prefix = iter->prefix;
- if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
+ if (iter->flags & REFS_FOR_EACH_PER_WORKTREE_ONLY &&
!is_per_worktree_ref(iter->base.ref.name))
continue;
- if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
+ if (!(iter->flags & REFS_FOR_EACH_INCLUDE_BROKEN) &&
!ref_resolves_to_object(iter->base.ref.name, iter->repo,
&iter->oid, iter->flags))
continue;
@@ -1164,7 +1164,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
struct ref_iterator *ref_iterator;
unsigned int required_flags = REF_STORE_READ;
- if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN))
+ if (!(flags & REFS_FOR_EACH_INCLUDE_BROKEN))
required_flags |= REF_STORE_ODB;
refs = packed_downcast(ref_store, required_flags, "ref_iterator_begin");
@@ -1406,7 +1406,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
* of updates is exhausted, leave i set to updates->nr.
*/
iter = packed_ref_iterator_begin(&refs->base, "", NULL,
- DO_FOR_EACH_INCLUDE_BROKEN);
+ REFS_FOR_EACH_INCLUDE_BROKEN);
if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
ref_iterator_free(iter);
iter = NULL;
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 57b5d48905..b124404663 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -629,7 +629,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
* the root refs are to be included. We emulate the same behaviour here.
*/
if (!starts_with(iter->ref.refname, "refs/") &&
- !(iter->flags & DO_FOR_EACH_INCLUDE_ROOT_REFS &&
+ !(iter->flags & REFS_FOR_EACH_INCLUDE_ROOT_REFS &&
is_root_ref(iter->ref.refname))) {
continue;
}
@@ -643,7 +643,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (iter->exclude_patterns && should_exclude_current_ref(iter))
continue;
- if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
+ if (iter->flags & REFS_FOR_EACH_PER_WORKTREE_ONLY &&
parse_worktree_ref(iter->ref.refname, NULL, NULL, NULL) !=
REF_WORKTREE_CURRENT)
continue;
@@ -681,12 +681,12 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
flags |= REF_BAD_NAME | REF_ISBROKEN;
}
- if (iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS &&
+ if (iter->flags & REFS_FOR_EACH_OMIT_DANGLING_SYMREFS &&
flags & REF_ISSYMREF &&
flags & REF_ISBROKEN)
continue;
- if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
+ if (!(iter->flags & REFS_FOR_EACH_INCLUDE_BROKEN) &&
!ref_resolves_to_object(iter->ref.refname, refs->base.repo,
&iter->oid, flags))
continue;
@@ -838,7 +838,7 @@ static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_sto
struct reftable_ref_store *refs;
unsigned int required_flags = REF_STORE_READ;
- if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN))
+ if (!(flags & REFS_FOR_EACH_INCLUDE_BROKEN))
required_flags |= REF_STORE_ODB;
refs = reftable_be_downcast(ref_store, required_flags, "ref_iterator_begin");
diff --git a/revision.c b/revision.c
index 402eb1b029..31808e3df0 100644
--- a/revision.c
+++ b/revision.c
@@ -1647,7 +1647,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
static void handle_refs(struct ref_store *refs,
struct rev_info *revs, unsigned flags,
- int (*for_each)(struct ref_store *, each_ref_fn, void *))
+ int (*for_each)(struct ref_store *, refs_for_each_cb, void *))
{
struct all_refs_cb cb;
@@ -2731,23 +2731,25 @@ void revision_opts_finish(struct rev_info *revs)
}
}
-static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
+static int for_each_bisect_ref(struct ref_store *refs, refs_for_each_cb fn,
void *cb_data, const char *term)
{
+ struct refs_for_each_ref_options opts = { 0 };
struct strbuf bisect_refs = STRBUF_INIT;
int status;
strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
- status = refs_for_each_fullref_in(refs, bisect_refs.buf, NULL, fn, cb_data);
+ opts.prefix = bisect_refs.buf;
+ status = refs_for_each_ref_ext(refs, fn, cb_data, &opts);
strbuf_release(&bisect_refs);
return status;
}
-static int for_each_bad_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+static int for_each_bad_bisect_ref(struct ref_store *refs, refs_for_each_cb fn, void *cb_data)
{
return for_each_bisect_ref(refs, fn, cb_data, term_bad);
}
-static int for_each_good_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+static int for_each_good_bisect_ref(struct ref_store *refs, refs_for_each_cb fn, void *cb_data)
{
return for_each_bisect_ref(refs, fn, cb_data, term_good);
}
@@ -2817,10 +2819,13 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
clear_ref_exclusions(&revs->ref_excludes);
} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
+ struct refs_for_each_ref_options opts = {
+ .pattern = optarg,
+ };
struct all_refs_cb cb;
init_all_refs_cb(&cb, revs, *flags);
- refs_for_each_glob_ref(get_main_ref_store(the_repository),
- handle_one_ref, optarg, &cb);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ handle_one_ref, &cb, &opts);
clear_ref_exclusions(&revs->ref_excludes);
return argcount;
} else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
@@ -2830,34 +2835,46 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
exclude_hidden_refs(&revs->ref_excludes, optarg);
return argcount;
} else if (skip_prefix(arg, "--branches=", &optarg)) {
+ struct refs_for_each_ref_options opts = {
+ .prefix = "refs/heads/",
+ .trim_prefix = strlen("refs/heads/"),
+ .pattern = optarg,
+ };
struct all_refs_cb cb;
if (revs->ref_excludes.hidden_refs_configured)
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--branches");
init_all_refs_cb(&cb, revs, *flags);
- refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
- handle_one_ref, optarg,
- "refs/heads/", &cb);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ handle_one_ref, &cb, &opts);
clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--tags=", &optarg)) {
+ struct refs_for_each_ref_options opts = {
+ .prefix = "refs/tags/",
+ .trim_prefix = strlen("refs/tags/"),
+ .pattern = optarg,
+ };
struct all_refs_cb cb;
if (revs->ref_excludes.hidden_refs_configured)
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--tags");
init_all_refs_cb(&cb, revs, *flags);
- refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
- handle_one_ref, optarg,
- "refs/tags/", &cb);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ handle_one_ref, &cb, &opts);
clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--remotes=", &optarg)) {
+ struct refs_for_each_ref_options opts = {
+ .prefix = "refs/remotes/",
+ .trim_prefix = strlen("refs/remotes/"),
+ .pattern = optarg,
+ };
struct all_refs_cb cb;
if (revs->ref_excludes.hidden_refs_configured)
return error(_("options '%s' and '%s' cannot be used together"),
"--exclude-hidden", "--remotes");
init_all_refs_cb(&cb, revs, *flags);
- refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
- handle_one_ref, optarg,
- "refs/remotes/", &cb);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ handle_one_ref, &cb, &opts);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--reflog")) {
add_reflogs_to_pending(revs, *flags);
diff --git a/submodule.c b/submodule.c
index 508938e4da..4f9aaa2c75 100644
--- a/submodule.c
+++ b/submodule.c
@@ -101,7 +101,7 @@ int is_staging_gitmodules_ok(struct index_state *istate)
}
static int for_each_remote_ref_submodule(const char *submodule,
- each_ref_fn fn, void *cb_data)
+ refs_for_each_cb fn, void *cb_data)
{
return refs_for_each_remote_ref(repo_get_submodule_ref_store(the_repository,
submodule),
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index b1215947c5..74edf2029a 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -163,17 +163,22 @@ static int each_ref(const struct reference *ref, void *cb_data UNUSED)
static int cmd_for_each_ref(struct ref_store *refs, const char **argv)
{
const char *prefix = notnull(*argv++, "prefix");
-
- return refs_for_each_ref_in(refs, prefix, each_ref, NULL);
+ struct refs_for_each_ref_options opts = {
+ .prefix = prefix,
+ .trim_prefix = strlen(prefix),
+ };
+ return refs_for_each_ref_ext(refs, each_ref, NULL, &opts);
}
static int cmd_for_each_ref__exclude(struct ref_store *refs, const char **argv)
{
const char *prefix = notnull(*argv++, "prefix");
- const char **exclude_patterns = argv;
+ struct refs_for_each_ref_options opts = {
+ .prefix = prefix,
+ .exclude_patterns = argv,
+ };
- return refs_for_each_fullref_in(refs, prefix, exclude_patterns, each_ref,
- NULL);
+ return refs_for_each_ref_ext(refs, each_ref, NULL, &opts);
}
static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
diff --git a/upload-pack.c b/upload-pack.c
index 88dac1b65c..e8c5cce1c7 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -607,10 +607,13 @@ static int allow_hidden_refs(enum allow_uor allow_uor)
return !(allow_uor & (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
}
-static void for_each_namespaced_ref_1(each_ref_fn fn,
+static void for_each_namespaced_ref_1(refs_for_each_cb fn,
struct upload_pack_data *data)
{
- const char **excludes = NULL;
+ struct refs_for_each_ref_options opts = {
+ .namespace = get_git_namespace(),
+ };
+
/*
* If `data->allow_uor` allows fetching hidden refs, we need to
* mark all references (including hidden ones), to check in
@@ -621,10 +624,10 @@ static void for_each_namespaced_ref_1(each_ref_fn fn,
* hidden references.
*/
if (allow_hidden_refs(data->allow_uor))
- excludes = hidden_refs_to_excludes(&data->hidden_refs);
+ opts.exclude_patterns = hidden_refs_to_excludes(&data->hidden_refs);
- refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
- excludes, fn, data);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ fn, data, &opts);
}
diff --git a/worktree.c b/worktree.c
index 6e2f0f7828..e9ff6e6ef2 100644
--- a/worktree.c
+++ b/worktree.c
@@ -595,7 +595,7 @@ void strbuf_worktree_ref(const struct worktree *wt,
strbuf_addstr(sb, refname);
}
-int other_head_refs(each_ref_fn fn, void *cb_data)
+int other_head_refs(refs_for_each_cb fn, void *cb_data)
{
struct worktree **worktrees, **p;
struct strbuf refname = STRBUF_INIT;
diff --git a/worktree.h b/worktree.h
index 06efe26b83..e450d1a331 100644
--- a/worktree.h
+++ b/worktree.h
@@ -197,7 +197,7 @@ int is_shared_symref(const struct worktree *wt,
* Similar to head_ref() for all HEADs _except_ one from the current
* worktree, which is covered by head_ref().
*/
-int other_head_refs(each_ref_fn fn, void *cb_data);
+int other_head_refs(refs_for_each_cb fn, void *cb_data);
int is_worktree_being_rebased(const struct worktree *wt, const char *target);
int is_worktree_being_bisected(const struct worktree *wt, const char *target);