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