diff options
-rw-r--r-- | options.go | 8 | ||||
-rw-r--r-- | worktree.go | 84 | ||||
-rw-r--r-- | worktree_test.go | 67 |
3 files changed, 124 insertions, 35 deletions
@@ -369,16 +369,16 @@ type CleanOptions struct { // GrepOptions describes how a grep should be performed. type GrepOptions struct { - // Pattern is a compiled Regexp object to be matched. - Pattern *regexp.Regexp + // Patterns are compiled Regexp objects to be matched. + Patterns []*regexp.Regexp // InvertMatch selects non-matching lines. InvertMatch bool // CommitHash is the hash of the commit from which worktree should be derived. CommitHash plumbing.Hash // ReferenceName is the branch or tag name from which worktree should be derived. ReferenceName plumbing.ReferenceName - // PathSpec is a compiled Regexp object of pathspec to use in the matching. - PathSpec *regexp.Regexp + // PathSpecs are compiled Regexp objects of pathspec to use in the matching. + PathSpecs []*regexp.Regexp } var ( diff --git a/worktree.go b/worktree.go index 2c35ffb..a23397e 100644 --- a/worktree.go +++ b/worktree.go @@ -765,50 +765,88 @@ func (w *Worktree) Grep(opts *GrepOptions) ([]GrepResult, error) { // findMatchInFiles takes a FileIter, worktree name and GrepOptions, and // returns a slice of GrepResult containing the result of regex pattern matching -// in the file content. +// in content of all the files. func findMatchInFiles(fileiter *object.FileIter, treeName string, opts *GrepOptions) ([]GrepResult, error) { var results []GrepResult - // Iterate through the files and look for any matches. err := fileiter.ForEach(func(file *object.File) error { - // Check if the file name matches with the pathspec. - if opts.PathSpec != nil && !opts.PathSpec.MatchString(file.Name) { + var fileInPathSpec bool + + // When no pathspecs are provided, search all the files. + if len(opts.PathSpecs) == 0 { + fileInPathSpec = true + } + + // Check if the file name matches with the pathspec. Break out of the + // loop once a match is found. + for _, pathSpec := range opts.PathSpecs { + if pathSpec != nil && pathSpec.MatchString(file.Name) { + fileInPathSpec = true + break + } + } + + // If the file does not match with any of the pathspec, skip it. + if !fileInPathSpec { return nil } - content, err := file.Contents() + grepResults, err := findMatchInFile(file, treeName, opts) if err != nil { return err } + results = append(results, grepResults...) + + return nil + }) + + return results, err +} - // Split the content and make parseable line-by-line. - contentByLine := strings.Split(content, "\n") - for lineNum, cnt := range contentByLine { - addToResult := false - // Match the pattern and content. - if opts.Pattern != nil && opts.Pattern.MatchString(cnt) { +// findMatchInFile takes a single File, worktree name and GrepOptions, +// and returns a slice of GrepResult containing the result of regex pattern +// matching in the given file. +func findMatchInFile(file *object.File, treeName string, opts *GrepOptions) ([]GrepResult, error) { + var grepResults []GrepResult + + content, err := file.Contents() + if err != nil { + return grepResults, err + } + + // Split the file content and parse line-by-line. + contentByLine := strings.Split(content, "\n") + for lineNum, cnt := range contentByLine { + addToResult := false + + // Match the patterns and content. Break out of the loop once a + // match is found. + for _, pattern := range opts.Patterns { + if pattern != nil && pattern.MatchString(cnt) { // Add to result only if invert match is not enabled. if !opts.InvertMatch { addToResult = true + break } } else if opts.InvertMatch { - // If matching fails, and invert match is enabled, add to results. + // If matching fails, and invert match is enabled, add to + // results. addToResult = true + break } + } - if addToResult { - results = append(results, GrepResult{ - FileName: file.Name, - LineNumber: lineNum + 1, - Content: cnt, - TreeName: treeName, - }) - } + if addToResult { + grepResults = append(grepResults, GrepResult{ + FileName: file.Name, + LineNumber: lineNum + 1, + Content: cnt, + TreeName: treeName, + }) } - return nil - }) + } - return results, err + return grepResults, nil } func rmFileAndDirIfEmpty(fs billy.Filesystem, name string) error { diff --git a/worktree_test.go b/worktree_test.go index 36e3a08..c139bb7 100644 --- a/worktree_test.go +++ b/worktree_test.go @@ -1330,7 +1330,7 @@ func (s *WorktreeSuite) TestGrep(c *C) { { name: "basic word match", options: GrepOptions{ - Pattern: regexp.MustCompile("import"), + Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, }, wantResult: []GrepResult{ { @@ -1349,7 +1349,7 @@ func (s *WorktreeSuite) TestGrep(c *C) { }, { name: "case insensitive match", options: GrepOptions{ - Pattern: regexp.MustCompile(`(?i)IMport`), + Patterns: []*regexp.Regexp{regexp.MustCompile(`(?i)IMport`)}, }, wantResult: []GrepResult{ { @@ -1368,7 +1368,7 @@ func (s *WorktreeSuite) TestGrep(c *C) { }, { name: "invert match", options: GrepOptions{ - Pattern: regexp.MustCompile("import"), + Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, InvertMatch: true, }, dontWantResult: []GrepResult{ @@ -1388,7 +1388,7 @@ func (s *WorktreeSuite) TestGrep(c *C) { }, { name: "match at a given commit hash", options: GrepOptions{ - Pattern: regexp.MustCompile("The MIT License"), + Patterns: []*regexp.Regexp{regexp.MustCompile("The MIT License")}, CommitHash: plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"), }, wantResult: []GrepResult{ @@ -1410,8 +1410,8 @@ func (s *WorktreeSuite) TestGrep(c *C) { }, { name: "match for a given pathspec", options: GrepOptions{ - Pattern: regexp.MustCompile("import"), - PathSpec: regexp.MustCompile("go/"), + Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, + PathSpecs: []*regexp.Regexp{regexp.MustCompile("go/")}, }, wantResult: []GrepResult{ { @@ -1432,7 +1432,7 @@ func (s *WorktreeSuite) TestGrep(c *C) { }, { name: "match at a given reference name", options: GrepOptions{ - Pattern: regexp.MustCompile("import"), + Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, ReferenceName: "refs/heads/master", }, wantResult: []GrepResult{ @@ -1446,11 +1446,62 @@ func (s *WorktreeSuite) TestGrep(c *C) { }, { name: "ambiguous options", options: GrepOptions{ - Pattern: regexp.MustCompile("import"), + Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, CommitHash: plumbing.NewHash("2d55a722f3c3ecc36da919dfd8b6de38352f3507"), ReferenceName: "somereferencename", }, wantError: ErrHashOrReference, + }, { + name: "multiple patterns", + options: GrepOptions{ + Patterns: []*regexp.Regexp{ + regexp.MustCompile("import"), + regexp.MustCompile("License"), + }, + }, + wantResult: []GrepResult{ + { + FileName: "go/example.go", + LineNumber: 3, + Content: "import (", + TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + }, + { + FileName: "vendor/foo.go", + LineNumber: 3, + Content: "import \"fmt\"", + TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + }, + { + FileName: "LICENSE", + LineNumber: 1, + Content: "The MIT License (MIT)", + TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + }, + }, + }, { + name: "multiple pathspecs", + options: GrepOptions{ + Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, + PathSpecs: []*regexp.Regexp{ + regexp.MustCompile("go/"), + regexp.MustCompile("vendor/"), + }, + }, + wantResult: []GrepResult{ + { + FileName: "go/example.go", + LineNumber: 3, + Content: "import (", + TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + }, + { + FileName: "vendor/foo.go", + LineNumber: 3, + Content: "import \"fmt\"", + TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + }, + }, }, } |