From 86bdbfbf45a0c13aca146955a3325207ebd66c75 Mon Sep 17 00:00:00 2001 From: Arran Walker Date: Tue, 23 Apr 2019 10:50:46 +0000 Subject: plumbing: format/gitattributes support Implements gitattributes parsing, matching and attribute extraction. Signed-off-by: Arran Walker --- plumbing/format/gitattributes/pattern.go | 101 +++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 plumbing/format/gitattributes/pattern.go (limited to 'plumbing/format/gitattributes/pattern.go') diff --git a/plumbing/format/gitattributes/pattern.go b/plumbing/format/gitattributes/pattern.go new file mode 100644 index 0000000..c5ca0c7 --- /dev/null +++ b/plumbing/format/gitattributes/pattern.go @@ -0,0 +1,101 @@ +package gitattributes + +import ( + "path/filepath" + "strings" +) + +const ( + patternDirSep = "/" + zeroToManyDirs = "**" +) + +// Pattern defines a gitattributes pattern. +type Pattern interface { + // Match matches the given path to the pattern. + Match(path []string) bool +} + +type pattern struct { + domain []string + pattern []string +} + +// ParsePattern parses a gitattributes pattern string into the Pattern +// structure. +func ParsePattern(p string, domain []string) Pattern { + return &pattern{ + domain: domain, + pattern: strings.Split(p, patternDirSep), + } +} + +func (p *pattern) Match(path []string) bool { + if len(path) <= len(p.domain) { + return false + } + for i, e := range p.domain { + if path[i] != e { + return false + } + } + + if len(p.pattern) == 1 { + // for a simple rule, .gitattribute matching rules differs from + // .gitignore and only the last part of the path is considered. + path = path[len(path)-1:] + } else { + path = path[len(p.domain):] + } + + pattern := p.pattern + var match, doublestar bool + var err error + for _, part := range path { + // skip empty + if pattern[0] == "" { + pattern = pattern[1:] + } + + // eat doublestar + if pattern[0] == zeroToManyDirs { + pattern = pattern[1:] + if len(pattern) == 0 { + return true + } + doublestar = true + } + + switch true { + case strings.Contains(pattern[0], "**"): + return false + + // keep going down the path until we hit a match + case doublestar: + match, err = filepath.Match(pattern[0], part) + if err != nil { + return false + } + + if match { + doublestar = false + pattern = pattern[1:] + } + + default: + match, err = filepath.Match(pattern[0], part) + if err != nil { + return false + } + if !match { + return false + } + pattern = pattern[1:] + } + } + + if len(pattern) > 0 { + return false + } + return match +} -- cgit