├── .github └── workflows │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── ignore.go ├── ignore_ported_test.go ├── ignore_test.go └── version_gen.go /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | name: run tests 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/cache@v2 15 | with: 16 | path: ~/go/pkg/mod 17 | key: x${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 18 | restore-keys: | 19 | ${{ runner.os }}-go- 20 | - name: Install Go 21 | uses: actions/setup-go@v2 22 | with: 23 | go-version: 1.16.x 24 | - name: Checkout code 25 | uses: actions/checkout@v2 26 | - name: Run linters 27 | uses: golangci/golangci-lint-action@v2 28 | with: 29 | version: v1.29 30 | 31 | test: 32 | needs: lint 33 | strategy: 34 | matrix: 35 | go-version: [1.16.x] 36 | platform: [ubuntu-latest, macos-latest, windows-latest] 37 | runs-on: ${{ matrix.platform }} 38 | steps: 39 | - uses: actions/cache@v2 40 | with: 41 | path: ~/go/pkg/mod 42 | key: x${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 43 | restore-keys: | 44 | ${{ runner.os }}-go- 45 | - name: Install Go 46 | if: success() 47 | uses: actions/setup-go@v2 48 | with: 49 | go-version: ${{ matrix.go-version }} 50 | - name: Checkout code 51 | uses: actions/checkout@v2 52 | - name: Run tests 53 | run: go test -v -covermode=count 54 | 55 | coverage: 56 | needs: lint 57 | runs-on: ubuntu-latest 58 | steps: 59 | - uses: actions/cache@v2 60 | with: 61 | path: ~/go/pkg/mod 62 | key: x${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 63 | restore-keys: | 64 | ${{ runner.os }}-go- 65 | - name: Install Go 66 | if: success() 67 | uses: actions/setup-go@v2 68 | with: 69 | go-version: 1.16.x 70 | - name: Checkout code 71 | uses: actions/checkout@v2 72 | - name: Calc coverage 73 | run: | 74 | go test -v -covermode=count -coverprofile=coverage.out 75 | - name: Convert coverage.out to coverage.lcov 76 | uses: jandelgado/gcov2lcov-action@v1.0.6 77 | - name: Coveralls 78 | uses: coverallsapp/github-action@v1.1.2 79 | with: 80 | github-token: ${{ secrets.github_token }} 81 | path-to-lcov: coverage.lcov 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Package test fixtures 2 | test_fixtures 3 | 4 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 5 | *.o 6 | *.a 7 | *.so 8 | 9 | # Folders 10 | _obj 11 | _test 12 | 13 | # Architecture specific extensions/prefixes 14 | *.[568vq] 15 | [568vq].out 16 | 17 | *.cgo1.go 18 | *.cgo2.c 19 | _cgo_defun.c 20 | _cgo_gotypes.go 21 | _cgo_export.* 22 | 23 | _testmain.go 24 | 25 | *.exe 26 | *.test 27 | *.prof 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Shaba Abhiram 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-gitignore 2 | 3 | [![Build Status](https://travis-ci.org/sabhiram/go-gitignore.svg)](https://travis-ci.org/sabhiram/go-gitignore) [![Coverage Status](https://coveralls.io/repos/github/sabhiram/go-gitignore/badge.svg?branch=master)](https://coveralls.io/github/sabhiram/go-gitignore?branch=master) 4 | 5 | A gitignore parser for `Go` 6 | 7 | ## Install 8 | 9 | ```shell 10 | go get github.com/sabhiram/go-gitignore 11 | ``` 12 | 13 | ## Usage 14 | 15 | For a quick sample of how to use this library, check out the tests under `ignore_test.go`. 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sabhiram/go-gitignore 2 | 3 | go 1.13 4 | 5 | require github.com/stretchr/testify v1.6.1 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 6 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 7 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 10 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /ignore.go: -------------------------------------------------------------------------------- 1 | /* 2 | ignore is a library which returns a new ignorer object which can 3 | test against various paths. This is particularly useful when trying 4 | to filter files based on a .gitignore document 5 | 6 | The rules for parsing the input file are the same as the ones listed 7 | in the Git docs here: http://git-scm.com/docs/gitignore 8 | 9 | The summarized version of the same has been copied here: 10 | 11 | 1. A blank line matches no files, so it can serve as a separator 12 | for readability. 13 | 2. A line starting with # serves as a comment. Put a backslash ("\") 14 | in front of the first hash for patterns that begin with a hash. 15 | 3. Trailing spaces are ignored unless they are quoted with backslash ("\"). 16 | 4. An optional prefix "!" which negates the pattern; any matching file 17 | excluded by a previous pattern will become included again. It is not 18 | possible to re-include a file if a parent directory of that file is 19 | excluded. Git doesn’t list excluded directories for performance reasons, 20 | so any patterns on contained files have no effect, no matter where they 21 | are defined. Put a backslash ("\") in front of the first "!" for 22 | patterns that begin with a literal "!", for example, "\!important!.txt". 23 | 5. If the pattern ends with a slash, it is removed for the purpose of the 24 | following description, but it would only find a match with a directory. 25 | In other words, foo/ will match a directory foo and paths underneath it, 26 | but will not match a regular file or a symbolic link foo (this is 27 | consistent with the way how pathspec works in general in Git). 28 | 6. If the pattern does not contain a slash /, Git treats it as a shell glob 29 | pattern and checks for a match against the pathname relative to the 30 | location of the .gitignore file (relative to the toplevel of the work 31 | tree if not from a .gitignore file). 32 | 7. Otherwise, Git treats the pattern as a shell glob suitable for 33 | consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the 34 | pattern will not match a / in the pathname. For example, 35 | "Documentation/*.html" matches "Documentation/git.html" but not 36 | "Documentation/ppc/ppc.html" or "tools/perf/Documentation/perf.html". 37 | 8. A leading slash matches the beginning of the pathname. For example, 38 | "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". 39 | 9. Two consecutive asterisks ("**") in patterns matched against full 40 | pathname may have special meaning: 41 | i. A leading "**" followed by a slash means match in all directories. 42 | For example, "** /foo" matches file or directory "foo" anywhere, 43 | the same as pattern "foo". "** /foo/bar" matches file or directory 44 | "bar" anywhere that is directly under directory "foo". 45 | ii. A trailing "/**" matches everything inside. For example, "abc/**" 46 | matches all files inside directory "abc", relative to the location 47 | of the .gitignore file, with infinite depth. 48 | iii. A slash followed by two consecutive asterisks then a slash matches 49 | zero or more directories. For example, "a/** /b" matches "a/b", 50 | "a/x/b", "a/x/y/b" and so on. 51 | iv. Other consecutive asterisks are considered invalid. */ 52 | package ignore 53 | 54 | import ( 55 | "io/ioutil" 56 | "os" 57 | "regexp" 58 | "strings" 59 | ) 60 | 61 | //////////////////////////////////////////////////////////// 62 | 63 | // IgnoreParser is an interface with `MatchesPaths`. 64 | type IgnoreParser interface { 65 | MatchesPath(f string) bool 66 | MatchesPathHow(f string) (bool, *IgnorePattern) 67 | } 68 | 69 | //////////////////////////////////////////////////////////// 70 | 71 | // This function pretty much attempts to mimic the parsing rules 72 | // listed above at the start of this file 73 | func getPatternFromLine(line string) (*regexp.Regexp, bool) { 74 | // Trim OS-specific carriage returns. 75 | line = strings.TrimRight(line, "\r") 76 | 77 | // Strip comments [Rule 2] 78 | if strings.HasPrefix(line, `#`) { 79 | return nil, false 80 | } 81 | 82 | // Trim string [Rule 3] 83 | // TODO: Handle [Rule 3], when the " " is escaped with a \ 84 | line = strings.Trim(line, " ") 85 | 86 | // Exit for no-ops and return nil which will prevent us from 87 | // appending a pattern against this line 88 | if line == "" { 89 | return nil, false 90 | } 91 | 92 | // TODO: Handle [Rule 4] which negates the match for patterns leading with "!" 93 | negatePattern := false 94 | if line[0] == '!' { 95 | negatePattern = true 96 | line = line[1:] 97 | } 98 | 99 | // Handle [Rule 2, 4], when # or ! is escaped with a \ 100 | // Handle [Rule 4] once we tag negatePattern, strip the leading ! char 101 | if regexp.MustCompile(`^(\#|\!)`).MatchString(line) { 102 | line = line[1:] 103 | } 104 | 105 | // If we encounter a foo/*.blah in a folder, prepend the / char 106 | if regexp.MustCompile(`([^\/+])/.*\*\.`).MatchString(line) && line[0] != '/' { 107 | line = "/" + line 108 | } 109 | 110 | // Handle escaping the "." char 111 | line = regexp.MustCompile(`\.`).ReplaceAllString(line, `\.`) 112 | 113 | magicStar := "#$~" 114 | 115 | // Handle "/**/" usage 116 | if strings.HasPrefix(line, "/**/") { 117 | line = line[1:] 118 | } 119 | line = regexp.MustCompile(`/\*\*/`).ReplaceAllString(line, `(/|/.+/)`) 120 | line = regexp.MustCompile(`\*\*/`).ReplaceAllString(line, `(|.`+magicStar+`/)`) 121 | line = regexp.MustCompile(`/\*\*`).ReplaceAllString(line, `(|/.`+magicStar+`)`) 122 | 123 | // Handle escaping the "*" char 124 | line = regexp.MustCompile(`\\\*`).ReplaceAllString(line, `\`+magicStar) 125 | line = regexp.MustCompile(`\*`).ReplaceAllString(line, `([^/]*)`) 126 | 127 | // Handle escaping the "?" char 128 | line = strings.Replace(line, "?", `\?`, -1) 129 | 130 | line = strings.Replace(line, magicStar, "*", -1) 131 | 132 | // Temporary regex 133 | var expr = "" 134 | if strings.HasSuffix(line, "/") { 135 | expr = line + "(|.*)$" 136 | } else { 137 | expr = line + "(|/.*)$" 138 | } 139 | if strings.HasPrefix(expr, "/") { 140 | expr = "^(|/)" + expr[1:] 141 | } else { 142 | expr = "^(|.*/)" + expr 143 | } 144 | pattern, _ := regexp.Compile(expr) 145 | 146 | return pattern, negatePattern 147 | } 148 | 149 | //////////////////////////////////////////////////////////// 150 | 151 | // IgnorePattern encapsulates a pattern and if it is a negated pattern. 152 | type IgnorePattern struct { 153 | Pattern *regexp.Regexp 154 | Negate bool 155 | LineNo int 156 | Line string 157 | } 158 | 159 | // GitIgnore wraps a list of ignore pattern. 160 | type GitIgnore struct { 161 | patterns []*IgnorePattern 162 | } 163 | 164 | // CompileIgnoreLines accepts a variadic set of strings, and returns a GitIgnore 165 | // instance which converts and appends the lines in the input to regexp.Regexp 166 | // patterns held within the GitIgnore objects "patterns" field. 167 | func CompileIgnoreLines(lines ...string) *GitIgnore { 168 | gi := &GitIgnore{} 169 | for i, line := range lines { 170 | pattern, negatePattern := getPatternFromLine(line) 171 | if pattern != nil { 172 | // LineNo is 1-based numbering to match `git check-ignore -v` output 173 | ip := &IgnorePattern{pattern, negatePattern, i + 1, line} 174 | gi.patterns = append(gi.patterns, ip) 175 | } 176 | } 177 | return gi 178 | } 179 | 180 | // CompileIgnoreFile uses an ignore file as the input, parses the lines out of 181 | // the file and invokes the CompileIgnoreLines method. 182 | func CompileIgnoreFile(fpath string) (*GitIgnore, error) { 183 | bs, err := ioutil.ReadFile(fpath) 184 | if err != nil { 185 | return nil, err 186 | } 187 | 188 | s := strings.Split(string(bs), "\n") 189 | return CompileIgnoreLines(s...), nil 190 | } 191 | 192 | // CompileIgnoreFileAndLines accepts a ignore file as the input, parses the 193 | // lines out of the file and invokes the CompileIgnoreLines method with 194 | // additional lines. 195 | func CompileIgnoreFileAndLines(fpath string, lines ...string) (*GitIgnore, error) { 196 | bs, err := ioutil.ReadFile(fpath) 197 | if err != nil { 198 | return nil, err 199 | } 200 | 201 | gi := CompileIgnoreLines(append(strings.Split(string(bs), "\n"), lines...)...) 202 | return gi, nil 203 | } 204 | 205 | //////////////////////////////////////////////////////////// 206 | 207 | // MatchesPath returns true if the given GitIgnore structure would target 208 | // a given path string `f`. 209 | func (gi *GitIgnore) MatchesPath(f string) bool { 210 | matchesPath, _ := gi.MatchesPathHow(f) 211 | return matchesPath 212 | } 213 | 214 | // MatchesPathHow returns true, `pattern` if the given GitIgnore structure would target 215 | // a given path string `f`. 216 | // The IgnorePattern has the Line, LineNo fields. 217 | func (gi *GitIgnore) MatchesPathHow(f string) (bool, *IgnorePattern) { 218 | // Replace OS-specific path separator. 219 | f = strings.Replace(f, string(os.PathSeparator), "/", -1) 220 | 221 | matchesPath := false 222 | var mip *IgnorePattern 223 | for _, ip := range gi.patterns { 224 | if ip.Pattern.MatchString(f) { 225 | // If this is a regular target (not negated with a gitignore 226 | // exclude "!" etc) 227 | if !ip.Negate { 228 | matchesPath = true 229 | mip = ip 230 | } else if matchesPath { 231 | // Negated pattern, and matchesPath is already set 232 | matchesPath = false 233 | } 234 | } 235 | } 236 | return matchesPath, mip 237 | } 238 | 239 | //////////////////////////////////////////////////////////// 240 | -------------------------------------------------------------------------------- /ignore_ported_test.go: -------------------------------------------------------------------------------- 1 | // Implement tests, ported from https://github.com/kaelzhang/node-ignore.git 2 | package ignore 3 | 4 | import ( 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestSimple(test *testing.T) { 11 | lines := []string{"foo"} 12 | object := CompileIgnoreLines(lines...) 13 | 14 | shouldMatch(test, object, "foo") 15 | shouldMatch(test, object, "foo/") 16 | shouldMatch(test, object, "/foo") 17 | shouldNotMatch(test, object, "fooo") 18 | shouldNotMatch(test, object, "ofoo") 19 | } 20 | 21 | func TestAnywhere(test *testing.T) { 22 | lines := []string{"**/foo"} 23 | object := CompileIgnoreLines(lines...) 24 | 25 | shouldMatch(test, object, "foo") 26 | shouldMatch(test, object, "foo/") 27 | shouldMatch(test, object, "/foo") 28 | shouldNotMatch(test, object, "fooo") 29 | shouldNotMatch(test, object, "ofoo") 30 | } 31 | 32 | func TestAnywhereFromRoot(test *testing.T) { 33 | lines := []string{"/**/foo"} 34 | object := CompileIgnoreLines(lines...) 35 | 36 | shouldMatch(test, object, "foo") 37 | shouldMatch(test, object, "foo/") 38 | shouldMatch(test, object, "/foo") 39 | shouldNotMatch(test, object, "fooo") 40 | shouldNotMatch(test, object, "ofoo") 41 | } 42 | 43 | func TestSimpleDir(test *testing.T) { 44 | lines := []string{"foo/"} 45 | object := CompileIgnoreLines(lines...) 46 | 47 | shouldMatch(test, object, "foo/") 48 | shouldMatch(test, object, "foo/a") 49 | shouldMatch(test, object, "/foo/") 50 | shouldNotMatch(test, object, "foo") 51 | shouldNotMatch(test, object, "/foo") 52 | } 53 | 54 | func TestRootExtensionOnly(test *testing.T) { 55 | lines := []string{"/.js"} 56 | object := CompileIgnoreLines(lines...) 57 | 58 | shouldMatch(test, object, ".js") 59 | shouldMatch(test, object, ".js/") 60 | shouldMatch(test, object, ".js/a") 61 | // ??? 62 | // shouldNotMatch(test, object, "/.js") 63 | shouldNotMatch(test, object, ".jsa") 64 | } 65 | 66 | func TestRootExtension(test *testing.T) { 67 | lines := []string{"/*.js"} 68 | object := CompileIgnoreLines(lines...) 69 | 70 | shouldMatch(test, object, ".js") 71 | shouldMatch(test, object, ".js/") 72 | shouldMatch(test, object, ".js/a") 73 | shouldMatch(test, object, "a.js/a") 74 | shouldMatch(test, object, "a.js/a.js") 75 | // ??? 76 | // shouldNotMatch(test, object, "/.js") 77 | shouldNotMatch(test, object, ".jsa") 78 | } 79 | 80 | func TestExtension(test *testing.T) { 81 | lines := []string{"*.js"} 82 | object := CompileIgnoreLines(lines...) 83 | 84 | shouldMatch(test, object, ".js") 85 | shouldMatch(test, object, ".js/") 86 | shouldMatch(test, object, ".js/a") 87 | shouldMatch(test, object, "a.js/a") 88 | shouldMatch(test, object, "a.js/a.js") 89 | shouldMatch(test, object, "/.js") 90 | shouldNotMatch(test, object, ".jsa") 91 | } 92 | 93 | func TestStarExtension(test *testing.T) { 94 | lines := []string{".js*"} 95 | object := CompileIgnoreLines(lines...) 96 | 97 | shouldMatch(test, object, ".js") 98 | shouldMatch(test, object, ".js/") 99 | shouldMatch(test, object, ".js/a") 100 | shouldNotMatch(test, object, "a.js/a") 101 | shouldNotMatch(test, object, "a.js/a.js") 102 | shouldMatch(test, object, "/.js") 103 | shouldMatch(test, object, ".jsa") 104 | } 105 | 106 | func TestDoubleStar(test *testing.T) { 107 | lines := []string{"foo/**/"} 108 | object := CompileIgnoreLines(lines...) 109 | 110 | shouldMatch(test, object, "foo/") 111 | shouldMatch(test, object, "foo/abc/") 112 | shouldMatch(test, object, "foo/x/y/z/") 113 | shouldNotMatch(test, object, "foo") 114 | shouldNotMatch(test, object, "/foo") 115 | } 116 | 117 | func TestStars(test *testing.T) { 118 | lines := []string{"foo/**/*.bar"} 119 | object := CompileIgnoreLines(lines...) 120 | 121 | shouldNotMatch(test, object, "foo/") 122 | shouldNotMatch(test, object, "abc.bar") 123 | shouldMatch(test, object, "foo/abc.bar") 124 | shouldMatch(test, object, "foo/abc.bar/") 125 | shouldMatch(test, object, "foo/x/y/z.bar") 126 | shouldMatch(test, object, "foo/x/y/z.bar/") 127 | } 128 | 129 | func TestCases_Comment(test *testing.T) { 130 | lines := []string{"#abc"} 131 | object := CompileIgnoreLines(lines...) 132 | 133 | shouldNotMatch(test, object, "#abc") 134 | } 135 | 136 | func TestCases_EscapedComment(test *testing.T) { 137 | lines := []string{`\#abc`} 138 | object := CompileIgnoreLines(lines...) 139 | 140 | shouldMatch(test, object, "#abc") 141 | } 142 | 143 | func TestCases_CouldFilterPaths(test *testing.T) { 144 | lines := []string{"abc", "!abc/b"} 145 | object := CompileIgnoreLines(lines...) 146 | 147 | shouldMatch(test, object, "abc/a.js") 148 | shouldNotMatch(test, object, "abc/b/b.js") 149 | } 150 | 151 | func TestCases_IgnoreSelect(test *testing.T) { 152 | lines := []string{"abc", "!abc/b", "#e", `\#f`} 153 | object := CompileIgnoreLines(lines...) 154 | 155 | shouldMatch(test, object, "abc/a.js") 156 | shouldNotMatch(test, object, "abc/b/b.js") 157 | shouldNotMatch(test, object, "#e") 158 | shouldMatch(test, object, "#f") 159 | } 160 | 161 | func TestCases_EscapeRegexMetacharacters(test *testing.T) { 162 | lines := []string{"*.js", `!\*.js`, "!a#b.js", "!?.js", "#abc", `\#abc`} 163 | object := CompileIgnoreLines(lines...) 164 | 165 | shouldNotMatch(test, object, "*.js") 166 | shouldMatch(test, object, "abc.js") 167 | shouldNotMatch(test, object, "a#b.js") 168 | shouldNotMatch(test, object, "abc") 169 | shouldMatch(test, object, "#abc") 170 | shouldNotMatch(test, object, "?.js") 171 | } 172 | 173 | func TestCases_QuestionMark(test *testing.T) { 174 | lines := []string{"/.project", "thumbs.db", "*.swp", ".sonar/*", ".*.sw?"} 175 | object := CompileIgnoreLines(lines...) 176 | 177 | shouldMatch(test, object, ".project") 178 | shouldNotMatch(test, object, "abc/.project") 179 | shouldNotMatch(test, object, ".a.sw") 180 | shouldMatch(test, object, ".a.sw?") 181 | shouldMatch(test, object, "thumbs.db") 182 | } 183 | 184 | func TestCases_DirEndedWithStar(test *testing.T) { 185 | lines := []string{"abc/*"} 186 | object := CompileIgnoreLines(lines...) 187 | 188 | shouldNotMatch(test, object, "abc") 189 | } 190 | 191 | func TestCases_FileEndedWithStar(test *testing.T) { 192 | lines := []string{"abc.js*"} 193 | object := CompileIgnoreLines(lines...) 194 | 195 | shouldMatch(test, object, "abc.js/") 196 | shouldMatch(test, object, "abc.js/abc") 197 | shouldMatch(test, object, "abc.jsa/") 198 | shouldMatch(test, object, "abc.jsa/abc") 199 | } 200 | 201 | func TestCases_WildcardAsFilename(test *testing.T) { 202 | lines := []string{"*.b"} 203 | object := CompileIgnoreLines(lines...) 204 | 205 | shouldMatch(test, object, "b/a.b") 206 | shouldMatch(test, object, "b/.b") 207 | shouldNotMatch(test, object, "b/.ba") 208 | shouldMatch(test, object, "b/c/a.b") 209 | } 210 | 211 | func TestCases_SlashAtBeginningAndComeWithWildcard(test *testing.T) { 212 | lines := []string{"/*.c"} 213 | object := CompileIgnoreLines(lines...) 214 | 215 | shouldMatch(test, object, ".c") 216 | shouldMatch(test, object, "c.c") 217 | shouldNotMatch(test, object, "c/c.c") 218 | shouldNotMatch(test, object, "c/d") 219 | } 220 | 221 | func TestCases_DotFile(test *testing.T) { 222 | lines := []string{".d"} 223 | object := CompileIgnoreLines(lines...) 224 | 225 | shouldMatch(test, object, ".d") 226 | shouldNotMatch(test, object, ".dd") 227 | shouldNotMatch(test, object, "d.d") 228 | shouldMatch(test, object, "d/.d") 229 | shouldNotMatch(test, object, "d/d.d") 230 | shouldNotMatch(test, object, "d/e") 231 | } 232 | 233 | func TestCases_DotDir(test *testing.T) { 234 | lines := []string{".e"} 235 | object := CompileIgnoreLines(lines...) 236 | 237 | shouldMatch(test, object, ".e/") 238 | shouldNotMatch(test, object, ".ee/") 239 | shouldNotMatch(test, object, "e.e/") 240 | shouldMatch(test, object, ".e/e") 241 | shouldMatch(test, object, "e/.e") 242 | shouldNotMatch(test, object, "e/e.e") 243 | shouldNotMatch(test, object, "e/f") 244 | } 245 | 246 | func TestCases_PatternOnce(test *testing.T) { 247 | lines := []string{"node_modules/"} 248 | object := CompileIgnoreLines(lines...) 249 | 250 | shouldMatch(test, object, "node_modules/gulp/node_modules/abc.md") 251 | shouldMatch(test, object, "node_modules/gulp/node_modules/abc.json") 252 | } 253 | 254 | func TestCases_PatternTwice(test *testing.T) { 255 | lines := []string{"node_modules/", "node_modules/"} 256 | object := CompileIgnoreLines(lines...) 257 | 258 | shouldMatch(test, object, "node_modules/gulp/node_modules/abc.md") 259 | shouldMatch(test, object, "node_modules/gulp/node_modules/abc.json") 260 | } 261 | 262 | //////////////////////////////////////////////////////////// 263 | 264 | func shouldMatch(test *testing.T, object *GitIgnore, path string) { 265 | assert.Equal(test, true, object.MatchesPath(path), path+" should match") 266 | } 267 | 268 | func shouldNotMatch(test *testing.T, object *GitIgnore, path string) { 269 | assert.Equal(test, false, object.MatchesPath(path), path+" should not match") 270 | } 271 | 272 | //////////////////////////////////////////////////////////// 273 | -------------------------------------------------------------------------------- /ignore_test.go: -------------------------------------------------------------------------------- 1 | // Implement tests for the `ignore` library 2 | package ignore 3 | 4 | import ( 5 | "os" 6 | 7 | "io/ioutil" 8 | "path/filepath" 9 | 10 | "fmt" 11 | "testing" 12 | 13 | "runtime" 14 | 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | const ( 19 | TEST_DIR = "test_fixtures" 20 | ) 21 | 22 | //////////////////////////////////////////////////////////// 23 | 24 | // Helper function to setup a test fixture dir and write to 25 | // it a file with the name "fname" and content "content" 26 | func writeFileToTestDir(fname, content string) { 27 | testDirPath := "." + string(filepath.Separator) + TEST_DIR 28 | testFilePath := testDirPath + string(filepath.Separator) + fname 29 | _ = os.MkdirAll(testDirPath, 0755) 30 | _ = ioutil.WriteFile(testFilePath, []byte(content), os.ModePerm) 31 | } 32 | 33 | func cleanupTestDir() { 34 | _ = os.RemoveAll(fmt.Sprintf(".%s%s", string(filepath.Separator), TEST_DIR)) 35 | } 36 | 37 | //////////////////////////////////////////////////////////// 38 | 39 | // Validate "CompileIgnoreLines()" 40 | func TestCompileIgnoreLines(t *testing.T) { 41 | lines := []string{"abc/def", "a/b/c", "b"} 42 | object := CompileIgnoreLines(lines...) 43 | 44 | // MatchesPath 45 | // Paths which are targeted by the above "lines" 46 | assert.Equal(t, true, object.MatchesPath("abc/def/child"), "abc/def/child should match") 47 | assert.Equal(t, true, object.MatchesPath("a/b/c/d"), "a/b/c/d should match") 48 | 49 | // Paths which are not targeted by the above "lines" 50 | assert.Equal(t, false, object.MatchesPath("abc"), "abc should not match") 51 | assert.Equal(t, false, object.MatchesPath("def"), "def should not match") 52 | assert.Equal(t, false, object.MatchesPath("bd"), "bd should not match") 53 | 54 | object = CompileIgnoreLines("abc/def", "a/b/c", "b") 55 | 56 | // Paths which are targeted by the above "lines" 57 | assert.Equal(t, true, object.MatchesPath("abc/def/child"), "abc/def/child should match") 58 | assert.Equal(t, true, object.MatchesPath("a/b/c/d"), "a/b/c/d should match") 59 | 60 | // Paths which are not targeted by the above "lines" 61 | assert.Equal(t, false, object.MatchesPath("abc"), "abc should not match") 62 | assert.Equal(t, false, object.MatchesPath("def"), "def should not match") 63 | assert.Equal(t, false, object.MatchesPath("bd"), "bd should not match") 64 | } 65 | 66 | // Validate the invalid files 67 | func TestCompileIgnoreFile_InvalidFile(t *testing.T) { 68 | object, err := CompileIgnoreFile("./test_fixtures/invalid.file") 69 | assert.Nil(t, object, "object should be nil") 70 | assert.NotNil(t, err, "err should be unknown file / dir") 71 | } 72 | 73 | // Validate the an empty files 74 | func TestCompileIgnoreLines_EmptyFile(t *testing.T) { 75 | writeFileToTestDir("test.gitignore", ``) 76 | defer cleanupTestDir() 77 | 78 | object, err := CompileIgnoreFile("./test_fixtures/test.gitignore") 79 | assert.Nil(t, err, "err should be nil") 80 | assert.NotNil(t, object, "object should not be nil") 81 | 82 | assert.Equal(t, false, object.MatchesPath("a"), "should not match any path") 83 | assert.Equal(t, false, object.MatchesPath("a/b"), "should not match any path") 84 | assert.Equal(t, false, object.MatchesPath(".foobar"), "should not match any path") 85 | } 86 | 87 | // Validate the correct handling of the negation operator "!" 88 | func TestCompileIgnoreLines_HandleIncludePattern(t *testing.T) { 89 | writeFileToTestDir("test.gitignore", ` 90 | # exclude everything except directory foo/bar 91 | /* 92 | !/foo 93 | /foo/* 94 | !/foo/bar 95 | `) 96 | defer cleanupTestDir() 97 | 98 | object, err := CompileIgnoreFile("./test_fixtures/test.gitignore") 99 | assert.Nil(t, err, "err should be nil") 100 | assert.NotNil(t, object, "object should not be nil") 101 | 102 | assert.Equal(t, true, object.MatchesPath("a"), "a should match") 103 | assert.Equal(t, true, object.MatchesPath("foo/baz"), "foo/baz should match") 104 | assert.Equal(t, false, object.MatchesPath("foo"), "foo should not match") 105 | assert.Equal(t, false, object.MatchesPath("/foo/bar"), "/foo/bar should not match") 106 | } 107 | 108 | // Validate the correct handling of comments and empty lines 109 | func TestCompileIgnoreLines_HandleSpaces(t *testing.T) { 110 | writeFileToTestDir("test.gitignore", ` 111 | # 112 | # A comment 113 | 114 | # Another comment 115 | 116 | 117 | # Invalid Comment 118 | 119 | abc/def 120 | `) 121 | defer cleanupTestDir() 122 | 123 | object, err := CompileIgnoreFile("./test_fixtures/test.gitignore") 124 | assert.Nil(t, err, "err should be nil") 125 | assert.NotNil(t, object, "object should not be nil") 126 | 127 | assert.Equal(t, 2, len(object.patterns), "should have two regex pattern") 128 | assert.Equal(t, false, object.MatchesPath("abc/abc"), "/abc/abc should not match") 129 | assert.Equal(t, true, object.MatchesPath("abc/def"), "/abc/def should match") 130 | } 131 | 132 | // Validate the correct handling of leading / chars 133 | func TestCompileIgnoreLines_HandleLeadingSlash(t *testing.T) { 134 | writeFileToTestDir("test.gitignore", ` 135 | /a/b/c 136 | d/e/f 137 | /g 138 | `) 139 | defer cleanupTestDir() 140 | 141 | object, err := CompileIgnoreFile("./test_fixtures/test.gitignore") 142 | assert.Nil(t, err, "err should be nil") 143 | assert.NotNil(t, object, "object should not be nil") 144 | 145 | assert.Equal(t, 3, len(object.patterns), "should have 3 regex patterns") 146 | assert.Equal(t, true, object.MatchesPath("a/b/c"), "a/b/c should match") 147 | assert.Equal(t, true, object.MatchesPath("a/b/c/d"), "a/b/c/d should match") 148 | assert.Equal(t, true, object.MatchesPath("d/e/f"), "d/e/f should match") 149 | assert.Equal(t, true, object.MatchesPath("g"), "g should match") 150 | } 151 | 152 | // Validate the correct handling of files starting with # or ! 153 | func TestCompileIgnoreLines_HandleLeadingSpecialChars(t *testing.T) { 154 | writeFileToTestDir("test.gitignore", ` 155 | # Comment 156 | \#file.txt 157 | \!file.txt 158 | file.txt 159 | `) 160 | defer cleanupTestDir() 161 | 162 | object, err := CompileIgnoreFile("./test_fixtures/test.gitignore") 163 | assert.Nil(t, err, "err should be nil") 164 | assert.NotNil(t, object, "object should not be nil") 165 | 166 | assert.Equal(t, true, object.MatchesPath("#file.txt"), "#file.txt should match") 167 | assert.Equal(t, true, object.MatchesPath("!file.txt"), "!file.txt should match") 168 | assert.Equal(t, true, object.MatchesPath("a/!file.txt"), "a/!file.txt should match") 169 | assert.Equal(t, true, object.MatchesPath("file.txt"), "file.txt should match") 170 | assert.Equal(t, true, object.MatchesPath("a/file.txt"), "a/file.txt should match") 171 | assert.Equal(t, false, object.MatchesPath("file2.txt"), "file2.txt should not match") 172 | 173 | } 174 | 175 | // Validate the correct handling matching files only within a given folder 176 | func TestCompileIgnoreLines_HandleAllFilesInDir(t *testing.T) { 177 | writeFileToTestDir("test.gitignore", ` 178 | Documentation/*.html 179 | `) 180 | defer cleanupTestDir() 181 | 182 | object, err := CompileIgnoreFile("./test_fixtures/test.gitignore") 183 | assert.Nil(t, err, "err should be nil") 184 | assert.NotNil(t, object, "object should not be nil") 185 | 186 | assert.Equal(t, true, object.MatchesPath("Documentation/git.html"), "Documentation/git.html should match") 187 | assert.Equal(t, false, object.MatchesPath("Documentation/ppc/ppc.html"), "Documentation/ppc/ppc.html should not match") 188 | assert.Equal(t, false, object.MatchesPath("tools/perf/Documentation/perf.html"), "tools/perf/Documentation/perf.html should not match") 189 | } 190 | 191 | // Validate the correct handling of "**" 192 | func TestCompileIgnoreLines_HandleDoubleStar(t *testing.T) { 193 | writeFileToTestDir("test.gitignore", ` 194 | **/foo 195 | bar 196 | `) 197 | defer cleanupTestDir() 198 | 199 | object, err := CompileIgnoreFile("./test_fixtures/test.gitignore") 200 | assert.Nil(t, err, "err should be nil") 201 | assert.NotNil(t, object, "object should not be nil") 202 | 203 | assert.Equal(t, true, object.MatchesPath("foo"), "foo should match") 204 | assert.Equal(t, true, object.MatchesPath("baz/foo"), "baz/foo should match") 205 | assert.Equal(t, true, object.MatchesPath("bar"), "bar should match") 206 | assert.Equal(t, true, object.MatchesPath("baz/bar"), "baz/bar should match") 207 | } 208 | 209 | // Validate the correct handling of leading slash 210 | func TestCompileIgnoreLines_HandleLeadingSlashPath(t *testing.T) { 211 | writeFileToTestDir("test.gitignore", ` 212 | /*.c 213 | `) 214 | defer cleanupTestDir() 215 | 216 | object, err := CompileIgnoreFile("./test_fixtures/test.gitignore") 217 | assert.Nil(t, err, "err should be nil") 218 | assert.NotNil(t, object, "object should not be nil") 219 | 220 | assert.Equal(t, true, object.MatchesPath("hello.c"), "hello.c should match") 221 | assert.Equal(t, false, object.MatchesPath("foo/hello.c"), "foo/hello.c should not match") 222 | } 223 | 224 | func TestCompileIgnoreFileAndLines(t *testing.T) { 225 | writeFileToTestDir("test.gitignore", ` 226 | /*.c 227 | `) 228 | defer cleanupTestDir() 229 | 230 | object, err := CompileIgnoreFileAndLines("./test_fixtures/test.gitignore", "**/foo", "bar") 231 | assert.Nil(t, err, "err should be nil") 232 | assert.NotNil(t, object, "object should not be nil") 233 | 234 | assert.Equal(t, true, object.MatchesPath("hello.c"), "hello.c should match") 235 | assert.Equal(t, false, object.MatchesPath("baz/hello.c"), "baz/hello.c should not match") 236 | 237 | assert.Equal(t, true, object.MatchesPath("foo"), "foo should match") 238 | assert.Equal(t, true, object.MatchesPath("baz/foo"), "baz/foo should match") 239 | assert.Equal(t, true, object.MatchesPath("bar"), "bar should match") 240 | assert.Equal(t, true, object.MatchesPath("baz/bar"), "baz/bar should match") 241 | } 242 | 243 | func TestCompileIgnoreFileAndLines_InvalidFile(t *testing.T) { 244 | object, err := CompileIgnoreFileAndLines("./test_fixtures/invalid.file") 245 | assert.Nil(t, object, "object should be nil") 246 | assert.NotNil(t, err, "err should be unknown file / dir") 247 | } 248 | 249 | func ExampleCompileIgnoreLines() { 250 | ignoreObject := CompileIgnoreLines([]string{"node_modules", "*.out", "foo/*.c"}...) 251 | 252 | // You can test the ignoreObject against various paths using the 253 | // "MatchesPath()" interface method. This pretty much is up to 254 | // the users interpretation. In the case of a ".gitignore" file, 255 | // a "match" would indicate that a given path would be ignored. 256 | fmt.Println(ignoreObject.MatchesPath("node_modules/test/foo.js")) 257 | fmt.Println(ignoreObject.MatchesPath("node_modules2/test.out")) 258 | fmt.Println(ignoreObject.MatchesPath("test/foo.js")) 259 | 260 | // Output: 261 | // true 262 | // true 263 | // false 264 | } 265 | 266 | func TestCompileIgnoreLines_CheckNestedDotFiles(t *testing.T) { 267 | lines := []string{ 268 | "**/external/**/*.md", 269 | "**/external/**/*.json", 270 | "**/external/**/*.gzip", 271 | "**/external/**/.*ignore", 272 | 273 | "**/external/foobar/*.css", 274 | "**/external/barfoo/less", 275 | "**/external/barfoo/scss", 276 | } 277 | object := CompileIgnoreLines(lines...) 278 | assert.NotNil(t, object, "returned object should not be nil") 279 | 280 | assert.Equal(t, true, object.MatchesPath("external/foobar/angular.foo.css"), "external/foobar/angular.foo.css") 281 | assert.Equal(t, true, object.MatchesPath("external/barfoo/.gitignore"), "external/barfoo/.gitignore") 282 | assert.Equal(t, true, object.MatchesPath("external/barfoo/.bower.json"), "external/barfoo/.bower.json") 283 | } 284 | 285 | func TestCompileIgnoreLines_CarriageReturn(t *testing.T) { 286 | lines := []string{"abc/def\r", "a/b/c\r", "b\r"} 287 | object := CompileIgnoreLines(lines...) 288 | 289 | assert.Equal(t, true, object.MatchesPath("abc/def/child"), "abc/def/child should match") 290 | assert.Equal(t, true, object.MatchesPath("a/b/c/d"), "a/b/c/d should match") 291 | 292 | assert.Equal(t, false, object.MatchesPath("abc"), "abc should not match") 293 | assert.Equal(t, false, object.MatchesPath("def"), "def should not match") 294 | assert.Equal(t, false, object.MatchesPath("bd"), "bd should not match") 295 | } 296 | 297 | func TestCompileIgnoreLines_WindowsPath(t *testing.T) { 298 | if runtime.GOOS != "windows" { 299 | return 300 | } 301 | lines := []string{"abc/def", "a/b/c", "b"} 302 | object := CompileIgnoreLines(lines...) 303 | 304 | assert.Equal(t, true, object.MatchesPath("abc\\def\\child"), "abc\\def\\child should match") 305 | assert.Equal(t, true, object.MatchesPath("a\\b\\c\\d"), "a\\b\\c\\d should match") 306 | } 307 | 308 | func TestWildCardFiles(t *testing.T) { 309 | gitIgnore := []string{"*.swp", "/foo/*.wat", "bar/*.txt"} 310 | object := CompileIgnoreLines(gitIgnore...) 311 | 312 | // Paths which are targeted by the above "lines" 313 | assert.Equal(t, true, object.MatchesPath("yo.swp"), "should ignore all swp files") 314 | assert.Equal(t, true, object.MatchesPath("something/else/but/it/hasyo.swp"), "should ignore all swp files in other directories") 315 | 316 | assert.Equal(t, true, object.MatchesPath("foo/bar.wat"), "should ignore all wat files in foo - nonpreceding /") 317 | assert.Equal(t, true, object.MatchesPath("/foo/something.wat"), "should ignore all wat files in foo - preceding /") 318 | 319 | assert.Equal(t, true, object.MatchesPath("bar/something.txt"), "should ignore all txt files in bar - nonpreceding /") 320 | assert.Equal(t, true, object.MatchesPath("/bar/somethingelse.txt"), "should ignore all txt files in bar - preceding /") 321 | 322 | // Paths which are not targeted by the above "lines" 323 | assert.Equal(t, false, object.MatchesPath("something/not/infoo/wat.wat"), "wat files should only be ignored in foo") 324 | assert.Equal(t, false, object.MatchesPath("something/not/infoo/wat.txt"), "txt files should only be ignored in bar") 325 | } 326 | 327 | func TestPrecedingSlash(t *testing.T) { 328 | gitIgnore := []string{"/foo", "bar/"} 329 | object := CompileIgnoreLines(gitIgnore...) 330 | 331 | assert.Equal(t, true, object.MatchesPath("foo/bar.wat"), "should ignore all files in foo - nonpreceding /") 332 | assert.Equal(t, true, object.MatchesPath("/foo/something.txt"), "should ignore all files in foo - preceding /") 333 | 334 | assert.Equal(t, true, object.MatchesPath("bar/something.txt"), "should ignore all files in bar - nonpreceding /") 335 | assert.Equal(t, true, object.MatchesPath("/bar/somethingelse.go"), "should ignore all files in bar - preceding /") 336 | assert.Equal(t, true, object.MatchesPath("/boo/something/bar/boo.txt"), "should block all files if bar is a sub directory") 337 | 338 | assert.Equal(t, false, object.MatchesPath("something/foo/something.txt"), "should only ignore top level foo directories- not nested") 339 | } 340 | 341 | func TestMatchesLineNumbers(t *testing.T) { 342 | gitIgnore := []string{"/foo", "bar/", "*.swp"} 343 | object := CompileIgnoreLines(gitIgnore...) 344 | 345 | var matchesPath bool 346 | var reason *IgnorePattern 347 | 348 | // /foo 349 | matchesPath, reason = object.MatchesPathHow("foo/bar.wat") 350 | assert.Equal(t, true, matchesPath, "should ignore all files in foo - nonpreceding /") 351 | assert.NotNil(t, reason, "reason should not be nil") 352 | assert.Equal(t, 1, reason.LineNo, "should match with line 1") 353 | assert.Equal(t, gitIgnore[0], reason.Line, "should match with line /foo") 354 | 355 | matchesPath, reason = object.MatchesPathHow("/foo/something.txt") 356 | assert.Equal(t, true, matchesPath, "should ignore all files in foo - preceding /") 357 | assert.NotNil(t, reason, "reason should not be nil") 358 | assert.Equal(t, 1, reason.LineNo, "should match with line 1") 359 | assert.Equal(t, gitIgnore[0], reason.Line, "should match with line /foo") 360 | 361 | // bar/ 362 | matchesPath, reason = object.MatchesPathHow("bar/something.txt") 363 | assert.Equal(t, true, matchesPath, "should ignore all files in bar - nonpreceding /") 364 | assert.NotNil(t, reason, "reason should not be nil") 365 | assert.Equal(t, 2, reason.LineNo, "should match with line 2") 366 | assert.Equal(t, gitIgnore[1], reason.Line, "should match with line bar/") 367 | 368 | matchesPath, reason = object.MatchesPathHow("/bar/somethingelse.go") 369 | assert.Equal(t, true, matchesPath, "should ignore all files in bar - preceding /") 370 | assert.NotNil(t, reason, "reason should not be nil") 371 | assert.Equal(t, 2, reason.LineNo, "should match with line 2") 372 | assert.Equal(t, gitIgnore[1], reason.Line, "should match with line bar/") 373 | 374 | matchesPath, reason = object.MatchesPathHow("/boo/something/bar/boo.txt") 375 | assert.Equal(t, true, matchesPath, "should block all files if bar is a sub directory") 376 | assert.NotNil(t, reason, "reason should not be nil") 377 | assert.Equal(t, 2, reason.LineNo, "should match with line 2") 378 | assert.Equal(t, gitIgnore[1], reason.Line, "should match with line bar/") 379 | 380 | // *.swp 381 | matchesPath, reason = object.MatchesPathHow("yo.swp") 382 | assert.Equal(t, true, matchesPath, "should ignore all swp files") 383 | assert.NotNil(t, reason, "reason should not be nil") 384 | assert.Equal(t, 3, reason.LineNo, "should match with line 3") 385 | assert.Equal(t, gitIgnore[2], reason.Line, "should match with line *.swp") 386 | 387 | matchesPath, reason = object.MatchesPathHow("something/else/but/it/hasyo.swp") 388 | assert.Equal(t, true, matchesPath, "should ignore all swp files in other directories") 389 | assert.NotNil(t, reason, "reason should not be nil") 390 | assert.Equal(t, 3, reason.LineNo, "should match with line 3") 391 | assert.Equal(t, gitIgnore[2], reason.Line, "should match with line *.swp") 392 | 393 | // other 394 | matchesPath, reason = object.MatchesPathHow("something/foo/something.txt") 395 | assert.Equal(t, false, matchesPath, "should only ignore top level foo directories- not nested") 396 | assert.Nil(t, reason, "reason should be nil as no match should happen") 397 | } 398 | -------------------------------------------------------------------------------- /version_gen.go: -------------------------------------------------------------------------------- 1 | package ignore 2 | 3 | // WARNING: Auto generated version file. Do not edit this file by hand. 4 | // WARNING: go get github.com/sabhiram/gover to manage this file. 5 | // Version: 1.1.0 6 | const ( 7 | Major = 1 8 | Minor = 1 9 | Patch = 0 10 | 11 | Version = "1.1.0" 12 | ) 13 | --------------------------------------------------------------------------------