Skip to content

Commit eef6778

Browse files
authored
Merge pull request go-git#3 from go-git/pr-1248
git.LogOptions: add `PathFilter func(string) bool`
2 parents 87d3d89 + 027dadf commit eef6778

File tree

4 files changed

+117
-16
lines changed

4 files changed

+117
-16
lines changed

options.go

+7
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,15 @@ type LogOptions struct {
343343

344344
// Show only those commits in which the specified file was inserted/updated.
345345
// It is equivalent to running `git log -- <file-name>`.
346+
// this field is kept for compatility, it can be replaced with PathFilter
346347
FileName *string
347348

349+
// Filter commits based on the path of files that are updated
350+
// takes file path as argument and should return true if the file is desired
351+
// It can be used to implement `git log -- <path>`
352+
// either <path> is a file path, or directory path, or a regexp of file/directory path
353+
PathFilter func(string) bool
354+
348355
// Pretend as if all the refs in refs/, along with HEAD, are listed on the command line as <commit>.
349356
// It is equivalent to running `git log --all`.
350357
// If set on true, the From option will be ignored.

plumbing/object/commit_walker_file.go renamed to plumbing/object/commit_walker_path.go

+24-12
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,39 @@ import (
88
"gopkg.in/src-d/go-git.v4/plumbing/storer"
99
)
1010

11-
type commitFileIter struct {
12-
fileName string
11+
type commitPathIter struct {
12+
pathFilter func(string) bool
1313
sourceIter CommitIter
1414
currentCommit *Commit
1515
checkParent bool
1616
}
1717

18-
// NewCommitFileIterFromIter returns a commit iterator which performs diffTree between
18+
// NewCommitPathIterFromIter returns a commit iterator which performs diffTree between
1919
// successive trees returned from the commit iterator from the argument. The purpose of this is
2020
// to find the commits that explain how the files that match the path came to be.
2121
// If checkParent is true then the function double checks if potential parent (next commit in a path)
2222
// is one of the parents in the tree (it's used by `git log --all`).
23-
func NewCommitFileIterFromIter(fileName string, commitIter CommitIter, checkParent bool) CommitIter {
24-
iterator := new(commitFileIter)
23+
// pathFilter is a function that takes path of file as argument and returns true if we want it
24+
func NewCommitPathIterFromIter(pathFilter func(string) bool, commitIter CommitIter, checkParent bool) CommitIter {
25+
iterator := new(commitPathIter)
2526
iterator.sourceIter = commitIter
26-
iterator.fileName = fileName
27+
iterator.pathFilter = pathFilter
2728
iterator.checkParent = checkParent
2829
return iterator
2930
}
3031

31-
func (c *commitFileIter) Next() (*Commit, error) {
32+
// this function is kept for compatibilty, can be replaced with NewCommitPathIterFromIter
33+
func NewCommitFileIterFromIter(fileName string, commitIter CommitIter, checkParent bool) CommitIter {
34+
return NewCommitPathIterFromIter(
35+
func(path string) bool {
36+
return path == fileName
37+
},
38+
commitIter,
39+
checkParent,
40+
)
41+
}
42+
43+
func (c *commitPathIter) Next() (*Commit, error) {
3244
if c.currentCommit == nil {
3345
var err error
3446
c.currentCommit, err = c.sourceIter.Next()
@@ -45,7 +57,7 @@ func (c *commitFileIter) Next() (*Commit, error) {
4557
return commit, commitErr
4658
}
4759

48-
func (c *commitFileIter) getNextFileCommit() (*Commit, error) {
60+
func (c *commitPathIter) getNextFileCommit() (*Commit, error) {
4961
for {
5062
// Parent-commit can be nil if the current-commit is the initial commit
5163
parentCommit, parentCommitErr := c.sourceIter.Next()
@@ -96,9 +108,9 @@ func (c *commitFileIter) getNextFileCommit() (*Commit, error) {
96108
}
97109
}
98110

99-
func (c *commitFileIter) hasFileChange(changes Changes, parent *Commit) bool {
111+
func (c *commitPathIter) hasFileChange(changes Changes, parent *Commit) bool {
100112
for _, change := range changes {
101-
if change.name() != c.fileName {
113+
if !c.pathFilter(change.name()) {
102114
continue
103115
}
104116

@@ -125,7 +137,7 @@ func isParentHash(hash plumbing.Hash, commit *Commit) bool {
125137
return false
126138
}
127139

128-
func (c *commitFileIter) ForEach(cb func(*Commit) error) error {
140+
func (c *commitPathIter) ForEach(cb func(*Commit) error) error {
129141
for {
130142
commit, nextErr := c.Next()
131143
if nextErr == io.EOF {
@@ -144,6 +156,6 @@ func (c *commitFileIter) ForEach(cb func(*Commit) error) error {
144156
return nil
145157
}
146158

147-
func (c *commitFileIter) Close() {
159+
func (c *commitPathIter) Close() {
148160
c.sourceIter.Close()
149161
}

repository.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,9 @@ func (r *Repository) Log(o *LogOptions) (object.CommitIter, error) {
10671067
// for `git log --all` also check parent (if the next commit comes from the real parent)
10681068
it = r.logWithFile(*o.FileName, it, o.All)
10691069
}
1070+
if o.PathFilter != nil {
1071+
it = r.logWithPathFilter(o.PathFilter, it, o.All)
1072+
}
10701073

10711074
if o.Since != nil || o.Until != nil {
10721075
limitOptions := object.LogLimitOptions{Since: o.Since, Until: o.Until}
@@ -1099,7 +1102,21 @@ func (r *Repository) logAll(commitIterFunc func(*object.Commit) object.CommitIte
10991102
}
11001103

11011104
func (*Repository) logWithFile(fileName string, commitIter object.CommitIter, checkParent bool) object.CommitIter {
1102-
return object.NewCommitFileIterFromIter(fileName, commitIter, checkParent)
1105+
return object.NewCommitPathIterFromIter(
1106+
func(path string) bool {
1107+
return path == fileName
1108+
},
1109+
commitIter,
1110+
checkParent,
1111+
)
1112+
}
1113+
1114+
func (*Repository) logWithPathFilter(pathFilter func(string) bool, commitIter object.CommitIter, checkParent bool) object.CommitIter {
1115+
return object.NewCommitPathIterFromIter(
1116+
pathFilter,
1117+
commitIter,
1118+
checkParent,
1119+
)
11031120
}
11041121

11051122
func (*Repository) logWithLimit(commitIter object.CommitIter, limitOptions object.LogLimitOptions) object.CommitIter {

repository_test.go

+68-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"os"
1111
"os/exec"
1212
"path/filepath"
13+
"regexp"
1314
"strings"
1415
"testing"
1516
"time"
@@ -1676,6 +1677,70 @@ func (s *RepositorySuite) TestLogFileWithError(c *C) {
16761677
c.Assert(err, NotNil)
16771678
}
16781679

1680+
func (s *RepositorySuite) TestLogPathWithError(c *C) {
1681+
fileName := "README"
1682+
pathIter := func(path string) bool {
1683+
return path == fileName
1684+
}
1685+
cIter := object.NewCommitPathIterFromIter(pathIter, &mockErrCommitIter{}, false)
1686+
defer cIter.Close()
1687+
1688+
err := cIter.ForEach(func(commit *object.Commit) error {
1689+
return nil
1690+
})
1691+
c.Assert(err, NotNil)
1692+
}
1693+
1694+
func (s *RepositorySuite) TestLogPathRegexpWithError(c *C) {
1695+
pathRE := regexp.MustCompile("R.*E")
1696+
pathIter := func(path string) bool {
1697+
return pathRE.MatchString(path)
1698+
}
1699+
cIter := object.NewCommitPathIterFromIter(pathIter, &mockErrCommitIter{}, false)
1700+
defer cIter.Close()
1701+
1702+
err := cIter.ForEach(func(commit *object.Commit) error {
1703+
return nil
1704+
})
1705+
c.Assert(err, NotNil)
1706+
}
1707+
1708+
func (s *RepositorySuite) TestLogPathFilterRegexp(c *C) {
1709+
pathRE := regexp.MustCompile(".*\\.go")
1710+
pathIter := func(path string) bool {
1711+
return pathRE.MatchString(path)
1712+
}
1713+
1714+
r, _ := Init(memory.NewStorage(), nil)
1715+
err := r.clone(context.Background(), &CloneOptions{
1716+
URL: s.GetBasicLocalRepositoryURL(),
1717+
})
1718+
c.Assert(err, IsNil)
1719+
1720+
expectedCommitIDs := []string{
1721+
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
1722+
"918c48b83bd081e863dbe1b80f8998f058cd8294",
1723+
}
1724+
commitIDs := []string{}
1725+
1726+
cIter, err := r.Log(&LogOptions{
1727+
PathFilter: pathIter,
1728+
From: plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
1729+
})
1730+
c.Assert(err, IsNil)
1731+
defer cIter.Close()
1732+
1733+
cIter.ForEach(func(commit *object.Commit) error {
1734+
commitIDs = append(commitIDs, commit.ID().String())
1735+
return nil
1736+
})
1737+
c.Assert(
1738+
strings.Join(commitIDs, ", "),
1739+
Equals,
1740+
strings.Join(expectedCommitIDs, ", "),
1741+
)
1742+
}
1743+
16791744
func (s *RepositorySuite) TestLogLimitNext(c *C) {
16801745
r, _ := Init(memory.NewStorage(), nil)
16811746
err := r.clone(context.Background(), &CloneOptions{
@@ -2615,9 +2680,9 @@ func (s *RepositorySuite) TestResolveRevisionWithErrors(c *C) {
26152680
c.Assert(err, IsNil)
26162681

26172682
datas := map[string]string{
2618-
"efs/heads/master~": "reference not found",
2619-
"HEAD^3": `Revision invalid : "3" found must be 0, 1 or 2 after "^"`,
2620-
"HEAD^{/whatever}": `No commit message match regexp : "whatever"`,
2683+
"efs/heads/master~": "reference not found",
2684+
"HEAD^3": `Revision invalid : "3" found must be 0, 1 or 2 after "^"`,
2685+
"HEAD^{/whatever}": `No commit message match regexp : "whatever"`,
26212686
"4e1243bd22c66e76c2ba9eddc1f91394e57f9f83": "reference not found",
26222687
}
26232688

0 commit comments

Comments
 (0)