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

Commit ded2146

Browse files
committed
revlist: do not revisit ancestors as long as all branches are visited
This change is the fixed version of the previous performance improvement that was reverted due to some bogus logic. Now it's fixed and only stops the iteration if and only if all of the branches we've come across have been visited, being a branch a parent commit of a commit we've visited. Signed-off-by: Miguel Molina <[email protected]>
1 parent 032ec28 commit ded2146

File tree

2 files changed

+84
-3
lines changed

2 files changed

+84
-3
lines changed

plumbing/revlist/revlist.go

+27-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func objects(
3737
) ([]plumbing.Hash, error) {
3838
seen := hashListToSet(ignore)
3939
result := make(map[plumbing.Hash]bool)
40+
visited := make(map[plumbing.Hash]bool)
4041

4142
walkerFunc := func(h plumbing.Hash) {
4243
if !seen[h] {
@@ -46,7 +47,7 @@ func objects(
4647
}
4748

4849
for _, h := range objects {
49-
if err := processObject(s, h, seen, ignore, walkerFunc); err != nil {
50+
if err := processObject(s, h, seen, visited, ignore, walkerFunc); err != nil {
5051
if allowMissingObjects && err == plumbing.ErrObjectNotFound {
5152
continue
5253
}
@@ -63,6 +64,7 @@ func processObject(
6364
s storer.EncodedObjectStorer,
6465
h plumbing.Hash,
6566
seen map[plumbing.Hash]bool,
67+
visited map[plumbing.Hash]bool,
6668
ignore []plumbing.Hash,
6769
walkerFunc func(h plumbing.Hash),
6870
) error {
@@ -82,12 +84,12 @@ func processObject(
8284

8385
switch do := do.(type) {
8486
case *object.Commit:
85-
return reachableObjects(do, seen, ignore, walkerFunc)
87+
return reachableObjects(do, seen, visited, ignore, walkerFunc)
8688
case *object.Tree:
8789
return iterateCommitTrees(seen, do, walkerFunc)
8890
case *object.Tag:
8991
walkerFunc(do.Hash)
90-
return processObject(s, do.Target, seen, ignore, walkerFunc)
92+
return processObject(s, do.Target, seen, visited, ignore, walkerFunc)
9193
case *object.Blob:
9294
walkerFunc(do.Hash)
9395
default:
@@ -105,10 +107,14 @@ func processObject(
105107
func reachableObjects(
106108
commit *object.Commit,
107109
seen map[plumbing.Hash]bool,
110+
visited map[plumbing.Hash]bool,
108111
ignore []plumbing.Hash,
109112
cb func(h plumbing.Hash),
110113
) error {
111114
i := object.NewCommitPreorderIter(commit, ignore)
115+
pending := make(map[plumbing.Hash]bool)
116+
addPendingParents(pending, visited, commit)
117+
112118
for {
113119
commit, err := i.Next()
114120
if err == io.EOF {
@@ -119,6 +125,16 @@ func reachableObjects(
119125
return err
120126
}
121127

128+
if pending[commit.Hash] {
129+
delete(pending, commit.Hash)
130+
}
131+
132+
addPendingParents(pending, visited, commit)
133+
134+
if visited[commit.Hash] && len(pending) == 0 {
135+
break
136+
}
137+
122138
if seen[commit.Hash] {
123139
continue
124140
}
@@ -138,6 +154,14 @@ func reachableObjects(
138154
return nil
139155
}
140156

157+
func addPendingParents(pending, visited map[plumbing.Hash]bool, commit *object.Commit) {
158+
for _, p := range commit.ParentHashes {
159+
if !visited[p] {
160+
pending[p] = true
161+
}
162+
}
163+
}
164+
141165
// iterateCommitTrees iterate all reachable trees from the given commit
142166
func iterateCommitTrees(
143167
seen map[plumbing.Hash]bool,

plumbing/revlist/revlist_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,60 @@ func (s *RevListSuite) TestRevListObjectsNewBranch(c *C) {
217217
}
218218
c.Assert(len(remoteHist), Equals, len(revList))
219219
}
220+
221+
// This tests will ensure that a5b8b09 and b8e471f will be visited even if
222+
// 35e8510 has already been visited and will not stop iterating until they
223+
// have been as well.
224+
//
225+
// * af2d6a6 some json
226+
// * 1669dce Merge branch 'master'
227+
// |\
228+
// | * a5b8b09 Merge pull request #1
229+
// | |\
230+
// | | * b8e471f Creating changelog
231+
// | |/
232+
// * | 35e8510 binary file
233+
// |/
234+
// * b029517 Initial commit
235+
func (s *RevListSuite) TestReachableObjectsNoRevisit(c *C) {
236+
obj, err := s.Storer.EncodedObject(plumbing.CommitObject, plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"))
237+
c.Assert(err, IsNil)
238+
239+
do, err := object.DecodeObject(s.Storer, obj)
240+
c.Assert(err, IsNil)
241+
242+
commit, ok := do.(*object.Commit)
243+
c.Assert(ok, Equals, true)
244+
245+
var visited []plumbing.Hash
246+
err = reachableObjects(
247+
commit,
248+
map[plumbing.Hash]bool{
249+
plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"): true,
250+
},
251+
map[plumbing.Hash]bool{
252+
plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"): true,
253+
},
254+
nil,
255+
func(h plumbing.Hash) {
256+
obj, err := s.Storer.EncodedObject(plumbing.AnyObject, h)
257+
c.Assert(err, IsNil)
258+
259+
do, err := object.DecodeObject(s.Storer, obj)
260+
c.Assert(err, IsNil)
261+
262+
if _, ok := do.(*object.Commit); ok {
263+
visited = append(visited, h)
264+
}
265+
},
266+
)
267+
c.Assert(err, IsNil)
268+
269+
c.Assert(visited, DeepEquals, []plumbing.Hash{
270+
plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"),
271+
plumbing.NewHash("1669dce138d9b841a518c64b10914d88f5e488ea"),
272+
plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
273+
plumbing.NewHash("a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69"),
274+
plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"),
275+
})
276+
}

0 commit comments

Comments
 (0)