package commitgraph
import (
"math"
"github.com/go-git/go-git/v5/plumbing"
"github.com/emirpasic/gods/trees/binaryheap"
)
// commitNodeStackable represents a common interface between heaps and stacks
type commitNodeStackable interface {
Push(c CommitNode)
Pop() (CommitNode, bool)
Peek() (CommitNode, bool)
Size() int
}
// commitNodeLifo is a stack implementation using an underlying slice
type commitNodeLifo struct {
l []CommitNode
}
// Push pushes a new CommitNode to the stack
func (l *commitNodeLifo) Push(c CommitNode) {
l.l = append(l.l, c)
}
// Pop pops the most recently added CommitNode from the stack
func (l *commitNodeLifo) Pop() (CommitNode, bool) {
if len(l.l) == 0 {
return nil, false
}
c := l.l[len(l.l)-1]
l.l = l.l[:len(l.l)-1]
return c, true
}
// Peek returns the most recently added CommitNode from the stack without removing it
func (l *commitNodeLifo) Peek() (CommitNode, bool) {
if len(l.l) == 0 {
return nil, false
}
return l.l[len(l.l)-1], true
}
// Size returns the number of CommitNodes in the stack
func (l *commitNodeLifo) Size() int {
return len(l.l)
}
// commitNodeHeap is a stack implementation using an underlying binary heap
type commitNodeHeap struct {
*binaryheap.Heap
}
// Push pushes a new CommitNode to the heap
func (h *commitNodeHeap) Push(c CommitNode) {
h.Heap.Push(c)
}
// Pop removes top element on heap and returns it, or nil if heap is empty.
// Second return parameter is true, unless the heap was empty and there was nothing to pop.
func (h *commitNodeHeap) Pop() (CommitNode, bool) {
c, ok := h.Heap.Pop()
if !ok {
return nil, false
}
return c.(CommitNode), true
}
// Peek returns top element on the heap without removing it, or nil if heap is empty.
// Second return parameter is true, unless the heap was empty and there was nothing to peek.
func (h *commitNodeHeap) Peek() (CommitNode, bool) {
c, ok := h.Heap.Peek()
if !ok {
return nil, false
}
return c.(CommitNode), true
}
// Size returns number of elements within the heap.
func (h *commitNodeHeap) Size() int {
return h.Heap.Size()
}
// generationAndDateOrderComparator compares two CommitNode objects based on their generation and commit time.
// If the left CommitNode object is in a higher generation or is newer than the right one, it returns a -1.
// If the left CommitNode object is in a lower generation or is older than the right one, it returns a 1.
// If the two CommitNode objects have the same commit time and generation, it returns 0.
func generationAndDateOrderComparator(left, right interface{}) int {
leftCommit := left.(CommitNode)
rightCommit := right.(CommitNode)
// if GenerationV2 is MaxUint64, then the node is not in the graph
if leftCommit.GenerationV2() == math.MaxUint64 {
if rightCommit.GenerationV2() == math.MaxUint64 {
switch {
case rightCommit.CommitTime().Before(leftCommit.CommitTime()):
return -1
case leftCommit.CommitTime().Before(rightCommit.CommitTime()):
return 1
}
return 0
}
// left is not in the graph, but right is, so it is newer than the right
return -1
}
if rightCommit.GenerationV2() == math.MaxInt64 {
// the right is not in the graph, therefore the left is before the right
return 1
}
if leftCommit.GenerationV2() == 0 || rightCommit.GenerationV2() == 0 {
// We need to assess generation and date
if leftCommit.Generation() < rightCommit.Generation() {
return 1
}
if leftCommit.Generation() > rightCommit.Generation() {
return -1
}
switch {
case rightCommit.CommitTime().Before(leftCommit.CommitTime()):
return -1
case leftCommit.CommitTime().Before(rightCommit.CommitTime()):
return 1
}
return 0
}
if leftCommit.GenerationV2() < rightCommit.GenerationV2() {
return 1
}
if leftCommit.GenerationV2() > rightCommit.GenerationV2() {
return -1
}
return 0
}
// composeIgnores composes the ignore list with the provided seenExternal list
func composeIgnores(ignore []plumbing.Hash, seenExternal map[plumbing.Hash]bool) map[plumbing.Hash]struct{} {
if len(ignore) == 0 {
seen := make(map[plumbing.Hash]struct{})
for h, ext := range seenExternal {
if ext {
seen[h] = struct{}{}
}
}
return seen
}
seen := make(map[plumbing.Hash]struct{})
for _, h := range ignore {
seen[h] = struct{}{}
}
for h, ext := range seenExternal {
if ext {
seen[h] = struct{}{}
}
}
return seen
}