1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-04-28 08:35:23 +02:00

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

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-21 13:47:26 -07:00
commit 39fe402d67
26 changed files with 579 additions and 106 deletions

View File

@ -14,6 +14,7 @@ SYNOPSIS
[--points-at=<object>]
[--merged[=<object>]] [--no-merged[=<object>]]
[--contains[=<object>]] [--no-contains[=<object>]]
[--exclude=<pattern> ...]
DESCRIPTION
-----------
@ -102,6 +103,11 @@ OPTIONS
Do not print a newline after formatted refs where the format expands
to the empty string.
--exclude=<pattern>::
If one or more patterns are given, only refs which do not match
any excluded pattern(s) are shown. Matching is done using the
same rules as `<pattern>` above.
FIELD NAMES
-----------

View File

@ -701,7 +701,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
int reflog = 0, quiet = 0, icase = 0, force = 0,
recurse_submodules_explicit = 0;
enum branch_track track;
struct ref_filter filter;
struct ref_filter filter = REF_FILTER_INIT;
static struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
@ -759,7 +759,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
setup_ref_filter_porcelain_msg();
memset(&filter, 0, sizeof(filter));
filter.kind = FILTER_REFS_BRANCHES;
filter.abbrev = -1;
@ -855,6 +854,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
ref_sorting_release(sorting);
ref_filter_clear(&filter);
return 0;
} else if (edit_description) {
const char *branch_name;

View File

@ -24,7 +24,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
struct string_list sorting_options = STRING_LIST_INIT_DUP;
int maxcount = 0, icase = 0, omit_empty = 0;
struct ref_array array;
struct ref_filter filter;
struct ref_filter filter = REF_FILTER_INIT;
struct ref_format format = REF_FORMAT_INIT;
struct strbuf output = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
@ -47,6 +47,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT__COLOR(&format.use_color, N_("respect format colors")),
OPT_REF_FILTER_EXCLUDE(&filter),
OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at,
N_("object"), N_("print only refs which points at the given object"),
@ -61,7 +62,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
};
memset(&array, 0, sizeof(array));
memset(&filter, 0, sizeof(filter));
format.format = "%(objectname) %(objecttype)\t%(refname)";
@ -121,8 +121,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
strbuf_release(&err);
strbuf_release(&output);
ref_array_clear(&array);
free_commit_list(filter.with_commit);
free_commit_list(filter.no_commit);
ref_filter_clear(&filter);
ref_sorting_release(sorting);
strvec_clear(&vec);
return 0;

View File

@ -90,7 +90,7 @@ static struct object_id push_cert_oid;
static struct signature_check sigcheck;
static const char *push_cert_nonce;
static const char *cert_nonce_seed;
static struct string_list hidden_refs = STRING_LIST_INIT_DUP;
static struct strvec hidden_refs = STRVEC_INIT;
static const char *NONCE_UNSOLICITED = "UNSOLICITED";
static const char *NONCE_BAD = "BAD";
@ -338,7 +338,9 @@ static void write_head_info(void)
{
static struct oidset seen = OIDSET_INIT;
for_each_ref(show_ref_cb, &seen);
refs_for_each_fullref_in(get_main_ref_store(the_repository), "",
hidden_refs_to_excludes(&hidden_refs),
show_ref_cb, &seen);
for_each_alternate_ref(show_one_alternate_ref, &seen);
oidset_clear(&seen);
if (!sent_capabilities)
@ -2620,7 +2622,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
packet_flush(1);
oid_array_clear(&shallow);
oid_array_clear(&ref);
string_list_clear(&hidden_refs, 0);
strvec_clear(&hidden_refs);
free((void *)push_cert_nonce);
return 0;
}

View File

@ -445,7 +445,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct msg_arg msg = { .buf = STRBUF_INIT };
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
struct ref_filter filter;
struct ref_filter filter = REF_FILTER_INIT;
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
@ -504,7 +504,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
git_config(git_tag_config, &sorting_options);
memset(&opt, 0, sizeof(opt));
memset(&filter, 0, sizeof(filter));
filter.lines = -1;
opt.sign = -1;
@ -660,6 +659,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
cleanup:
ref_sorting_release(sorting);
ref_filter_clear(&filter);
strbuf_release(&buf);
strbuf_release(&ref);
strbuf_release(&reflog_msg);

View File

@ -558,7 +558,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
} else {
select_getanyfile(hdr);
for_each_namespaced_ref(show_text_ref, &buf);
for_each_namespaced_ref(NULL, show_text_ref, &buf);
send_strbuf(hdr, "text/plain", &buf);
}
strbuf_release(&buf);

View File

