]> git.ipfire.org Git - thirdparty/git.git/commitdiff
ls-refs.c: traverse prefixes of disjoint "ref-prefix" sets
authorTaylor Blau <me@ttaylorr.com>
Wed, 20 Jan 2021 16:04:30 +0000 (11:04 -0500)
committerJunio C Hamano <gitster@pobox.com>
Sat, 23 Jan 2021 02:57:27 +0000 (18:57 -0800)
ls-refs performs a single revision walk over the whole ref namespace,
and sends ones that match with one of the given ref prefixes down to the
user.

This can be expensive if there are many refs overall, but the portion of
them covered by the given prefixes is small by comparison.

To attempt to reduce the difference between the number of refs
traversed, and the number of refs sent, only traverse references which
are in the longest common prefix of the given prefixes. This is very
reminiscent of the approach taken in b31e2680c4 (ref-filter.c: find
disjoint pattern prefixes, 2019-06-26) which does an analogous thing for
multi-patterned 'git for-each-ref' invocations.

The callback 'send_ref' is resilient to ignore extra patterns by
discarding any arguments which do not begin with at least one of the
specified prefixes.

Similarly, the code introduced in b31e2680c4 is resilient to stop early
at metacharacters, but we only pass strict prefixes here. At worst we
would return too many results, but the double checking done by send_ref
will throw away anything that doesn't start with something in the prefix
list.

Finally, if no prefixes were provided, then implicitly add the empty
string (which will match all references) since this matches the existing
behavior (see the "no restrictions" comment in "ls-refs.c:ref_match()").

Original-patch-by: Jacob Vosmaer <jacob@gitlab.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
ls-refs.c

index 367597d4477380fc2c9a0cc4355285cf64ca3c18..eaaa36d0dfbcaa3613e73e941fc2da3febd3f934 100644 (file)
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -110,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;