aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Sjoding <joshua.sjoding@scjalliance.com>2016-02-16 12:29:06 -0800
committerJoshua Sjoding <joshua.sjoding@scjalliance.com>2016-02-17 04:46:57 -0800
commit9df17e545a445f58c5c43a1ece49bf1ff09e3b02 (patch)
treef1e50178d47e6c4bd68df613a338036280c30c9b
parent6b0a5984ac0c69742e60a39ad9437fd981dbe31b (diff)
downloadgo-git-9df17e545a445f58c5c43a1ece49bf1ff09e3b02.tar.gz
New iteration behavior via FileIter and TreeWalker
Instead of returning a channel of files, Tree.Files() now returns a FileIter with these qualities: * It returns files in the original order of the repository (relying on a * new Tree.OrderedNames property) * It can return errors encountered when retrieving files and trees from * underlying storage * It can be Closed without having to drain the entire channel * It defers the heavy lifting to a new TreeWalker type * Its behavior is a little more consistent with other Iter types * It's a little less prone to memory leaks This update includes a new TreeWalker type that will iterate through all of the entries of a tree and its descendant subtrees. It does the dirty work that Tree.walkEntries() used to do, but with a public API. A new TreeIter type is also included that just walks through subtrees. This could be useful for performing a directory search while ignoring files/blobs altogether.
-rw-r--r--commit_test.go10
-rw-r--r--file.go31
-rw-r--r--file_test.go48
-rw-r--r--formats/packfile/reader_test.go2
-rw-r--r--objects_test.go7
-rw-r--r--remote_test.go2
-rw-r--r--repository.go2
-rw-r--r--tree.go114
-rw-r--r--tree_test.go4
-rw-r--r--tree_walker.go110
-rw-r--r--tree_walker_test.go134
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)
diff --git a/file.go b/file.go
index b0b0141..9ef0e56 100644
--- a/file.go
+++ b/file.go
@@ -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 (
diff --git a/tree.go b/tree.go
index 4e67f0e..9a750e0 100644
--- a/tree.go
+++ b/tree.go
@@ -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)
+ }
+}