1package commitgraph
2
3import (
4 "io"
5
6 "github.com/go-git/go-git/v5/plumbing"
7 "github.com/go-git/go-git/v5/plumbing/storer"
8
9 "github.com/emirpasic/gods/trees/binaryheap"
10)
11
12type commitNodeIteratorByCTime struct {
13 heap *binaryheap.Heap
14 seenExternal map[plumbing.Hash]bool
15 seen map[plumbing.Hash]bool
16}
17
18// NewCommitNodeIterCTime returns a CommitNodeIter that walks the commit history,
19// starting at the given commit and visiting its parents while preserving Committer Time order.
20// this is close in order to `git log` but does not guarantee topological order and will
21// order things incorrectly occasionally.
22// The given callback will be called for each visited commit. Each commit will
23// be visited only once. If the callback returns an error, walking will stop
24// and will return the error. Other errors might be returned if the history
25// cannot be traversed (e.g. missing objects). Ignore allows to skip some
26// commits from being iterated.
27func NewCommitNodeIterCTime(
28 c CommitNode,
29 seenExternal map[plumbing.Hash]bool,
30 ignore []plumbing.Hash,
31) CommitNodeIter {
32 seen := make(map[plumbing.Hash]bool)
33 for _, h := range ignore {
34 seen[h] = true
35 }
36
37 heap := binaryheap.NewWith(func(a, b interface{}) int {
38 if a.(CommitNode).CommitTime().Before(b.(CommitNode).CommitTime()) {
39 return 1
40 }
41 return -1
42 })
43
44 heap.Push(c)
45
46 return &commitNodeIteratorByCTime{
47 heap: heap,
48 seenExternal: seenExternal,
49 seen: seen,
50 }
51}
52
53func (w *commitNodeIteratorByCTime) Next() (CommitNode, error) {
54 var c CommitNode
55 for {
56 cIn, ok := w.heap.Pop()
57 if !ok {
58 return nil, io.EOF
59 }
60 c = cIn.(CommitNode)
61 cID := c.ID()
62
63 if w.seen[cID] || w.seenExternal[cID] {
64 continue
65 }
66
67 w.seen[cID] = true
68
69 for i, h := range c.ParentHashes() {
70 if w.seen[h] || w.seenExternal[h] {
71 continue
72 }
73 pc, err := c.ParentNode(i)
74 if err != nil {
75 return nil, err
76 }
77 w.heap.Push(pc)
78 }
79
80 return c, nil
81 }
82}
83
84func (w *commitNodeIteratorByCTime) ForEach(cb func(CommitNode) error) error {
85 for {
86 c, err := w.Next()
87 if err == io.EOF {
88 break
89 }
90 if err != nil {
91 return err
92 }
93
94 err = cb(c)
95 if err == storer.ErrStop {
96 break
97 }
98 if err != nil {
99 return err
100 }
101 }
102
103 return nil
104}
105
106func (w *commitNodeIteratorByCTime) Close() {}