Make **/ in an exclude pattern match zero directories (closes #338)
check / check (push) Waiting to run

The glob library needs a slash on both sides of **, so the anchored
pattern /**/x missed an x directly under the snapshot path, and
a/**/*.log missed a/x.log. As in gitignore, a leading **/ now makes any
pattern, anchored or not, match at every depth, and a /**/ inside a
pattern also matches a single slash.

Judgement call: the test for a/**/b uses a/**/*.log, because with a
plain name after the ** the glob library already matched a/b.

Model: opus-5-5
This commit is contained in:
2026-10-13 15:05:02 +00:00
parent e48bafccef
commit 910db05ef1
2 changed files with 60 additions and 4 deletions
+11 -4
View File
@@ -2023,13 +2023,20 @@ func compileExcludePatterns(patterns []string) []compiledPattern {
pattern = strings.TrimSuffix(pattern, "/")
// A leading "**/" demands a "/" before the name and so misses an
// entry directly under the source path. An unanchored pattern
// already matches at every depth, so drop the prefix: as in
// gitignore, "**/x" then also matches a top-level "x".
if !anchored {
// entry directly under the source path. It means "at every depth",
// which is what an unanchored pattern already does, so drop the
// prefix and match unanchored: as in gitignore, "**/x" and
// "/**/x" then also match a top-level "x".
if strings.HasPrefix(pattern, "**/") {
pattern = strings.TrimPrefix(pattern, "**/")
anchored = false
}
// Likewise "/**/" inside a pattern demands two slashes, so
// "a/**/b" misses "a/b". As in gitignore it stands for zero or
// more directories, so let it match a single "/" too.
pattern = strings.ReplaceAll(pattern, "/**/", "{/,/**/}")
// Compile the glob pattern
// For patterns without path separators, we need to match them as components
// e.g., ".git" should match ".git" anywhere in the path