diff options
author | Máximo Cuadros <mcuadros@gmail.com> | 2016-09-22 23:41:48 +0200 |
---|---|---|
committer | Máximo Cuadros <mcuadros@gmail.com> | 2016-09-22 23:41:48 +0200 |
commit | 5913aee5004ff5b32c7926dda3af1f946d203f33 (patch) | |
tree | c9b3ce9e4439faf0313749690a3a0e496e32447d | |
parent | 001bb130fe6186421f3ddcc556854410edd8d95e (diff) | |
download | go-git-5913aee5004ff5b32c7926dda3af1f946d203f33.tar.gz |
test coverage improved, Remote.Refs and Repository.Refs returns error, TreeWalker -> TreeIter
-rw-r--r-- | common_test.go | 2 | ||||
-rw-r--r-- | core/object.go | 2 | ||||
-rw-r--r-- | cshared/tree_cshared.go | 90 | ||||
-rw-r--r-- | examples/remotes/main.go | 3 | ||||
-rw-r--r-- | file.go | 4 | ||||
-rw-r--r-- | remote.go | 12 | ||||
-rw-r--r-- | remote_test.go | 54 | ||||
-rw-r--r-- | repository.go | 16 | ||||
-rw-r--r-- | repository_test.go | 126 | ||||
-rw-r--r-- | tag_test.go | 24 | ||||
-rw-r--r-- | tree.go | 141 | ||||
-rw-r--r-- | tree_diff.go | 4 | ||||
-rw-r--r-- | tree_test.go | 206 | ||||
-rw-r--r-- | tree_walker.go | 113 | ||||
-rw-r--r-- | tree_walker_test.go | 56 |
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 { @@ -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) { @@ -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) @@ -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"}, -} |