Correctly handle (?i) in ignores (fixes #1953)

This commit is contained in:
Jakob Borg 2015-09-02 20:54:18 +02:00
parent 70b37dc469
commit e3e1036dda
4 changed files with 21 additions and 27 deletions

View File

@ -38,14 +38,6 @@ func Convert(pattern string, flags int) (*regexp.Regexp, error) {
} }
} }
// Support case insensitive ignores. We do the loop because we may in some
// circumstances end up with multiple insensitivity prefixes
// ("(?i)(?i)foo"), which should be accepted.
for ignore := strings.TrimPrefix(pattern, "(?i)"); ignore != pattern; ignore = strings.TrimPrefix(pattern, "(?i)") {
flags |= CaseFold
pattern = ignore
}
if flags&NoEscape != 0 { if flags&NoEscape != 0 {
pattern = strings.Replace(pattern, "\\", "\\\\", -1) pattern = strings.Replace(pattern, "\\", "\\\\", -1)
} else { } else {

View File

@ -53,10 +53,6 @@ var testcases = []testcase{
{"**/foo.txt", "bar/baz/foo.txt", PathName, true}, {"**/foo.txt", "bar/baz/foo.txt", PathName, true},
{"foo.txt", "foo.TXT", CaseFold, true}, {"foo.txt", "foo.TXT", CaseFold, true},
{"(?i)foo.txt", "foo.TXT", 0, true},
{"(?i)(?i)foo.txt", "foo.TXT", 0, true}, // repeated prefix should be fine
{"(?i)**foo.txt", "/dev/tmp/foo.TXT", 0, true},
{"(?i)!**foo.txt", "/dev/tmp/foo.TXT", 0, false},
// These characters are literals in glob, but not in regexp. // These characters are literals in glob, but not in regexp.
{"hey$hello", "hey$hello", 0, true}, {"hey$hello", "hey$hello", 0, true},

View File

@ -206,22 +206,28 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
include = false include = false
} }
flags := fnmatch.PathName
if strings.HasPrefix(line, "(?i)") {
line = line[4:]
flags |= fnmatch.CaseFold
}
if strings.HasPrefix(line, "/") { if strings.HasPrefix(line, "/") {
// Pattern is rooted in the current dir only // Pattern is rooted in the current dir only
exp, err := fnmatch.Convert(line[1:], fnmatch.PathName) exp, err := fnmatch.Convert(line[1:], flags)
if err != nil { if err != nil {
return fmt.Errorf("Invalid pattern %q in ignore file", line) return fmt.Errorf("Invalid pattern %q in ignore file", line)
} }
patterns = append(patterns, Pattern{exp, include}) patterns = append(patterns, Pattern{exp, include})
} else if strings.HasPrefix(line, "**/") { } else if strings.HasPrefix(line, "**/") {
// Add the pattern as is, and without **/ so it matches in current dir // Add the pattern as is, and without **/ so it matches in current dir
exp, err := fnmatch.Convert(line, fnmatch.PathName) exp, err := fnmatch.Convert(line, flags)
if err != nil { if err != nil {
return fmt.Errorf("Invalid pattern %q in ignore file", line) return fmt.Errorf("Invalid pattern %q in ignore file", line)
} }
patterns = append(patterns, Pattern{exp, include}) patterns = append(patterns, Pattern{exp, include})
exp, err = fnmatch.Convert(line[3:], fnmatch.PathName) exp, err = fnmatch.Convert(line[3:], flags)
if err != nil { if err != nil {
return fmt.Errorf("Invalid pattern %q in ignore file", line) return fmt.Errorf("Invalid pattern %q in ignore file", line)
} }
@ -236,13 +242,13 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
} else { } else {
// Path name or pattern, add it so it matches files both in // Path name or pattern, add it so it matches files both in
// current directory and subdirs. // current directory and subdirs.
exp, err := fnmatch.Convert(line, fnmatch.PathName) exp, err := fnmatch.Convert(line, flags)
if err != nil { if err != nil {
return fmt.Errorf("Invalid pattern %q in ignore file", line) return fmt.Errorf("Invalid pattern %q in ignore file", line)
} }
patterns = append(patterns, Pattern{exp, include}) patterns = append(patterns, Pattern{exp, include})
exp, err = fnmatch.Convert("**/"+line, fnmatch.PathName) exp, err = fnmatch.Convert("**/"+line, flags)
if err != nil { if err != nil {
return fmt.Errorf("Invalid pattern %q in ignore file", line) return fmt.Errorf("Invalid pattern %q in ignore file", line)
} }

View File

@ -31,10 +31,17 @@ func TestIgnores(t *testing.T) {
// Create eight empty files and directories // Create eight empty files and directories
files := []string{"f1", "f2", "f3", "f4", "f11", "f12", "f13", "f14"}
dirs := []string{"d1", "d2", "d3", "d4", "d11", "d12", "d13", "d14"} dirs := []string{"d1", "d2", "d3", "d4", "d11", "d12", "d13", "d14"}
files := []string{"f1", "f2", "f3", "f4", "f11", "f12", "f13", "f14", "d1/f1.TXT"}
all := append(files, dirs...) all := append(files, dirs...)
for _, dir := range dirs {
err := os.Mkdir(filepath.Join("s1", dir), 0755)
if err != nil {
t.Fatal(err)
}
}
for _, file := range files { for _, file := range files {
fd, err := os.Create(filepath.Join("s1", file)) fd, err := os.Create(filepath.Join("s1", file))
if err != nil { if err != nil {
@ -43,13 +50,6 @@ func TestIgnores(t *testing.T) {
fd.Close() fd.Close()
} }
for _, dir := range dirs {
err := os.Mkdir(filepath.Join("s1", dir), 0755)
if err != nil {
t.Fatal(err)
}
}
var syms []string var syms []string
if symlinksSupported() { if symlinksSupported() {
syms = []string{"s1", "s2", "s3", "s4", "s11", "s12", "s13", "s14"} syms = []string{"s1", "s2", "s3", "s4", "s11", "s12", "s13", "s14"}
@ -81,7 +81,7 @@ func TestIgnores(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, err = fd.WriteString("f1*\nf2\nd1*\nd2\ns1*\ns2") // [fds][34] only non-ignored items _, err = fd.WriteString("f1*\nf2\nd1*\nd2\ns1*\ns2\n(?i)*.txt") // [fds][34] only non-ignored items
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -130,7 +130,7 @@ func TestIgnores(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
expected = len(all) * 7 / 8 // seven out of eight items of each type should remain expected = len(all)*7/8 + 1 // seven out of eight items of each type should remain, plus the foo.TXT
if m.LocalFiles != expected { if m.LocalFiles != expected {
t.Fatalf("Incorrect number of files after second ignore, %d != %d", m.LocalFiles, expected) t.Fatalf("Incorrect number of files after second ignore, %d != %d", m.LocalFiles, expected)
} }