|
|
|
@ -55,18 +55,41 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
|
|
|
|
includePatterns = dedupePaths(includePatterns)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
patternChars := "*[]?^"
|
|
|
|
|
if os.PathSeparator != '\\' {
|
|
|
|
|
patternChars += `\`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onlyPrefixIncludes := true
|
|
|
|
|
if len(includePatterns) != 0 {
|
|
|
|
|
includeMatcher, err = fileutils.NewPatternMatcher(includePatterns)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrapf(err, "invalid includepatterns: %s", opt.IncludePatterns)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, p := range includeMatcher.Patterns() {
|
|
|
|
|
if !p.Exclusion() && strings.ContainsAny(patternWithoutTrailingGlob(p), patternChars) {
|
|
|
|
|
onlyPrefixIncludes = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onlyPrefixExcludeExceptions := true
|
|
|
|
|
if opt != nil && opt.ExcludePatterns != nil {
|
|
|
|
|
excludeMatcher, err = fileutils.NewPatternMatcher(opt.ExcludePatterns)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrapf(err, "invalid excludepatterns: %s", opt.ExcludePatterns)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, p := range excludeMatcher.Patterns() {
|
|
|
|
|
if p.Exclusion() && strings.ContainsAny(patternWithoutTrailingGlob(p), patternChars) {
|
|
|
|
|
onlyPrefixExcludeExceptions = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type visitedDir struct {
|
|
|
|
@ -83,15 +106,12 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
|
|
|
|
var parentDirs []visitedDir
|
|
|
|
|
|
|
|
|
|
seenFiles := make(map[uint64]string)
|
|
|
|
|
return filepath.Walk(root, func(path string, fi os.FileInfo, err error) (retErr error) {
|
|
|
|
|
return filepath.Walk(root, func(path string, fi os.FileInfo, walkErr error) (retErr error) {
|
|
|
|
|
defer func() {
|
|
|
|
|
if retErr != nil && isNotExist(retErr) {
|
|
|
|
|
retErr = filepath.SkipDir
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
origpath := path
|
|
|
|
|
path, err = filepath.Rel(root, path)
|
|
|
|
@ -141,6 +161,22 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !m {
|
|
|
|
|
if fi.IsDir() && onlyPrefixIncludes {
|
|
|
|
|
// Optimization: we can skip walking this dir if no include
|
|
|
|
|
// patterns could match anything inside it.
|
|
|
|
|
dirSlash := path + string(filepath.Separator)
|
|
|
|
|
for _, pat := range includeMatcher.Patterns() {
|
|
|
|
|
if pat.Exclusion() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
patStr := patternWithoutTrailingGlob(pat) + string(filepath.Separator)
|
|
|
|
|
if strings.HasPrefix(patStr, dirSlash) {
|
|
|
|
|
goto passedIncludeFilter
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return filepath.SkipDir
|
|
|
|
|
}
|
|
|
|
|
passedIncludeFilter:
|
|
|
|
|
skip = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -160,13 +196,38 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if m {
|
|
|
|
|
if fi.IsDir() && !excludeMatcher.Exclusions() {
|
|
|
|
|
if fi.IsDir() && onlyPrefixExcludeExceptions {
|
|
|
|
|
// Optimization: we can skip walking this dir if no
|
|
|
|
|
// exceptions to exclude patterns could match anything
|
|
|
|
|
// inside it.
|
|
|
|
|
if !excludeMatcher.Exclusions() {
|
|
|
|
|
return filepath.SkipDir
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dirSlash := path + string(filepath.Separator)
|
|
|
|
|
for _, pat := range excludeMatcher.Patterns() {
|
|
|
|
|
if !pat.Exclusion() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
patStr := patternWithoutTrailingGlob(pat) + string(filepath.Separator)
|
|
|
|
|
if strings.HasPrefix(patStr, dirSlash) {
|
|
|
|
|
goto passedExcludeFilter
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return filepath.SkipDir
|
|
|
|
|
}
|
|
|
|
|
passedExcludeFilter:
|
|
|
|
|
skip = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if walkErr != nil {
|
|
|
|
|
if skip && errors.Is(walkErr, os.ErrPermission) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return walkErr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if includeMatcher != nil || excludeMatcher != nil {
|
|
|
|
|
defer func() {
|
|
|
|
|
if fi.IsDir() {
|
|
|
|
@ -228,6 +289,16 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func patternWithoutTrailingGlob(p *fileutils.Pattern) string {
|
|
|
|
|
patStr := p.String()
|
|
|
|
|
// We use filepath.Separator here because fileutils.Pattern patterns
|
|
|
|
|
// get transformed to use the native path separator:
|
|
|
|
|
// https://github.com/moby/moby/blob/79651b7a979b40e26af353ad283ca7ea5d67a855/pkg/fileutils/fileutils.go#L54
|
|
|
|
|
patStr = strings.TrimSuffix(patStr, string(filepath.Separator)+"**")
|
|
|
|
|
patStr = strings.TrimSuffix(patStr, string(filepath.Separator)+"*")
|
|
|
|
|
return patStr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type StatInfo struct {
|
|
|
|
|
*types.Stat
|
|
|
|
|
}
|
|
|
|
|