@@ -5,20 +5,30 @@ import (
55 "errors"
66 "io"
77 "os"
8- "path"
98 "strconv"
109 "strings"
1110
1211 "gopkg.in/src-d/go-git.v3/core"
1312)
1413
14+ const (
15+ maxTreeDepth = 1024
16+ )
17+
18+ // New errors defined by this package.
19+ var (
20+ ErrMaxTreeDepth = errors .New ("maximum tree depth exceeded" )
21+ ErrFileNotFound = errors .New ("file not found" )
22+ )
23+
1524// Tree is basically like a directory - it references a bunch of other trees
1625// and/or blobs (i.e. files and sub-directories)
1726type Tree struct {
18- Entries map [ string ]TreeEntry
27+ Entries [ ]TreeEntry
1928 Hash core.Hash
2029
2130 r * Repository
31+ m map [string ]* TreeEntry
2232}
2333
2434// TreeEntry represents a file
@@ -28,9 +38,6 @@ type TreeEntry struct {
2838 Hash core.Hash
2939}
3040
31- // New errors defined by this package.
32- var ErrFileNotFound = errors .New ("file not found" )
33-
3441// File returns the hash of the file identified by the `path` argument.
3542// The path is interpreted as relative to the tree receiver.
3643func (t * Tree ) File (path string ) (* File , error ) {
@@ -105,48 +112,19 @@ func (t *Tree) dir(baseName string) (*Tree, error) {
105112var errEntryNotFound = errors .New ("entry not found" )
106113
107114func (t * Tree ) entry (baseName string ) (* TreeEntry , error ) {
108- entry , ok := t .Entries [baseName ]
115+ if t .m == nil {
116+ t .buildMap ()
117+ }
118+ entry , ok := t .m [baseName ]
109119 if ! ok {
110120 return nil , errEntryNotFound
111121 }
112122
113- return & entry , nil
123+ return entry , nil
114124}
115125
116- func (t * Tree ) Files () chan * File {
117- ch := make (chan * File , 1 )
118-
119- go func () {
120- defer func () { close (ch ) }()
121- t .walkEntries ("" , ch )
122- }()
123-
124- return ch
125- }
126-
127- func (t * Tree ) walkEntries (base string , ch chan * File ) {
128- for _ , entry := range t .Entries {
129- obj , err := t .r .Storage .Get (entry .Hash )
130- if err != nil {
131- if err == core .ObjectNotFoundErr {
132- continue // ignore entries without hash (= submodule dirs)
133- }
134- //FIXME: Refactor this function to return an error. Ideally this would be
135- // moved into a FileIter type.
136- }
137-
138- if obj .Type () == core .TreeObject {
139- tree := & Tree {r : t .r }
140- tree .Decode (obj )
141- tree .walkEntries (path .Join (base , entry .Name ), ch )
142- continue
143- }
144-
145- blob := & Blob {}
146- blob .Decode (obj )
147-
148- ch <- & File {Name : path .Join (base , entry .Name ), Reader : blob .Reader (), Hash : entry .Hash }
149- }
126+ func (t * Tree ) Files () * FileIter {
127+ return NewFileIter (t .r , t )
150128}
151129
152130// Decode transform an core.Object into a Tree struct
@@ -160,7 +138,8 @@ func (t *Tree) Decode(o core.Object) error {
160138 return nil
161139 }
162140
163- t .Entries = make (map [string ]TreeEntry )
141+ t .Entries = nil
142+ t .m = nil
164143
165144 r := bufio .NewReader (o .Reader ())
166145 for {
@@ -190,31 +169,69 @@ func (t *Tree) Decode(o core.Object) error {
190169 }
191170
192171 baseName := name [:len (name )- 1 ]
193- t .Entries [ baseName ] = TreeEntry {
172+ t .Entries = append ( t . Entries , TreeEntry {
194173 Hash : hash ,
195174 Mode : os .FileMode (fm ),
196175 Name : baseName ,
197- }
176+ })
198177 }
199178
200179 return nil
201180}
202181
182+ func (t * Tree ) buildMap () {
183+ t .m = make (map [string ]* TreeEntry )
184+ for i := 0 ; i < len (t .Entries ); i ++ {
185+ t .m [t .Entries [i ].Name ] = & t .Entries [i ]
186+ }
187+ }
188+
189+ // TreeEntryIter facilitates iterating through the TreeEntry objects in a Tree.
190+ type TreeEntryIter struct {
191+ t * Tree
192+ pos int
193+ }
194+
195+ func NewTreeEntryIter (t * Tree ) * TreeEntryIter {
196+ return & TreeEntryIter {t , 0 }
197+ }
198+
199+ func (iter * TreeEntryIter ) Next () (TreeEntry , error ) {
200+ if iter .pos >= len (iter .t .Entries ) {
201+ return TreeEntry {}, io .EOF
202+ }
203+ iter .pos ++
204+ return iter .t .Entries [iter .pos - 1 ], nil
205+ }
206+
207+ // TreeEntryIter facilitates iterating through the descendent subtrees of a
208+ // Tree.
203209type TreeIter struct {
204- core.ObjectIter
205- r * Repository
210+ w TreeWalker
206211}
207212
208- func NewTreeIter (r * Repository , iter core.ObjectIter ) * TreeIter {
209- return & TreeIter {iter , r }
213+ func NewTreeIter (r * Repository , t * Tree ) * TreeIter {
214+ return & TreeIter {
215+ w : * NewTreeWalker (r , t ),
216+ }
210217}
211218
212219func (iter * TreeIter ) Next () (* Tree , error ) {
213- obj , err := iter .ObjectIter .Next ()
214- if err != nil {
215- return nil , err
220+ for {
221+ _ , _ , obj , err := iter .w .Next ()
222+ if err != nil {
223+ return nil , err
224+ }
225+
226+ if obj .Type () != core .TreeObject {
227+ // Skip non-tree objects
228+ continue
229+ }
230+
231+ return iter .w .Tree (), nil
216232 }
233+ }
217234
218- tree := & Tree { r : iter . r }
219- return tree , tree . Decode ( obj )
235+ func ( iter * TreeIter ) Close () {
236+ iter . w . Close ( )
220237}
0 commit comments