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>]
|
[--points-at=<object>]
|
||||||
[--merged[=<object>]] [--no-merged[=<object>]]
|
[--merged[=<object>]] [--no-merged[=<object>]]
|
||||||
[--contains[=<object>]] [--no-contains[=<object>]]
|
[--contains[=<object>]] [--no-contains[=<object>]]
|
||||||
|
[--exclude=<pattern> ...]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -102,6 +103,11 @@ OPTIONS
|
||||||
Do not print a newline after formatted refs where the format expands
|
Do not print a newline after formatted refs where the format expands
|
||||||
to the empty string.
|
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
|
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,
|
int reflog = 0, quiet = 0, icase = 0, force = 0,
|
||||||
recurse_submodules_explicit = 0;
|
recurse_submodules_explicit = 0;
|
||||||
enum branch_track track;
|
enum branch_track track;
|
||||||
struct ref_filter filter;
|
struct ref_filter filter = REF_FILTER_INIT;
|
||||||
static struct ref_sorting *sorting;
|
static struct ref_sorting *sorting;
|
||||||
struct string_list sorting_options = STRING_LIST_INIT_DUP;
|
struct string_list sorting_options = STRING_LIST_INIT_DUP;
|
||||||
struct ref_format format = REF_FORMAT_INIT;
|
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();
|
setup_ref_filter_porcelain_msg();
|
||||||
|
|
||||||
memset(&filter, 0, sizeof(filter));
|
|
||||||
filter.kind = FILTER_REFS_BRANCHES;
|
filter.kind = FILTER_REFS_BRANCHES;
|
||||||
filter.abbrev = -1;
|
filter.abbrev = -1;
|
||||||
|
|
||||||
|
@ -855,6 +854,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||||
print_columns(&output, colopts, NULL);
|
print_columns(&output, colopts, NULL);
|
||||||
string_list_clear(&output, 0);
|
string_list_clear(&output, 0);
|
||||||
ref_sorting_release(sorting);
|
ref_sorting_release(sorting);
|
||||||
|
ref_filter_clear(&filter);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (edit_description) {
|
} else if (edit_description) {
|
||||||
const char *branch_name;
|
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;
|
struct string_list sorting_options = STRING_LIST_INIT_DUP;
|
||||||
int maxcount = 0, icase = 0, omit_empty = 0;
|
int maxcount = 0, icase = 0, omit_empty = 0;
|
||||||
struct ref_array array;
|
struct ref_array array;
|
||||||
struct ref_filter filter;
|
struct ref_filter filter = REF_FILTER_INIT;
|
||||||
struct ref_format format = REF_FORMAT_INIT;
|
struct ref_format format = REF_FORMAT_INIT;
|
||||||
struct strbuf output = STRBUF_INIT;
|
struct strbuf output = STRBUF_INIT;
|
||||||
struct strbuf err = 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_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_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
|
||||||
OPT__COLOR(&format.use_color, N_("respect format colors")),
|
OPT__COLOR(&format.use_color, N_("respect format colors")),
|
||||||
|
OPT_REF_FILTER_EXCLUDE(&filter),
|
||||||
OPT_REF_SORT(&sorting_options),
|
OPT_REF_SORT(&sorting_options),
|
||||||
OPT_CALLBACK(0, "points-at", &filter.points_at,
|
OPT_CALLBACK(0, "points-at", &filter.points_at,
|
||||||
N_("object"), N_("print only refs which points at the given object"),
|
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(&array, 0, sizeof(array));
|
||||||
memset(&filter, 0, sizeof(filter));
|
|
||||||
|
|
||||||
format.format = "%(objectname) %(objecttype)\t%(refname)";
|
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(&err);
|
||||||
strbuf_release(&output);
|
strbuf_release(&output);
|
||||||
ref_array_clear(&array);
|
ref_array_clear(&array);
|
||||||
free_commit_list(filter.with_commit);
|
ref_filter_clear(&filter);
|
||||||
free_commit_list(filter.no_commit);
|
|
||||||
ref_sorting_release(sorting);
|
ref_sorting_release(sorting);
|
||||||
strvec_clear(&vec);
|
strvec_clear(&vec);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -90,7 +90,7 @@ static struct object_id push_cert_oid;
|
||||||
static struct signature_check sigcheck;
|
static struct signature_check sigcheck;
|
||||||
static const char *push_cert_nonce;
|
static const char *push_cert_nonce;
|
||||||
static const char *cert_nonce_seed;
|
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_UNSOLICITED = "UNSOLICITED";
|
||||||
static const char *NONCE_BAD = "BAD";
|
static const char *NONCE_BAD = "BAD";
|
||||||
|
@ -338,7 +338,9 @@ static void write_head_info(void)
|
||||||
{
|
{
|
||||||
static struct oidset seen = OIDSET_INIT;
|
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);
|
for_each_alternate_ref(show_one_alternate_ref, &seen);
|
||||||
oidset_clear(&seen);
|
oidset_clear(&seen);
|
||||||
if (!sent_capabilities)
|
if (!sent_capabilities)
|
||||||
|
@ -2620,7 +2622,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
|
||||||
packet_flush(1);
|
packet_flush(1);
|
||||||
oid_array_clear(&shallow);
|
oid_array_clear(&shallow);
|
||||||
oid_array_clear(&ref);
|
oid_array_clear(&ref);
|
||||||
string_list_clear(&hidden_refs, 0);
|
strvec_clear(&hidden_refs);
|
||||||
free((void *)push_cert_nonce);
|
free((void *)push_cert_nonce);
|
||||||
return 0;
|
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 msg_arg msg = { .buf = STRBUF_INIT };
|
||||||
struct ref_transaction *transaction;
|
struct ref_transaction *transaction;
|
||||||
struct strbuf err = STRBUF_INIT;
|
struct strbuf err = STRBUF_INIT;
|
||||||
struct ref_filter filter;
|
struct ref_filter filter = REF_FILTER_INIT;
|
||||||
struct ref_sorting *sorting;
|
struct ref_sorting *sorting;
|
||||||
struct string_list sorting_options = STRING_LIST_INIT_DUP;
|
struct string_list sorting_options = STRING_LIST_INIT_DUP;
|
||||||
struct ref_format format = REF_FORMAT_INIT;
|
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);
|
git_config(git_tag_config, &sorting_options);
|
||||||
|
|
||||||
memset(&opt, 0, sizeof(opt));
|
memset(&opt, 0, sizeof(opt));
|
||||||
memset(&filter, 0, sizeof(filter));
|
|
||||||
filter.lines = -1;
|
filter.lines = -1;
|
||||||
opt.sign = -1;
|
opt.sign = -1;
|
||||||
|
|
||||||
|
@ -660,6 +659,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
ref_sorting_release(sorting);
|
ref_sorting_release(sorting);
|
||||||
|
ref_filter_clear(&filter);
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
strbuf_release(&ref);
|
strbuf_release(&ref);
|
||||||
strbuf_release(&reflog_msg);
|
strbuf_release(&reflog_msg);
|
||||||
|
|
|
@ -558,7 +558,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
select_getanyfile(hdr);
|
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);
|
send_strbuf(hdr, "text/plain", &buf);
|
||||||
}
|
}
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
|
|
|
@ -72,7 +72,7 @@ struct ls_refs_data {
|
||||||
unsigned symrefs;
|
unsigned symrefs;
|
||||||
struct strvec prefixes;
|
struct strvec prefixes;
|
||||||
struct strbuf buf;
|
struct strbuf buf;
|
||||||
struct string_list hidden_refs;
|
struct strvec hidden_refs;
|
||||||
unsigned unborn : 1;
|
unsigned unborn : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ int ls_refs(struct repository *r, struct packet_reader *request)
|
||||||
memset(&data, 0, sizeof(data));
|
memset(&data, 0, sizeof(data));
|
||||||
strvec_init(&data.prefixes);
|
strvec_init(&data.prefixes);
|
||||||
strbuf_init(&data.buf, 0);
|
strbuf_init(&data.buf, 0);
|
||||||
string_list_init_dup(&data.hidden_refs);
|
strvec_init(&data.hidden_refs);
|
||||||
|
|
||||||
git_config(ls_refs_config, &data);
|
git_config(ls_refs_config, &data);
|
||||||
|
|
||||||
|
@ -194,11 +194,12 @@ int ls_refs(struct repository *r, struct packet_reader *request)
|
||||||
strvec_push(&data.prefixes, "");
|
strvec_push(&data.prefixes, "");
|
||||||
refs_for_each_fullref_in_prefixes(get_main_ref_store(r),
|
refs_for_each_fullref_in_prefixes(get_main_ref_store(r),
|
||||||
get_git_namespace(), data.prefixes.v,
|
get_git_namespace(), data.prefixes.v,
|
||||||
|
hidden_refs_to_excludes(&data.hidden_refs),
|
||||||
send_ref, &data);
|
send_ref, &data);
|
||||||
packet_fflush(stdout);
|
packet_fflush(stdout);
|
||||||
strvec_clear(&data.prefixes);
|
strvec_clear(&data.prefixes);
|
||||||
strbuf_release(&data.buf);
|
strbuf_release(&data.buf);
|
||||||
string_list_clear(&data.hidden_refs, 0);
|
strvec_clear(&data.hidden_refs);
|
||||||
return 0;
|
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 a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
|
||||||
* matches "refs/heads/mas*", too).
|
* 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;
|
unsigned flags = 0;
|
||||||
|
|
||||||
if (filter->ignore_case)
|
if (ignore_case)
|
||||||
flags |= WM_CASEFOLD;
|
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
|
* matches a pattern "refs/heads/" but not "refs/heads/m") or a
|
||||||
* wildcard (e.g. the same ref matches "refs/heads/m*", too).
|
* 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);
|
int namelen = strlen(refname);
|
||||||
unsigned flags = WM_PATHNAME;
|
unsigned flags = WM_PATHNAME;
|
||||||
|
|
||||||
if (filter->ignore_case)
|
if (ignore_case)
|
||||||
flags |= WM_CASEFOLD;
|
flags |= WM_CASEFOLD;
|
||||||
|
|
||||||
for (; *pattern; pattern++) {
|
for (; *pattern; pattern++) {
|
||||||
|
@ -2286,8 +2286,20 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
|
||||||
if (!*filter->name_patterns)
|
if (!*filter->name_patterns)
|
||||||
return 1; /* No pattern always matches */
|
return 1; /* No pattern always matches */
|
||||||
if (filter->match_as_path)
|
if (filter->match_as_path)
|
||||||
return match_name_as_path(filter, refname);
|
return match_name_as_path(filter->name_patterns, refname,
|
||||||
return match_pattern(filter, 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]) {
|
if (!filter->name_patterns[0]) {
|
||||||
/* no patterns; we have to look at everything */
|
/* 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),
|
return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),
|
||||||
NULL, filter->name_patterns,
|
NULL, filter->name_patterns,
|
||||||
|
filter->exclude.v,
|
||||||
cb, cb_data);
|
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))
|
if (!filter_pattern_match(filter, refname))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (filter_exclude_match(filter, refname))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
|
if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -2539,13 +2556,13 @@ void ref_array_clear(struct ref_array *array)
|
||||||
#define EXCLUDE_REACHED 0
|
#define EXCLUDE_REACHED 0
|
||||||
#define INCLUDE_REACHED 1
|
#define INCLUDE_REACHED 1
|
||||||
static void reach_filter(struct ref_array *array,
|
static void reach_filter(struct ref_array *array,
|
||||||
struct commit_list *check_reachable,
|
struct commit_list **check_reachable,
|
||||||
int include_reached)
|
int include_reached)
|
||||||
{
|
{
|
||||||
int i, old_nr;
|
int i, old_nr;
|
||||||
struct commit **to_clear;
|
struct commit **to_clear;
|
||||||
|
|
||||||
if (!check_reachable)
|
if (!*check_reachable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CALLOC_ARRAY(to_clear, array->nr);
|
CALLOC_ARRAY(to_clear, array->nr);
|
||||||
|
@ -2555,7 +2572,7 @@ static void reach_filter(struct ref_array *array,
|
||||||
}
|
}
|
||||||
|
|
||||||
tips_reachable_from_bases(the_repository,
|
tips_reachable_from_bases(the_repository,
|
||||||
check_reachable,
|
*check_reachable,
|
||||||
to_clear, array->nr,
|
to_clear, array->nr,
|
||||||
UNINTERESTING);
|
UNINTERESTING);
|
||||||
|
|
||||||
|
@ -2576,8 +2593,8 @@ static void reach_filter(struct ref_array *array,
|
||||||
|
|
||||||
clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS);
|
clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS);
|
||||||
|
|
||||||
while (check_reachable) {
|
while (*check_reachable) {
|
||||||
struct commit *merge_commit = pop_commit(&check_reachable);
|
struct commit *merge_commit = pop_commit(check_reachable);
|
||||||
clear_commit_marks(merge_commit, ALL_REV_FLAGS);
|
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);
|
clear_contains_cache(&ref_cbdata.no_contains_cache);
|
||||||
|
|
||||||
/* Filters that need revision walking */
|
/* Filters that need revision walking */
|
||||||
reach_filter(array, filter->reachable_from, INCLUDE_REACHED);
|
reach_filter(array, &filter->reachable_from, INCLUDE_REACHED);
|
||||||
reach_filter(array, filter->unreachable_from, EXCLUDE_REACHED);
|
reach_filter(array, &filter->unreachable_from, EXCLUDE_REACHED);
|
||||||
|
|
||||||
save_commit_buffer = save_commit_buffer_orig;
|
save_commit_buffer = save_commit_buffer_orig;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2987,3 +3004,20 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
|
||||||
|
|
||||||
return 0;
|
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 "refs.h"
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "string-list.h"
|
#include "string-list.h"
|
||||||
|
#include "strvec.h"
|
||||||
|
|
||||||
/* Quoting styles */
|
/* Quoting styles */
|
||||||
#define QUOTE_NONE 0
|
#define QUOTE_NONE 0
|
||||||
|
@ -59,6 +60,7 @@ struct ref_array {
|
||||||
|
|
||||||
struct ref_filter {
|
struct ref_filter {
|
||||||
const char **name_patterns;
|
const char **name_patterns;
|
||||||
|
struct strvec exclude;
|
||||||
struct oid_array points_at;
|
struct oid_array points_at;
|
||||||
struct commit_list *with_commit;
|
struct commit_list *with_commit;
|
||||||
struct commit_list *no_commit;
|
struct commit_list *no_commit;
|
||||||
|
@ -92,6 +94,10 @@ struct ref_format {
|
||||||
struct string_list bases;
|
struct string_list bases;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define REF_FILTER_INIT { \
|
||||||
|
.points_at = OID_ARRAY_INIT, \
|
||||||
|
.exclude = STRVEC_INIT, \
|
||||||
|
}
|
||||||
#define REF_FORMAT_INIT { \
|
#define REF_FORMAT_INIT { \
|
||||||
.use_color = -1, \
|
.use_color = -1, \
|
||||||
.bases = STRING_LIST_INIT_DUP, \
|
.bases = STRING_LIST_INIT_DUP, \
|
||||||
|
@ -109,6 +115,9 @@ struct ref_format {
|
||||||
#define OPT_REF_SORT(var) \
|
#define OPT_REF_SORT(var) \
|
||||||
OPT_STRING_LIST(0, "sort", (var), \
|
OPT_STRING_LIST(0, "sort", (var), \
|
||||||
N_("key"), N_("field name to sort on"))
|
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
|
* 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_format *format,
|
||||||
struct ref_array *array);
|
struct ref_array *array);
|
||||||
|
|
||||||
|
void ref_filter_init(struct ref_filter *filter);
|
||||||
|
void ref_filter_clear(struct ref_filter *filter);
|
||||||
|
|
||||||
#endif /* REF_FILTER_H */
|
#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);
|
oid, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The argument to filter_refs */
|
/* The argument to for_each_filter_refs */
|
||||||
struct ref_filter {
|
struct for_each_ref_filter {
|
||||||
const char *pattern;
|
const char *pattern;
|
||||||
const char *prefix;
|
const char *prefix;
|
||||||
each_ref_fn *fn;
|
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);
|
return refs_ref_exists(get_main_ref_store(the_repository), refname);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int filter_refs(const char *refname, const struct object_id *oid,
|
static int for_each_filter_refs(const char *refname,
|
||||||
int flags, void *data)
|
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))
|
if (wildmatch(filter->pattern, refname, 0))
|
||||||
return 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)
|
const char *prefix, void *cb_data)
|
||||||
{
|
{
|
||||||
struct strbuf real_pattern = STRBUF_INIT;
|
struct strbuf real_pattern = STRBUF_INIT;
|
||||||
struct ref_filter filter;
|
struct for_each_ref_filter filter;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!prefix && !starts_with(pattern, "refs/"))
|
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.prefix = prefix;
|
||||||
filter.fn = fn;
|
filter.fn = fn;
|
||||||
filter.cb_data = cb_data;
|
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);
|
strbuf_release(&real_pattern);
|
||||||
return ret;
|
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,
|
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;
|
const char *key;
|
||||||
if (!strcmp("transfer.hiderefs", var) ||
|
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)
|
if (!value)
|
||||||
return config_error_nonbool(var);
|
return config_error_nonbool(var);
|
||||||
ref = xstrdup(value);
|
|
||||||
|
/* drop const to remove trailing '/' characters */
|
||||||
|
ref = (char *)strvec_push(hide_refs, value);
|
||||||
len = strlen(ref);
|
len = strlen(ref);
|
||||||
while (len && ref[len - 1] == '/')
|
while (len && ref[len - 1] == '/')
|
||||||
ref[--len] = '\0';
|
ref[--len] = '\0';
|
||||||
string_list_append_nodup(hide_refs, ref);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ref_is_hidden(const char *refname, const char *refname_full,
|
int ref_is_hidden(const char *refname, const char *refname_full,
|
||||||
const struct string_list *hide_refs)
|
const struct strvec *hide_refs)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = hide_refs->nr - 1; i >= 0; 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;
|
const char *subject;
|
||||||
int neg = 0;
|
int neg = 0;
|
||||||
const char *p;
|
const char *p;
|
||||||
|
@ -1478,6 +1480,30 @@ int ref_is_hidden(const char *refname, const char *refname_full,
|
||||||
return 0;
|
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 char *find_descendant_ref(const char *dirname,
|
||||||
const struct string_list *extras,
|
const struct string_list *extras,
|
||||||
const struct string_list *skip)
|
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_iterator *refs_ref_iterator_begin(
|
||||||
struct ref_store *refs,
|
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)
|
enum do_for_each_ref_flags flags)
|
||||||
{
|
{
|
||||||
struct ref_iterator *iter;
|
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
|
* `iterator_begin()` already takes care of prefix, but we
|
||||||
* might need to do some trimming:
|
* 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)
|
if (!refs)
|
||||||
return 0;
|
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);
|
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,
|
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
|
||||||
|
const char **exclude_patterns,
|
||||||
each_ref_fn fn, int trim,
|
each_ref_fn fn, int trim,
|
||||||
enum do_for_each_ref_flags flags, void *cb_data)
|
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)
|
if (!refs)
|
||||||
return 0;
|
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,
|
return do_for_each_repo_ref_iterator(the_repository, iter,
|
||||||
do_for_each_ref_helper, &hp);
|
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)
|
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)
|
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,
|
int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
|
||||||
each_ref_fn fn, void *cb_data)
|
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)
|
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)
|
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),
|
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,
|
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
|
||||||
|
const char **exclude_patterns,
|
||||||
each_ref_fn fn, void *cb_data)
|
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)
|
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);
|
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;
|
struct strbuf buf = STRBUF_INIT;
|
||||||
int ret;
|
int ret;
|
||||||
strbuf_addf(&buf, "%srefs/", get_git_namespace());
|
strbuf_addf(&buf, "%srefs/", get_git_namespace());
|
||||||
ret = do_for_each_ref(get_main_ref_store(the_repository),
|
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);
|
strbuf_release(&buf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
|
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);
|
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,
|
int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
|
||||||
const char *namespace,
|
const char *namespace,
|
||||||
const char **patterns,
|
const char **patterns,
|
||||||
|
const char **exclude_patterns,
|
||||||
each_ref_fn fn, void *cb_data)
|
each_ref_fn fn, void *cb_data)
|
||||||
{
|
{
|
||||||
struct string_list prefixes = STRING_LIST_INIT_DUP;
|
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) {
|
for_each_string_list_item(prefix, &prefixes) {
|
||||||
strbuf_addstr(&buf, prefix->string);
|
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)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
strbuf_setlen(&buf, namespace_len);
|
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_addstr(&dirname, refname + dirname.len);
|
||||||
strbuf_addch(&dirname, '/');
|
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);
|
DO_FOR_EACH_INCLUDE_BROKEN);
|
||||||
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
|
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
|
||||||
if (skip &&
|
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);
|
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,
|
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
|
||||||
|
const char **exclude_patterns,
|
||||||
each_ref_fn fn, void *cb_data);
|
each_ref_fn fn, void *cb_data);
|
||||||
int for_each_fullref_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);
|
||||||
|
|
||||||
|
@ -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
|
* iterate all refs in "patterns" by partitioning patterns into disjoint sets
|
||||||
* and iterating the longest-common prefix of each set.
|
* 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.
|
* callers should be prepared to ignore references that they did not ask for.
|
||||||
*/
|
*/
|
||||||
int refs_for_each_fullref_in_prefixes(struct ref_store *refs,
|
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);
|
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);
|
const char *prefix, void *cb_data);
|
||||||
|
|
||||||
int head_ref_namespaced(each_ref_fn fn, 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 */
|
/* can be used to learn about broken ref and symref */
|
||||||
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
|
int refs_for_each_rawref(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);
|
unsigned int flags, enum action_on_err onerr);
|
||||||
|
|
||||||
int parse_hide_refs_config(const char *var, const char *value, const char *,
|
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
|
* 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
|
* the ref is outside that namespace, the first parameter is NULL. The second
|
||||||
* parameter always points to the full ref name.
|
* 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? */
|
/* Is this a per-worktree ref living in the refs/ namespace? */
|
||||||
int is_per_worktree_ref(const char *refname);
|
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 *
|
static struct ref_iterator *
|
||||||
debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
|
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 debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
|
||||||
struct ref_iterator *res =
|
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));
|
struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
|
||||||
base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
|
base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
|
||||||
diter->iter = res;
|
diter->iter = res;
|
||||||
|
|
|
@ -832,7 +832,8 @@ static struct ref_iterator_vtable files_ref_iterator_vtable = {
|
||||||
|
|
||||||
static struct ref_iterator *files_ref_iterator_begin(
|
static struct ref_iterator *files_ref_iterator_begin(
|
||||||
struct ref_store *ref_store,
|
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 files_ref_store *refs;
|
||||||
struct ref_iterator *loose_iter, *packed_iter, *overlay_iter;
|
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.
|
* the packed and loose references.
|
||||||
*/
|
*/
|
||||||
packed_iter = refs_ref_iterator_begin(
|
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);
|
DO_FOR_EACH_INCLUDE_BROKEN);
|
||||||
|
|
||||||
overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter);
|
overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "../statinfo.h"
|
#include "../statinfo.h"
|
||||||
#include "../wrapper.h"
|
#include "../wrapper.h"
|
||||||
#include "../write-or-die.h"
|
#include "../write-or-die.h"
|
||||||
|
#include "../trace2.h"
|
||||||
|
|
||||||
enum mmap_strategy {
|
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
|
* Compare a snapshot record at `rec` to the specified NUL-terminated
|
||||||
* refname.
|
* 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 *r1 = rec + the_hash_algo->hexsz + 1;
|
||||||
const char *r2 = refname;
|
const char *r2 = refname;
|
||||||
|
@ -313,7 +315,7 @@ static int cmp_record_to_refname(const char *rec, const char *refname)
|
||||||
if (*r1 == '\n')
|
if (*r1 == '\n')
|
||||||
return *r2 ? -1 : 0;
|
return *r2 ? -1 : 0;
|
||||||
if (!*r2)
|
if (!*r2)
|
||||||
return 1;
|
return start ? 1 : -1;
|
||||||
if (*r1 != *r2)
|
if (*r1 != *r2)
|
||||||
return (unsigned char)*r1 < (unsigned char)*r2 ? -1 : +1;
|
return (unsigned char)*r1 < (unsigned char)*r2 ? -1 : +1;
|
||||||
r1++;
|
r1++;
|
||||||
|
@ -528,22 +530,9 @@ static int load_contents(struct snapshot *snapshot)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static const char *find_reference_location_1(struct snapshot *snapshot,
|
||||||
* Find the place in `snapshot->buf` where the start of the record for
|
const char *refname, int mustexist,
|
||||||
* `refname` starts. If `mustexist` is true and the reference doesn't
|
int start)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This is not *quite* a garden-variety binary search, because
|
* 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;
|
mid = lo + (hi - lo) / 2;
|
||||||
rec = find_start_of_record(lo, mid);
|
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) {
|
if (cmp < 0) {
|
||||||
lo = find_end_of_record(mid, hi);
|
lo = find_end_of_record(mid, hi);
|
||||||
} else if (cmp > 0) {
|
} else if (cmp > 0) {
|
||||||
|
@ -589,6 +578,41 @@ static const char *find_reference_location(struct snapshot *snapshot,
|
||||||
return lo;
|
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
|
* Create a newly-allocated `snapshot` of the `packed-refs` file in
|
||||||
* its current state and return it. The return value will already have
|
* 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: */
|
/* The end of the part of the buffer that will be iterated over: */
|
||||||
const char *eof;
|
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: */
|
/* Scratch space for current values: */
|
||||||
struct object_id oid, peeled;
|
struct object_id oid, peeled;
|
||||||
struct strbuf refname_buf;
|
struct strbuf refname_buf;
|
||||||
|
@ -797,14 +828,36 @@ struct packed_ref_iterator {
|
||||||
*/
|
*/
|
||||||
static int next_record(struct packed_ref_iterator *iter)
|
static int next_record(struct packed_ref_iterator *iter)
|
||||||
{
|
{
|
||||||
const char *p = iter->pos, *eol;
|
const char *p, *eol;
|
||||||
|
|
||||||
strbuf_reset(&iter->refname_buf);
|
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)
|
if (iter->pos == iter->eof)
|
||||||
return ITER_DONE;
|
return ITER_DONE;
|
||||||
|
|
||||||
iter->base.flags = REF_ISPACKED;
|
iter->base.flags = REF_ISPACKED;
|
||||||
|
p = iter->pos;
|
||||||
|
|
||||||
if (iter->eof - p < the_hash_algo->hexsz + 2 ||
|
if (iter->eof - p < the_hash_algo->hexsz + 2 ||
|
||||||
parse_oid_hex(p, &iter->oid, &p) ||
|
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;
|
int ok = ITER_DONE;
|
||||||
|
|
||||||
strbuf_release(&iter->refname_buf);
|
strbuf_release(&iter->refname_buf);
|
||||||
|
free(iter->jump);
|
||||||
release_snapshot(iter->snapshot);
|
release_snapshot(iter->snapshot);
|
||||||
base_ref_iterator_free(ref_iterator);
|
base_ref_iterator_free(ref_iterator);
|
||||||
return ok;
|
return ok;
|
||||||
|
@ -923,9 +977,112 @@ static struct ref_iterator_vtable packed_ref_iterator_vtable = {
|
||||||
.abort = packed_ref_iterator_abort
|
.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(
|
static struct ref_iterator *packed_ref_iterator_begin(
|
||||||
struct ref_store *ref_store,
|
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 packed_ref_store *refs;
|
||||||
struct snapshot *snapshot;
|
struct snapshot *snapshot;
|
||||||
|
@ -957,6 +1114,9 @@ static struct ref_iterator *packed_ref_iterator_begin(
|
||||||
ref_iterator = &iter->base;
|
ref_iterator = &iter->base;
|
||||||
base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1);
|
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;
|
iter->snapshot = snapshot;
|
||||||
acquire_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
|
* list of refs is exhausted, set iter to NULL. When the list
|
||||||
* of updates is exhausted, leave i set to updates->nr.
|
* 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);
|
DO_FOR_EACH_INCLUDE_BROKEN);
|
||||||
if ((ok = ref_iterator_advance(iter)) != ITER_OK)
|
if ((ok = ref_iterator_advance(iter)) != ITER_OK)
|
||||||
iter = NULL;
|
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_iterator *refs_ref_iterator_begin(
|
||||||
struct ref_store *refs,
|
struct ref_store *refs,
|
||||||
const char *prefix, int trim,
|
const char *prefix, const char **exclude_patterns,
|
||||||
enum do_for_each_ref_flags flags);
|
int trim, enum do_for_each_ref_flags flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A callback function used to instruct merge_ref_iterator how to
|
* 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(
|
typedef struct ref_iterator *ref_iterator_begin_fn(
|
||||||
struct ref_store *ref_store,
|
struct ref_store *ref_store,
|
||||||
const char *prefix, unsigned int flags);
|
const char *prefix, const char **exclude_patterns,
|
||||||
|
unsigned int flags);
|
||||||
|
|
||||||
/* reflog functions */
|
/* reflog functions */
|
||||||
|
|
||||||
|
|
|
@ -1560,7 +1560,7 @@ void init_ref_exclusions(struct ref_exclusions *exclusions)
|
||||||
void clear_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->excluded_refs, 0);
|
||||||
string_list_clear(&exclusions->hidden_refs, 0);
|
strvec_clear(&exclusions->hidden_refs);
|
||||||
exclusions->hidden_refs_configured = 0;
|
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;
|
struct strbuf bisect_refs = STRBUF_INIT;
|
||||||
int status;
|
int status;
|
||||||
strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
|
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);
|
strbuf_release(&bisect_refs);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "decorate.h"
|
#include "decorate.h"
|
||||||
#include "ident.h"
|
#include "ident.h"
|
||||||
#include "list-objects-filter-options.h"
|
#include "list-objects-filter-options.h"
|
||||||
|
#include "strvec.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The revision walking API offers functions to build a list of revisions
|
* 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
|
* Hidden refs is a list of patterns that is to be hidden via
|
||||||
* `ref_is_hidden()`.
|
* `ref_is_hidden()`.
|
||||||
*/
|
*/
|
||||||
struct string_list hidden_refs;
|
struct strvec hidden_refs;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Indicates whether hidden refs have been configured. This is to
|
* Indicates whether hidden refs have been configured. This is to
|
||||||
|
@ -110,7 +111,7 @@ struct ref_exclusions {
|
||||||
*/
|
*/
|
||||||
#define REF_EXCLUSIONS_INIT { \
|
#define REF_EXCLUSIONS_INIT { \
|
||||||
.excluded_refs = STRING_LIST_INIT_DUP, \
|
.excluded_refs = STRING_LIST_INIT_DUP, \
|
||||||
.hidden_refs = STRING_LIST_INIT_DUP, \
|
.hidden_refs = STRVEC_INIT, \
|
||||||
}
|
}
|
||||||
|
|
||||||
struct oidset;
|
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));
|
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")) {
|
} else if (!strcmp(av[1], "commit_contains")) {
|
||||||
struct ref_filter filter;
|
struct ref_filter filter = REF_FILTER_INIT;
|
||||||
struct contains_cache cache;
|
struct contains_cache cache;
|
||||||
init_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);
|
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)
|
static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
|
||||||
{
|
{
|
||||||
struct object_id oid = *null_oid();
|
struct object_id oid = *null_oid();
|
||||||
|
@ -318,6 +327,7 @@ static struct command commands[] = {
|
||||||
{ "delete-refs", cmd_delete_refs },
|
{ "delete-refs", cmd_delete_refs },
|
||||||
{ "rename-ref", cmd_rename_ref },
|
{ "rename-ref", cmd_rename_ref },
|
||||||
{ "for-each-ref", cmd_for_each_ref },
|
{ "for-each-ref", cmd_for_each_ref },
|
||||||
|
{ "for-each-ref--exclude", cmd_for_each_ref__exclude },
|
||||||
{ "resolve-ref", cmd_resolve_ref },
|
{ "resolve-ref", cmd_resolve_ref },
|
||||||
{ "verify-ref", cmd_verify_ref },
|
{ "verify-ref", cmd_verify_ref },
|
||||||
{ "for-each-reflog", cmd_for_each_reflog },
|
{ "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
|
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||||
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||||
|
|
||||||
|
TEST_PASSES_SANITIZE_LEAK=true
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
test_expect_success 'setup ' '
|
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
|
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||||
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||||
|
|
||||||
|
TEST_PASSES_SANITIZE_LEAK=true
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
T="A quick brown fox
|
T="A quick brown fox
|
||||||
|
|
|
@ -448,6 +448,41 @@ test_expect_success 'exercise glob patterns with prefixes' '
|
||||||
test_cmp expected actual
|
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
|
cat >expected <<\EOF
|
||||||
'refs/heads/main'
|
'refs/heads/main'
|
||||||
'refs/remotes/origin/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_TEST1 = 0, /* emits summary event only */
|
||||||
TRACE2_COUNTER_ID_TEST2, /* emits summary and thread events */
|
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. */
|
/* Add additional counter definitions before here. */
|
||||||
TRACE2_NUMBER_OF_COUNTERS
|
TRACE2_NUMBER_OF_COUNTERS
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,11 @@ static struct tr2_counter_metadata tr2_counter_metadata[TRACE2_NUMBER_OF_COUNTER
|
||||||
.name = "test2",
|
.name = "test2",
|
||||||
.want_per_thread_events = 1,
|
.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. */
|
/* Add additional metadata before here. */
|
||||||
};
|
};
|
||||||
|
|
|
@ -68,7 +68,7 @@ struct upload_pack_data {
|
||||||
struct object_array have_obj;
|
struct object_array have_obj;
|
||||||
struct oid_array haves; /* v2 only */
|
struct oid_array haves; /* v2 only */
|
||||||
struct string_list wanted_refs; /* v2 only */
|
struct string_list wanted_refs; /* v2 only */
|
||||||
struct string_list hidden_refs;
|
struct strvec hidden_refs;
|
||||||
|
|
||||||
struct object_array shallows;
|
struct object_array shallows;
|
||||||
struct string_list deepen_not;
|
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 symref = STRING_LIST_INIT_DUP;
|
||||||
struct string_list wanted_refs = 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 want_obj = OBJECT_ARRAY_INIT;
|
||||||
struct object_array have_obj = OBJECT_ARRAY_INIT;
|
struct object_array have_obj = OBJECT_ARRAY_INIT;
|
||||||
struct oid_array haves = OID_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->symref, 1);
|
||||||
string_list_clear(&data->wanted_refs, 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->want_obj);
|
||||||
object_array_clear(&data->have_obj);
|
object_array_clear(&data->have_obj);
|
||||||
oid_array_clear(&data->haves);
|
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)
|
static int is_our_ref(struct object *o, enum allow_uor allow_uor)
|
||||||
{
|
{
|
||||||
int allow_hidden_ref = (allow_uor &
|
return o->flags & ((allow_hidden_refs(allow_uor) ? 0 : HIDDEN_REF) | OUR_REF);
|
||||||
(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
|
|
||||||
return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -854,7 +879,7 @@ static void deepen(struct upload_pack_data *data, int depth)
|
||||||
* marked with OUR_REF.
|
* marked with OUR_REF.
|
||||||
*/
|
*/
|
||||||
head_ref_namespaced(check_ref, data);
|
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);
|
get_reachable_list(data, &reachable_shallows);
|
||||||
result = get_shallow_commits(&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 */
|
/* return non-zero if the ref is hidden, otherwise 0 */
|
||||||
static int mark_our_ref(const char *refname, const char *refname_full,
|
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);
|
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)
|
if (advertise_refs)
|
||||||
data.no_done = 1;
|
data.no_done = 1;
|
||||||
head_ref_namespaced(send_ref, &data);
|
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) {
|
if (!data.sent_capabilities) {
|
||||||
const char *refname = "capabilities^{}";
|
const char *refname = "capabilities^{}";
|
||||||
write_v0_ref(&data, refname, refname, null_oid());
|
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);
|
packet_flush(1);
|
||||||
} else {
|
} else {
|
||||||
head_ref_namespaced(check_ref, &data);
|
head_ref_namespaced(check_ref, &data);
|
||||||
for_each_namespaced_ref(check_ref, &data);
|
for_each_namespaced_ref_1(check_ref, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!advertise_refs) {
|
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,
|
static int parse_want_ref(struct packet_writer *writer, const char *line,
|
||||||
struct string_list *wanted_refs,
|
struct string_list *wanted_refs,
|
||||||
struct string_list *hidden_refs,
|
struct strvec *hidden_refs,
|
||||||
struct object_array *want_obj)
|
struct object_array *want_obj)
|
||||||
{
|
{
|
||||||
const char *refname_nons;
|
const char *refname_nons;
|
||||||
|
|
Loading…
Reference in New Issue