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`maint
commit
39fe402d67
|
@ -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
|
||||
-----------
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
66
ref-filter.c
66
ref-filter.c
|
@ -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);
|
||||
}
|
||||
|
|
12
ref-filter.h
12
ref-filter.h
|
@ -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
85
refs.c
|
@ -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
29
refs.h
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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 ' '
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
2
trace2.h
2
trace2.h
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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. */
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue