git-lfs/filepathfilter/filepathfilter.go

176 lines
3.9 KiB
Go
Raw Normal View History

2016-11-21 18:38:39 +00:00
package filepathfilter
import (
"path/filepath"
"strings"
"github.com/git-lfs/wildmatch"
"github.com/rubyist/tracerx"
)
2016-11-21 23:44:41 +00:00
type Pattern interface {
Match(filename string) bool
// String returns a string representation (see: regular expressions) of
// the underlying pattern used to match filenames against this Pattern.
String() string
}
2016-11-21 18:38:39 +00:00
type Filter struct {
2016-11-21 23:44:41 +00:00
include []Pattern
exclude []Pattern
}
func NewFromPatterns(include, exclude []Pattern) *Filter {
return &Filter{include: include, exclude: exclude}
2016-11-21 18:38:39 +00:00
}
func New(include, exclude []string) *Filter {
return NewFromPatterns(
convertToWildmatch(include),
convertToWildmatch(exclude))
2016-11-21 18:38:39 +00:00
}
// Include returns the result of calling String() on each Pattern in the
// include set of this *Filter.
func (f *Filter) Include() []string { return wildmatchToString(f.include...) }
// Exclude returns the result of calling String() on each Pattern in the
// exclude set of this *Filter.
func (f *Filter) Exclude() []string { return wildmatchToString(f.exclude...) }
// wildmatchToString maps the given set of Pattern's to a string slice by
// calling String() on each pattern.
func wildmatchToString(ps ...Pattern) []string {
s := make([]string, 0, len(ps))
for _, p := range ps {
s = append(s, p.String())
}
return s
}
func (f *Filter) Allows(filename string) bool {
if f == nil {
return true
}
var included bool
for _, inc := range f.include {
if included = inc.Match(filename); included {
break
}
}
if !included && len(f.include) > 0 {
tracerx.Printf("filepathfilter: rejecting %q via %v", filename, f.include)
return false
}
for _, ex := range f.exclude {
if ex.Match(filename) {
tracerx.Printf("filepathfilter: rejecting %q via %q", filename, ex.String())
return false
}
}
tracerx.Printf("filepathfilter: accepting %q", filename)
return true
}
type wm struct {
w *wildmatch.Wildmatch
2018-02-08 02:36:12 +00:00
p string
dirs bool
}
func (w *wm) Match(filename string) bool {
return w.w.Match(w.chomp(filename))
}
func (w *wm) chomp(filename string) string {
return strings.TrimSuffix(filename, string(filepath.Separator))
}
func (w *wm) String() string {
2018-02-08 02:36:12 +00:00
return w.p
}
const (
sep byte = '/'
)
func NewPattern(p string) Pattern {
pp := p
// Special case: the below patterns match anything according to existing
// behavior.
2018-02-08 02:36:12 +00:00
switch pp {
case `*`, `.`, `./`, `.\`:
pp = join("**", "*")
}
dirs := strings.Contains(pp, string(sep))
rooted := strings.HasPrefix(pp, string(sep))
2018-02-08 02:36:12 +00:00
wild := strings.Contains(pp, "*")
2018-02-08 02:36:12 +00:00
if !dirs && !wild {
// Special case: if pp is a literal string (optionally including
2018-02-27 00:16:40 +00:00
// a character class), rewrite it is a substring match.
pp = join("**", pp, "**")
} else {
2018-02-08 02:36:12 +00:00
if dirs && !rooted {
// Special case: if there are any directory separators,
2018-02-27 00:16:40 +00:00
// rewrite "pp" as a substring match.
2018-02-08 02:36:12 +00:00
if !wild {
pp = join("**", pp, "**")
2018-02-08 02:36:12 +00:00
}
} else {
2018-02-08 02:36:12 +00:00
if rooted {
// Special case: if there are not any directory
2018-02-27 00:16:40 +00:00
// separators, rewrite "pp" as a substring
// match.
pp = join(pp, "**")
2018-02-08 02:36:12 +00:00
} else {
// Special case: if there are not any directory
2018-02-27 00:16:40 +00:00
// separators, rewrite "pp" as a substring
// match.
pp = join("**", pp)
2018-02-08 02:36:12 +00:00
}
}
}
tracerx.Printf("filepathfilter: rewrite %q as %q", p, pp)
return &wm{
2018-02-08 02:36:12 +00:00
p: p,
w: wildmatch.NewWildmatch(
pp,
wildmatch.SystemCase,
),
dirs: dirs,
}
}
// join joins path elements together via the separator "sep" and produces valid
// paths without multiple separators (unless multiple separators were included
// in the original paths []string).
func join(paths ...string) string {
var joined string
for i, path := range paths {
joined = joined + path
if i != len(paths)-1 && !strings.HasSuffix(path, string(sep)) {
joined = joined + string(sep)
}
}
return joined
}
func convertToWildmatch(rawpatterns []string) []Pattern {
patterns := make([]Pattern, len(rawpatterns))
for i, raw := range rawpatterns {
patterns[i] = NewPattern(raw)
}
return patterns
2016-11-21 23:34:35 +00:00
}