aboutsummaryrefslogblamecommitdiffstats
path: root/plumbing/object/commitnode_walker_ctime.go
blob: 86b6c5765c493c48f1ae20eae0841dbe02f4af49 (plain) (tree)











































































































                                                                                                
package object

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
	nodeIndex    CommitNodeIndex
}

// 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,
	nodeIndex CommitNodeIndex,
	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,
		nodeIndex:    nodeIndex,
	}
}

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 w.nodeIndex.ParentHashes(c) {
			if w.seen[h] || w.seenExternal[h] {
				continue
			}
			pc, err := w.nodeIndex.ParentNode(c, 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() {}