Merge branch 'tb/ls-refs-optim'

The ls-refs protocol operation has been optimized to narrow the
sub-hierarchy of refs/ it walks to produce response.

* tb/ls-refs-optim:
  ls-refs.c: traverse prefixes of disjoint "ref-prefix" sets
  ls-refs.c: initialize 'prefixes' before using it
  refs: expose 'for_each_fullref_in_prefixes'
This commit is contained in:
Junio C Hamano 2021-02-05 16:40:45 -08:00
commit 6254fa1359
4 changed files with 103 additions and 73 deletions

View File

@ -90,6 +90,7 @@ int ls_refs(struct repository *r, struct strvec *keys,
struct ls_refs_data data;
memset(&data, 0, sizeof(data));
strvec_init(&data.prefixes);
git_config(ls_refs_config, NULL);
@ -109,7 +110,10 @@ int ls_refs(struct repository *r, struct strvec *keys,
die(_("expected flush after ls-refs arguments"));
head_ref_namespaced(send_ref, &data);
for_each_namespaced_ref(send_ref, &data);
if (!data.prefixes.nr)
strvec_push(&data.prefixes, "");
for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
send_ref, &data, 0);
packet_flush(1);
strvec_clear(&data.prefixes);
return 0;

View File

@ -1920,64 +1920,6 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
return match_pattern(filter, refname);
}
static int qsort_strcmp(const void *va, const void *vb)
{
const char *a = *(const char **)va;
const char *b = *(const char **)vb;
return strcmp(a, b);
}
static void find_longest_prefixes_1(struct string_list *out,
struct strbuf *prefix,
const char **patterns, size_t nr)
{
size_t i;
for (i = 0; i < nr; i++) {
char c = patterns[i][prefix->len];
if (!c || is_glob_special(c)) {
string_list_append(out, prefix->buf);
return;
}
}
i = 0;
while (i < nr) {
size_t end;
/*
* Set "end" to the index of the element _after_ the last one
* in our group.
*/
for (end = i + 1; end < nr; end++) {
if (patterns[i][prefix->len] != patterns[end][prefix->len])
break;
}
strbuf_addch(prefix, patterns[i][prefix->len]);
find_longest_prefixes_1(out, prefix, patterns + i, end - i);
strbuf_setlen(prefix, prefix->len - 1);
i = end;
}
}
static void find_longest_prefixes(struct string_list *out,
const char **patterns)
{
struct strvec sorted = STRVEC_INIT;
struct strbuf prefix = STRBUF_INIT;
strvec_pushv(&sorted, patterns);
QSORT(sorted.v, sorted.nr, qsort_strcmp);
find_longest_prefixes_1(out, &prefix, sorted.v, sorted.nr);
strvec_clear(&sorted);
strbuf_release(&prefix);
}
/*
* This is the same as for_each_fullref_in(), but it tries to iterate
* only over the patterns we'll care about. Note that it _doesn't_ do a full
@ -1988,10 +1930,6 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
void *cb_data,
int broken)
{
struct string_list prefixes = STRING_LIST_INIT_DUP;
struct string_list_item *prefix;
int ret;
if (!filter->match_as_path) {
/*
* in this case, the patterns are applied after
@ -2015,16 +1953,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
return for_each_fullref_in("", cb, cb_data, broken);
}
find_longest_prefixes(&prefixes, filter->name_patterns);
for_each_string_list_item(prefix, &prefixes) {
ret = for_each_fullref_in(prefix->string, cb, cb_data, broken);
if (ret)
break;
}
string_list_clear(&prefixes, 0);
return ret;
return for_each_fullref_in_prefixes(NULL, filter->name_patterns,
cb, cb_data, broken);
}
/*

87
refs.c
View File

@ -1564,6 +1564,93 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
}
static int qsort_strcmp(const void *va, const void *vb)
{
const char *a = *(const char **)va;
const char *b = *(const char **)vb;
return strcmp(a, b);
}
static void find_longest_prefixes_1(struct string_list *out,
struct strbuf *prefix,
const char **patterns, size_t nr)
{
size_t i;
for (i = 0; i < nr; i++) {
char c = patterns[i][prefix->len];
if (!c || is_glob_special(c)) {
string_list_append(out, prefix->buf);
return;
}
}
i = 0;
while (i < nr) {
size_t end;
/*
* Set "end" to the index of the element _after_ the last one
* in our group.
*/
for (end = i + 1; end < nr; end++) {
if (patterns[i][prefix->len] != patterns[end][prefix->len])
break;
}
strbuf_addch(prefix, patterns[i][prefix->len]);
find_longest_prefixes_1(out, prefix, patterns + i, end - i);
strbuf_setlen(prefix, prefix->len - 1);
i = end;
}
}
static void find_longest_prefixes(struct string_list *out,
const char **patterns)
{
struct strvec sorted = STRVEC_INIT;
struct strbuf prefix = STRBUF_INIT;
strvec_pushv(&sorted, patterns);
QSORT(sorted.v, sorted.nr, qsort_strcmp);
find_longest_prefixes_1(out, &prefix, sorted.v, sorted.nr);
strvec_clear(&sorted);
strbuf_release(&prefix);
}
int for_each_fullref_in_prefixes(const char *namespace,
const char **patterns,
each_ref_fn fn, void *cb_data,
unsigned int broken)
{
struct string_list prefixes = STRING_LIST_INIT_DUP;
struct string_list_item *prefix;
struct strbuf buf = STRBUF_INIT;
int ret = 0, namespace_len;
find_longest_prefixes(&prefixes, patterns);
if (namespace)
strbuf_addstr(&buf, namespace);
namespace_len = buf.len;
for_each_string_list_item(prefix, &prefixes) {
strbuf_addstr(&buf, prefix->string);
ret = for_each_fullref_in(buf.buf, fn, cb_data, broken);
if (ret)
break;
strbuf_setlen(&buf, namespace_len);
}
string_list_clear(&prefixes, 0);
strbuf_release(&buf);
return ret;
}
static int refs_read_special_head(struct ref_store *ref_store,
const char *refname, struct object_id *oid,
struct strbuf *referent, unsigned int *type)

9
refs.h
View File

@ -347,6 +347,15 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
unsigned int broken);
/**
* iterate all refs in "patterns" by partitioning patterns into disjoint sets
* and iterating the longest-common prefix of each set.
*
* callers should be prepared to ignore references that they did not ask for.
*/
int for_each_fullref_in_prefixes(const char *namespace, const char **patterns,
each_ref_fn fn, void *cb_data,
unsigned int broken);
/**
* iterate refs from the respective area.
*/