aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2016-09-22 23:41:48 +0200
committerMáximo Cuadros <mcuadros@gmail.com>2016-09-22 23:41:48 +0200
commit5913aee5004ff5b32c7926dda3af1f946d203f33 (patch)
treec9b3ce9e4439faf0313749690a3a0e496e32447d
parent001bb130fe6186421f3ddcc556854410edd8d95e (diff)
downloadgo-git-5913aee5004ff5b32c7926dda3af1f946d203f33.tar.gz
test coverage improved, Remote.Refs and Repository.Refs returns error, TreeWalker -> TreeIter
-rw-r--r--common_test.go2
-rw-r--r--core/object.go2
-rw-r--r--cshared/tree_cshared.go90
-rw-r--r--examples/remotes/main.go3
-rw-r--r--file.go4
-rw-r--r--remote.go12
-rw-r--r--remote_test.go54
-rw-r--r--repository.go16
-rw-r--r--repository_test.go126
-rw-r--r--tag_test.go24
-rw-r--r--tree.go141
-rw-r--r--tree_diff.go4
-rw-r--r--tree_test.go206
-rw-r--r--tree_walker.go113
-rw-r--r--tree_walker_test.go56
15 files changed, 503 insertions, 350 deletions
diff --git a/common_test.go b/common_test.go
index 6281d0a..90ac612 100644
--- a/common_test.go
+++ b/common_test.go
@@ -88,7 +88,7 @@ func (p *MockGitUploadPackService) Info() (*common.GitUploadPackInfo, error) {
return nil, errors.New("not connected")
}
- h := core.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
+ h := fixtures.ByURL(p.endpoint.String()).One().Head
c := common.NewCapabilities()
c.Decode("6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEADmulti_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress include-tag multi_ack_detailed no-done symref=HEAD:refs/heads/master agent=git/2:2.4.8~dbussink-fix-enterprise-tokens-compilation-1167-gc7006cf")
diff --git a/core/object.go b/core/object.go
index 60407a1..5b8b16b 100644
--- a/core/object.go
+++ b/core/object.go
@@ -218,7 +218,7 @@ func (iter *ObjectSliceIter) ForEach(cb func(Object) error) error {
return nil
}
- return nil
+ return err
}
}
diff --git a/cshared/tree_cshared.go b/cshared/tree_cshared.go
index ddaf0f1..4974904 100644
--- a/cshared/tree_cshared.go
+++ b/cshared/tree_cshared.go
@@ -1,14 +1,21 @@
-// +build ignore
+// Created by cgo - DO NOT EDIT
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:2
package main
-import (
- "C"
+/*
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:5
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:4
+import (
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:7
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/core"
)
-//export c_Tree_get_Entries_len
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:13
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:12
func c_Tree_get_Entries_len(t uint64) int {
obj, ok := GetObject(Handle(t))
if !ok {
@@ -18,19 +25,23 @@ func c_Tree_get_Entries_len(t uint64) int {
return len(tree.Entries)
}
-//export c_Tree_get_Entries_item
-func c_Tree_get_Entries_item(t uint64, index int) (*C.char, uint32, *C.char) {
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:23
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:22
+func c_Tree_get_Entries_item(t uint64, index int) (*_Ctype_char, uint32, *_Ctype_char) {
obj, ok := GetObject(Handle(t))
if !ok {
return nil, 0, nil
}
tree := obj.(*git.Tree)
item := tree.Entries[index]
- return C.CString(item.Name), uint32(item.Mode), CBytes(item.Hash[:])
+ return _Cfunc_CString(item.Name), uint32(item.Mode), CBytes(item.Hash[:])
}
-//export c_Tree_get_Hash
-func c_Tree_get_Hash(t uint64) *C.char {
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:34
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:33
+func c_Tree_get_Hash(t uint64) *_Ctype_char {
obj, ok := GetObject(Handle(t))
if !ok {
return nil
@@ -39,21 +50,25 @@ func c_Tree_get_Hash(t uint64) *C.char {
return CBytes(tree.Hash[:])
}
-//export c_Tree_File
-func c_Tree_File(t uint64, path string) (uint64, int, *C.char) {
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:44
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:43
+func c_Tree_File(t uint64, path string) (uint64, int, *_Ctype_char) {
obj, ok := GetObject(Handle(t))
if !ok {
- return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ return IH, ErrorCodeNotFound, _Cfunc_CString(MessageNotFound)
}
tree := obj.(*git.Tree)
file, err := tree.File(CopyString(path))
if err != nil {
- return IH, ErrorCodeInternal, C.CString(err.Error())
+ return IH, ErrorCodeInternal, _Cfunc_CString(err.Error())
}
return uint64(RegisterObject(file)), ErrorCodeSuccess, nil
}
-//export c_Tree_Type
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:58
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:57
func c_Tree_Type(t uint64) int8 {
obj, ok := GetObject(Handle(t))
if !ok {
@@ -63,7 +78,9 @@ func c_Tree_Type(t uint64) int8 {
return int8(tree.Type())
}
-//export c_Tree_Files
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:68
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:67
func c_Tree_Files(t uint64) uint64 {
obj, ok := GetObject(Handle(t))
if !ok {
@@ -74,22 +91,26 @@ func c_Tree_Files(t uint64) uint64 {
return uint64(RegisterObject(iter))
}
-//export c_Tree_Decode
-func c_Tree_Decode(o uint64) (uint64, int, *C.char) {
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:79
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:78
+func c_Tree_Decode(o uint64) (uint64, int, *_Ctype_char) {
obj, ok := GetObject(Handle(o))
if !ok {
- return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ return IH, ErrorCodeNotFound, _Cfunc_CString(MessageNotFound)
}
cobj := obj.(*core.Object)
tree := git.Tree{}
err := tree.Decode(*cobj)
if err != nil {
- return IH, ErrorCodeInternal, C.CString(err.Error())
+ return IH, ErrorCodeInternal, _Cfunc_CString(err.Error())
}
return uint64(RegisterObject(&tree)), ErrorCodeSuccess, nil
}
-//export c_NewTreeWalker
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:94
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:93
func c_NewTreeWalker(r uint64, t uint64) uint64 {
obj, ok := GetObject(Handle(r))
if !ok {
@@ -101,41 +122,48 @@ func c_NewTreeWalker(r uint64, t uint64) uint64 {
return IH
}
tree := obj.(*git.Tree)
- walker := git.NewTreeWalker(repo, tree)
+ walker := git.NewTreeIter(repo, tree)
return uint64(RegisterObject(walker))
}
-//export c_TreeWalker_Next
-func c_TreeWalker_Next(tw uint64) (*C.char, *C.char, uint32, *C.char, int, *C.char) {
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:110
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:109
+func c_TreeWalker_Next(tw uint64) (*_Ctype_char, *_Ctype_char, uint32, *_Ctype_char, int, *_Ctype_char) {
obj, ok := GetObject(Handle(tw))
if !ok {
- return nil, nil, 0, nil, ErrorCodeNotFound, C.CString(MessageNotFound)
+ return nil, nil, 0, nil, ErrorCodeNotFound, _Cfunc_CString(MessageNotFound)
}
- walker := obj.(*git.TreeWalker)
+ walker := obj.(*git.TreeIter)
name, entry, err := walker.Next()
if err != nil {
- return nil, nil, 0, nil, ErrorCodeInternal, C.CString(err.Error())
+ return nil, nil, 0, nil, ErrorCodeInternal, _Cfunc_CString(err.Error())
}
- return C.CString(name), C.CString(entry.Name), uint32(entry.Mode),
+ return _Cfunc_CString(name), _Cfunc_CString(entry.Name), uint32(entry.Mode),
CBytes(entry.Hash[:]), ErrorCodeSuccess, nil
}
-//export c_TreeWalker_Tree
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:125
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:124
func c_TreeWalker_Tree(tw uint64) uint64 {
obj, ok := GetObject(Handle(tw))
if !ok {
return IH
}
- walker := obj.(*git.TreeWalker)
+ walker := obj.(*git.TreeIter)
return uint64(RegisterObject(walker.Tree()))
}
-//export c_TreeWalker_Close
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:135
+
+//line /home/mcuadros/workspace/go/src/gopkg.in/src-d/go-git.v4/cshared/tree_cshared.go:134
func c_TreeWalker_Close(tw uint64) {
obj, ok := GetObject(Handle(tw))
if !ok {
return
}
- walker := obj.(*git.TreeWalker)
+ walker := obj.(*git.TreeIter)
walker.Close()
}
+*/
diff --git a/examples/remotes/main.go b/examples/remotes/main.go
index 88662cd..552d6a5 100644
--- a/examples/remotes/main.go
+++ b/examples/remotes/main.go
@@ -45,7 +45,8 @@ func main() {
// > git show-ref
color.Blue("git show-ref")
- r.Refs().ForEach(func(ref *core.Reference) error {
+ refs, _ := r.Refs()
+ refs.ForEach(func(ref *core.Reference) error {
// The HEAD is ommitted in a `git show-ref` so we ignore the symbolic
// references, the HEAD
if ref.Type() == core.SymbolicReference {
diff --git a/file.go b/file.go
index 9180528..6ba9d0b 100644
--- a/file.go
+++ b/file.go
@@ -55,11 +55,11 @@ func (f *File) Lines() ([]string, error) {
type FileIter struct {
r *Repository
- w TreeWalker
+ w TreeIter
}
func NewFileIter(r *Repository, t *Tree) *FileIter {
- return &FileIter{r: r, w: *NewTreeWalker(r, t)}
+ return &FileIter{r: r, w: *NewTreeIter(r, t, true)}
}
func (iter *FileIter) Next() (*File, error) {
diff --git a/remote.go b/remote.go
index 3e341fa..a2fa80e 100644
--- a/remote.go
+++ b/remote.go
@@ -114,7 +114,12 @@ func (r *Remote) Fetch(o *FetchOptions) (err error) {
func (r *Remote) getWantedReferences(spec []config.RefSpec) ([]*core.Reference, error) {
var refs []*core.Reference
- return refs, r.Refs().ForEach(func(ref *core.Reference) error {
+ iter, err := r.Refs()
+ if err != nil {
+ return refs, err
+ }
+
+ return refs, iter.ForEach(func(ref *core.Reference) error {
if ref.Type() != core.HashReference {
return nil
}
@@ -220,9 +225,8 @@ func (r *Remote) Ref(name core.ReferenceName, resolved bool) (*core.Reference, e
}
// Refs returns a map with all the References
-func (r *Remote) Refs() core.ReferenceIter {
- i, _ := r.upInfo.Refs.Iter()
- return i
+func (r *Remote) Refs() (core.ReferenceIter, error) {
+ return r.upInfo.Refs.Iter()
}
// Disconnect from the remote and save the config
diff --git a/remote_test.go b/remote_test.go
index e19f7b8..12e4625 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -1,9 +1,14 @@
package git
import (
+ "io/ioutil"
+ "os"
+
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/core"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
"gopkg.in/src-d/go-git.v4/storage/memory"
+ "gopkg.in/src-d/go-git.v4/utils/fs"
. "gopkg.in/check.v1"
)
@@ -14,10 +19,6 @@ type RemoteSuite struct {
var _ = Suite(&RemoteSuite{})
-func (s *RemoteSuite) SetUpSuite(c *C) {
- s.installMockProtocol(c)
-}
-
func (s *RemoteSuite) TestConnect(c *C) {
r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: RepositoryFixture})
@@ -80,6 +81,38 @@ func (s *RemoteSuite) TestFetch(c *C) {
c.Assert(sto.ObjectStorage().(*memory.ObjectStorage).Objects, HasLen, 31)
}
+func (s *RemoteSuite) TestFetchObjectStorageWriter(c *C) {
+ dir, err := ioutil.TempDir("", "fetch")
+ c.Assert(err, IsNil)
+
+ defer os.RemoveAll(dir) // clean up
+
+ var sto Storage
+ sto, err = filesystem.NewStorage(fs.NewOS(dir))
+ c.Assert(err, IsNil)
+
+ r := newRemote(sto, &config.RemoteConfig{Name: "foo", URL: RepositoryFixture})
+ r.upSrv = &MockGitUploadPackService{}
+
+ c.Assert(r.Connect(), IsNil)
+
+ err = r.Fetch(&FetchOptions{
+ RefSpecs: []config.RefSpec{config.DefaultRefSpec},
+ })
+
+ c.Assert(err, IsNil)
+
+ var count int
+ iter, err := sto.ObjectStorage().Iter(core.AnyObject)
+ c.Assert(err, IsNil)
+
+ iter.ForEach(func(core.Object) error {
+ count++
+ return nil
+ })
+ c.Assert(count, Equals, 31)
+}
+
func (s *RemoteSuite) TestFetchNoErrAlreadyUpToDate(c *C) {
sto := memory.NewStorage()
r := newRemote(sto, &config.RemoteConfig{Name: "foo", URL: RepositoryFixture})
@@ -128,5 +161,16 @@ func (s *RemoteSuite) TestRefs(c *C) {
err := r.Connect()
c.Assert(err, IsNil)
- c.Assert(r.Refs(), NotNil)
+
+ iter, err := r.Refs()
+ c.Assert(err, IsNil)
+ c.Assert(iter, NotNil)
+}
+
+func (s *RemoteSuite) TestString(c *C) {
+ r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: RepositoryFixture})
+ c.Assert(r.String(), Equals, ""+
+ "foo\thttps://github.com/git-fixtures/basic.git (fetch)\n"+
+ "foo\thttps://github.com/git-fixtures/basic.git (push)",
+ )
}
diff --git a/repository.go b/repository.go
index 11b7fae..4d6b4e3 100644
--- a/repository.go
+++ b/repository.go
@@ -178,8 +178,13 @@ func (r *Repository) createReferences(ref *core.Reference) error {
// IsEmpty returns true if the repository is empty
func (r *Repository) IsEmpty() (bool, error) {
+ iter, err := r.Refs()
+ if err != nil {
+ return false, err
+ }
+
var count int
- return count == 0, r.Refs().ForEach(func(r *core.Reference) error {
+ return count == 0, iter.ForEach(func(r *core.Reference) error {
count++
return nil
})
@@ -328,11 +333,6 @@ func (r *Repository) Ref(name core.ReferenceName, resolved bool) (*core.Referenc
}
// Refs returns a map with all the References
-func (r *Repository) Refs() core.ReferenceIter {
- i, err := r.s.ReferenceStorage().Iter()
- if err != nil {
- panic(err)
- }
-
- return i
+func (r *Repository) Refs() (core.ReferenceIter, error) {
+ return r.s.ReferenceStorage().Iter()
}
diff --git a/repository_test.go b/repository_test.go
index 4567597..07fa763 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -1,32 +1,15 @@
package git
import (
+ "gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/core"
+ "gopkg.in/src-d/go-git.v4/storage/memory"
. "gopkg.in/check.v1"
)
-var dirFixturesInit = [...]struct {
- name string
- tgz string
- head string
-}{
- {
- name: "binrels",
- tgz: "storage/filesystem/internal/dotgit/fixtures/alcortesm-binary-relations.tgz",
- head: "c44b5176e99085c8fe36fa27b045590a7b9d34c9",
- },
-}
-
-type dirFixture struct {
- path string
- head core.Hash
-}
-
type RepositorySuite struct {
BaseSuite
- repos map[string]*Repository
- dirFixtures map[string]dirFixture
}
var _ = Suite(&RepositorySuite{})
@@ -36,6 +19,47 @@ func (s *RepositorySuite) TestNewRepository(c *C) {
c.Assert(r, NotNil)
}
+func (s *RepositorySuite) TestCreateRemoteAndRemote(c *C) {
+ r := NewMemoryRepository()
+ remote, err := r.CreateRemote(&config.RemoteConfig{
+ Name: "foo",
+ URL: "http://foo/foo.git",
+ })
+
+ c.Assert(err, IsNil)
+ c.Assert(remote.Config().Name, Equals, "foo")
+
+ alt, err := r.Remote("foo")
+ c.Assert(err, IsNil)
+ c.Assert(alt, Not(Equals), remote)
+ c.Assert(alt.Config().Name, Equals, "foo")
+}
+
+func (s *RepositorySuite) TestCreateRemoteInvalid(c *C) {
+ r := NewMemoryRepository()
+ remote, err := r.CreateRemote(&config.RemoteConfig{})
+
+ c.Assert(err, Equals, config.ErrRemoteConfigEmptyName)
+ c.Assert(remote, IsNil)
+}
+
+func (s *RepositorySuite) TestDeleteRemote(c *C) {
+ r := NewMemoryRepository()
+ _, err := r.CreateRemote(&config.RemoteConfig{
+ Name: "foo",
+ URL: "http://foo/foo.git",
+ })
+
+ c.Assert(err, IsNil)
+
+ err = r.DeleteRemote("foo")
+ c.Assert(err, IsNil)
+
+ alt, err := r.Remote("foo")
+ c.Assert(err, Equals, config.ErrRemoteConfigNotFound)
+ c.Assert(alt, IsNil)
+}
+
func (s *RepositorySuite) TestClone(c *C) {
r := NewMemoryRepository()
@@ -179,6 +203,39 @@ func (s *RepositorySuite) TestCloneDetachedHEAD(c *C) {
c.Assert(head.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
}
+func (s *RepositorySuite) TestPull(c *C) {
+ r := NewMemoryRepository()
+ err := r.Clone(&CloneOptions{
+ URL: RepositoryFixture,
+ SingleBranch: true,
+ })
+
+ c.Assert(err, IsNil)
+
+ err = r.Pull(&PullOptions{})
+ c.Assert(err, Equals, NoErrAlreadyUpToDate)
+
+ branch, err := r.Ref("refs/heads/master", false)
+ c.Assert(err, IsNil)
+ c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
+
+ storage := r.s.ObjectStorage().(*memory.ObjectStorage)
+ c.Assert(storage.Objects, HasLen, 31)
+
+ r.CreateRemote(&config.RemoteConfig{
+ Name: "foo",
+ URL: "https://github.com/git-fixtures/tags.git",
+ })
+
+ err = r.Pull(&PullOptions{RemoteName: "foo"})
+ c.Assert(err, IsNil)
+ c.Assert(storage.Objects, HasLen, 38)
+
+ branch, err = r.Ref("refs/heads/master", false)
+ c.Assert(err, IsNil)
+ c.Assert(branch.Hash().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f")
+}
+
func (s *RepositorySuite) TestIsEmpty(c *C) {
r := NewMemoryRepository()
@@ -253,7 +310,6 @@ func (s *RepositorySuite) TestTag(c *C) {
c.Assert(tag.Hash.IsZero(), Equals, false)
c.Assert(tag.Hash, Equals, hash)
c.Assert(tag.Type(), Equals, core.TagObject)
-
}
func (s *RepositorySuite) TestTags(c *C) {
@@ -308,5 +364,33 @@ func (s *RepositorySuite) TestRefs(c *C) {
c.Assert(err, IsNil)
c.Assert(err, IsNil)
- c.Assert(r.Refs(), NotNil)
+
+ iter, err := r.Refs()
+ c.Assert(err, IsNil)
+ c.Assert(iter, NotNil)
+}
+
+func (s *RepositorySuite) TestObject(c *C) {
+ r := NewMemoryRepository()
+ err := r.Clone(&CloneOptions{URL: "https://github.com/spinnaker/spinnaker.git"})
+ c.Assert(err, IsNil)
+
+ hash := core.NewHash("0a3fb06ff80156fb153bcdcc58b5e16c2d27625c")
+ tag, err := r.Tag(hash)
+ c.Assert(err, IsNil)
+
+ c.Assert(tag.Hash.IsZero(), Equals, false)
+ c.Assert(tag.Hash, Equals, hash)
+ c.Assert(tag.Type(), Equals, core.TagObject)
+}
+
+func (s *RepositorySuite) TestObjectNotFound(c *C) {
+ r := NewMemoryRepository()
+ err := r.Clone(&CloneOptions{URL: "https://github.com/git-fixtures/basic.git"})
+ c.Assert(err, IsNil)
+
+ hash := core.NewHash("0a3fb06ff80156fb153bcdcc58b5e16c2d27625c")
+ tag, err := r.Object(core.TagObject, hash)
+ c.Assert(err, DeepEquals, core.ErrObjectNotFound)
+ c.Assert(tag, IsNil)
}
diff --git a/tag_test.go b/tag_test.go
index df3092b..180518a 100644
--- a/tag_test.go
+++ b/tag_test.go
@@ -1,6 +1,7 @@
package git
import (
+ "fmt"
"time"
. "gopkg.in/check.v1"
@@ -112,6 +113,29 @@ func (s *TagSuite) TestObject(c *C) {
c.Assert(obj.ID().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f")
}
+func (s *TagSuite) TestTagItter(c *C) {
+ r := s.Repositories["https://github.com/git-fixtures/tags.git"]
+ iter, err := r.s.ObjectStorage().Iter(core.TagObject)
+ c.Assert(err, IsNil)
+
+ var count int
+ i := NewTagIter(r, iter)
+ err = i.ForEach(func(t *Tag) error {
+ count++
+ return nil
+ })
+
+ c.Assert(err, IsNil)
+ c.Assert(count, Equals, 4)
+
+ i = NewTagIter(r, iter)
+ err = i.ForEach(func(t *Tag) error {
+ return fmt.Errorf("a random error")
+ })
+
+ c.Assert(err, NotNil)
+}
+
func (s *TagSuite) TestTagEncodeDecodeIdempotent(c *C) {
ts, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05-07:00")
c.Assert(err, IsNil)
diff --git a/tree.go b/tree.go
index 462589d..baec587 100644
--- a/tree.go
+++ b/tree.go
@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"os"
+ "path"
"strconv"
"strings"
@@ -13,7 +14,10 @@ import (
)
const (
- maxTreeDepth = 1024
+ maxTreeDepth = 1024
+ startingStackSize = 8
+ submoduleMode = 0160000
+ directoryMode = 0040000
)
// New errors defined by this package.
@@ -49,16 +53,9 @@ func (t *Tree) File(path string) (*File, error) {
obj, err := t.r.s.ObjectStorage().Get(core.BlobObject, e.Hash)
if err != nil {
- if err == core.ErrObjectNotFound {
- return nil, ErrFileNotFound // a git submodule
- }
return nil, err
}
- if obj.Type() != core.BlobObject {
- return nil, ErrFileNotFound // a directory
- }
-
blob := &Blob{}
blob.Decode(obj)
@@ -89,16 +86,9 @@ func (t *Tree) dir(baseName string) (*Tree, error) {
obj, err := t.r.s.ObjectStorage().Get(core.TreeObject, entry.Hash)
if err != nil {
- if err == core.ErrObjectNotFound { // git submodule
- return nil, errDirNotFound
- }
return nil, err
}
- if obj.Type() != core.TreeObject {
- return nil, errDirNotFound // a file
- }
-
tree := &Tree{r: t.r}
tree.Decode(obj)
@@ -266,64 +256,115 @@ func (iter *treeEntryIter) Next() (TreeEntry, error) {
return iter.t.Entries[iter.pos-1], nil
}
-// TreeEntryIter facilitates iterating through the descendent subtrees of a
-// Tree.
+// TreeWalker provides a means of walking through all of the entries in a Tree.
type TreeIter struct {
+ stack []treeEntryIter
+ base string
+ recursive bool
+
r *Repository
- w TreeWalker
+ t *Tree
}
-// NewTreeIter returns a new TreeIter instance
-func NewTreeIter(r *Repository, t *Tree) *TreeIter {
+// NewTreeIter returns a new TreeIter for the given repository and tree.
+//
+// It is the caller's responsibility to call Close() when finished with the
+// tree walker.
+func NewTreeIter(r *Repository, t *Tree, recursive bool) *TreeIter {
+ stack := make([]treeEntryIter, 0, startingStackSize)
+ stack = append(stack, treeEntryIter{t, 0})
+
return &TreeIter{
+ stack: stack,
+ recursive: recursive,
+
r: r,
- w: *NewTreeWalker(r, t),
+ t: t,
}
}
-// Next returns the next Tree from the tree.
-func (iter *TreeIter) Next() (*Tree, error) {
+// 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 *TreeIter) Next() (name string, entry TreeEntry, err error) {
+ var obj Object
for {
- _, entry, err := iter.w.Next()
+ 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 nil, err
+ return
}
- if !entry.Mode.IsDir() {
+ if entry.Mode == submoduleMode {
+ err = nil
continue
}
- return iter.r.Tree(entry.Hash)
- }
-}
+ if entry.Mode.IsDir() {
+ obj, err = w.r.Tree(entry.Hash)
+ }
-// ForEach call the cb function for each tree contained on this iter until
-// an error happends or the end of the iter is reached. If core.ErrStop is sent
-// the iteration is stop but no error is returned. The iterator is closed.
-func (iter *TreeIter) ForEach(cb func(*Tree) error) error {
- defer iter.Close()
+ name = path.Join(w.base, entry.Name)
- for {
- t, err := iter.Next()
if err != nil {
- if err == io.EOF {
- return nil
- }
-
- return err
+ err = io.EOF
+ return
}
- if err := cb(t); err != nil {
- if err == core.ErrStop {
- return nil
- }
+ break
+ }
- return err
- }
+ if !w.recursive {
+ return
}
+
+ if t, ok := obj.(*Tree); ok {
+ w.stack = append(w.stack, treeEntryIter{t, 0})
+ w.base = path.Join(w.base, entry.Name)
+ }
+
+ return
+}
+
+// Tree returns the tree that the tree walker most recently operated on.
+func (w *TreeIter) Tree() *Tree {
+ current := len(w.stack) - 1
+ if w.stack[current].pos == 0 {
+ current--
+ }
+
+ if current < 0 {
+ return nil
+ }
+
+ return w.stack[current].t
}
-// Close closes the TreeIter
-func (iter *TreeIter) Close() {
- iter.w.Close()
+// Close releases any resources used by the TreeWalker.
+func (w *TreeIter) Close() {
+ w.stack = nil
}
diff --git a/tree_diff.go b/tree_diff.go
index 1c613bd..e142bcd 100644
--- a/tree_diff.go
+++ b/tree_diff.go
@@ -140,7 +140,7 @@ func newWithEmpty(a, b *Tree) (Changes, error) {
tree = a
}
- w := NewTreeWalker(tree.r, tree)
+ w := NewTreeIter(tree.r, tree, true)
defer w.Close()
for {
@@ -178,7 +178,7 @@ func newWithEmpty(a, b *Tree) (Changes, error) {
// while taking advantage of the tree hashes to avoid traversing
// subtrees when the hash is equal in both inputs.
func newDiffTree(a, b *Tree) ([]*Change, error) {
- result := make([]*Change, 0)
+ var result []*Change
aChanges, err := newWithEmpty(a, nil)
if err != nil {
diff --git a/tree_test.go b/tree_test.go
index 0780ca9..18d6b14 100644
--- a/tree_test.go
+++ b/tree_test.go
@@ -9,14 +9,14 @@ import (
. "gopkg.in/check.v1"
)
-type SuiteTree struct {
+type TreeSuite struct {
BaseSuite
Tree *Tree
}
-var _ = Suite(&SuiteTree{})
+var _ = Suite(&TreeSuite{})
-func (s *SuiteTree) SetUpSuite(c *C) {
+func (s *TreeSuite) SetUpSuite(c *C) {
s.BaseSuite.SetUpSuite(c)
hash := core.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c")
@@ -25,7 +25,7 @@ func (s *SuiteTree) SetUpSuite(c *C) {
c.Assert(err, IsNil)
}
-func (s *SuiteTree) TestDecode(c *C) {
+func (s *TreeSuite) TestDecode(c *C) {
c.Assert(s.Tree.Entries, HasLen, 8)
c.Assert(s.Tree.Entries[0].Name, Equals, ".gitignore")
c.Assert(s.Tree.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88")
@@ -35,7 +35,7 @@ func (s *SuiteTree) TestDecode(c *C) {
c.Assert(s.Tree.Entries[4].Mode.String(), Equals, "d---------")
}
-func (s *SuiteTree) TestDecodeNonTree(c *C) {
+func (s *TreeSuite) TestDecodeNonTree(c *C) {
hash := core.NewHash("9a48f23120e880dfbe41f7c9b7b708e9ee62a492")
blob, err := s.Repository.s.ObjectStorage().Get(core.BlobObject, hash)
c.Assert(err, IsNil)
@@ -45,23 +45,23 @@ func (s *SuiteTree) TestDecodeNonTree(c *C) {
c.Assert(err, Equals, ErrUnsupportedObject)
}
-func (s *SuiteTree) TestType(c *C) {
+func (s *TreeSuite) TestType(c *C) {
c.Assert(s.Tree.Type(), Equals, core.TreeObject)
}
-func (s *SuiteTree) TestFile(c *C) {
+func (s *TreeSuite) TestFile(c *C) {
f, err := s.Tree.File("LICENSE")
c.Assert(err, IsNil)
c.Assert(f.Name, Equals, "LICENSE")
}
-func (s *SuiteTree) TestFileNotFound(c *C) {
+func (s *TreeSuite) TestFileNotFound(c *C) {
f, err := s.Tree.File("not-found")
c.Assert(f, IsNil)
c.Assert(err, Equals, ErrFileNotFound)
}
-func (s *SuiteTree) TestFiles(c *C) {
+func (s *TreeSuite) TestFiles(c *C) {
var count int
err := s.Tree.Files().ForEach(func(f *File) error {
count++
@@ -124,6 +124,146 @@ func (o *SortReadCloser) Read(p []byte) (int, error) {
return nw, nil
}
+func (s *TreeSuite) TestTreeDecodeEncodeIdempotent(c *C) {
+ trees := []*Tree{
+ &Tree{
+ Entries: []TreeEntry{
+ TreeEntry{"foo", os.FileMode(0), core.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d")},
+ TreeEntry{"bar", os.FileMode(0), core.NewHash("c029517f6300c2da0f4b651b8642506cd6aaf45d")},
+ TreeEntry{"baz", os.FileMode(0), core.NewHash("d029517f6300c2da0f4b651b8642506cd6aaf45d")},
+ },
+ },
+ }
+ for _, tree := range trees {
+ obj := &core.MemoryObject{}
+ err := tree.Encode(obj)
+ c.Assert(err, IsNil)
+ newTree := &Tree{}
+ err = newTree.Decode(obj)
+ c.Assert(err, IsNil)
+ tree.Hash = obj.Hash()
+ c.Assert(newTree, DeepEquals, tree)
+ }
+}
+
+func (s *TreeSuite) TestTreeIterNext(c *C) {
+ r := s.Repository
+ commit, err := r.Commit(core.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ c.Assert(err, IsNil)
+
+ tree, err := commit.Tree()
+ c.Assert(err, IsNil)
+
+ walker := NewTreeIter(r, tree, true)
+ for _, e := range treeWalkerExpects {
+ name, entry, err := walker.Next()
+ if err == io.EOF {
+ break
+ }
+
+ c.Assert(err, IsNil)
+ c.Assert(name, Equals, e.Path)
+ c.Assert(entry.Name, Equals, e.Name)
+ c.Assert(entry.Mode.String(), Equals, e.Mode)
+ c.Assert(entry.Hash.String(), Equals, e.Hash)
+
+ c.Assert(walker.Tree().ID().String(), Equals, e.Tree)
+ }
+}
+
+func (s *TreeSuite) TestTreeIterNextNonRecursive(c *C) {
+ r := s.Repository
+ commit, err := r.Commit(core.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ c.Assert(err, IsNil)
+
+ tree, err := commit.Tree()
+ c.Assert(err, IsNil)
+
+ var count int
+ walker := NewTreeIter(r, tree, false)
+ for {
+ name, entry, err := walker.Next()
+ if err == io.EOF {
+ break
+ }
+
+ c.Assert(err, IsNil)
+ c.Assert(name, Not(Equals), "")
+ c.Assert(entry, NotNil)
+
+ c.Assert(walker.Tree().ID().String(), Equals, "a8d315b2b1c615d43042c3a62402b8a54288cf5c")
+
+ count++
+ }
+
+ c.Assert(count, Equals, 8)
+}
+
+var treeWalkerExpects = []struct {
+ Path, Mode, Name, Hash, Tree string
+}{{
+ Path: ".gitignore", Mode: "-rw-r--r--", Name: ".gitignore",
+ Hash: "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "CHANGELOG", Mode: "-rw-r--r--", Name: "CHANGELOG",
+ Hash: "d3ff53e0564a9f87d8e84b6e28e5060e517008aa", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "LICENSE", Mode: "-rw-r--r--", Name: "LICENSE",
+ Hash: "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "binary.jpg", Mode: "-rw-r--r--", Name: "binary.jpg",
+ Hash: "d5c0f4ab811897cadf03aec358ae60d21f91c50d", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "go", Mode: "d---------", Name: "go",
+ Hash: "a39771a7651f97faf5c72e08224d857fc35133db", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "go/example.go", Mode: "-rw-r--r--", Name: "example.go",
+ Hash: "880cd14280f4b9b6ed3986d6671f907d7cc2a198", Tree: "a39771a7651f97faf5c72e08224d857fc35133db",
+}, {
+ Path: "json", Mode: "d---------", Name: "json",
+ Hash: "5a877e6a906a2743ad6e45d99c1793642aaf8eda", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "json/long.json", Mode: "-rw-r--r--", Name: "long.json",
+ Hash: "49c6bb89b17060d7b4deacb7b338fcc6ea2352a9", Tree: "5a877e6a906a2743ad6e45d99c1793642aaf8eda",
+}, {
+ Path: "json/short.json", Mode: "-rw-r--r--", Name: "short.json",
+ Hash: "c8f1d8c61f9da76f4cb49fd86322b6e685dba956", Tree: "5a877e6a906a2743ad6e45d99c1793642aaf8eda",
+}, {
+ Path: "php", Mode: "d---------", Name: "php",
+ Hash: "586af567d0bb5e771e49bdd9434f5e0fb76d25fa", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "php/crappy.php", Mode: "-rw-r--r--", Name: "crappy.php",
+ Hash: "9a48f23120e880dfbe41f7c9b7b708e9ee62a492", Tree: "586af567d0bb5e771e49bdd9434f5e0fb76d25fa",
+}, {
+ Path: "vendor", Mode: "d---------", Name: "vendor",
+ Hash: "cf4aa3b38974fb7d81f367c0830f7d78d65ab86b", Tree: "a8d315b2b1c615d43042c3a62402b8a54288cf5c",
+}, {
+ Path: "vendor/foo.go", Mode: "-rw-r--r--", Name: "foo.go",
+ Hash: "9dea2395f5403188298c1dabe8bdafe562c491e3", Tree: "cf4aa3b38974fb7d81f367c0830f7d78d65ab86b",
+}}
+
+func entriesEquals(a, b []TreeEntry) bool {
+ if a == nil && b == nil {
+ return true
+ }
+
+ if a == nil || b == nil {
+ return false
+ }
+
+ if len(a) != len(b) {
+ return false
+ }
+
+ for i, v := range a {
+ if v != b[i] {
+ return false
+ }
+ }
+
+ return true
+}
+
// When decoding a tree we were not checking the return value of read
// when reading hashes. As a hash is quite small, it worked well nearly
// all the time.
@@ -135,7 +275,7 @@ func (o *SortReadCloser) Read(p []byte) (int, error) {
//
// This tests is performed with that object but using a SortReadObject to
// simulate incomplete reads on all platforms and operating systems.
-func (s *SuiteTree) TestTreeDecodeReadBug(c *C) {
+func (s *TreeSuite) TestTreeDecodeReadBug(c *C) {
cont := []byte{
0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x61, 0x6c, 0x74,
0x65, 0x72, 0x2e, 0x63, 0x0, 0xa4, 0x9d, 0x33, 0x49, 0xd7,
@@ -1252,49 +1392,5 @@ func (s *SuiteTree) TestTreeDecodeReadBug(c *C) {
var obtained Tree
err := obtained.Decode(obj)
c.Assert(err, IsNil)
- c.Assert(EntriesEquals(obtained.Entries, expected.Entries), Equals, true)
-}
-
-func (s *SuiteTree) TestTreeDecodeEncodeIdempotent(c *C) {
- trees := []*Tree{
- &Tree{
- Entries: []TreeEntry{
- TreeEntry{"foo", os.FileMode(0), core.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d")},
- TreeEntry{"bar", os.FileMode(0), core.NewHash("c029517f6300c2da0f4b651b8642506cd6aaf45d")},
- TreeEntry{"baz", os.FileMode(0), core.NewHash("d029517f6300c2da0f4b651b8642506cd6aaf45d")},
- },
- },
- }
- for _, tree := range trees {
- obj := &core.MemoryObject{}
- err := tree.Encode(obj)
- c.Assert(err, IsNil)
- newTree := &Tree{}
- err = newTree.Decode(obj)
- c.Assert(err, IsNil)
- tree.Hash = obj.Hash()
- c.Assert(newTree, DeepEquals, tree)
- }
-}
-
-func EntriesEquals(a, b []TreeEntry) bool {
- if a == nil && b == nil {
- return true
- }
-
- if a == nil || b == nil {
- return false
- }
-
- if len(a) != len(b) {
- return false
- }
-
- for i, v := range a {
- if v != b[i] {
- return false
- }
- }
-
- return true
+ c.Assert(entriesEquals(obtained.Entries, expected.Entries), Equals, true)
}
diff --git a/tree_walker.go b/tree_walker.go
deleted file mode 100644
index cbd81c4..0000000
--- a/tree_walker.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package git
-
-import (
- "io"
- "path"
-)
-
-const (
- startingStackSize = 8
-)
-
-const submoduleMode = 0160000
-const directoryMode = 0040000
-
-// TreeWalker provides a means of walking through all of the entries in a Tree.
-type TreeWalker struct {
- stack []treeEntryIter
- base string
-
- r *Repository
- t *Tree
-}
-
-// 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,
- t: t,
- }
- w.stack = append(w.stack, treeEntryIter{t, 0})
- 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, err error) {
- var obj Object
- 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
- }
-
- if entry.Mode == submoduleMode {
- err = nil
- continue
- }
-
- if entry.Mode.IsDir() {
- obj, err = w.r.Tree(entry.Hash)
- }
-
- name = path.Join(w.base, entry.Name)
-
- if err != nil {
- return
- }
-
- break
- }
-
- if t, ok := obj.(*Tree); ok {
- w.stack = append(w.stack, treeEntryIter{t, 0})
- 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
deleted file mode 100644
index 973b293..0000000
--- a/tree_walker_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package git
-
-import (
- "io"
-
- "gopkg.in/src-d/go-git.v4/core"
-
- . "gopkg.in/check.v1"
-)
-
-type TreeWalkerSuite struct {
- BaseSuite
-}
-
-var _ = Suite(&TreeWalkerSuite{})
-
-func (s *TreeWalkerSuite) TestNext(c *C) {
- r := s.Repository
- commit, err := r.Commit(core.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
- c.Assert(err, IsNil)
-
- tree, err := commit.Tree()
- c.Assert(err, IsNil)
-
- walker := NewTreeWalker(r, tree)
- for _, e := range treeWalkerExpects {
- name, entry, err := walker.Next()
- if err == io.EOF {
- break
- }
-
- c.Assert(err, IsNil)
- c.Assert(name, Equals, e.Path)
- c.Assert(entry.Name, Equals, e.Name)
- c.Assert(entry.Mode.String(), Equals, e.Mode)
- c.Assert(entry.Hash.String(), Equals, e.Hash)
- }
-}
-
-var treeWalkerExpects = []struct {
- Path, Mode, Name, Hash string
-}{
- {Path: ".gitignore", Mode: "-rw-r--r--", Name: ".gitignore", Hash: "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88"},
- {Path: "CHANGELOG", Mode: "-rw-r--r--", Name: "CHANGELOG", Hash: "d3ff53e0564a9f87d8e84b6e28e5060e517008aa"},
- {Path: "LICENSE", Mode: "-rw-r--r--", Name: "LICENSE", Hash: "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f"},
- {Path: "binary.jpg", Mode: "-rw-r--r--", Name: "binary.jpg", Hash: "d5c0f4ab811897cadf03aec358ae60d21f91c50d"},
- {Path: "go", Mode: "d---------", Name: "go", Hash: "a39771a7651f97faf5c72e08224d857fc35133db"},
- {Path: "go/example.go", Mode: "-rw-r--r--", Name: "example.go", Hash: "880cd14280f4b9b6ed3986d6671f907d7cc2a198"},
- {Path: "json", Mode: "d---------", Name: "json", Hash: "5a877e6a906a2743ad6e45d99c1793642aaf8eda"},
- {Path: "json/long.json", Mode: "-rw-r--r--", Name: "long.json", Hash: "49c6bb89b17060d7b4deacb7b338fcc6ea2352a9"},
- {Path: "json/short.json", Mode: "-rw-r--r--", Name: "short.json", Hash: "c8f1d8c61f9da76f4cb49fd86322b6e685dba956"},
- {Path: "php", Mode: "d---------", Name: "php", Hash: "586af567d0bb5e771e49bdd9434f5e0fb76d25fa"},
- {Path: "php/crappy.php", Mode: "-rw-r--r--", Name: "crappy.php", Hash: "9a48f23120e880dfbe41f7c9b7b708e9ee62a492"},
- {Path: "vendor", Mode: "d---------", Name: "vendor", Hash: "cf4aa3b38974fb7d81f367c0830f7d78d65ab86b"},
- {Path: "vendor/foo.go", Mode: "-rw-r--r--", Name: "foo.go", Hash: "9dea2395f5403188298c1dabe8bdafe562c491e3"},
-}