Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit d0fcd71

Browse files
committed
add LogOrder, LogOptions.Order, implement Order=LogOrderCommitterTime and Order=LogOrderBSF
Signed-off-by: Saeed Rasooli <saeed.gnu@gmail.com>
1 parent 43fe660 commit d0fcd71

File tree

4 files changed

+231
-1
lines changed

4 files changed

+231
-1
lines changed

options.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,12 +307,27 @@ func (o *ResetOptions) Validate(r *Repository) error {
307307
return nil
308308
}
309309

310+
type LogOrder int8
311+
312+
const (
313+
LogOrderDefault LogOrder = iota
314+
LogOrderDFS
315+
LogOrderDFSPost
316+
LogOrderBSF
317+
LogOrderCommitterTime
318+
)
319+
310320
// LogOptions describes how a log action should be performed.
311321
type LogOptions struct {
312322
// When the From option is set the log will only contain commits
313323
// reachable from it. If this option is not set, HEAD will be used as
314324
// the default From.
315325
From plumbing.Hash
326+
327+
// The default traversal algorithm is Depth-first search
328+
// set Order=LogOrderCommitterTime for ordering by committer time (more compatible with `git log`)
329+
// set Order=LogOrderBSF for Breadth-first search
330+
Order LogOrder
316331
}
317332

318333
var (
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package object
2+
3+
import (
4+
"io"
5+
6+
"gopkg.in/src-d/go-git.v4/plumbing"
7+
"gopkg.in/src-d/go-git.v4/plumbing/storer"
8+
)
9+
10+
type bfsCommitIterator struct {
11+
seenExternal map[plumbing.Hash]bool
12+
seen map[plumbing.Hash]bool
13+
queue []*Commit
14+
}
15+
16+
// NewCommitIterBSF returns a CommitIter that walks the commit history,
17+
// starting at the given commit and visiting its parents in pre-order.
18+
// The given callback will be called for each visited commit. Each commit will
19+
// be visited only once. If the callback returns an error, walking will stop
20+
// and will return the error. Other errors might be returned if the history
21+
// cannot be traversed (e.g. missing objects). Ignore allows to skip some
22+
// commits from being iterated.
23+
func NewCommitIterBSF(
24+
c *Commit,
25+
seenExternal map[plumbing.Hash]bool,
26+
ignore []plumbing.Hash,
27+
) CommitIter {
28+
seen := make(map[plumbing.Hash]bool)
29+
for _, h := range ignore {
30+
seen[h] = true
31+
}
32+
33+
return &bfsCommitIterator{
34+
seenExternal: seenExternal,
35+
seen: seen,
36+
queue: []*Commit{c},
37+
}
38+
}
39+
40+
func (w *bfsCommitIterator) appendHash(store storer.EncodedObjectStorer, h plumbing.Hash) error {
41+
if w.seen[h] || w.seenExternal[h] {
42+
return nil
43+
}
44+
c, err := GetCommit(store, h)
45+
if err != nil {
46+
return err
47+
}
48+
w.queue = append(w.queue, c)
49+
return nil
50+
}
51+
52+
func (w *bfsCommitIterator) Next() (*Commit, error) {
53+
var c *Commit
54+
for {
55+
if len(w.queue) == 0 {
56+
return nil, io.EOF
57+
}
58+
c = w.queue[0]
59+
w.queue = w.queue[1:]
60+
61+
if w.seen[c.Hash] || w.seenExternal[c.Hash] {
62+
continue
63+
}
64+
65+
w.seen[c.Hash] = true
66+
67+
for _, h := range c.ParentHashes {
68+
err := w.appendHash(c.s, h)
69+
if err != nil {
70+
return nil, nil
71+
}
72+
}
73+
74+
return c, nil
75+
}
76+
}
77+
78+
func (w *bfsCommitIterator) ForEach(cb func(*Commit) error) error {
79+
for {
80+
c, err := w.Next()
81+
if err == io.EOF {
82+
break
83+
}
84+
if err != nil {
85+
return err
86+
}
87+
88+
err = cb(c)
89+
if err == storer.ErrStop {
90+
break
91+
}
92+
if err != nil {
93+
return err
94+
}
95+
}
96+
97+
return nil
98+
}
99+
100+
func (w *bfsCommitIterator) Close() {}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package object
2+
3+
import (
4+
"io"
5+
6+
"github.com/emirpasic/gods/trees/binaryheap"
7+
8+
"gopkg.in/src-d/go-git.v4/plumbing"
9+
"gopkg.in/src-d/go-git.v4/plumbing/storer"
10+
)
11+
12+
type 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.
26+
func 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+
51+
func (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+
81+
func (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+
103+
func (w *commitIteratorByCTime) Close() {}

repository.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,19 @@ func (r *Repository) Log(o *LogOptions) (object.CommitIter, error) {
728728
return nil, err
729729
}
730730

731-
return object.NewCommitPreorderIter(commit, nil, nil), nil
731+
switch o.Order {
732+
case LogOrderDefault:
733+
return object.NewCommitPreorderIter(commit, nil, nil), nil
734+
case LogOrderDFS:
735+
return object.NewCommitPreorderIter(commit, nil, nil), nil
736+
case LogOrderDFSPost:
737+
return object.NewCommitPostorderIter(commit, nil), nil
738+
case LogOrderBSF:
739+
return object.NewCommitIterBSF(commit, nil, nil), nil
740+
case LogOrderCommitterTime:
741+
return object.NewCommitIterCTime(commit, nil, nil), nil
742+
}
743+
return nil, fmt.Errorf("invalid Order=%v", o.Order)
732744
}
733745

734746
// Tags returns all the References from Tags. This method returns all the tag

0 commit comments

Comments
 (0)