@ -72,7 +72,7 @@ struct ls_refs_data {
unsigned symrefs;
struct strvec prefixes;
struct strbuf buf;
struct string_list hidden_refs;
struct strvec hidden_refs;
unsigned unborn : 1;
};
@ -156,7 +156,7 @@ int ls_refs(struct repository *r, struct packet_reader *request)
memset(&data, 0, sizeof(data));
strvec_init(&data.prefixes);
strbuf_init(&data.buf, 0);
string_list_init_dup(&data.hidden_refs);
strvec_init(&data.hidden_refs);
git_config(ls_refs_config, &data);
@ -194,11 +194,12 @@ int ls_refs(struct repository *r, struct packet_reader *request)
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);
packet_fflush(stdout);
strvec_clear(&data.prefixes);
strbuf_release(&data.buf);
string_list_clear(&data.hidden_refs, 0);
strvec_clear(&data.hidden_refs);
return 0;
}

View File

@ -2225,12 +2225,12 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom,
* matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
* matches "refs/heads/mas*", too).
*/
static int match_pattern(const struct ref_filter *filter, const char *refname)
static int match_pattern(const char **patterns, const char *refname,
int ignore_case)
{
const char **patterns = filter->name_patterns;
unsigned flags = 0;
if (filter->ignore_case)
if (ignore_case)
flags |= WM_CASEFOLD;
/*
@ -2255,13 +2255,13 @@ static int match_pattern(const struct ref_filter *filter, const char *refname)
* matches a pattern "refs/heads/" but not "refs/heads/m") or a
* wildcard (e.g. the same ref matches "refs/heads/m*", too).
*/
static int match_name_as_path(const struct ref_filter *filter, const char *refname)
static int match_name_as_path(const char **pattern, const char *refname,
int ignore_case)
{
const char **pattern = filter->name_patterns;
int namelen = strlen(refname);
unsigned flags = WM_PATHNAME;
if (filter->ignore_case)
if (ignore_case)
flags |= WM_CASEFOLD;
for (; *pattern; pattern++) {
@ -2286,8 +2286,20 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
if (!*filter->name_patterns)
return 1; /* No pattern always matches */
if (filter->match_as_path)
return match_name_as_path(filter, refname);
return match_pattern(filter, refname);
return match_name_as_path(filter->name_patterns, refname,
filter->ignore_case);
return match_pattern(filter->name_patterns, refname,
filter->ignore_case);
}
static int filter_exclude_match(struct ref_filter *filter, const char *refname)
{
if (!filter->exclude.nr)
return 0;
if (filter->match_as_path)
return match_name_as_path(filter->exclude.v, refname,
filter->ignore_case);
return match_pattern(filter->exclude.v, refname, filter->ignore_case);
}
/*
@ -2319,11 +2331,13 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
if (!filter->name_patterns[0]) {
/* no patterns; we have to look at everything */
return for_each_fullref_in("", cb, cb_data);
return refs_for_each_fullref_in(get_main_ref_store(the_repository),
"", filter->exclude.v, cb, cb_data);
}
return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),
NULL, filter->name_patterns,
filter->exclude.v,
cb, cb_data);
}
@ -2457,6 +2471,9 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
if (!filter_pattern_match(filter, refname))
return 0;
if (filter_exclude_match(filter, refname))
return 0;
if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
return 0;
@ -2539,13 +2556,13 @@ void ref_array_clear(struct ref_array *array)
#define EXCLUDE_REACHED 0
#define INCLUDE_REACHED 1
static void reach_filter(struct ref_array *array,
struct commit_list *check_reachable,
struct commit_list **check_reachable,
int include_reached)
{
int i, old_nr;
struct commit **to_clear;
if (!check_reachable)
if (!*check_reachable)
return;
CALLOC_ARRAY(to_clear, array->nr);
@ -2555,7 +2572,7 @@ static void reach_filter(struct ref_array *array,
}
tips_reachable_from_bases(the_repository,
check_reachable,
*check_reachable,
to_clear, array->nr,
UNINTERESTING);
@ -2576,8 +2593,8 @@ static void reach_filter(struct ref_array *array,
clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS);
while (check_reachable) {
struct commit *merge_commit = pop_commit(&check_reachable);
while (*check_reachable) {
struct commit *merge_commit = pop_commit(check_reachable);
clear_commit_marks(merge_commit, ALL_REV_FLAGS);
}
@ -2674,8 +2691,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
clear_contains_cache(&ref_cbdata.no_contains_cache);
/* Filters that need revision walking */
reach_filter(array, filter->reachable_from, INCLUDE_REACHED);
reach_filter(array, filter->unreachable_from, EXCLUDE_REACHED);
reach_filter(array, &filter->reachable_from, INCLUDE_REACHED);
reach_filter(array, &filter->unreachable_from, EXCLUDE_REACHED);
save_commit_buffer = save_commit_buffer_orig;
return ret;
@ -2987,3 +3004,20 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
return 0;
}
void ref_filter_init(struct ref_filter *filter)
{
struct ref_filter blank = REF_FILTER_INIT;
memcpy(filter, &blank, sizeof(blank));
}
void ref_filter_clear(struct ref_filter *filter)
{
strvec_clear(&filter->exclude);
oid_array_clear(&filter->points_at);
free_commit_list(filter->with_commit);
free_commit_list(filter->no_commit);
free_commit_list(filter->reachable_from);
free_commit_list(filter->unreachable_from);
ref_filter_init(filter);
}

View File

@ -6,6 +6,7 @@
#include "refs.h"
#include "commit.h"
#include "string-list.h"
#include "strvec.h"
/* Quoting styles */
#define QUOTE_NONE 0
@ -59,6 +60,7 @@ struct ref_array {
struct ref_filter {
const char **name_patterns;
struct strvec exclude;
struct oid_array points_at;
struct commit_list *with_commit;
struct commit_list *no_commit;
@ -92,6 +94,10 @@ struct ref_format {
struct string_list bases;
};
#define REF_FILTER_INIT { \
.points_at = OID_ARRAY_INIT, \
.exclude = STRVEC_INIT, \
}
#define REF_FORMAT_INIT { \
.use_color = -1, \
.bases = STRING_LIST_INIT_DUP, \
@ -109,6 +115,9 @@ struct ref_format {
#define OPT_REF_SORT(var) \
OPT_STRING_LIST(0, "sort", (var), \
N_("key"), N_("field name to sort on"))
#define OPT_REF_FILTER_EXCLUDE(var) \
OPT_STRVEC(0, "exclude", &(var)->exclude, \
N_("pattern"), N_("exclude refs which match pattern"))
/*
* API for filtering a set of refs. Based on the type of refs the user
@ -167,4 +176,7 @@ void filter_ahead_behind(struct repository *r,
struct ref_format *format,
struct ref_array *array);
void ref_filter_init(struct ref_filter *filter);
void ref_filter_clear(struct ref_filter *filter);
#endif /* REF_FILTER_H */

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 &&

29
refs.h
View File

@ -343,7 +343,12 @@ int for_each_ref(each_ref_fn fn, void *cb_data);
*/
int for_each_ref_in(const char *prefix, each_ref_fn 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);
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data);
@ -351,10 +356,15 @@ int for_each_fullref_in(const char *prefix, 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 *namespace,
const char **patterns,
const char **exclude_patterns,
each_ref_fn fn, void *cb_data);
/**
@ -372,7 +382,12 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data);
int head_ref_namespaced(each_ref_fn fn, void *cb_data);
int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
/*
* references matching any pattern in "exclude_patterns" are omitted from the
* result set on a best-effort basis.
*/
int for_each_namespaced_ref(const char **exclude_patterns,
each_ref_fn fn, void *cb_data);
/* 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);
@ -810,7 +825,7 @@ int update_ref(const char *msg, const char *refname,
unsigned int flags, enum action_on_err onerr);
int parse_hide_refs_config(const char *var, const char *value, const char *,
struct string_list *);
struct strvec *);
/*
* Check whether a ref is hidden. If no namespace is set, both the first and
@ -820,7 +835,13 @@ int parse_hide_refs_config(const char *var, const char *value, const char *,
* the ref is outside that namespace, the first parameter is NULL. The second
* parameter always points to the full ref name.
*/
int ref_is_hidden(const char *, const char *, const struct string_list *);
int ref_is_hidden(const char *, const char *, const struct strvec *);
/*
* Returns an array of patterns to use as excluded_patterns, if none of the
* hidden references use the token '!' or '^'.
*/
const char **hidden_refs_to_excludes(const struct strvec *hide_refs);
/* Is this a per-worktree ref living in the refs/ namespace? */
int is_per_worktree_ref(const char *refname);

View File

@ -229,11 +229,12 @@ static struct ref_iterator_vtable debug_ref_iterator_vtable = {
static struct ref_iterator *
debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
unsigned int flags)
const char **exclude_patterns, unsigned int flags)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
struct ref_iterator *res =
drefs->refs->be->iterator_begin(drefs->refs, prefix, flags);
drefs->refs->be->iterator_begin(drefs->refs, prefix,
exclude_patterns, flags);
struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
diter->iter = res;

View File

@ -832,7 +832,8 @@ static struct ref_iterator_vtable files_ref_iterator_vtable = {
static struct ref_iterator *files_ref_iterator_begin(
struct ref_store *ref_store,
const char *prefix, unsigned int flags)
const char *prefix, const char **exclude_patterns,
unsigned int flags)
{
struct files_ref_store *refs;
struct ref_iterator *loose_iter, *packed_iter, *overlay_iter;
@ -877,7 +878,7 @@ static struct ref_iterator *files_ref_iterator_begin(
* the packed and loose references.
*/
packed_iter = refs_ref_iterator_begin(
refs->packed_ref_store, prefix, 0,
refs->packed_ref_store, prefix, exclude_patterns, 0,
DO_FOR_EACH_INCLUDE_BROKEN);
overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter);

View File

@ -13,6 +13,7 @@
#include "../statinfo.h"
#include "../wrapper.h"
#include "../write-or-die.h"
#include "../trace2.h"
enum mmap_strategy {
/*
@ -304,7 +305,8 @@ static int cmp_packed_ref_records(const void *v1, const void *v2)
* Compare a snapshot record at `rec` to the specified NUL-terminated
* refname.
*/
static int cmp_record_to_refname(const char *rec, const char *refname)
static int cmp_record_to_refname(const char *rec, const char *refname,
int start)
{
const char *r1 = rec + the_hash_algo->hexsz + 1;
const char *r2 = refname;
@ -313,7 +315,7 @@ static int cmp_record_to_refname(const char *rec, const char *refname)
if (*r1 == '\n')
return *r2 ? -1 : 0;
if (!*r2)
return 1;
return start ? 1 : -1;
if (*r1 != *r2)
return (unsigned char)*r1 < (unsigned char)*r2 ? -1 : +1;
r1++;
@ -528,22 +530,9 @@ static int load_contents(struct snapshot *snapshot)
return 1;
}
/*
* Find the place in `snapshot->buf` where the start of the record for
* `refname` starts. If `mustexist` is true and the reference doesn't
* exist, then return NULL. If `mustexist` is false and the reference
* doesn't exist, then return the point where that reference would be
* inserted, or `snapshot->eof` (which might be NULL) if it would be
* inserted at the end of the file. In the latter mode, `refname`
* doesn't have to be a proper reference name; for example, one could
* search for "refs/replace/" to find the start of any replace
* references.
*
* The record is sought using a binary search, so `snapshot->buf` must
* be sorted.
*/
static const char *find_reference_location(struct snapshot *snapshot,
const char *refname, int mustexist)
static const char *find_reference_location_1(struct snapshot *snapshot,
const char *refname, int mustexist,
int start)
{
/*
* This is not *quite* a garden-variety binary search, because
@ -573,7 +562,7 @@ static const char *find_reference_location(struct snapshot *snapshot,
mid = lo + (hi - lo) / 2;
rec = find_start_of_record(lo, mid);
cmp = cmp_record_to_refname(rec, refname);
cmp = cmp_record_to_refname(rec, refname, start);
if (cmp < 0) {
lo = find_end_of_record(mid, hi);
} else if (cmp > 0) {
@ -589,6 +578,41 @@ static const char *find_reference_location(struct snapshot *snapshot,
return lo;
}
/*
* Find the place in `snapshot->buf` where the start of the record for
* `refname` starts. If `mustexist` is true and the reference doesn't
* exist, then return NULL. If `mustexist` is false and the reference
* doesn't exist, then return the point where that reference would be
* inserted, or `snapshot->eof` (which might be NULL) if it would be
* inserted at the end of the file. In the latter mode, `refname`
* doesn't have to be a proper reference name; for example, one could
* search for "refs/replace/" to find the start of any replace
* references.
*
* The record is sought using a binary search, so `snapshot->buf` must
* be sorted.
*/
static const char *find_reference_location(struct snapshot *snapshot,
const char *refname, int mustexist)
{
return find_reference_location_1(snapshot, refname, mustexist, 1);
}
/*
* Find the place in `snapshot->buf` after the end of the record for
* `refname`. In other words, find the location of first thing *after*
* `refname`.
*
* Other semantics are identical to the ones in
* `find_reference_location()`.
*/
static const char *find_reference_location_end(struct snapshot *snapshot,
const char *refname,
int mustexist)
{
return find_reference_location_1(snapshot, refname, mustexist, 0);
}
/*
* Create a newly-allocated `snapshot` of the `packed-refs` file in
* its current state and return it. The return value will already have
@ -780,6 +804,13 @@ struct packed_ref_iterator {
/* The end of the part of the buffer that will be iterated over: */
const char *eof;
struct jump_list_entry {
const char *start;
const char *end;
} *jump;
size_t jump_nr, jump_alloc;
size_t jump_cur;
/* Scratch space for current values: */
struct object_id oid, peeled;
struct strbuf refname_buf;
@ -797,14 +828,36 @@ struct packed_ref_iterator {
*/
static int next_record(struct packed_ref_iterator *iter)
{
const char *p = iter->pos, *eol;
const char *p, *eol;
strbuf_reset(&iter->refname_buf);
/*
* If iter->pos is contained within a skipped region, jump past
* it.
*
* Note that each skipped region is considered at most once,
* since they are ordered based on their starting position.
*/
while (iter->jump_cur < iter->jump_nr) {
struct jump_list_entry *curr = &iter->jump[iter->jump_cur];
if (iter->pos < curr->start)
break; /* not to the next jump yet */
iter->jump_cur++;
if (iter->pos < curr->end) {
iter->pos = curr->end;
trace2_counter_add(TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, 1);
/* jumps are coalesced, so only one jump is necessary */
break;
}
}
if (iter->pos == iter->eof)
return ITER_DONE;
iter->base.flags = REF_ISPACKED;
p = iter->pos;
if (iter->eof - p < the_hash_algo->hexsz + 2 ||
parse_oid_hex(p, &iter->oid, &p) ||
@ -912,6 +965,7 @@ static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator)
int ok = ITER_DONE;
strbuf_release(&iter->refname_buf);
free(iter->jump);
release_snapshot(iter->snapshot);
base_ref_iterator_free(ref_iterator);
return ok;
@ -923,9 +977,112 @@ static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.abort = packed_ref_iterator_abort
};
static int jump_list_entry_cmp(const void *va, const void *vb)
{
const struct jump_list_entry *a = va;
const struct jump_list_entry *b = vb;
if (a->start < b->start)
return -1;
if (a->start > b->start)
return 1;
return 0;
}
static int has_glob_special(const char *str)
{
const char *p;
for (p = str; *p; p++) {
if (is_glob_special(*p))
return 1;
}
return 0;
}
static void populate_excluded_jump_list(struct packed_ref_iterator *iter,
struct snapshot *snapshot,
const char **excluded_patterns)
{
size_t i, j;
const char **pattern;
struct jump_list_entry *last_disjoint;
if (!excluded_patterns)
return;
for (pattern = excluded_patterns; *pattern; pattern++) {
struct jump_list_entry *e;
const char *start, *end;
/*
* We can't feed any excludes with globs in them to the
* refs machinery. It only understands prefix matching.
* We likewise can't even feed the string leading up to
* the first meta-character, as something like "foo[a]"
* should not exclude "foobar" (but the prefix "foo"
* would match that and mark it for exclusion).
*/
if (has_glob_special(*pattern))
continue;
start = find_reference_location(snapshot, *pattern, 0);
end = find_reference_location_end(snapshot, *pattern, 0);
if (start == end)
continue; /* nothing to jump over */
ALLOC_GROW(iter->jump, iter->jump_nr + 1, iter->jump_alloc);
e = &iter->jump[iter->jump_nr++];
e->start = start;
e->end = end;
}
if (!iter->jump_nr) {
/*
* Every entry in exclude_patterns has a meta-character,
* nothing to do here.
*/
return;
}
QSORT(iter->jump, iter->jump_nr, jump_list_entry_cmp);
/*
* As an optimization, merge adjacent entries in the jump list
* to jump forwards as far as possible when entering a skipped
* region.
*
* For example, if we have two skipped regions:
*
* [[A, B], [B, C]]
*
* we want to combine that into a single entry jumping from A to
* C.
*/
last_disjoint = iter->jump;
for (i = 1, j = 1; i < iter->jump_nr; i++) {
struct jump_list_entry *ours = &iter->jump[i];
if (ours->start <= last_disjoint->end) {
/* overlapping regions extend the previous one */
last_disjoint->end = last_disjoint->end > ours->end
? last_disjoint->end : ours->end;
} else {
/* otherwise, insert a new region */
iter->jump[j++] = *ours;
last_disjoint = ours;
}
}
iter->jump_nr = j;
iter->jump_cur = 0;
}
static struct ref_iterator *packed_ref_iterator_begin(
struct ref_store *ref_store,
const char *prefix, unsigned int flags)
const char *prefix, const char **exclude_patterns,
unsigned int flags)
{
struct packed_ref_store *refs;
struct snapshot *snapshot;
@ -957,6 +1114,9 @@ static struct ref_iterator *packed_ref_iterator_begin(
ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1);
if (exclude_patterns)
populate_excluded_jump_list(iter, snapshot, exclude_patterns);
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
@ -1150,7 +1310,7 @@ static int write_with_updates(struct packed_ref_store *refs,
* list of refs is exhausted, set iter to NULL. When the list
* of updates is exhausted, leave i set to updates->nr.
*/
iter = packed_ref_iterator_begin(&refs->base, "",
iter = packed_ref_iterator_begin(&refs->base, "", NULL,
DO_FOR_EACH_INCLUDE_BROKEN);
if ((ok = ref_iterator_advance(iter)) != ITER_OK)
iter = NULL;

View File

@ -367,8 +367,8 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator);
*/
struct ref_iterator *refs_ref_iterator_begin(
struct ref_store *refs,
const char *prefix, int trim,
enum do_for_each_ref_flags flags);
const char *prefix, const char **exclude_patterns,
int trim, enum do_for_each_ref_flags flags);
/*
* A callback function used to instruct merge_ref_iterator how to
@ -571,7 +571,8 @@ typedef int copy_ref_fn(struct ref_store *ref_store,
*/
typedef struct ref_iterator *ref_iterator_begin_fn(
struct ref_store *ref_store,
const char *prefix, unsigned int flags);
const char *prefix, const char **exclude_patterns,
unsigned int flags);
/* reflog functions */

View File

@ -1560,7 +1560,7 @@ void init_ref_exclusions(struct ref_exclusions *exclusions)
void clear_ref_exclusions(struct ref_exclusions *exclusions)
{
string_list_clear(&exclusions->excluded_refs, 0);
string_list_clear(&exclusions->hidden_refs, 0);
strvec_clear(&exclusions->hidden_refs);
exclusions->hidden_refs_configured = 0;
}
@ -2641,7 +2641,7 @@ static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
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, fn, cb_data);
status = refs_for_each_fullref_in(refs, bisect_refs.buf, NULL, fn, cb_data);
strbuf_release(&bisect_refs);
return status;
}

View File

@ -10,6 +10,7 @@
#include "decorate.h"
#include "ident.h"
#include "list-objects-filter-options.h"
#include "strvec.h"
/**
* The revision walking API offers functions to build a list of revisions
@ -95,7 +96,7 @@ struct ref_exclusions {
* Hidden refs is a list of patterns that is to be hidden via
* `ref_is_hidden()`.
*/
struct string_list hidden_refs;
struct strvec hidden_refs;
/*
* Indicates whether hidden refs have been configured. This is to
@ -110,7 +111,7 @@ struct ref_exclusions {
*/
#define REF_EXCLUSIONS_INIT { \
.excluded_refs = STRING_LIST_INIT_DUP, \
.hidden_refs = STRING_LIST_INIT_DUP, \
.hidden_refs = STRVEC_INIT, \
}
struct oidset;

View File

@ -138,7 +138,7 @@ int cmd__reach(int ac, const char **av)
printf("%s(X,_,_,0,0):%d\n", av[1], can_all_from_reach_with_flag(&X_obj, 2, 4, 0, 0));
} else if (!strcmp(av[1], "commit_contains")) {
struct ref_filter filter;
struct ref_filter filter = REF_FILTER_INIT;
struct contains_cache cache;
init_contains_cache(&cache);

View File

@ -186,6 +186,15 @@ static int cmd_for_each_ref(struct ref_store *refs, const char **argv)
return refs_for_each_ref_in(refs, prefix, each_ref, NULL);
}
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;
return refs_for_each_fullref_in(refs, prefix, exclude_patterns, each_ref,
NULL);
}
static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
{
struct object_id oid = *null_oid();
@ -318,6 +327,7 @@ static struct command commands[] = {
{ "delete-refs", cmd_delete_refs },
{ "rename-ref", cmd_rename_ref },
{ "for-each-ref", cmd_for_each_ref },
{ "for-each-ref--exclude", cmd_for_each_ref__exclude },
{ "resolve-ref", cmd_resolve_ref },
{ "verify-ref", cmd_verify_ref },
{ "for-each-reflog", cmd_for_each_reflog },

View File

@ -5,6 +5,7 @@ test_description='Test commands behavior when given invalid argument value'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup ' '

122
t/t1419-exclude-refs.sh Executable file
View File

@ -0,0 +1,122 @@
#!/bin/sh
test_description='test exclude_patterns functionality in main ref store'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
for_each_ref__exclude () {
GIT_TRACE2_PERF=1 test-tool ref-store main \
for-each-ref--exclude "$@" >actual.raw
cut -d ' ' -f 2 actual.raw
}
for_each_ref () {
git for-each-ref --format='%(refname)' "$@"
}
assert_jumps () {
local nr="$1"
local trace="$2"
grep -q "name:jumps_made value:$nr$" $trace
}
assert_no_jumps () {
! assert_jumps ".*" "$1"
}
test_expect_success 'setup' '
test_commit --no-tag base &&
base="$(git rev-parse HEAD)" &&
for name in foo bar baz quux
do
for i in 1 2 3
do
echo "create refs/heads/$name/$i $base" || return 1
done || return 1
done >in &&
echo "delete refs/heads/main" >>in &&
git update-ref --stdin <in &&
git pack-refs --all
'
test_expect_success 'excluded region in middle' '
for_each_ref__exclude refs/heads refs/heads/foo >actual 2>perf &&
for_each_ref refs/heads/bar refs/heads/baz refs/heads/quux >expect &&
test_cmp expect actual &&
assert_jumps 1 perf
'
test_expect_success 'excluded region at beginning' '
for_each_ref__exclude refs/heads refs/heads/bar >actual 2>perf &&
for_each_ref refs/heads/baz refs/heads/foo refs/heads/quux >expect &&
test_cmp expect actual &&
assert_jumps 1 perf
'
test_expect_success 'excluded region at end' '
for_each_ref__exclude refs/heads refs/heads/quux >actual 2>perf &&
for_each_ref refs/heads/foo refs/heads/bar refs/heads/baz >expect &&
test_cmp expect actual &&
assert_jumps 1 perf
'
test_expect_success 'disjoint excluded regions' '
for_each_ref__exclude refs/heads refs/heads/bar refs/heads/quux >actual 2>perf &&
for_each_ref refs/heads/baz refs/heads/foo >expect &&
test_cmp expect actual &&
assert_jumps 2 perf
'
test_expect_success 'adjacent, non-overlapping excluded regions' '
for_each_ref__exclude refs/heads refs/heads/bar refs/heads/baz >actual 2>perf &&
for_each_ref refs/heads/foo refs/heads/quux >expect &&
test_cmp expect actual &&
assert_jumps 1 perf
'
test_expect_success 'overlapping excluded regions' '
for_each_ref__exclude refs/heads refs/heads/ba refs/heads/baz >actual 2>perf &&
for_each_ref refs/heads/foo refs/heads/quux >expect &&
test_cmp expect actual &&
assert_jumps 1 perf
'
test_expect_success 'several overlapping excluded regions' '
for_each_ref__exclude refs/heads \
refs/heads/bar refs/heads/baz refs/heads/foo >actual 2>perf &&
for_each_ref refs/heads/quux >expect &&
test_cmp expect actual &&
assert_jumps 1 perf
'
test_expect_success 'non-matching excluded section' '
for_each_ref__exclude refs/heads refs/heads/does/not/exist >actual 2>perf &&
for_each_ref >expect &&
test_cmp expect actual &&
assert_no_jumps perf
'
test_expect_success 'meta-characters are discarded' '
for_each_ref__exclude refs/heads "refs/heads/ba*" >actual 2>perf &&
for_each_ref >expect &&
test_cmp expect actual &&
assert_no_jumps perf
'
test_done

View File

@ -8,6 +8,7 @@ test_description='git rebase --merge test'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
T="A quick brown fox

View File

@ -448,6 +448,41 @@ test_expect_success 'exercise glob patterns with prefixes' '
test_cmp expected actual
'
cat >expected <<\EOF
refs/tags/bar
refs/tags/baz
refs/tags/testtag
EOF
test_expect_success 'exercise patterns with prefix exclusions' '
for tag in foo/one foo/two foo/three bar baz
do
git tag "$tag" || return 1
done &&
test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
git for-each-ref --format="%(refname)" \
refs/tags/ --exclude=refs/tags/foo >actual &&
test_cmp expected actual
'
cat >expected <<\EOF
refs/tags/bar
refs/tags/baz
refs/tags/foo/one
refs/tags/testtag
EOF
test_expect_success 'exercise patterns with pattern exclusions' '
for tag in foo/one foo/two foo/three bar baz
do
git tag "$tag" || return 1
done &&
test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
git for-each-ref --format="%(refname)" \
refs/tags/ --exclude="refs/tags/foo/t*" >actual &&
test_cmp expected actual
'
cat >expected <<\EOF
'refs/heads/main'
'refs/remotes/origin/main'

View File

@ -552,6 +552,8 @@ enum trace2_counter_id {
TRACE2_COUNTER_ID_TEST1 = 0, /* emits summary event only */
TRACE2_COUNTER_ID_TEST2, /* emits summary and thread events */
TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, /* counts number of jumps */
/* Add additional counter definitions before here. */
TRACE2_NUMBER_OF_COUNTERS
};

View File

@ -27,6 +27,11 @@ static struct tr2_counter_metadata tr2_counter_metadata[TRACE2_NUMBER_OF_COUNTER
.name = "test2",
.want_per_thread_events = 1,
},
[TRACE2_COUNTER_ID_PACKED_REFS_JUMPS] = {
.category = "packed-refs",
.name = "jumps_made",
.want_per_thread_events = 0,
},
/* Add additional metadata before here. */
};

View File

@ -68,7 +68,7 @@ struct upload_pack_data {
struct object_array have_obj;
struct oid_array haves; /* v2 only */
struct string_list wanted_refs; /* v2 only */
struct string_list hidden_refs;
struct strvec hidden_refs;
struct object_array shallows;
struct string_list deepen_not;
@ -126,7 +126,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
{
struct string_list symref = STRING_LIST_INIT_DUP;
struct string_list wanted_refs = STRING_LIST_INIT_DUP;
struct string_list hidden_refs = STRING_LIST_INIT_DUP;
struct strvec hidden_refs = STRVEC_INIT;
struct object_array want_obj = OBJECT_ARRAY_INIT;
struct object_array have_obj = OBJECT_ARRAY_INIT;
struct oid_array haves = OID_ARRAY_INIT;
@ -161,7 +161,7 @@ static void upload_pack_data_clear(struct upload_pack_data *data)
{
string_list_clear(&data->symref, 1);
string_list_clear(&data->wanted_refs, 1);
string_list_clear(&data->hidden_refs, 0);
strvec_clear(&data->hidden_refs);
object_array_clear(&data->want_obj);
object_array_clear(&data->have_obj);
oid_array_clear(&data->haves);
@ -601,11 +601,36 @@ static int get_common_commits(struct upload_pack_data *data,
}
}
static int allow_hidden_refs(enum allow_uor allow_uor)
{
if ((allow_uor & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1)
return 1;
return !(allow_uor & (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
}
static void for_each_namespaced_ref_1(each_ref_fn fn,
struct upload_pack_data *data)
{
const char **excludes = NULL;
/*
* If `data->allow_uor` allows fetching hidden refs, we need to
* mark all references (including hidden ones), to check in
* `is_our_ref()` below.
*
* Otherwise, we only care about whether each reference's object
* has the OUR_REF bit set or not, so do not need to visit
* hidden references.
*/
if (allow_hidden_refs(data->allow_uor))
excludes = hidden_refs_to_excludes(&data->hidden_refs);
for_each_namespaced_ref(excludes, fn, data);
}
static int is_our_ref(struct object *o, enum allow_uor allow_uor)
{
int allow_hidden_ref = (allow_uor &
(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
return o->flags & ((allow_hidden_refs(allow_uor) ? 0 : HIDDEN_REF) | OUR_REF);
}
/*
@ -854,7 +879,7 @@ static void deepen(struct upload_pack_data *data, int depth)
* marked with OUR_REF.
*/
head_ref_namespaced(check_ref, data);
for_each_namespaced_ref(check_ref, data);
for_each_namespaced_ref_1(check_ref, data);
get_reachable_list(data, &reachable_shallows);
result = get_shallow_commits(&reachable_shallows,
@ -1169,7 +1194,7 @@ static void receive_needs(struct upload_pack_data *data,
/* return non-zero if the ref is hidden, otherwise 0 */
static int mark_our_ref(const char *refname, const char *refname_full,
const struct object_id *oid, const struct string_list *hidden_refs)
const struct object_id *oid, const struct strvec *hidden_refs)
{
struct object *o = lookup_unknown_object(the_repository, oid);
@ -1391,7 +1416,7 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
if (advertise_refs)
data.no_done = 1;
head_ref_namespaced(send_ref, &data);
for_each_namespaced_ref(send_ref, &data);
for_each_namespaced_ref_1(send_ref, &data);
if (!data.sent_capabilities) {
const char *refname = "capabilities^{}";
write_v0_ref(&data, refname, refname, null_oid());
@ -1405,7 +1430,7 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
packet_flush(1);
} else {
head_ref_namespaced(check_ref, &data);
for_each_namespaced_ref(check_ref, &data);
for_each_namespaced_ref_1(check_ref, &data);
}
if (!advertise_refs) {
@ -1470,7 +1495,7 @@ static int parse_want(struct packet_writer *writer, const char *line,
static int parse_want_ref(struct packet_writer *writer, const char *line,
struct string_list *wanted_refs,
struct string_list *hidden_refs,
struct strvec *hidden_refs,
struct object_array *want_obj)
{
const char *refname_nons;