forked from git-lfs/git-lfs
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfilepathfilter.go
More file actions
155 lines (130 loc) · 3.89 KB
/
filepathfilter.go
File metadata and controls
155 lines (130 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package filepathfilter
import (
"fmt"
"path/filepath"
"regexp"
"strings"
)
type Pattern interface {
Match(filename string) bool
}
type Filter struct {
include []Pattern
exclude []Pattern
}
func NewFromPatterns(include, exclude []Pattern) *Filter {
return &Filter{include: include, exclude: exclude}
}
func New(include, exclude []string) *Filter {
return NewFromPatterns(convertToPatterns(include), convertToPatterns(exclude))
}
func (f *Filter) Allows(filename string) bool {
if f == nil {
return true
}
if len(f.include)+len(f.exclude) == 0 {
return true
}
cleanedName := filepath.Clean(filename)
if len(f.include) > 0 {
matched := false
for _, inc := range f.include {
matched = inc.Match(cleanedName)
if matched {
break
}
}
if !matched {
return false
}
}
if len(f.exclude) > 0 {
for _, ex := range f.exclude {
if ex.Match(cleanedName) {
return false
}
}
}
return true
}
func NewPattern(rawpattern string) Pattern {
cleanpattern := filepath.Clean(rawpattern)
// Special case local dir, matches all (inc subpaths)
if _, local := localDirSet[cleanpattern]; local {
return noOpMatcher{}
}
hasPathSep := strings.Contains(cleanpattern, string(filepath.Separator))
// special case * when there are no path separators
// filepath.Match never allows * to match a path separator, which is correct
// for gitignore IF the pattern includes a path separator, but not otherwise
// So *.txt should match in any subdir, as should test*, but sub/*.txt would
// only match directly in the sub dir
// Don't need to test cross-platform separators as both cleaned above
if !hasPathSep && strings.Contains(cleanpattern, "*") {
pattern := regexp.QuoteMeta(cleanpattern)
regpattern := fmt.Sprintf("^%s$", strings.Replace(pattern, "\\*", ".*", -1))
return &pathlessWildcardPattern{
rawPattern: cleanpattern,
wildcardRE: regexp.MustCompile(regpattern),
}
// Also support ** with path separators
} else if hasPathSep && strings.Contains(cleanpattern, "**") {
pattern := regexp.QuoteMeta(cleanpattern)
regpattern := fmt.Sprintf("^%s$", strings.Replace(pattern, "\\*\\*", ".*", -1))
return &doubleWildcardPattern{
rawPattern: cleanpattern,
wildcardRE: regexp.MustCompile(regpattern),
}
} else {
return &basicPattern{rawPattern: cleanpattern}
}
}
func convertToPatterns(rawpatterns []string) []Pattern {
patterns := make([]Pattern, len(rawpatterns))
for i, raw := range rawpatterns {
patterns[i] = NewPattern(raw)
}
return patterns
}
type basicPattern struct {
rawPattern string
}
// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *basicPattern) Match(name string) bool {
matched, _ := filepath.Match(p.rawPattern, name)
// Also support matching a parent directory without a wildcard
return matched || strings.HasPrefix(name, p.rawPattern+string(filepath.Separator))
}
type pathlessWildcardPattern struct {
rawPattern string
wildcardRE *regexp.Regexp
}
// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *pathlessWildcardPattern) Match(name string) bool {
matched, _ := filepath.Match(p.rawPattern, name)
// Match the whole of the base name but allow matching in folders if no path
return matched || p.wildcardRE.MatchString(filepath.Base(name))
}
type doubleWildcardPattern struct {
rawPattern string
wildcardRE *regexp.Regexp
}
// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *doubleWildcardPattern) Match(name string) bool {
matched, _ := filepath.Match(p.rawPattern, name)
// Match the whole of the base name but allow matching in folders if no path
return matched || p.wildcardRE.MatchString(name)
}
type noOpMatcher struct {
}
func (n noOpMatcher) Match(name string) bool {
return true
}
var localDirSet = map[string]struct{}{
".": struct{}{},
"./": struct{}{},
".\\": struct{}{},
}