Merge branch 'tb/refs-exclusion-and-packed-refs' into next

Enumerating refs in the packed-refs file, while excluding refs that
match certain patterns, has been optimized.

* tb/refs-exclusion-and-packed-refs:
  ls-refs.c: avoid enumerating hidden refs where possible
  upload-pack.c: avoid enumerating hidden refs where possible
  builtin/receive-pack.c: avoid enumerating hidden references
  refs.h: implement `hidden_refs_to_excludes()`
  refs.h: let `for_each_namespaced_ref()` take excluded patterns
  revision.h: store hidden refs in a `strvec`
  refs/packed-backend.c: add trace2 counters for jump list
  refs/packed-backend.c: implement jump lists to avoid excluded pattern(s)
  refs/packed-backend.c: refactor `find_reference_location()`
  refs: plumb `exclude_patterns` argument throughout
  builtin/for-each-ref.c: add `--exclude` option
  ref-filter.c: parameterize match functions over patterns
  ref-filter: add `ref_filter_clear()`
  ref-filter: clear reachable list pointers after freeing
  ref-filter.h: provide `REF_FILTER_INIT`
  refs.c: rename `ref_filter`
This commit is contained in:
Junio C Hamano
2023-07-13 11:18:53 -07:00
26 changed files with 579 additions and 106 deletions

85
refs.c
View File

