aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/object/commit_walker_path.go
diff options
context:
space:
mode:
authorSaeed Rasooli <saeed.gnu@gmail.com>2019-11-29 13:25:05 +0330
committerSaeed Rasooli <saeed.gnu@gmail.com>2019-11-29 16:46:43 +0330
commitf54ee6d2b1f92d67ed567027f7a8ec1f2139c532 (patch)
tree588e34921a0dd445455fb709ea171c4d6b5ad445 /plumbing/object/commit_walker_path.go
parent1a7db85bca7027d90afdb5ce711622aaac9feaed (diff)
downloadgo-git-f54ee6d2b1f92d67ed567027f7a8ec1f2139c532.tar.gz
add NewCommitPathIterFromIter that accepts pathFilter func(string) bool
keep NewCommitFileIterFromIter for compatibilty for now Signed-off-by: Saeed Rasooli <saeed.gnu@gmail.com>
Diffstat (limited to 'plumbing/object/commit_walker_path.go')
-rw-r--r--plumbing/object/commit_walker_path.go161
1 files changed, 161 insertions, 0 deletions
diff --git a/plumbing/object/commit_walker_path.go b/plumbing/object/commit_walker_path.go
new file mode 100644
index 0000000..6a49fd1
--- /dev/null
+++ b/plumbing/object/commit_walker_path.go
@@ -0,0 +1,161 @@
+package object
+
+import (
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+)
+
+type commitPathIter struct {
+ pathFilter func(string) bool
+ sourceIter CommitIter
+ currentCommit *Commit
+ checkParent bool
+}
+
+// NewCommitPathIterFromIter returns a commit iterator which performs diffTree between
+// successive trees returned from the commit iterator from the argument. The purpose of this is
+// to find the commits that explain how the files that match the path came to be.
+// If checkParent is true then the function double checks if potential parent (next commit in a path)
+// is one of the parents in the tree (it's used by `git log --all`).
+// pathFilter is a function that takes path of file as argument and returns true if we want it
+func NewCommitPathIterFromIter(pathFilter func(string) bool, commitIter CommitIter, checkParent bool) CommitIter {
+ iterator := new(commitPathIter)
+ iterator.sourceIter = commitIter
+ iterator.pathFilter = pathFilter
+ iterator.checkParent = checkParent
+ return iterator
+}
+
+// this function is kept for compatibilty, can be replaced with NewCommitPathIterFromIter
+func NewCommitFileIterFromIter(fileName string, commitIter CommitIter, checkParent bool) CommitIter {
+ return NewCommitPathIterFromIter(
+ func(path string) bool {
+ return path == fileName
+ },
+ commitIter,
+ checkParent,
+ )
+}
+
+func (c *commitPathIter) Next() (*Commit, error) {
+ if c.currentCommit == nil {
+ var err error
+ c.currentCommit, err = c.sourceIter.Next()
+ if err != nil {
+ return nil, err
+ }
+ }
+ commit, commitErr := c.getNextFileCommit()
+
+ // Setting current-commit to nil to prevent unwanted states when errors are raised
+ if commitErr != nil {
+ c.currentCommit = nil
+ }
+ return commit, commitErr
+}
+
+func (c *commitPathIter) getNextFileCommit() (*Commit, error) {
+ for {
+ // Parent-commit can be nil if the current-commit is the initial commit
+ parentCommit, parentCommitErr := c.sourceIter.Next()
+ if parentCommitErr != nil {
+ // If the parent-commit is beyond the initial commit, keep it nil
+ if parentCommitErr != io.EOF {
+ return nil, parentCommitErr
+ }
+ parentCommit = nil
+ }
+
+ // Fetch the trees of the current and parent commits
+ currentTree, currTreeErr := c.currentCommit.Tree()
+ if currTreeErr != nil {
+ return nil, currTreeErr
+ }
+
+ var parentTree *Tree
+ if parentCommit != nil {
+ var parentTreeErr error
+ parentTree, parentTreeErr = parentCommit.Tree()
+ if parentTreeErr != nil {
+ return nil, parentTreeErr
+ }
+ }
+
+ // Find diff between current and parent trees
+ changes, diffErr := DiffTree(currentTree, parentTree)
+ if diffErr != nil {
+ return nil, diffErr
+ }
+
+ found := c.hasFileChange(changes, parentCommit)
+
+ // Storing the current-commit in-case a change is found, and
+ // Updating the current-commit for the next-iteration
+ prevCommit := c.currentCommit
+ c.currentCommit = parentCommit
+
+ if found {
+ return prevCommit, nil
+ }
+
+ // If not matches found and if parent-commit is beyond the initial commit, then return with EOF
+ if parentCommit == nil {
+ return nil, io.EOF
+ }
+ }
+}
+
+func (c *commitPathIter) hasFileChange(changes Changes, parent *Commit) bool {
+ for _, change := range changes {
+ if !c.pathFilter(change.name()) {
+ continue
+ }
+
+ // filename matches, now check if source iterator contains all commits (from all refs)
+ if c.checkParent {
+ if parent != nil && isParentHash(parent.Hash, c.currentCommit) {
+ return true
+ }
+ continue
+ }
+
+ return true
+ }
+
+ return false
+}
+
+func isParentHash(hash plumbing.Hash, commit *Commit) bool {
+ for _, h := range commit.ParentHashes {
+ if h == hash {
+ return true
+ }
+ }
+ return false
+}
+
+func (c *commitPathIter) ForEach(cb func(*Commit) error) error {
+ for {
+ commit, nextErr := c.Next()
+ if nextErr == io.EOF {
+ break
+ }
+ if nextErr != nil {
+ return nextErr
+ }
+ err := cb(commit)
+ if err == storer.ErrStop {
+ return nil
+ } else if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (c *commitPathIter) Close() {
+ c.sourceIter.Close()
+}