@@ -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(
105107func 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 , seen , 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
142166func iterateCommitTrees (
143167 seen map [plumbing.Hash ]bool ,
0 commit comments