refs: generalize refs_for_each_fullref_in_prefixes()

The function `refs_for_each_fullref_in_prefixes()` can be used to
iterate over all references part of any of the user-provided prefixes.
In contrast to the `prefix` parameter of `refs_for_each_ref_ext()` it
knows to handle the case well where multiple of the passed-in prefixes
start with a common prefix by computing longest common prefixes and then
iterating over those.

While we could move this logic into `refs_for_each_ref_ext()`, this one
feels somewhat special as we perform multiple iterations. But what we
_can_ do is to generalize how this function works: instead of accepting
only a small handful of parameters, we can have it accept the full
options structure.

One obvious exception is that the caller must not provide a prefix via
the options. But this case can be easily detected.

Refactor the code accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Patrick Steinhardt
2026-02-23 12:59:43 +01:00
committed by Junio C Hamano
parent 5387919327
commit f503bb7dc9
4 changed files with 34 additions and 43 deletions

View File

@@ -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);

View File

@@ -2807,6 +2807,10 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
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,
@@ -2836,10 +2840,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);
}
/*

39
refs.c
View File

@@ -2039,40 +2039,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,
refs_for_each_cb 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;
}

16
refs.h
View File

@@ -521,19 +521,13 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
refs_for_each_cb fn, void *cb_data);
/**
* iterate all refs in "patterns" by partitioning patterns into disjoint sets
* Iterate all refs in "prefixes" by partitioning prefixes 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,
refs_for_each_cb 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);
/* iterates all refs that match the specified glob pattern. */
int refs_for_each_glob_ref(struct ref_store *refs, refs_for_each_cb fn,