Skip to content

Commit cfe004a

Browse files
peffgitster
authored andcommitted
ref-filter: limit traversal to prefix
When we are matching refnames against a pattern, then we know that the beginning of any refname that can match the pattern has to match the part of the pattern up to the first glob character. For example, if the pattern is `refs/heads/foo*bar`, then it can only match a reference that has the prefix `refs/heads/foo`. So pass that prefix to `for_each_fullref_in()`. This lets the ref code avoid passing us the full set of refs, and in some cases avoid reading them in the first place. Note that this applies only when the `match_as_path` flag is set (i.e., when `for-each-ref` is the caller), as the matching rules for git-branch and git-tag are subtly different. This could be generalized to the case of multiple patterns, but (a) it probably doesn't come up that often, and (b) it is more awkward to deal with multiple patterns (e.g., the patterns might not be disjoint). So, since this is just an optimization, punt on the case of multiple patterns. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Michael Haggerty <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c1da06c commit cfe004a

File tree

1 file changed

+63
-1
lines changed

1 file changed

+63
-1
lines changed

ref-filter.c

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1665,6 +1665,68 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
16651665
return match_pattern(filter, refname);
16661666
}
16671667

1668+
/*
1669+
* Find the longest prefix of pattern we can pass to
1670+
* `for_each_fullref_in()`, namely the part of pattern preceding the
1671+
* first glob character. (Note that `for_each_fullref_in()` is
1672+
* perfectly happy working with a prefix that doesn't end at a
1673+
* pathname component boundary.)
1674+
*/
1675+
static void find_longest_prefix(struct strbuf *out, const char *pattern)
1676+
{
1677+
const char *p;
1678+
1679+
for (p = pattern; *p && !is_glob_special(*p); p++)
1680+
;
1681+
1682+
strbuf_add(out, pattern, p - pattern);
1683+
}
1684+
1685+
/*
1686+
* This is the same as for_each_fullref_in(), but it tries to iterate
1687+
* only over the patterns we'll care about. Note that it _doesn't_ do a full
1688+
* pattern match, so the callback still has to match each ref individually.
1689+
*/
1690+
static int for_each_fullref_in_pattern(struct ref_filter *filter,
1691+
each_ref_fn cb,
1692+
void *cb_data,
1693+
int broken)
1694+
{
1695+
struct strbuf prefix = STRBUF_INIT;
1696+
int ret;
1697+
1698+
if (!filter->match_as_path) {
1699+
/*
1700+
* in this case, the patterns are applied after
1701+
* prefixes like "refs/heads/" etc. are stripped off,
1702+
* so we have to look at everything:
1703+
*/
1704+
return for_each_fullref_in("", cb, cb_data, broken);
1705+
}
1706+
1707+
if (!filter->name_patterns[0]) {
1708+
/* no patterns; we have to look at everything */
1709+
return for_each_fullref_in("", cb, cb_data, broken);
1710+
}
1711+
1712+
if (filter->name_patterns[1]) {
1713+
/*
1714+
* multiple patterns; in theory this could still work as long
1715+
* as the patterns are disjoint. We'd just make multiple calls
1716+
* to for_each_ref(). But if they're not disjoint, we'd end up
1717+
* reporting the same ref multiple times. So let's punt on that
1718+
* for now.
1719+
*/
1720+
return for_each_fullref_in("", cb, cb_data, broken);
1721+
}
1722+
1723+
find_longest_prefix(&prefix, filter->name_patterns[0]);
1724+
1725+
ret = for_each_fullref_in(prefix.buf, cb, cb_data, broken);
1726+
strbuf_release(&prefix);
1727+
return ret;
1728+
}
1729+
16681730
/*
16691731
* Given a ref (sha1, refname), check if the ref belongs to the array
16701732
* of sha1s. If the given ref is a tag, check if the given tag points
@@ -1911,7 +1973,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
19111973
else if (filter->kind == FILTER_REFS_TAGS)
19121974
ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, broken);
19131975
else if (filter->kind & FILTER_REFS_ALL)
1914-
ret = for_each_fullref_in("", ref_filter_handler, &ref_cbdata, broken);
1976+
ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata, broken);
19151977
if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
19161978
head_ref(ref_filter_handler, &ref_cbdata);
19171979
}

0 commit comments

Comments
 (0)