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

Commit 5e0de4e

Browse files
committed
Remove recursive call to 'independents'
Signed-off-by: David Pordomingo <[email protected]>
1 parent 3ad3486 commit 5e0de4e

File tree

2 files changed

+43
-32
lines changed

2 files changed

+43
-32
lines changed

merge_base.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -90,52 +90,53 @@ func ancestorsIndex(excluded, starting *object.Commit) (map[plumbing.Hash]struct
9090
// It mimics the behavior of `git merge-base --independent commit...`.
9191
func Independents(commits []*object.Commit) ([]*object.Commit, error) {
9292
// use sortedByCommitDateDesc strategy
93-
cleaned := sortByCommitDateDesc(commits...)
94-
cleaned = removeDuplicated(cleaned)
95-
return independents(cleaned, map[plumbing.Hash]bool{}, 0)
96-
}
93+
candidates := sortByCommitDateDesc(commits...)
94+
candidates = removeDuplicated(candidates)
9795

98-
func independents(
99-
candidates []*object.Commit,
100-
excluded map[plumbing.Hash]bool,
101-
start int,
102-
) ([]*object.Commit, error) {
103-
if len(candidates) == 1 {
96+
seen := map[plumbing.Hash]struct{}{}
97+
var isLimit object.CommitFilter = func(commit *object.Commit) bool {
98+
_, ok := seen[commit.Hash]
99+
return ok
100+
}
101+
102+
if len(candidates) < 2 {
104103
return candidates, nil
105104
}
106105

107-
res := candidates
108-
for i := start; i < len(candidates); i++ {
109-
from := candidates[i]
110-
others := remove(res, from)
111-
fromHistoryIter := object.NewCommitIterBSF(from, excluded, nil)
106+
pos := 0
107+
for {
108+
from := candidates[pos]
109+
others := remove(candidates, from)
110+
fromHistoryIter := object.NewFilterCommitIter(from, nil, &isLimit)
112111
err := fromHistoryIter.ForEach(func(fromAncestor *object.Commit) error {
113112
for _, other := range others {
114113
if fromAncestor.Hash == other.Hash {
115-
res = remove(res, other)
114+
candidates = remove(candidates, other)
116115
others = remove(others, other)
117116
}
118117
}
119118

120-
if len(res) == 1 {
119+
if len(candidates) == 1 {
121120
return storer.ErrStop
122121
}
123122

124-
excluded[fromAncestor.Hash] = true
123+
seen[fromAncestor.Hash] = struct{}{}
125124
return nil
126125
})
127126

128127
if err != nil {
129128
return nil, err
130129
}
131130

132-
if len(res) < len(candidates) {
133-
return independents(res, excluded, indexOf(res, from)+1)
131+
nextPos := indexOf(candidates, from) + 1
132+
if nextPos >= len(candidates) {
133+
break
134134
}
135135

136+
pos = nextPos
136137
}
137138

138-
return res, nil
139+
return candidates, nil
139140
}
140141

141142
// sortByCommitDateDesc returns the passed commits, sorted by `committer.When desc`

merge_base_test.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func alphabeticSortCommits(commits []*object.Commit) {
3939
4040
The following tests consider this history having two root commits: V and W
4141
42-
V---o---M----AB----A---CD1--R---C--------S-------------------Q < master
42+
V---o---M----AB----A---CD1--P---C--------S-------------------Q < master
4343
\ \ / / /
4444
\ X GQ1---G < feature /
4545
\ / \ / / /
@@ -57,14 +57,16 @@ passed merge-base
5757
5858
Independents
5959
----------------------------
60-
candidates result
61-
A A Only one commit returns it
62-
A, A, A A Repeated commits are ignored
63-
A, A, M, M, N A, N M is reachable from A, no it is not independent
64-
CD1, CD2, M, N CD1, CD2 M and N are reachable from CD2, so they're not
65-
C, G, dev, M, N C, G, dev M and N are reachable from G, so they're not
66-
A, A^, A, N, N^ A, N A^ and N^ are rechable from A and N
67-
60+
candidates result
61+
A A Only one commit returns it
62+
A, A, A A Repeated commits are ignored
63+
A, A, M, M, N A, N M is reachable from A, so it is not independent
64+
S, G, P S, G P is reachable from S, so it is not independent
65+
CD1, CD2, M, N CD1, CD2 M and N are reachable from CD2, so they're not
66+
C, G, dev, M, N C, G, dev M and N are reachable from G, so they're not
67+
C, D, M, N C, D M and N are reachable from C, so they're not
68+
A, A^, A, N, N^ A, N A^ and N^ are rechable from A and N
69+
A^^^, A^, A^^, A, N A, N A^^^, A^^ and A^ are rechable from A, so they're not
6870
6971
IsAncestor
7072
----------------------------
@@ -246,14 +248,22 @@ func (s *orphansSuite) TestIndependentAcrossCrossMerges(c *C) {
246248
s.AssertIndependents(c, revs, expectedRevs)
247249
}
248250

249-
// TestIndependentChangingOrder validates that Independents works well
251+
// TestIndependentChangingOrderRepetition validates that Independents works well
250252
// when the order and repetition is tricky (A, A^, A, N, N^ -> A, N)
251-
func (s *orphansSuite) TestIndependentChangingOrder(c *C) {
253+
func (s *orphansSuite) TestIndependentChangingOrderRepetition(c *C) {
252254
revs := []string{"A", "A^", "A", "N", "N^"}
253255
expectedRevs := []string{"A", "N"}
254256
s.AssertIndependents(c, revs, expectedRevs)
255257
}
256258

259+
// TestIndependentChangingOrder validates that Independents works well
260+
// when the order is tricky (A^^^, A^, A^^, A, N -> A, N)
261+
func (s *orphansSuite) TestIndependentChangingOrder(c *C) {
262+
revs := []string{"A^^^", "A^", "A^^", "A", "N"}
263+
expectedRevs := []string{"A", "N"}
264+
s.AssertIndependents(c, revs, expectedRevs)
265+
}
266+
257267
// TestAncestor validates that IsAncestor returns true if walking from first
258268
// commit, through its parents, it can be reached the second ( A^^, A -> true )
259269
func (s *orphansSuite) TestAncestor(c *C) {

0 commit comments

Comments
 (0)