summaryrefslogtreecommitdiff
path: root/ref-filter.c
diff options
context:
space:
mode:
authorTaylor Blau <me@ttaylorr.com>2023-07-10 21:12:19 (GMT)
committerJunio C Hamano <gitster@pobox.com>2023-07-10 21:48:55 (GMT)
commit8255dd8a3dce094129690d17595dc822171d1e61 (patch)
tree1510f3253380578945473d1341dd9b967105d63b /ref-filter.c
parent284c55deb53921457929ca2afe231c48e6cada85 (diff)
downloadgit-8255dd8a3dce094129690d17595dc822171d1e61.zip
git-8255dd8a3dce094129690d17595dc822171d1e61.tar.gz
git-8255dd8a3dce094129690d17595dc822171d1e61.tar.bz2
builtin/for-each-ref.c: add `--exclude` option
When using `for-each-ref`, it is sometimes convenient for the caller to be able to exclude certain parts of the references. For example, if there are many `refs/__hidden__/*` references, the caller may want to emit all references *except* the hidden ones. Currently, the only way to do this is to post-process the output, like: $ git for-each-ref --format='%(refname)' | grep -v '^refs/hidden/' Which is do-able, but requires processing a potentially large quantity of references. Teach `git for-each-ref` a new `--exclude=<pattern>` option, which excludes references from the results if they match one or more excluded patterns. This patch provides a naive implementation where the `ref_filter` still sees all references (including ones that it will discard) and is left to check whether each reference matches any excluded pattern(s) before emitting them. By culling out references we know the caller doesn't care about, we can avoid allocating memory for their storage, as well as spending time sorting the output (among other things). Even the naive implementation provides a significant speed-up on a modified copy of linux.git (that has a hidden ref pointing at each commit): $ hyperfine \ 'git.compile for-each-ref --format="%(objectname) %(refname)" | grep -vE "[0-9a-f]{40} refs/pull/"' \ 'git.compile for-each-ref --format="%(objectname) %(refname)" --exclude refs/pull/' Benchmark 1: git.compile for-each-ref --format="%(objectname) %(refname)" | grep -vE "[0-9a-f]{40} refs/pull/" Time (mean ± σ): 820.1 ms ± 2.0 ms [User: 703.7 ms, System: 152.0 ms] Range (min … max): 817.7 ms … 823.3 ms 10 runs Benchmark 2: git.compile for-each-ref --format="%(objectname) %(refname)" --exclude refs/pull/ Time (mean ± σ): 106.6 ms ± 1.1 ms [User: 99.4 ms, System: 7.1 ms] Range (min … max): 104.7 ms … 109.1 ms 27 runs Summary 'git.compile for-each-ref --format="%(objectname) %(refname)" --exclude refs/pull/' ran 7.69 ± 0.08 times faster than 'git.compile for-each-ref --format="%(objectname) %(refname)" | grep -vE "[0-9a-f]{40} refs/pull/"' Subsequent patches will improve on this by avoiding visiting excluded sections of the `packed-refs` file in certain cases. Co-authored-by: Jeff King <peff@peff.net> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'ref-filter.c')
-rw-r--r--ref-filter.c14
1 files changed, 14 insertions, 0 deletions
diff --git a/ref-filter.c b/ref-filter.c
index 91acf53..ec9b79c 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2171,6 +2171,16 @@ static int filter_pattern_match(struct ref_filter *filter, const char *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);
+}
+
/*
* 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
@@ -2338,6 +2348,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;
@@ -2877,6 +2890,7 @@ void ref_filter_init(struct ref_filter *filter)
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);