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

Commit d68f45f

Browse files
committed
plumbing: use seen map in tree walker
This helps avoids iterating down the same trees for every commit. For a big-ish repo with 35K objects (17K commits), this reduced the time for calling `revlist.Objects` during a push (with 0 hashes to ignore) from more than ten minutes to less than a minute.
1 parent 631a45f commit d68f45f

File tree

5 files changed

+43
-11
lines changed

5 files changed

+43
-11
lines changed

plumbing/object/file.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ type FileIter struct {
8181
// NewFileIter takes a storer.EncodedObjectStorer and a Tree and returns a
8282
// *FileIter that iterates over all files contained in the tree, recursively.
8383
func NewFileIter(s storer.EncodedObjectStorer, t *Tree) *FileIter {
84-
return &FileIter{s: s, w: *NewTreeWalker(t, true)}
84+
return &FileIter{s: s, w: *NewTreeWalker(t, true, nil)}
8585
}
8686

8787
// Next moves the iterator to the next file and returns a pointer to it. If

plumbing/object/tree.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,10 @@ func (iter *treeEntryIter) Next() (TreeEntry, error) {
297297

298298
// TreeWalker provides a means of walking through all of the entries in a Tree.
299299
type TreeWalker struct {
300-
stack []treeEntryIter
300+
stack []*treeEntryIter
301301
base string
302302
recursive bool
303+
seen map[plumbing.Hash]bool
303304

304305
s storer.EncodedObjectStorer
305306
t *Tree
@@ -309,13 +310,14 @@ type TreeWalker struct {
309310
//
310311
// It is the caller's responsibility to call Close() when finished with the
311312
// tree walker.
312-
func NewTreeWalker(t *Tree, recursive bool) *TreeWalker {
313-
stack := make([]treeEntryIter, 0, startingStackSize)
314-
stack = append(stack, treeEntryIter{t, 0})
313+
func NewTreeWalker(t *Tree, recursive bool, seen map[plumbing.Hash]bool) *TreeWalker {
314+
stack := make([]*treeEntryIter, 0, startingStackSize)
315+
stack = append(stack, &treeEntryIter{t, 0})
315316

316317
return &TreeWalker{
317318
stack: stack,
318319
recursive: recursive,
320+
seen: seen,
319321

320322
s: t.s,
321323
t: t,
@@ -358,6 +360,10 @@ func (w *TreeWalker) Next() (name string, entry TreeEntry, err error) {
358360
return
359361
}
360362

363+
if w.seen[entry.Hash] {
364+
continue
365+
}
366+
361367
if entry.Mode == filemode.Dir {
362368
obj, err = GetTree(w.s, entry.Hash)
363369
}
@@ -377,7 +383,7 @@ func (w *TreeWalker) Next() (name string, entry TreeEntry, err error) {
377383
}
378384

379385
if t, ok := obj.(*Tree); ok {
380-
w.stack = append(w.stack, treeEntryIter{t, 0})
386+
w.stack = append(w.stack, &treeEntryIter{t, 0})
381387
w.base = path.Join(w.base, entry.Name)
382388
}
383389

plumbing/object/tree_test.go

+29-3
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func (s *TreeSuite) TestTreeWalkerNext(c *C) {
228228
tree, err := commit.Tree()
229229
c.Assert(err, IsNil)
230230

231-
walker := NewTreeWalker(tree, true)
231+
walker := NewTreeWalker(tree, true, nil)
232232
for _, e := range treeWalkerExpects {
233233
name, entry, err := walker.Next()
234234
if err == io.EOF {
@@ -245,13 +245,39 @@ func (s *TreeSuite) TestTreeWalkerNext(c *C) {
245245
}
246246
}
247247

248+
func (s *TreeSuite) TestTreeWalkerNextSkipSeen(c *C) {
249+
commit, err := GetCommit(s.Storer, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
250+
c.Assert(err, IsNil)
251+
tree, err := commit.Tree()
252+
c.Assert(err, IsNil)
253+
254+
seen := map[plumbing.Hash]bool{
255+
plumbing.NewHash(treeWalkerExpects[0].Hash): true,
256+
}
257+
walker := NewTreeWalker(tree, true, seen)
258+
for _, e := range treeWalkerExpects[1:] {
259+
name, entry, err := walker.Next()
260+
if err == io.EOF {
261+
break
262+
}
263+
264+
c.Assert(err, IsNil)
265+
c.Assert(name, Equals, e.Path)
266+
c.Assert(entry.Name, Equals, e.Name)
267+
c.Assert(entry.Mode, Equals, e.Mode)
268+
c.Assert(entry.Hash.String(), Equals, e.Hash)
269+
270+
c.Assert(walker.Tree().ID().String(), Equals, e.Tree)
271+
}
272+
}
273+
248274
func (s *TreeSuite) TestTreeWalkerNextNonRecursive(c *C) {
249275
commit := s.commit(c, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
250276
tree, err := commit.Tree()
251277
c.Assert(err, IsNil)
252278

253279
var count int
254-
walker := NewTreeWalker(tree, false)
280+
walker := NewTreeWalker(tree, false, nil)
255281
for {
256282
name, entry, err := walker.Next()
257283
if err == io.EOF {
@@ -290,7 +316,7 @@ func (s *TreeSuite) TestTreeWalkerNextSubmodule(c *C) {
290316
}
291317

292318
var count int
293-
walker := NewTreeWalker(tree, true)
319+
walker := NewTreeWalker(tree, true, nil)
294320
defer walker.Close()
295321

296322
for {

plumbing/object/treenoder.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func transformChildren(t *Tree) ([]noder.Noder, error) {
9999
// is bigger than needed.
100100
ret := make([]noder.Noder, 0, len(t.Entries))
101101

102-
walker := NewTreeWalker(t, false) // don't recurse
102+
walker := NewTreeWalker(t, false, nil) // don't recurse
103103
// don't defer walker.Close() for efficiency reasons.
104104
for {
105105
_, e, err = walker.Next()

plumbing/revlist/revlist.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func iterateCommitTrees(
137137

138138
cb(tree.Hash)
139139

140-
treeWalker := object.NewTreeWalker(tree, true)
140+
treeWalker := object.NewTreeWalker(tree, true, seen)
141141

142142
for {
143143
_, e, err := treeWalker.Next()

0 commit comments

Comments
 (0)