In subsequent commits, we'll teach `receive-pack` and `upload-pack` to use the new skip-list feature in the packed-refs iterator by ignoring references which are mentioned via its respective hideRefs lists. However, the packed-ref skip lists cannot handle un-hiding rules (that begin with '!'), or namespace comparisons (that begin with '^'). Detect and avoid these cases by falling back to the normal enumeration without a skip list when such patterns exist. Signed-off-by: Taylor Blau <me@xxxxxxxxxxxx> --- refs/packed-backend.c | 19 +++++++++++++++++++ t/t1419-exclude-refs.sh | 10 ++++++++++ 2 files changed, 29 insertions(+) diff --git a/refs/packed-backend.c b/refs/packed-backend.c index ddfa9add14..7f09201f35 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -1013,6 +1013,25 @@ static void populate_excluded_skip_list(struct packed_ref_iterator *iter, if (!excluded_patterns) return; + for (pattern = excluded_patterns; *pattern; pattern++) { + /* + * We also can't feed any excludes from hidden refs + * config sections, since later rules may override + * previous ones. For example, with rules "refs/foo" and + * "!refs/foo/bar", we should show "refs/foo/bar" (and + * everything underneath it), but the earlier exclusion + * would cause us to skip all of "refs/foo". We likewise + * don't implement the namespace stripping required for + * '^' rules. + * + * Both are possible to do, but complicated, so avoid + * populating the skip list at all if we see either of + * these patterns. + */ + if (**pattern == '!' || **pattern == '^') + return; + } + for (pattern = excluded_patterns; *pattern; pattern++) { struct skip_list_entry *e; diff --git a/t/t1419-exclude-refs.sh b/t/t1419-exclude-refs.sh index 051b5a54ce..026e4414cd 100755 --- a/t/t1419-exclude-refs.sh +++ b/t/t1419-exclude-refs.sh @@ -118,4 +118,14 @@ test_expect_success 'for_each_ref__exclude(refs/heads/ba*)' ' assert_no_skips ' +test_expect_success 'for_each_ref__exclude(refs/heads/foo, !refs/heads/foo/1)' ' + # discards complex hidden ref rules + for_each_ref__exclude refs/heads refs/heads/foo "!refs/heads/foo/1" \ + >actual 2>perf && + for_each_ref >expect && + + test_cmp expect actual && + assert_no_skips +' + test_done -- 2.40.1.477.g956c797dfc