@@ -186,98 +186,104 @@ func (w *commitPostIterator) Close() {}
186186
187187// commitAllIterator stands for commit iterator for all refs.
188188type commitAllIterator struct {
189- // el points to the current commit.
190- el * list.Element
189+ // currCommit points to the current commit.
190+ currCommit * list.Element
191191}
192192
193193// NewCommitAllIter returns a new commit iterator for all refs.
194- // s is a repo Storer used to get commits and references.
195- // fn is a commit iterator function, used to iterate through ref commits in chosen order
196- func NewCommitAllIter (s storage.Storer , fn func (* Commit ) CommitIter ) (CommitIter , error ) {
197- l := list .New ()
198- m := make (map [plumbing.Hash ]* list.Element )
199-
200- // ...along with the HEAD
201- head , err := storer .ResolveReference (s , plumbing .HEAD )
194+ // repoStorer is a repo Storer used to get commits and references.
195+ // commitIterFunc is a commit iterator function, used to iterate through ref commits in chosen order
196+ func NewCommitAllIter (repoStorer storage.Storer , commitIterFunc func (* Commit ) CommitIter ) (CommitIter , error ) {
197+ commitsPath := list .New ()
198+ commitsLookup := make (map [plumbing.Hash ]* list.Element )
199+ head , err := storer .ResolveReference (repoStorer , plumbing .HEAD )
202200 if err != nil {
203201 return nil , err
204202 }
205- headCommit , err := GetCommit (s , head .Hash ())
206- if err != nil {
203+
204+ // add all references along with the HEAD
205+ if err = addReference (repoStorer , commitIterFunc , head , commitsPath , commitsLookup ); err != nil {
207206 return nil , err
208207 }
209- err = fn (headCommit ).ForEach (func (c * Commit ) error {
210- el := l .PushBack (c )
211- m [c .Hash ] = el
212- return nil
213- })
208+ refIter , err := repoStorer .IterReferences ()
214209 if err != nil {
215210 return nil , err
216211 }
217-
218- refIter , err := s .IterReferences ()
212+ defer refIter .Close ()
213+ err = refIter .ForEach (
214+ func (ref * plumbing.Reference ) error {
215+ return addReference (repoStorer , commitIterFunc , ref , commitsPath , commitsLookup )
216+ },
217+ )
219218 if err != nil {
220219 return nil , err
221220 }
222- defer refIter .Close ()
223- err = refIter .ForEach (func (r * plumbing.Reference ) error {
224- if r .Hash () == head .Hash () {
225- // we already have the HEAD
226- return nil
227- }
228221
229- el , ok := m [r .Hash ()]
230- if ok {
231- return nil
232- }
222+ return & commitAllIterator {commitsPath .Front ()}, nil
223+ }
224+
225+ func addReference (
226+ repoStorer storage.Storer ,
227+ commitIterFunc func (* Commit ) CommitIter ,
228+ ref * plumbing.Reference ,
229+ commitsPath * list.List ,
230+ commitsLookup map [plumbing.Hash ]* list.Element ) error {
231+
232+ _ , exists := commitsLookup [ref .Hash ()]
233+ if exists {
234+ // we already have it - skip the reference.
235+ return nil
236+ }
233237
234- var refCommits [] * Commit
235- c , _ := GetCommit ( s , r . Hash ())
238+ refCommit , _ := GetCommit ( repoStorer , ref . Hash ())
239+ if refCommit == nil {
236240 // if it's not a commit - skip it.
237- if c == nil {
238- return nil
241+ return nil
242+ }
243+
244+ var (
245+ refCommits []* Commit
246+ parent * list.Element
247+ )
248+ // collect all ref commits to add
249+ commitIter := commitIterFunc (refCommit )
250+ for c , e := commitIter .Next (); e == nil ; {
251+ parent , exists = commitsLookup [c .Hash ]
252+ if exists {
253+ break
239254 }
240- cit := fn (c )
241- for c , e := cit .Next (); e == nil ; {
242- el , ok = m [c .Hash ]
243- if ok {
244- break
245- }
246- refCommits = append (refCommits , c )
247- c , e = cit .Next ()
255+ refCommits = append (refCommits , c )
256+ c , e = commitIter .Next ()
257+ }
258+ commitIter .Close ()
259+
260+ if parent == nil {
261+ // common parent - not found
262+ // add all commits to the path from this ref (maybe it's a HEAD and we don't have anything, yet)
263+ for _ , c := range refCommits {
264+ parent = commitsPath .PushBack (c )
265+ commitsLookup [c .Hash ] = parent
248266 }
249- cit .Close ()
250-
251- if el == nil {
252- // push back all commits from this ref.
253- for _ , c := range refCommits {
254- el = l .PushBack (c )
255- m [c .Hash ] = el
256- }
257- } else {
258- // insert ref's commits into the list
259- for i := len (refCommits ) - 1 ; i >= 0 ; i -- {
260- c := refCommits [i ]
261- el = l .InsertBefore (c , el )
262- m [c .Hash ] = el
263- }
267+ } else {
268+ // add ref's commits to the path in reverse order (from the latest)
269+ for i := len (refCommits ) - 1 ; i >= 0 ; i -- {
270+ c := refCommits [i ]
271+ // insert before found common parent
272+ parent = commitsPath .InsertBefore (c , parent )
273+ commitsLookup [c .Hash ] = parent
264274 }
265- return nil
266- })
267- if err != nil {
268- return nil , err
269275 }
270276
271- return & commitAllIterator { l . Front ()}, nil
277+ return nil
272278}
273279
274280func (it * commitAllIterator ) Next () (* Commit , error ) {
275- if it .el == nil {
281+ if it .currCommit == nil {
276282 return nil , io .EOF
277283 }
278284
279- c := it .el .Value .(* Commit )
280- it .el = it .el .Next ()
285+ c := it .currCommit .Value .(* Commit )
286+ it .currCommit = it .currCommit .Next ()
281287
282288 return c , nil
283289}
@@ -305,5 +311,5 @@ func (it *commitAllIterator) ForEach(cb func(*Commit) error) error {
305311}
306312
307313func (it * commitAllIterator ) Close () {
308- it .el = nil
314+ it .currCommit = nil
309315}
0 commit comments