1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
package commitgraph
import (
"io"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/emirpasic/gods/trees/binaryheap"
)
type commitNodeIteratorTopological struct {
exploreStack commitNodeStackable
visitStack commitNodeStackable
inCounts map[plumbing.Hash]int
ignore map[plumbing.Hash]struct{}
}
// NewCommitNodeIterTopoOrder returns a CommitNodeIter that walks the commit history,
// starting at the given commit and visiting its parents in a topological order but
// with the constraint that no parent is emitted before its children are emitted.
//
// This matches `git log --topo-order`
func NewCommitNodeIterTopoOrder(c CommitNode,
seenExternal map[plumbing.Hash]bool,
ignore []plumbing.Hash,
) CommitNodeIter {
seen := composeIgnores(ignore, seenExternal)
inCounts := make(map[plumbing.Hash]int)
heap := &commitNodeHeap{binaryheap.NewWith(generationAndDateOrderComparator)}
heap.Push(c)
lifo := &commitNodeLifo{make([]CommitNode, 0, 8)}
lifo.Push(c)
return &commitNodeIteratorTopological{
exploreStack: heap,
visitStack: lifo,
inCounts: inCounts,
ignore: seen,
}
}
func (iter *commitNodeIteratorTopological) Next() (CommitNode, error) {
var next CommitNode
for {
var ok bool
next, ok = iter.visitStack.Pop()
if !ok {
return nil, io.EOF
}
if iter.inCounts[next.ID()] == 0 {
break
}
}
minimumLevel, generationV2 := next.GenerationV2(), true
if minimumLevel == 0 {
minimumLevel, generationV2 = next.Generation(), false
}
parents := make([]CommitNode, 0, len(next.ParentHashes()))
for i := range next.ParentHashes() {
pc, err := next.ParentNode(i)
if err != nil {
return nil, err
}
parents = append(parents, pc)
if generationV2 {
if pc.GenerationV2() < minimumLevel {
minimumLevel = pc.GenerationV2()
}
continue
}
if pc.Generation() < minimumLevel {
minimumLevel = pc.Generation()
}
}
// EXPLORE
for {
toExplore, ok := iter.exploreStack.Peek()
if !ok {
break
}
if toExplore.ID() != next.ID() && iter.exploreStack.Size() == 1 {
break
}
if generationV2 {
if toExplore.GenerationV2() < minimumLevel {
break
}
} else {
if toExplore.Generation() < minimumLevel {
break
}
}
iter.exploreStack.Pop()
for i, h := range toExplore.ParentHashes() {
if _, has := iter.ignore[h]; has {
continue
}
iter.inCounts[h]++
if iter.inCounts[h] == 1 {
pc, err := toExplore.ParentNode(i)
if err != nil {
return nil, err
}
iter.exploreStack.Push(pc)
}
}
}
// VISIT
for i, h := range next.ParentHashes() {
if _, has := iter.ignore[h]; has {
continue
}
iter.inCounts[h]--
if iter.inCounts[h] == 0 {
iter.visitStack.Push(parents[i])
}
}
delete(iter.inCounts, next.ID())
return next, nil
}
func (iter *commitNodeIteratorTopological) ForEach(cb func(CommitNode) error) error {
for {
obj, err := iter.Next()
if err != nil {
if err == io.EOF {
return nil
}
return err
}
if err := cb(obj); err != nil {
if err == storer.ErrStop {
return nil
}
return err
}
}
}
func (iter *commitNodeIteratorTopological) Close() {
}
|