@@ -37,6 +37,7 @@ func objects(
37
37
) ([]plumbing.Hash , error ) {
38
38
seen := hashListToSet (ignore )
39
39
result := make (map [plumbing.Hash ]bool )
40
+ visited := make (map [plumbing.Hash ]bool )
40
41
41
42
walkerFunc := func (h plumbing.Hash ) {
42
43
if ! seen [h ] {
@@ -46,7 +47,7 @@ func objects(
46
47
}
47
48
48
49
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 {
50
51
if allowMissingObjects && err == plumbing .ErrObjectNotFound {
51
52
continue
52
53
}
@@ -63,6 +64,7 @@ func processObject(
63
64
s storer.EncodedObjectStorer ,
64
65
h plumbing.Hash ,
65
66
seen map [plumbing.Hash ]bool ,
67
+ visited map [plumbing.Hash ]bool ,
66
68
ignore []plumbing.Hash ,
67
69
walkerFunc func (h plumbing.Hash ),
68
70
) error {
@@ -82,12 +84,12 @@ func processObject(
82
84
83
85
switch do := do .(type ) {
84
86
case * object.Commit :
85
- return reachableObjects (do , seen , ignore , walkerFunc )
87
+ return reachableObjects (do , seen , visited , ignore , walkerFunc )
86
88
case * object.Tree :
87
89
return iterateCommitTrees (seen , do , walkerFunc )
88
90
case * object.Tag :
89
91
walkerFunc (do .Hash )
90
- return processObject (s , do .Target , seen , ignore , walkerFunc )
92
+ return processObject (s , do .Target , seen , visited , ignore , walkerFunc )
91
93
case * object.Blob :
92
94
walkerFunc (do .Hash )
93
95
default :
@@ -105,10 +107,14 @@ func processObject(
105
107
func reachableObjects (
106
108
commit * object.Commit ,
107
109
seen map [plumbing.Hash ]bool ,
110
+ visited map [plumbing.Hash ]bool ,
108
111
ignore []plumbing.Hash ,
109
112
cb func (h plumbing.Hash ),
110
113
) error {
111
114
i := object .NewCommitPreorderIter (commit , seen , ignore )
115
+ pending := make (map [plumbing.Hash ]bool )
116
+ addPendingParents (pending , visited , commit )
117
+
112
118
for {
113
119
commit , err := i .Next ()
114
120
if err == io .EOF {
@@ -119,6 +125,16 @@ func reachableObjects(
119
125
return err
120
126
}
121
127
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
+
122
138
if seen [commit .Hash ] {
123
139
continue
124
140
}
@@ -138,6 +154,14 @@ func reachableObjects(
138
154
return nil
139
155
}
140
156
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
+
141
165
// iterateCommitTrees iterate all reachable trees from the given commit
142
166
func iterateCommitTrees (
143
167
seen map [plumbing.Hash ]bool ,
0 commit comments