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

Commit f9a1c7a

Browse files
authored
Merge pull request #579 from erizocosmico/perf/revlist-no-revisit-ancestors
revlist: do not visit again already visited parents
2 parents a33a60d + bda82e2 commit f9a1c7a

File tree

1 file changed

+38
-11
lines changed

1 file changed

+38
-11
lines changed

plumbing/revlist/revlist.go

+38-11
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ func objects(
3535
ignore []plumbing.Hash,
3636
allowMissingObjects bool,
3737
) ([]plumbing.Hash, error) {
38-
3938
seen := hashListToSet(ignore)
4039
result := make(map[plumbing.Hash]bool)
40+
visited := make(map[plumbing.Hash]bool)
4141

4242
walkerFunc := func(h plumbing.Hash) {
4343
if !seen[h] {
@@ -47,7 +47,7 @@ func objects(
4747
}
4848

4949
for _, h := range objects {
50-
if err := processObject(s, h, seen, ignore, walkerFunc); err != nil {
50+
if err := processObject(s, h, seen, visited, ignore, walkerFunc); err != nil {
5151
if allowMissingObjects && err == plumbing.ErrObjectNotFound {
5252
continue
5353
}
@@ -64,6 +64,7 @@ func processObject(
6464
s storer.EncodedObjectStorer,
6565
h plumbing.Hash,
6666
seen map[plumbing.Hash]bool,
67+
visited map[plumbing.Hash]bool,
6768
ignore []plumbing.Hash,
6869
walkerFunc func(h plumbing.Hash),
6970
) error {
@@ -83,12 +84,12 @@ func processObject(
8384

8485
switch do := do.(type) {
8586
case *object.Commit:
86-
return reachableObjects(do, seen, ignore, walkerFunc)
87+
return reachableObjects(do, seen, visited, ignore, walkerFunc)
8788
case *object.Tree:
8889
return iterateCommitTrees(seen, do, walkerFunc)
8990
case *object.Tag:
9091
walkerFunc(do.Hash)
91-
return processObject(s, do.Target, seen, ignore, walkerFunc)
92+
return processObject(s, do.Target, seen, visited, ignore, walkerFunc)
9293
case *object.Blob:
9394
walkerFunc(do.Hash)
9495
default:
@@ -103,34 +104,60 @@ func processObject(
103104
// objects from the specified commit. To avoid to iterate over seen commits,
104105
// if a commit hash is into the 'seen' set, we will not iterate all his trees
105106
// and blobs objects.
107+
// We assume all commits have the same parents, unless a commit has no parents.
108+
// So when we've visited a commit before, we can stop iterating commits, as we've
109+
// already processed all its ancestors before as well. `visited` keeps track of
110+
// all the commits that have been visited that had parents.
106111
func reachableObjects(
107112
commit *object.Commit,
108113
seen map[plumbing.Hash]bool,
114+
visited map[plumbing.Hash]bool,
109115
ignore []plumbing.Hash,
110-
cb func(h plumbing.Hash)) error {
111-
116+
cb func(h plumbing.Hash),
117+
) error {
112118
i := object.NewCommitPreorderIter(commit, ignore)
113-
return i.ForEach(func(commit *object.Commit) error {
119+
for {
120+
commit, err := i.Next()
121+
if err == io.EOF {
122+
break
123+
}
124+
125+
if err != nil {
126+
return err
127+
}
128+
129+
if visited[commit.Hash] {
130+
break
131+
}
132+
114133
if seen[commit.Hash] {
115-
return nil
134+
continue
116135
}
117136

118137
cb(commit.Hash)
138+
if commit.NumParents() > 0 {
139+
visited[commit.Hash] = true
140+
}
119141

120142
tree, err := commit.Tree()
121143
if err != nil {
122144
return err
123145
}
124146

125-
return iterateCommitTrees(seen, tree, cb)
126-
})
147+
if err := iterateCommitTrees(seen, tree, cb); err != nil {
148+
return err
149+
}
150+
}
151+
152+
return nil
127153
}
128154

129155
// iterateCommitTrees iterate all reachable trees from the given commit
130156
func iterateCommitTrees(
131157
seen map[plumbing.Hash]bool,
132158
tree *object.Tree,
133-
cb func(h plumbing.Hash)) error {
159+
cb func(h plumbing.Hash),
160+
) error {
134161
if seen[tree.Hash] {
135162
return nil
136163
}

0 commit comments

Comments
 (0)