aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md32
-rw-r--r--options.go8
-rw-r--r--worktree.go84
-rw-r--r--worktree_test.go67
4 files changed, 138 insertions, 53 deletions
diff --git a/README.md b/README.md
index bd22288..8cdfef8 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,19 @@
-# go-git [![GoDoc](https://godoc.org/gopkg.in/src-d/go-git.v4?status.svg)](https://godoc.org/github.com/src-d/go-git) [![Build Status](https://travis-ci.org/src-d/go-git.svg)](https://travis-ci.org/src-d/go-git) [![Build status](https://ci.appveyor.com/api/projects/status/nyidskwifo4py6ub?svg=true)](https://ci.appveyor.com/project/mcuadros/go-git) [![codecov.io](https://codecov.io/github/src-d/go-git/coverage.svg)](https://codecov.io/github/src-d/go-git) [![codebeat badge](https://codebeat.co/badges/b6cb2f73-9e54-483d-89f9-4b95a911f40c)](https://codebeat.co/projects/github-com-src-d-go-git)
+![go-git logo](https://cdn.rawgit.com/src-d/artwork/02036484/go-git/files/go-git-github-readme-header.png)
+[![GoDoc](https://godoc.org/gopkg.in/src-d/go-git.v4?status.svg)](https://godoc.org/github.com/src-d/go-git) [![Build Status](https://travis-ci.org/src-d/go-git.svg)](https://travis-ci.org/src-d/go-git) [![Build status](https://ci.appveyor.com/api/projects/status/nyidskwifo4py6ub?svg=true)](https://ci.appveyor.com/project/mcuadros/go-git) [![codecov.io](https://codecov.io/github/src-d/go-git/coverage.svg)](https://codecov.io/github/src-d/go-git) [![Go Report Card](https://goreportcard.com/badge/github.com/src-d/go-git)](https://goreportcard.com/report/github.com/src-d/go-git)
-A highly extensible git implementation in **pure Go**.
+*go-git* is a highly extensible git implementation library written in **pure Go**.
-*go-git* aims to reach the completeness of [libgit2](https://libgit2.github.com/) or [jgit](http://www.eclipse.org/jgit/), nowadays covers the **majority** of the plumbing **read operations** and **some** of the main **write operations**, but lacks the main porcelain operations such as merges.
+It can be used to manipulate git repositories at low level *(plumbing)* or high level *(porcelain)*, through an idiomatic Go API. It also supports several type of storage, such as in-memory filesystems, or custom implementations thanks to the [`Storer`](https://godoc.org/gopkg.in/src-d/go-git.v4/plumbing/storer) interface.
-It is **highly extensible**, we have been following the open/close principle in its design to facilitate extensions, mainly focusing the efforts on the persistence of the objects.
+It's being actively develop since 2015 and is being use extensively by [source{d}](https://sourced.tech/) and [Keybase](https://keybase.io/blog/encrypted-git-for-everyone), and by many other libraries and tools.
-### ... is this production ready?
+Comparison with git
+-------------------
-The master branch represents the `v4` of the library, it is currently under active development and is planned to be released in early 2017.
+*go-git* aims to be fully compatible with [git](https://github.com/git/git), all the *porcelain* operations are implemented to work exactly as *git* does.
-If you are looking for a production ready version, please take a look to the [`v3`](https://github.com/src-d/go-git/tree/v3) which is being used in production at [source{d}](http://sourced.tech) since August 2015 to analyze all GitHub public repositories (i.e. 16M repositories).
+*git* is a humongous project with years of development by thousands of contributors, making it challenging for *go-git* implement all the features. You can find a comparison of *go-git* vs *git* in the [compatibility documentation](COMPATIBILITY.md).
-We recommend the use of `v4` to develop new projects since it includes much new functionality and provides a more *idiomatic git* API
Installation
------------
@@ -23,6 +24,7 @@ The recommended way to install *go-git* is:
go get -u gopkg.in/src-d/go-git.v4/...
```
+> We use [gopkg.in](http://labix.org/gopkg.in) for having a versioned API, this means that when `go get` clones the package, is the latest tag matching `v4.*` cloned and not the master branch.
Examples
--------
@@ -108,20 +110,14 @@ Date: Fri Nov 11 13:23:22 2016 +0100
...
```
-You can find this [example](_examples/log/main.go) and many other at the [examples](_examples) folder
-
-Comparison With Git
--------------------
-
-In the [compatibility documentation](COMPATIBILITY.md) you can find a comparison
-table of git with go-git.
+You can find this [example](_examples/log/main.go) and many others at the [examples](_examples) folder
Contribute
----------
-If you are interested in contributing to go-git, open an [issue](https://github.com/src-d/go-git/issues) explaining which missing functionality you want to work on, and we will guide you through the implementation.
+[Contributions](https://github.com/src-d/go-git/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) are more than welcome, if you are interested please take a look to
+our [Contributing Guidelines](CONTRIBUTING.md).
License
-------
-
-MIT, see [LICENSE](LICENSE)
+Apache License Version 2.0, see [LICENSE](LICENSE)
diff --git a/options.go b/options.go
index d0898db..9fb315e 100644
--- a/options.go
+++ b/options.go
@@ -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 ee9bf2f..e51e89a 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",
+ },
+ },
},
}