diff options
-rw-r--r-- | commit_test.go | 10 | ||||
-rw-r--r-- | file.go | 31 | ||||
-rw-r--r-- | file_test.go | 48 | ||||
-rw-r--r-- | formats/packfile/reader_test.go | 2 | ||||
-rw-r--r-- | objects_test.go | 7 | ||||
-rw-r--r-- | remote_test.go | 2 | ||||
-rw-r--r-- | repository.go | 2 | ||||
-rw-r--r-- | tree.go | 114 | ||||
-rw-r--r-- | tree_test.go | 4 | ||||
-rw-r--r-- | tree_walker.go | 110 | ||||
-rw-r--r-- | tree_walker_test.go | 134 |
11 files changed, 402 insertions, 62 deletions
diff --git a/commit_test.go b/commit_test.go index fbaccd6..62af890 100644 --- a/commit_test.go +++ b/commit_test.go @@ -41,7 +41,7 @@ func (s *SuiteCommit) SetUpSuite(c *C) { } } -var iterTests = []struct { +var commitIterTests = []struct { repo string // the repo name in the test suite's map of fixtures commits []string // the commit hashes to iterate over in the test }{ @@ -59,7 +59,7 @@ var iterTests = []struct { } func (s *SuiteCommit) TestIterSlice(c *C) { - for i, t := range iterTests { + for i, t := range commitIterTests { r := s.repos[t.repo] iter := NewCommitIter(r, core.NewObjectSliceIter(makeObjectSlice(t.commits, r.Storage))) s.checkIter(c, r, i, iter, t.commits) @@ -67,7 +67,7 @@ func (s *SuiteCommit) TestIterSlice(c *C) { } func (s *SuiteCommit) TestIterLookup(c *C) { - for i, t := range iterTests { + for i, t := range commitIterTests { r := s.repos[t.repo] iter := NewCommitIter(r, core.NewObjectLookupIter(r.Storage, makeHashSlice(t.commits))) s.checkIter(c, r, i, iter, t.commits) @@ -85,7 +85,7 @@ func (s *SuiteCommit) checkIter(c *C, r *Repository, subtest int, iter *CommitIt } func (s *SuiteCommit) TestIterSliceClose(c *C) { - for i, t := range iterTests { + for i, t := range commitIterTests { r := s.repos[t.repo] iter := NewCommitIter(r, core.NewObjectSliceIter(makeObjectSlice(t.commits, r.Storage))) s.checkIterClose(c, i, iter) @@ -93,7 +93,7 @@ func (s *SuiteCommit) TestIterSliceClose(c *C) { } func (s *SuiteCommit) TestIterLookupClose(c *C) { - for i, t := range iterTests { + for i, t := range commitIterTests { r := s.repos[t.repo] iter := NewCommitIter(r, core.NewObjectLookupIter(r.Storage, makeHashSlice(t.commits))) s.checkIterClose(c, i, iter) @@ -33,3 +33,34 @@ func (f *File) Lines() []string { } return splits } + +type FileIter struct { + w TreeWalker +} + +func NewFileIter(r *Repository, t *Tree) *FileIter { + return &FileIter{w: *NewTreeWalker(r, t)} +} + +func (iter *FileIter) Next() (*File, error) { + for { + name, entry, obj, err := iter.w.Next() + if err != nil { + return nil, err + } + + if obj.Type() != core.BlobObject { + // Skip non-blob objects + continue + } + + blob := &Blob{} + blob.Decode(obj) + + return &File{Name: name, Reader: blob.Reader(), Hash: entry.Hash}, nil + } +} + +func (iter *FileIter) Close() { + iter.w.Close() +} diff --git a/file_test.go b/file_test.go index 25b4cf8..e3b9b4b 100644 --- a/file_test.go +++ b/file_test.go @@ -1,6 +1,7 @@ package git import ( + "io" "os" "gopkg.in/src-d/go-git.v3/core" @@ -41,6 +42,49 @@ func (s *SuiteFile) SetUpSuite(c *C) { } } +type fileIterExpectedEntry struct { + Name string + Hash string +} + +var fileIterTests = []struct { + repo string // the repo name as in localRepos + commit string // the commit to search for the file + files []fileIterExpectedEntry +}{ + // https://api.github.com/repos/tyba/git-fixture/git/trees/6ecf0ef2c2dffb796033e5a02219af86ec6584e5 + {"https://github.com/tyba/git-fixture.git", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", []fileIterExpectedEntry{ + {".gitignore", "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88"}, + {"CHANGELOG", "d3ff53e0564a9f87d8e84b6e28e5060e517008aa"}, + {"LICENSE", "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f"}, + {"binary.jpg", "d5c0f4ab811897cadf03aec358ae60d21f91c50d"}, + {"go/example.go", "880cd14280f4b9b6ed3986d6671f907d7cc2a198"}, + {"json/long.json", "49c6bb89b17060d7b4deacb7b338fcc6ea2352a9"}, + {"json/short.json", "c8f1d8c61f9da76f4cb49fd86322b6e685dba956"}, + {"php/crappy.php", "9a48f23120e880dfbe41f7c9b7b708e9ee62a492"}, + {"vendor/foo.go", "9dea2395f5403188298c1dabe8bdafe562c491e3"}, + }}, +} + +func (s *SuiteFile) TestIter(c *C) { + for i, t := range fileIterTests { + r := s.repos[t.repo] + commit, err := r.Commit(core.NewHash(t.commit)) + c.Assert(err, IsNil, Commentf("subtest %d: %v (%s)", i, err, t.commit)) + + iter := NewFileIter(r, commit.Tree()) + for k := 0; k < len(t.files); k++ { + expected := t.files[k] + file, err := iter.Next() + c.Assert(err, IsNil, Commentf("subtest %d, iter %d, err=%v", i, k, err)) + c.Assert(file.Name, Equals, expected.Name, Commentf("subtest %d, iter %d, name=%s, expected=%s", i, k, file.Name, expected.Hash)) + c.Assert(file.Hash.String(), Equals, expected.Hash, Commentf("subtest %d, iter %d, hash=%v, expected=%s", i, k, file.Hash.String(), expected.Hash)) + } + _, err = iter.Next() + c.Assert(err, Equals, io.EOF) + } +} + var contentsTests = []struct { repo string // the repo name as in localRepos commit string // the commit to search for the file @@ -154,7 +198,9 @@ func (s *SuiteFile) TestIgnoreEmptyDirEntries(c *C) { commit, err := s.repos[t.repo].Commit(core.NewHash(t.commit)) c.Assert(err, IsNil, Commentf("subtest %d: %v (%s)", i, err, t.commit)) - for file := range commit.Tree().Files() { + iter := commit.Tree().Files() + defer iter.Close() + for file, err := iter.Next(); err == nil; file, err = iter.Next() { _ = file.Contents() // this would probably panic if we are not ignoring empty dirs } diff --git a/formats/packfile/reader_test.go b/formats/packfile/reader_test.go index 040dd02..9ae569d 100644 --- a/formats/packfile/reader_test.go +++ b/formats/packfile/reader_test.go @@ -10,7 +10,7 @@ import ( "time" "gopkg.in/src-d/go-git.v3/core" - "gopkg.in/src-d/go-git.v3/storages/memory" + "gopkg.in/src-d/go-git.v3/storage/memory" "github.com/dustin/go-humanize" . "gopkg.in/check.v1" diff --git a/objects_test.go b/objects_test.go index e8c5d6e..eadf65a 100644 --- a/objects_test.go +++ b/objects_test.go @@ -6,7 +6,7 @@ import ( . "gopkg.in/check.v1" "gopkg.in/src-d/go-git.v3/core" - "gopkg.in/src-d/go-git.v3/storages/memory" + "gopkg.in/src-d/go-git.v3/storage/memory" ) type ObjectsSuite struct { @@ -59,8 +59,9 @@ func (s *ObjectsSuite) TestParseTree(c *C) { c.Assert(tree.Entries[".gitignore"].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88") count := 0 - ch := tree.Files() - for f := range ch { + iter := tree.Files() + defer iter.Close() + for f, err := iter.Next(); err == nil; f, err = iter.Next() { count++ if f.Name == "go/example.go" { content, _ := ioutil.ReadAll(f) diff --git a/remote_test.go b/remote_test.go index d814402..7bba597 100644 --- a/remote_test.go +++ b/remote_test.go @@ -3,7 +3,7 @@ package git import ( "gopkg.in/src-d/go-git.v3/clients/http" "gopkg.in/src-d/go-git.v3/formats/packfile" - "gopkg.in/src-d/go-git.v3/storages/memory" + "gopkg.in/src-d/go-git.v3/storage/memory" . "gopkg.in/check.v1" ) diff --git a/repository.go b/repository.go index 3af45c9..f60f910 100644 --- a/repository.go +++ b/repository.go @@ -7,7 +7,7 @@ import ( "gopkg.in/src-d/go-git.v3/clients/common" "gopkg.in/src-d/go-git.v3/core" "gopkg.in/src-d/go-git.v3/formats/packfile" - "gopkg.in/src-d/go-git.v3/storages/memory" + "gopkg.in/src-d/go-git.v3/storage/memory" ) var ( @@ -5,18 +5,28 @@ import ( "errors" "io" "os" - "path" "strconv" "strings" "gopkg.in/src-d/go-git.v3/core" ) +const ( + maxTreeDepth = 1024 +) + +// New errors defined by this package. +var ( + ErrMaxTreeDepth = errors.New("maximum tree depth exceeded") + ErrFileNotFound = errors.New("file not found") +) + // Tree is basically like a directory - it references a bunch of other trees // and/or blobs (i.e. files and sub-directories) type Tree struct { - Entries map[string]TreeEntry - Hash core.Hash + Entries map[string]TreeEntry + OrderedNames []string + Hash core.Hash r *Repository } @@ -28,9 +38,6 @@ type TreeEntry struct { Hash core.Hash } -// New errors defined by this package. -var ErrFileNotFound = errors.New("file not found") - // File returns the hash of the file identified by the `path` argument. // The path is interpreted as relative to the tree receiver. func (t *Tree) File(path string) (*File, error) { @@ -113,40 +120,8 @@ func (t *Tree) entry(baseName string) (*TreeEntry, error) { return &entry, nil } -func (t *Tree) Files() chan *File { - ch := make(chan *File, 1) - - go func() { - defer func() { close(ch) }() - t.walkEntries("", ch) - }() - - return ch -} - -func (t *Tree) walkEntries(base string, ch chan *File) { - for _, entry := range t.Entries { - obj, err := t.r.Storage.Get(entry.Hash) - if err != nil { - if err == core.ObjectNotFoundErr { - continue // ignore entries without hash (= submodule dirs) - } - //FIXME: Refactor this function to return an error. Ideally this would be - // moved into a FileIter type. - } - - if obj.Type() == core.TreeObject { - tree := &Tree{r: t.r} - tree.Decode(obj) - tree.walkEntries(path.Join(base, entry.Name), ch) - continue - } - - blob := &Blob{} - blob.Decode(obj) - - ch <- &File{Name: path.Join(base, entry.Name), Reader: blob.Reader(), Hash: entry.Hash} - } +func (t *Tree) Files() *FileIter { + return NewFileIter(t.r, t) } // Decode transform an core.Object into a Tree struct @@ -195,26 +170,67 @@ func (t *Tree) Decode(o core.Object) error { Mode: os.FileMode(fm), Name: baseName, } + t.OrderedNames = append(t.OrderedNames, baseName) } return nil } +// TreeEntryIter facilitates iterating through the TreeEntry objects in a Tree. +type TreeEntryIter struct { + t *Tree + pos int +} + +func NewTreeEntryIter(t *Tree) *TreeEntryIter { + return &TreeEntryIter{t, 0} +} + +func (iter *TreeEntryIter) Next() (TreeEntry, error) { + if iter.pos >= len(iter.t.OrderedNames) { + return TreeEntry{}, io.EOF + } + + entry, ok := iter.t.Entries[iter.t.OrderedNames[iter.pos]] + if !ok { + // Probable race condition or internal bug + // FIXME: Report more severe error or panic + return TreeEntry{}, io.EOF + } + + iter.pos++ + + return entry, nil +} + +// TreeEntryIter facilitates iterating through the descendent subtrees of a +// Tree. type TreeIter struct { - core.ObjectIter - r *Repository + w TreeWalker } -func NewTreeIter(r *Repository, iter core.ObjectIter) *TreeIter { - return &TreeIter{iter, r} +func NewTreeIter(r *Repository, t *Tree) *TreeIter { + return &TreeIter{ + w: *NewTreeWalker(r, t), + } } func (iter *TreeIter) Next() (*Tree, error) { - obj, err := iter.ObjectIter.Next() - if err != nil { - return nil, err + for { + _, _, obj, err := iter.w.Next() + if err != nil { + return nil, err + } + + if obj.Type() != core.TreeObject { + // Skip non-tree objects + continue + } + + return iter.w.Tree(), nil } +} - tree := &Tree{r: iter.r} - return tree, tree.Decode(obj) +func (iter *TreeIter) Close() { + iter.w.Close() } diff --git a/tree_test.go b/tree_test.go index bec55dd..803ec56 100644 --- a/tree_test.go +++ b/tree_test.go @@ -192,7 +192,9 @@ func (s *SuiteTree) TestFiles(c *C) { tree := commit.Tree() var output []string - for file := range tree.Files() { + iter := tree.Files() + defer iter.Close() + for file, err := iter.Next(); err == nil; file, err = iter.Next() { output = append(output, file.Name) } sort.Strings(output) diff --git a/tree_walker.go b/tree_walker.go new file mode 100644 index 0000000..3272718 --- /dev/null +++ b/tree_walker.go @@ -0,0 +1,110 @@ +package git + +import ( + "io" + "path" + + "gopkg.in/src-d/go-git.v3/core" +) + +const ( + startingStackSize = 8 +) + +// TreeWalker provides a means of walking through all of the entries in a Tree. +type TreeWalker struct { + stack []TreeEntryIter + base string + + r *Repository +} + +// NewTreeWalker returns a new TreeWalker for the given repository and tree. +// +// It is the caller's responsibility to call Close() when finished with the +// tree walker. +func NewTreeWalker(r *Repository, t *Tree) *TreeWalker { + w := TreeWalker{ + stack: make([]TreeEntryIter, 0, startingStackSize), + base: "", + r: r, + } + w.stack = append(w.stack, *NewTreeEntryIter(t)) + return &w +} + +// Next returns the next object from the tree. Objects are returned in order +// and subtrees are included. After the last object has been returned further +// calls to Next() will return io.EOF. +// +// In the current implementation any objects which cannot be found in the +// underlying repository will be skipped automatically. It is possible that this +// may change in future versions. +func (w *TreeWalker) Next() (name string, entry TreeEntry, obj core.Object, err error) { + for { + current := len(w.stack) - 1 + if current < 0 { + // Nothing left on the stack so we're finished + err = io.EOF + return + } + if current > maxTreeDepth { + // We're probably following bad data or some self-referencing tree + err = ErrMaxTreeDepth + return + } + + entry, err = w.stack[current].Next() + if err == io.EOF { + // Finished with the current tree, move back up to the parent + w.stack = w.stack[:current] + w.base, _ = path.Split(w.base) + w.base = path.Clean(w.base) // Remove trailing slash + continue + } + if err != nil { + return + } + + obj, err = w.r.Storage.Get(entry.Hash) + if err == core.ObjectNotFoundErr { + // FIXME: Avoid doing this here in case the caller actually cares about + // missing objects. + continue // ignore entries without hash (= submodule dirs) + } + + name = path.Join(w.base, entry.Name) + + if err != nil { + return + } + + break + } + + if obj.Type() == core.TreeObject { + tree := &Tree{r: w.r} + err = tree.Decode(obj) + if err != nil { + return + } + w.stack = append(w.stack, *NewTreeEntryIter(tree)) + w.base = path.Join(w.base, entry.Name) + } + + return +} + +// Tree returns the tree that the tree walker most recently operated on. +func (w *TreeWalker) Tree() *Tree { + current := len(w.stack) - 1 + if current < 0 { + return nil + } + return w.stack[current].t +} + +// Close releases any resources used by the TreeWalker. +func (w *TreeWalker) Close() { + w.stack = nil +} diff --git a/tree_walker_test.go b/tree_walker_test.go new file mode 100644 index 0000000..bdae79f --- /dev/null +++ b/tree_walker_test.go @@ -0,0 +1,134 @@ +package git + +import ( + "io" + "os" + "strconv" + + "gopkg.in/src-d/go-git.v3/core" + "gopkg.in/src-d/go-git.v3/formats/packfile" + + . "gopkg.in/check.v1" +) + +type SuiteTreeWalker struct { + repos map[string]*Repository +} + +var _ = Suite(&SuiteTreeWalker{}) + +// create the repositories of the fixtures +func (s *SuiteTreeWalker) SetUpSuite(c *C) { + fixtureRepos := [...]struct { + url string + packfile string + }{ + {"https://github.com/alcortesm/binary-relations.git", "formats/packfile/fixtures/alcortesm-binary-relations.pack"}, + {"https://github.com/Tribler/dispersy.git", "formats/packfile/fixtures/tribler-dispersy.pack"}, + } + s.repos = make(map[string]*Repository, 0) + for _, fixRepo := range fixtureRepos { + s.repos[fixRepo.url] = NewPlainRepository() + + d, err := os.Open(fixRepo.packfile) + c.Assert(err, IsNil) + + r := packfile.NewReader(d) + r.Format = packfile.OFSDeltaFormat // TODO: how to know the format of a pack file ahead of time? + + _, err = r.Read(s.repos[fixRepo.url].Storage) + c.Assert(err, IsNil) + + c.Assert(d.Close(), IsNil) + } +} + +type treeWalkerExpectedEntry struct { + Kind core.ObjectType + Mode string + Name string + Hash string +} + +func (s *SuiteTreeWalker) TestNext(c *C) { + for i, t := range []struct { + repo string // the repo name as in localRepos + commit string // the commit to search for the file + objs []treeWalkerExpectedEntry // the expected objects in the commit + }{ + // https://api.github.com/repos/alcortesm/binary-relations/git/trees/b373f85fa2594d7dcd9989f4a5858a81647fb8ea + {"https://github.com/alcortesm/binary-relations.git", "b373f85fa2594d7dcd9989f4a5858a81647fb8ea", []treeWalkerExpectedEntry{ + {core.BlobObject, "100644", ".gitignore", "7f41905b4d77ab4a9a2d334fcd0fb5db6e8e2183"}, + {core.BlobObject, "100644", "Makefile", "d441e4e769b53cbd4b1215a1387f8c3108bac97d"}, + {core.BlobObject, "100644", "binary-relations.tex", "cb50b067cc8cd9f639611d41416575c991ad8e97"}, + {core.TreeObject, "040000", "imgs-gen", "b33007b7e83a738576c3f44369fe2f674bb23d5d"}, + {core.TreeObject, "040000", "imgs-gen/simple-graph", "056633542b8ee990d6c89b7a812209dba13d6766"}, + {core.BlobObject, "100644", "imgs-gen/simple-graph/Makefile", "49560402c1707f29c159ad14f369027250fb154a"}, + {core.BlobObject, "100644", "imgs-gen/simple-graph/fig.fig", "2c414eb36f0c2e9a2f9c6382d85e63355752170c"}, + {core.TreeObject, "040000", "src", "ec9d27c4df99caec3a817e9c018812a6c56c1b00"}, + {core.TreeObject, "040000", "src/map-slice", "00cefb8e77f7a8c61b99dd2491ff48a3b0b16679"}, + {core.BlobObject, "100644", "src/map-slice/map-slice.go", "12431e98381dd5097e1a19fe53429c72ef1f328e"}, + {core.TreeObject, "040000", "src/simple-arrays", "9a3781b7fd9d2851e2a4488c035ed9ac905aec79"}, + {core.BlobObject, "100644", "src/simple-arrays/simple-arrays.go", "104fbb4b0520c192f2e207a2dfd39162f6cdabf7"}, + }}, + // https://api.github.com/repos/Tribler/dispersy/git/trees/f5a1fca709f760bf75a7adaa480bf0f0e1a547ee + {"https://github.com/Tribler/dispersy.git", "f5a1fca709f760bf75a7adaa480bf0f0e1a547ee", []treeWalkerExpectedEntry{ + {core.BlobObject, "100644", "__init__.py", "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"}, + {core.BlobObject, "100644", "authentication.py", "ca2fb017dce4506c4144ba81d3cbb70563487718"}, + {core.BlobObject, "100644", "bloomfilter.py", "944e8ccc76779aad923f88f4a73b0d4e8999b6ea"}, + {core.BlobObject, "100644", "bootstrap.py", "379a4400b992310f54ea56a4691760bdea8b1592"}, + {core.BlobObject, "100644", "cache.py", "744d48dce50703e7d4ff14531ab2ab77e6b54685"}, + {core.BlobObject, "100644", "callback.py", "f3a380cbe9eb1c02fb305f2bd2fc0fcfb103892f"}, + {core.BlobObject, "100644", "candidate.py", "87309a51d3681bf6c46b22ce044dad41c97d32d2"}, + {core.BlobObject, "100644", "community.py", "38226ffc2139a2349edaf016747d02b199508d41"}, + {core.BlobObject, "100644", "conversion.py", "4e2fcefba40d99c2a6237768ed0fbb8e2e770c83"}, + {core.BlobObject, "100644", "crypto.py", "8a6bb00df982fa806ce18838673ab1ef3fd52fed"}, + {core.BlobObject, "100644", "database.py", "bb484bfd31a92f7775dbd3acf8740abf00bb3d74"}, + {core.BlobObject, "100644", "debug.py", "3743f20d321f7b2b6d3b47211f93317818c3673e"}, + {core.BlobObject, "100644", "debugcommunity.py", "1598ec5a773cc561430c5bb9b87157ef7d3e1c7c"}, + {core.BlobObject, "100644", "decorator.py", "a1e913e674aec5402cc7b4e9fc0801e8155d2cec"}, + {core.BlobObject, "100644", "destination.py", "d5c02588117d260e728d5c64aba885522ba508c5"}, + {core.BlobObject, "100644", "dispersy.py", "63a08602e2ac8294b20543f0c89c75c740bf6c1c"}, + {core.BlobObject, "100644", "dispersydatabase.py", "76dd222444c308c470efabde7ed511825004b4d3"}, + {core.BlobObject, "100644", "distribution.py", "55a11beca7c09013f5b8ff46baa85b15948c756a"}, + {core.BlobObject, "100644", "dprint.py", "fd6a8608d62bf415a65e78c9e1ca8df97513e598"}, + {core.BlobObject, "100644", "encoding.py", "f29b0ebf65f06a0aa7b2ff1aea364f7889c58d56"}, + {core.BlobObject, "100644", "endpoint.py", "5aa76efd3501de522dbdf2e6374440cf64131423"}, + {core.BlobObject, "100644", "member.py", "c114c73f710b4c291305e353b4aa0106fafabd52"}, + {core.BlobObject, "100644", "message.py", "e55bfe0efa851c4e94264dc745141f7f65b1d239"}, + {core.BlobObject, "100644", "meta.py", "0f62db0fb93326daad6b4925a7d12155a1687f67"}, + {core.BlobObject, "100644", "payload.py", "0aef13194c51dab3624665340b33dd4040516c86"}, + {core.BlobObject, "100644", "requestcache.py", "7772c7d81b4b205970cac1a3cdabc2c2deb48b12"}, + {core.BlobObject, "100644", "resolution.py", "525d6ec81c1fb098d2fe12ae0d5b10a368bfcace"}, + {core.BlobObject, "100644", "script.py", "ef64e12cc1a4c0b3a5d42ff1b33adef202f30da3"}, + {core.BlobObject, "100644", "singleton.py", "34662093edf45bbffa91125c13735e37410a185b"}, + {core.BlobObject, "100644", "timeline.py", "826bb5e1802fb5eaf3144a9a195a994920101880"}, + {core.TreeObject, "040000", "tool", "da97281af01b5b2dad1de6c84c5acb44da60ef7a"}, + {core.BlobObject, "100644", "tool/__init__.py", "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"}, + {core.BlobObject, "100644", "tool/callbackscript.py", "eb9f8184ef08d9e031936e61bfa86fb9b45b965c"}, + {core.BlobObject, "100644", "tool/scenarioscript.py", "245c41af66aab8f0a6fd00259b30a47f4d6c00dd"}, + }}, + {"https://github.com/Tribler/dispersy.git", "9d38ff85ca03adcf68dc14f5b68b8994f15229f4", []treeWalkerExpectedEntry(nil)}, + } { + r := s.repos[t.repo] + commit, err := r.Commit(core.NewHash(t.commit)) + c.Assert(err, IsNil, Commentf("subtest %d: %v (%s)", i, err, t.commit)) + + walker := NewTreeWalker(r, commit.Tree()) + for k := 0; k < len(t.objs); k++ { + info := t.objs[k] + mode, err := strconv.ParseInt(info.Mode, 8, 32) + c.Assert(err, IsNil) + name, entry, obj, err := walker.Next() + + c.Assert(err, IsNil, Commentf("subtest %d, iter %d, err=%v", i, k, err)) + c.Assert(name, Equals, info.Name, Commentf("subtest %d, iter %d, name=%v, expected=%s, stack=%v, base=%v", i, k, name, info.Name, walker.stack, walker.base)) + c.Assert(entry.Mode, Equals, os.FileMode(mode), Commentf("subtest %d, iter %d, entry.Mode=%v expected=%v", i, k, entry.Mode, mode)) + c.Assert(obj.Type(), Equals, info.Kind, Commentf("subtest %d, iter %d, obj.Type()=%v expected=%v", i, k, obj.Type(), info.Kind)) + c.Assert(entry.Hash.String(), Equals, info.Hash, Commentf("subtest %d, iter %d, entry.Hash=%v, expected=%s", i, k, entry.Hash, info.Hash)) + c.Assert(obj.Hash().String(), Equals, info.Hash, Commentf("subtest %d, iter %d, obj.Hash()=%v, expected=%s", i, k, obj.Hash(), info.Hash)) + } + _, _, _, err = walker.Next() + c.Assert(err, Equals, io.EOF) + } +} |