177 lines
3.9 KiB
Go
177 lines
3.9 KiB
Go
package object
|
|
|
|
import (
|
|
"io"
|
|
|
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
|
"gopkg.in/src-d/go-git.v4/plumbing/storer"
|
|
)
|
|
|
|
// NewFilterCommitIter returns a CommitIter that walks the commit history,
|
|
// starting at the passed commit and visiting its parents in Breadth-first order.
|
|
// The commits returned by the CommitIter will validate the passed CommitFilter.
|
|
// The history won't be transversed beyond a commit if isLimit is true for it.
|
|
// Each commit will be visited only once.
|
|
// If the commit history can not be traversed, or the Close() method is called,
|
|
// the CommitIter won't return more commits.
|
|
// If no isValid is passed, all ancestors of from commit will be valid.
|
|
// If no isLimit is limmit, all ancestors of all commits will be visited.
|
|
func NewFilterCommitIter(
|
|
from *Commit,
|
|
isValid *CommitFilter,
|
|
isLimit *CommitFilter,
|
|
) CommitIter {
|
|
var validFilter CommitFilter
|
|
if isValid == nil {
|
|
validFilter = func(_ *Commit) bool {
|
|
return true
|
|
}
|
|
} else {
|
|
validFilter = *isValid
|
|
}
|
|
|
|
var limitFilter CommitFilter
|
|
if isLimit == nil {
|
|
limitFilter = func(_ *Commit) bool {
|
|
return false
|
|
}
|
|
} else {
|
|
limitFilter = *isLimit
|
|
}
|
|
|
|
return &filterCommitIter{
|
|
isValid: validFilter,
|
|
isLimit: limitFilter,
|
|
visited: map[plumbing.Hash]struct{}{},
|
|
queue: []*Commit{from},
|
|
}
|
|
}
|
|
|
|
// CommitFilter returns a boolean for the passed Commit
|
|
type CommitFilter func(*Commit) bool
|
|
|
|
// filterCommitIter implments CommitIter
|
|
type filterCommitIter struct {
|
|
isValid CommitFilter
|
|
isLimit CommitFilter
|
|
visited map[plumbing.Hash]struct{}
|
|
queue []*Commit
|
|
lastErr error
|
|
}
|
|
|
|
// Next returns the next commit of the CommitIter.
|
|
// It will return io.EOF if there are no more commits to visit,
|
|
// or an error if the history could not be traversed.
|
|
func (w *filterCommitIter) Next() (*Commit, error) {
|
|
var commit *Commit
|
|
var err error
|
|
for {
|
|
commit, err = w.popNewFromQueue()
|
|
if err != nil {
|
|
return nil, w.close(err)
|
|
}
|
|
|
|
w.visited[commit.Hash] = struct{}{}
|
|
|
|
if !w.isLimit(commit) {
|
|
err = w.addToQueue(commit.s, commit.ParentHashes...)
|
|
if err != nil {
|
|
return nil, w.close(err)
|
|
}
|
|
}
|
|
|
|
if w.isValid(commit) {
|
|
return commit, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// ForEach runs the passed callback over each Commit returned by the CommitIter
|
|
// until the callback returns an error or there is no more commits to traverse.
|
|
func (w *filterCommitIter) ForEach(cb func(*Commit) error) error {
|
|
for {
|
|
commit, err := w.Next()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := cb(commit); err == storer.ErrStop {
|
|
break
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Error returns the error that caused that the CommitIter is no longer returning commits
|
|
func (w *filterCommitIter) Error() error {
|
|
return w.lastErr
|
|
}
|
|
|
|
// Close closes the CommitIter
|
|
func (w *filterCommitIter) Close() {
|
|
w.visited = map[plumbing.Hash]struct{}{}
|
|
w.queue = []*Commit{}
|
|
w.isLimit = nil
|
|
w.isValid = nil
|
|
}
|
|
|
|
// close closes the CommitIter with an error
|
|
func (w *filterCommitIter) close(err error) error {
|
|
w.Close()
|
|
w.lastErr = err
|
|
return err
|
|
}
|
|
|
|
// popNewFromQueue returns the first new commit from the internal fifo queue,
|
|
// or an io.EOF error if the queue is empty
|
|
func (w *filterCommitIter) popNewFromQueue() (*Commit, error) {
|
|
var first *Commit
|
|
for {
|
|
if len(w.queue) == 0 {
|
|
if w.lastErr != nil {
|
|
return nil, w.lastErr
|
|
}
|
|
|
|
return nil, io.EOF
|
|
}
|
|
|
|
first = w.queue[0]
|
|
w.queue = w.queue[1:]
|
|
if _, ok := w.visited[first.Hash]; ok {
|
|
continue
|
|
}
|
|
|
|
return first, nil
|
|
}
|
|
}
|
|
|
|
// addToQueue adds the passed commits to the internal fifo queue if they weren't seen
|
|
// or returns an error if the passed hashes could not be used to get valid commits
|
|
func (w *filterCommitIter) addToQueue(
|
|
store storer.EncodedObjectStorer,
|
|
hashes ...plumbing.Hash,
|
|
) error {
|
|
for _, hash := range hashes {
|
|
if _, ok := w.visited[hash]; ok {
|
|
continue
|
|
}
|
|
|
|
commit, err := GetCommit(store, hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
w.queue = append(w.queue, commit)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|