package commitgraph import ( "io" "github.com/emirpasic/gods/trees/binaryheap" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/storer" ) type commitNodeIteratorByCTime struct { heap *binaryheap.Heap seenExternal map[plumbing.Hash]bool seen map[plumbing.Hash]bool } // NewCommitNodeIterCTime returns a CommitNodeIter that walks the commit history, // starting at the given commit and visiting its parents while preserving Committer Time order. // this appears to be the closest order to `git log` // The given callback will be called for each visited commit. Each commit will // be visited only once. If the callback returns an error, walking will stop // and will return the error. Other errors might be returned if the history // cannot be traversed (e.g. missing objects). Ignore allows to skip some // commits from being iterated. func NewCommitNodeIterCTime( c CommitNode, seenExternal map[plumbing.Hash]bool, ignore []plumbing.Hash, ) CommitNodeIter { seen := make(map[plumbing.Hash]bool) for _, h := range ignore { seen[h] = true } heap := binaryheap.NewWith(func(a, b interface{}) int { if a.(CommitNode).CommitTime().Before(b.(CommitNode).CommitTime()) { return 1 } return -1 }) heap.Push(c) return &commitNodeIteratorByCTime{ heap: heap, seenExternal: seenExternal, seen: seen, } } func (w *commitNodeIteratorByCTime) Next() (CommitNode, error) { var c CommitNode for { cIn, ok := w.heap.Pop() if !ok { return nil, io.EOF } c = cIn.(CommitNode) cID := c.ID() if w.seen[cID] || w.seenExternal[cID] { continue } w.seen[cID] = true for i, h := range c.ParentHashes() { if w.seen[h] || w.seenExternal[h] { continue } pc, err := c.ParentNode(i) if err != nil { return nil, err } w.heap.Push(pc) } return c, nil } } func (w *commitNodeIteratorByCTime) ForEach(cb func(CommitNode) error) error { for { c, err := w.Next() if err == io.EOF { break } if err != nil { return err } err = cb(c) if err == storer.ErrStop { break } if err != nil { return err } } return nil } func (w *commitNodeIteratorByCTime) Close() {}