Skip to content

Commit b4b8c35

Browse files
committed
Merge branch 'tb/ref-filter-multiple-patterns'
"git for-each-ref" with multiple patterns have been optimized. * tb/ref-filter-multiple-patterns: ref-filter.c: find disjoint pattern prefixes
2 parents bd48ccf + b31e268 commit b4b8c35

File tree

2 files changed

+89
-26
lines changed

2 files changed

+89
-26
lines changed

ref-filter.c

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "commit-reach.h"
2323
#include "worktree.h"
2424
#include "hashmap.h"
25+
#include "argv-array.h"
2526

2627
static struct ref_msg {
2728
const char *gone;
@@ -1863,21 +1864,62 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
18631864
return match_pattern(filter, refname);
18641865
}
18651866

1866-
/*
1867-
* Find the longest prefix of pattern we can pass to
1868-
* `for_each_fullref_in()`, namely the part of pattern preceding the
1869-
* first glob character. (Note that `for_each_fullref_in()` is
1870-
* perfectly happy working with a prefix that doesn't end at a
1871-
* pathname component boundary.)
1872-
*/
1873-
static void find_longest_prefix(struct strbuf *out, const char *pattern)
1867+
static int qsort_strcmp(const void *va, const void *vb)
1868+
{
1869+
const char *a = *(const char **)va;
1870+
const char *b = *(const char **)vb;
1871+
1872+
return strcmp(a, b);
1873+
}
1874+
1875+
static void find_longest_prefixes_1(struct string_list *out,
1876+
struct strbuf *prefix,
1877+
const char **patterns, size_t nr)
18741878
{
1875-
const char *p;
1879+
size_t i;
1880+
1881+
for (i = 0; i < nr; i++) {
1882+
char c = patterns[i][prefix->len];
1883+
if (!c || is_glob_special(c)) {
1884+
string_list_append(out, prefix->buf);
1885+
return;
1886+
}
1887+
}
1888+
1889+
i = 0;
1890+
while (i < nr) {
1891+
size_t end;
1892+
1893+
/*
1894+
* Set "end" to the index of the element _after_ the last one
1895+
* in our group.
1896+
*/
1897+
for (end = i + 1; end < nr; end++) {
1898+
if (patterns[i][prefix->len] != patterns[end][prefix->len])
1899+
break;
1900+
}
18761901

1877-
for (p = pattern; *p && !is_glob_special(*p); p++)
1878-
;
1902+
strbuf_addch(prefix, patterns[i][prefix->len]);
1903+
find_longest_prefixes_1(out, prefix, patterns + i, end - i);
1904+
strbuf_setlen(prefix, prefix->len - 1);
18791905

1880-
strbuf_add(out, pattern, p - pattern);
1906+
i = end;
1907+
}
1908+
}
1909+
1910+
static void find_longest_prefixes(struct string_list *out,
1911+
const char **patterns)
1912+
{
1913+
struct argv_array sorted = ARGV_ARRAY_INIT;
1914+
struct strbuf prefix = STRBUF_INIT;
1915+
1916+
argv_array_pushv(&sorted, patterns);
1917+
QSORT(sorted.argv, sorted.argc, qsort_strcmp);
1918+
1919+
find_longest_prefixes_1(out, &prefix, sorted.argv, sorted.argc);
1920+
1921+
argv_array_clear(&sorted);
1922+
strbuf_release(&prefix);
18811923
}
18821924

18831925
/*
@@ -1890,7 +1932,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
18901932
void *cb_data,
18911933
int broken)
18921934
{
1893-
struct strbuf prefix = STRBUF_INIT;
1935+
struct string_list prefixes = STRING_LIST_INIT_DUP;
1936+
struct string_list_item *prefix;
18941937
int ret;
18951938

18961939
if (!filter->match_as_path) {
@@ -1916,21 +1959,15 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
19161959
return for_each_fullref_in("", cb, cb_data, broken);
19171960
}
19181961

1919-
if (filter->name_patterns[1]) {
1920-
/*
1921-
* multiple patterns; in theory this could still work as long
1922-
* as the patterns are disjoint. We'd just make multiple calls
1923-
* to for_each_ref(). But if they're not disjoint, we'd end up
1924-
* reporting the same ref multiple times. So let's punt on that
1925-
* for now.
1926-
*/
1927-
return for_each_fullref_in("", cb, cb_data, broken);
1928-
}
1962+
find_longest_prefixes(&prefixes, filter->name_patterns);
19291963

1930-
find_longest_prefix(&prefix, filter->name_patterns[0]);
1964+
for_each_string_list_item(prefix, &prefixes) {
1965+
ret = for_each_fullref_in(prefix->string, cb, cb_data, broken);
1966+
if (ret)
1967+
break;
1968+
}
19311969

1932-
ret = for_each_fullref_in(prefix.buf, cb, cb_data, broken);
1933-
strbuf_release(&prefix);
1970+
string_list_clear(&prefixes, 0);
19341971
return ret;
19351972
}
19361973

t/t6300-for-each-ref.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,32 @@ test_expect_success 'Verify descending sort' '
345345
test_cmp expected actual
346346
'
347347

348+
cat >expected <<\EOF
349+
refs/tags/testtag
350+
refs/tags/testtag-2
351+
EOF
352+
353+
test_expect_success 'exercise patterns with prefixes' '
354+
git tag testtag-2 &&
355+
test_when_finished "git tag -d testtag-2" &&
356+
git for-each-ref --format="%(refname)" \
357+
refs/tags/testtag refs/tags/testtag-2 >actual &&
358+
test_cmp expected actual
359+
'
360+
361+
cat >expected <<\EOF
362+
refs/tags/testtag
363+
refs/tags/testtag-2
364+
EOF
365+
366+
test_expect_success 'exercise glob patterns with prefixes' '
367+
git tag testtag-2 &&
368+
test_when_finished "git tag -d testtag-2" &&
369+
git for-each-ref --format="%(refname)" \
370+
refs/tags/testtag "refs/tags/testtag-*" >actual &&
371+
test_cmp expected actual
372+
'
373+
348374
cat >expected <<\EOF
349375
'refs/heads/master'
350376
'refs/remotes/origin/master'

0 commit comments

Comments
 (0)