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

Commit 37b8072

Browse files
authored
Merge pull request #1097 from dpordomingo/merge-base-core
Create merge-base feature
2 parents 91db86b + f62852b commit 37b8072

File tree

4 files changed

+971
-0
lines changed

4 files changed

+971
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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+
// NewFilterCommitIter returns a CommitIter that walks the commit history,
11+
// starting at the passed commit and visiting its parents in Breadth-first order.
12+
// The commits returned by the CommitIter will validate the passed CommitFilter.
13+
// The history won't be transversed beyond a commit if isLimit is true for it.
14+
// Each commit will be visited only once.
15+
// If the commit history can not be traversed, or the Close() method is called,
16+
// the CommitIter won't return more commits.
17+
// If no isValid is passed, all ancestors of from commit will be valid.
18+
// If no isLimit is limmit, all ancestors of all commits will be visited.
19+
func NewFilterCommitIter(
20+
from *Commit,
21+
isValid *CommitFilter,
22+
isLimit *CommitFilter,
23+
) CommitIter {
24+
var validFilter CommitFilter
25+
if isValid == nil {
26+
validFilter = func(_ *Commit) bool {
27+
return true
28+
}
29+
} else {
30+
validFilter = *isValid
31+
}
32+
33+
var limitFilter CommitFilter
34+
if isLimit == nil {
35+
limitFilter = func(_ *Commit) bool {
36+
return false
37+
}
38+
} else {
39+
limitFilter = *isLimit
40+
}
41+
42+
return &filterCommitIter{
43+
isValid: validFilter,
44+
isLimit: limitFilter,
45+
visited: map[plumbing.Hash]struct{}{},
46+
queue: []*Commit{from},
47+
}
48+
}
49+
50+
// CommitFilter returns a boolean for the passed Commit
51+
type CommitFilter func(*Commit) bool
52+
53+
// filterCommitIter implments CommitIter
54+
type filterCommitIter struct {
55+
isValid CommitFilter
56+
isLimit CommitFilter
57+
visited map[plumbing.Hash]struct{}
58+
queue []*Commit
59+
lastErr error
60+
}
61+
62+
// Next returns the next commit of the CommitIter.
63+
// It will return io.EOF if there are no more commits to visit,
64+
// or an error if the history could not be traversed.
65+
func (w *filterCommitIter) Next() (*Commit, error) {
66+
var commit *Commit
67+
var err error
68+
for {
69+
commit, err = w.popNewFromQueue()
70+
if err != nil {
71+
return nil, w.close(err)
72+
}
73+
74+
w.visited[commit.Hash] = struct{}{}
75+
76+
if !w.isLimit(commit) {
77+
err = w.addToQueue(commit.s, commit.ParentHashes...)
78+
if err != nil {
79+
return nil, w.close(err)
80+
}
81+
}
82+
83+
if w.isValid(commit) {
84+
return commit, nil
85+
}
86+
}
87+
}
88+
89+
// ForEach runs the passed callback over each Commit returned by the CommitIter
90+
// until the callback returns an error or there is no more commits to traverse.
91+
func (w *filterCommitIter) ForEach(cb func(*Commit) error) error {
92+
for {
93+
commit, err := w.Next()
94+
if err == io.EOF {
95+
break
96+
}
97+
98+
if err != nil {
99+
return err
100+
}
101+
102+
if err := cb(commit); err == storer.ErrStop {
103+
break
104+
} else if err != nil {
105+
return err
106+
}
107+
}
108+
109+
return nil
110+
}
111+
112+
// Error returns the error that caused that the CommitIter is no longer returning commits
113+
func (w *filterCommitIter) Error() error {
114+
return w.lastErr
115+
}
116+
117+
// Close closes the CommitIter
118+
func (w *filterCommitIter) Close() {
119+
w.visited = map[plumbing.Hash]struct{}{}
120+
w.queue = []*Commit{}
121+
w.isLimit = nil
122+
w.isValid = nil
123+
}
124+
125+
// close closes the CommitIter with an error
126+
func (w *filterCommitIter) close(err error) error {
127+
w.Close()
128+
w.lastErr = err
129+
return err
130+
}
131+
132+
// popNewFromQueue returns the first new commit from the internal fifo queue,
133+
// or an io.EOF error if the queue is empty
134+
func (w *filterCommitIter) popNewFromQueue() (*Commit, error) {
135+
var first *Commit
136+
for {
137+
if len(w.queue) == 0 {
138+
if w.lastErr != nil {
139+
return nil, w.lastErr
140+
}
141+
142+
return nil, io.EOF
143+
}
144+
145+
first = w.queue[0]
146+
w.queue = w.queue[1:]
147+
if _, ok := w.visited[first.Hash]; ok {
148+
continue
149+
}
150+
151+
return first, nil
152+
}
153+
}
154+
155+
// addToQueue adds the passed commits to the internal fifo queue if they weren't seen
156+
// or returns an error if the passed hashes could not be used to get valid commits
157+
func (w *filterCommitIter) addToQueue(
158+
store storer.EncodedObjectStorer,
159+
hashes ...plumbing.Hash,
160+
) error {
161+
for _, hash := range hashes {
162+
if _, ok := w.visited[hash]; ok {
163+
continue
164+
}
165+
166+
commit, err := GetCommit(store, hash)
167+
if err != nil {
168+
return err
169+
}
170+
171+
w.queue = append(w.queue, commit)
172+
}
173+
174+
return nil
175+
}
176+

0 commit comments

Comments
 (0)