1package commitgraph
2
3import (
4 "math"
5
6 "github.com/go-git/go-git/v5/plumbing"
7
8 "github.com/emirpasic/gods/trees/binaryheap"
9)
10
11// commitNodeStackable represents a common interface between heaps and stacks
12type commitNodeStackable interface {
13 Push(c CommitNode)
14 Pop() (CommitNode, bool)
15 Peek() (CommitNode, bool)
16 Size() int
17}
18
19// commitNodeLifo is a stack implementation using an underlying slice
20type commitNodeLifo struct {
21 l []CommitNode
22}
23
24// Push pushes a new CommitNode to the stack
25func (l *commitNodeLifo) Push(c CommitNode) {
26 l.l = append(l.l, c)
27}
28
29// Pop pops the most recently added CommitNode from the stack
30func (l *commitNodeLifo) Pop() (CommitNode, bool) {
31 if len(l.l) == 0 {
32 return nil, false
33 }
34 c := l.l[len(l.l)-1]
35 l.l = l.l[:len(l.l)-1]
36 return c, true
37}
38
39// Peek returns the most recently added CommitNode from the stack without removing it
40func (l *commitNodeLifo) Peek() (CommitNode, bool) {
41 if len(l.l) == 0 {
42 return nil, false
43 }
44 return l.l[len(l.l)-1], true
45}
46
47// Size returns the number of CommitNodes in the stack
48func (l *commitNodeLifo) Size() int {
49 return len(l.l)
50}
51
52// commitNodeHeap is a stack implementation using an underlying binary heap
53type commitNodeHeap struct {
54 *binaryheap.Heap
55}
56
57// Push pushes a new CommitNode to the heap
58func (h *commitNodeHeap) Push(c CommitNode) {
59 h.Heap.Push(c)
60}
61
62// Pop removes top element on heap and returns it, or nil if heap is empty.
63// Second return parameter is true, unless the heap was empty and there was nothing to pop.
64func (h *commitNodeHeap) Pop() (CommitNode, bool) {
65 c, ok := h.Heap.Pop()
66 if !ok {
67 return nil, false
68 }
69 return c.(CommitNode), true
70}
71
72// Peek returns top element on the heap without removing it, or nil if heap is empty.
73// Second return parameter is true, unless the heap was empty and there was nothing to peek.
74func (h *commitNodeHeap) Peek() (CommitNode, bool) {
75 c, ok := h.Heap.Peek()
76 if !ok {
77 return nil, false
78 }
79 return c.(CommitNode), true
80}
81
82// Size returns number of elements within the heap.
83func (h *commitNodeHeap) Size() int {
84 return h.Heap.Size()
85}
86
87// generationAndDateOrderComparator compares two CommitNode objects based on their generation and commit time.
88// If the left CommitNode object is in a higher generation or is newer than the right one, it returns a -1.
89// If the left CommitNode object is in a lower generation or is older than the right one, it returns a 1.
90// If the two CommitNode objects have the same commit time and generation, it returns 0.
91func generationAndDateOrderComparator(left, right interface{}) int {
92 leftCommit := left.(CommitNode)
93 rightCommit := right.(CommitNode)
94
95 // if GenerationV2 is MaxUint64, then the node is not in the graph
96 if leftCommit.GenerationV2() == math.MaxUint64 {
97 if rightCommit.GenerationV2() == math.MaxUint64 {
98 switch {
99 case rightCommit.CommitTime().Before(leftCommit.CommitTime()):
100 return -1
101 case leftCommit.CommitTime().Before(rightCommit.CommitTime()):
102 return 1
103 }
104 return 0
105 }
106 // left is not in the graph, but right is, so it is newer than the right
107 return -1
108 }
109
110 if rightCommit.GenerationV2() == math.MaxInt64 {
111 // the right is not in the graph, therefore the left is before the right
112 return 1
113 }
114
115 if leftCommit.GenerationV2() == 0 || rightCommit.GenerationV2() == 0 {
116 // We need to assess generation and date
117 if leftCommit.Generation() < rightCommit.Generation() {
118 return 1
119 }
120 if leftCommit.Generation() > rightCommit.Generation() {
121 return -1
122 }
123 switch {
124 case rightCommit.CommitTime().Before(leftCommit.CommitTime()):
125 return -1
126 case leftCommit.CommitTime().Before(rightCommit.CommitTime()):
127 return 1
128 }
129 return 0
130 }
131
132 if leftCommit.GenerationV2() < rightCommit.GenerationV2() {
133 return 1
134 }
135 if leftCommit.GenerationV2() > rightCommit.GenerationV2() {
136 return -1
137 }
138
139 return 0
140}
141
142// composeIgnores composes the ignore list with the provided seenExternal list
143func composeIgnores(ignore []plumbing.Hash, seenExternal map[plumbing.Hash]bool) map[plumbing.Hash]struct{} {
144 if len(ignore) == 0 {
145 seen := make(map[plumbing.Hash]struct{})
146 for h, ext := range seenExternal {
147 if ext {
148 seen[h] = struct{}{}
149 }
150 }
151 return seen
152 }
153
154 seen := make(map[plumbing.Hash]struct{})
155 for _, h := range ignore {
156 seen[h] = struct{}{}
157 }
158 for h, ext := range seenExternal {
159 if ext {
160 seen[h] = struct{}{}
161 }
162 }
163 return seen
164}