@ -375,8 +375,8 @@ char *resolve_refdup(const char *refname, int resolve_flags,
oid, flags);
}
/* The argument to filter_refs */
struct ref_filter {
/* The argument to for_each_filter_refs */
struct for_each_ref_filter {
const char *pattern;
const char *prefix;
each_ref_fn *fn;
@ -409,10 +409,11 @@ int ref_exists(const char *refname)
return refs_ref_exists(get_main_ref_store(the_repository), refname);
}
static int filter_refs(const char *refname, const struct object_id *oid,
int flags, void *data)
static int for_each_filter_refs(const char *refname,
const struct object_id *oid,
int flags, void *data)
{
struct ref_filter *filter = (struct ref_filter *)data;
struct for_each_ref_filter *filter = data;
if (wildmatch(filter->pattern, refname, 0))
return 0;
@ -569,7 +570,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data)
{
struct strbuf real_pattern = STRBUF_INIT;
struct ref_filter filter;
struct for_each_ref_filter filter;
int ret;
if (!prefix && !starts_with(pattern, "refs/"))
@ -589,7 +590,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
filter.prefix = prefix;
filter.fn = fn;
filter.cb_data = cb_data;
ret = for_each_ref(filter_refs, &filter);
ret = for_each_ref(for_each_filter_refs, &filter);
strbuf_release(&real_pattern);
return ret;
@ -1426,7 +1427,7 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
}
int parse_hide_refs_config(const char *var, const char *value, const char *section,
struct string_list *hide_refs)
struct strvec *hide_refs)
{
const char *key;
if (!strcmp("transfer.hiderefs", var) ||
@ -1437,22 +1438,23 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
if (!value)
return config_error_nonbool(var);
ref = xstrdup(value);
/* drop const to remove trailing '/' characters */
ref = (char *)strvec_push(hide_refs, value);
len = strlen(ref);
while (len && ref[len - 1] == '/')
ref[--len] = '\0';
string_list_append_nodup(hide_refs, ref);
}
return 0;
}
int ref_is_hidden(const char *refname, const char *refname_full,
const struct string_list *hide_refs)
const struct strvec *hide_refs)
{
int i;
for (i = hide_refs->nr - 1; i >= 0; i--) {
const char *match = hide_refs->items[i].string;
const char *match = hide_refs->v[i];
const char *subject;
int neg = 0;
const char *p;
@ -1478,6 +1480,30 @@ int ref_is_hidden(const char *refname, const char *refname_full,
return 0;
}
const char **hidden_refs_to_excludes(const struct strvec *hide_refs)
{
const char **pattern;
for (pattern = hide_refs->v; *pattern; pattern++) {
/*
* We can't feed any excludes from hidden refs config
* sections, since later rules may override previous
* ones. For example, with rules "refs/foo" and
* "!refs/foo/bar", we should show "refs/foo/bar" (and
* everything underneath it), but the earlier exclusion
* would cause us to skip all of "refs/foo". We
* likewise don't implement the namespace stripping
* required for '^' rules.
*
* Both are possible to do, but complicated, so avoid
* populating the jump list at all if we see either of
* these patterns.
*/
if (**pattern == '!' || **pattern == '^')
return NULL;
}
return hide_refs->v;
}
const char *find_descendant_ref(const char *dirname,
const struct string_list *extras,
const struct string_list *skip)
@ -1525,7 +1551,9 @@ int head_ref(each_ref_fn fn, void *cb_data)
struct ref_iterator *refs_ref_iterator_begin(
struct ref_store *refs,
const char *prefix, int trim,
const char *prefix,
const char **exclude_patterns,
int trim,
enum do_for_each_ref_flags flags)
{
struct ref_iterator *iter;
@ -1541,8 +1569,7 @@ struct ref_iterator *refs_ref_iterator_begin(
}
}
iter = refs->be->iterator_begin(refs, prefix, flags);
iter = refs->be->iterator_begin(refs, prefix, exclude_patterns, flags);
/*
* `iterator_begin()` already takes care of prefix, but we
* might need to do some trimming:
@ -1576,7 +1603,7 @@ static int do_for_each_repo_ref(struct repository *r, const char *prefix,
if (!refs)
return 0;
iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
iter = refs_ref_iterator_begin(refs, prefix, NULL, trim, flags);
return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
}
@ -1598,6 +1625,7 @@ static int do_for_each_ref_helper(struct repository *r,
}
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)
{
@ -1607,7 +1635,8 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
if (!refs)
return 0;
iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
iter = refs_ref_iterator_begin(refs, prefix, exclude_patterns, trim,
flags);
return do_for_each_repo_ref_iterator(the_repository, iter,
do_for_each_ref_helper, &hp);
@ -1615,7 +1644,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
return do_for_each_ref(refs, "", fn, 0, 0, cb_data);
return do_for_each_ref(refs, "", NULL, fn, 0, 0, cb_data);
}
int for_each_ref(each_ref_fn fn, void *cb_data)
@ -1626,7 +1655,7 @@ int for_each_ref(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)
{
return do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);
return do_for_each_ref(refs, prefix, NULL, fn, strlen(prefix), 0, cb_data);
}
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
@ -1637,13 +1666,14 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data)
{
return do_for_each_ref(get_main_ref_store(the_repository),
prefix, fn, 0, 0, cb_data);
prefix, NULL, fn, 0, 0, cb_data);
}
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, fn, 0, 0, cb_data);
return do_for_each_ref(refs, prefix, exclude_patterns, fn, 0, 0, cb_data);
}
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
@ -1654,20 +1684,21 @@ int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
int for_each_namespaced_ref(const char **exclude_patterns,
each_ref_fn fn, void *cb_data)
{
struct strbuf buf = STRBUF_INIT;
int ret;
strbuf_addf(&buf, "%srefs/", get_git_namespace());
ret = do_for_each_ref(get_main_ref_store(the_repository),
buf.buf, fn, 0, 0, cb_data);
buf.buf, exclude_patterns, fn, 0, 0, cb_data);
strbuf_release(&buf);
return ret;
}
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
return do_for_each_ref(refs, "", fn, 0,
return do_for_each_ref(refs, "", NULL, fn, 0,
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
@ -1737,6 +1768,7 @@ static void find_longest_prefixes(struct string_list *out,
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)
{
struct string_list prefixes = STRING_LIST_INIT_DUP;
@ -1752,7 +1784,8 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
for_each_string_list_item(prefix, &prefixes) {
strbuf_addstr(&buf, prefix->string);
ret = refs_for_each_fullref_in(ref_store, buf.buf, fn, cb_data);
ret = refs_for_each_fullref_in(ref_store, buf.buf,
exclude_patterns, fn, cb_data);
if (ret)
break;
strbuf_setlen(&buf, namespace_len);
@ -2407,7 +2440,7 @@ int refs_verify_refname_available(struct ref_store *refs,
strbuf_addstr(&dirname, refname + dirname.len);
strbuf_addch(&dirname, '/');
iter = refs_ref_iterator_begin(refs, dirname.buf, 0,
iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
DO_FOR_EACH_INCLUDE_BROKEN);
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
if (skip &&