Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit b35fc29

Browse files
committed
Merge pull request #39 from scjalliance/git-object-interface
Added Object interface for Commit, Tree, Blob and Tag
2 parents 9c9cdff + 9e6a03b commit b35fc29

File tree

14 files changed

+316
-220
lines changed

14 files changed

+316
-220
lines changed

commit.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,28 @@ func (c *Commit) NumParents() int {
4343
}
4444

4545
// File returns the file with the specified "path" in the commit and a
46-
// nil error if the file exists. If the file does not exists, it returns
46+
// nil error if the file exists. If the file does not exist, it returns
4747
// a nil file and the ErrFileNotFound error.
4848
func (c *Commit) File(path string) (file *File, err error) {
4949
return c.Tree().File(path)
5050
}
5151

52-
// Decode transform an core.Object into a Blob struct
52+
// ID returns the object ID of the commit. The returned value will always match
53+
// the current value of Commit.Hash.
54+
//
55+
// ID is present to fufill the Object interface.
56+
func (c *Commit) ID() core.Hash {
57+
return c.Hash
58+
}
59+
60+
// Type returns the type of object. It always returns core.CommitObject.
61+
//
62+
// Type is present to fufill the Object interface.
63+
func (c *Commit) Type() core.ObjectType {
64+
return core.CommitObject
65+
}
66+
67+
// Decode transforms a core.Object into a Commit struct.
5368
func (c *Commit) Decode(o core.Object) (err error) {
5469
if o.Type() != core.CommitObject {
5570
return ErrUnsupportedObject
@@ -107,15 +122,22 @@ func (c *Commit) String() string {
107122
)
108123
}
109124

125+
// CommitIter provides an iterator for a set of commits.
110126
type CommitIter struct {
111127
core.ObjectIter
112128
r *Repository
113129
}
114130

131+
// NewCommitIter returns a CommitIter for the given repository and underlying
132+
// object iterator.
133+
//
134+
// The returned CommitIter will automatically skip over non-commit objects.
115135
func NewCommitIter(r *Repository, iter core.ObjectIter) *CommitIter {
116136
return &CommitIter{iter, r}
117137
}
118138

139+
// Next moves the iterator to the next commit and returns a pointer to it. If it
140+
// has reached the end of the set it will return io.EOF.
119141
func (iter *CommitIter) Next() (*Commit, error) {
120142
obj, err := iter.ObjectIter.Next()
121143
if err != nil {

commit_test.go

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package git
22

33
import (
44
"io"
5-
"os"
65

76
"gopkg.in/src-d/go-git.v3/core"
8-
"gopkg.in/src-d/go-git.v3/formats/packfile"
97

108
. "gopkg.in/check.v1"
119
)
@@ -18,27 +16,10 @@ var _ = Suite(&SuiteCommit{})
1816

1917
// create the repositories of the fixtures
2018
func (s *SuiteCommit) SetUpSuite(c *C) {
21-
fixtureRepos := [...]struct {
22-
url string
23-
packfile string
24-
}{
19+
commitFixtures := []packedFixture{
2520
{"https://github.com/tyba/git-fixture.git", "formats/packfile/fixtures/git-fixture.ofs-delta"},
2621
}
27-
s.repos = make(map[string]*Repository, 0)
28-
for _, fixRepo := range fixtureRepos {
29-
s.repos[fixRepo.url] = NewPlainRepository()
30-
31-
d, err := os.Open(fixRepo.packfile)
32-
c.Assert(err, IsNil)
33-
34-
r := packfile.NewReader(d)
35-
r.Format = packfile.OFSDeltaFormat // TODO: how to know the format of a pack file ahead of time?
36-
37-
_, err = r.Read(s.repos[fixRepo.url].Storage)
38-
c.Assert(err, IsNil)
39-
40-
c.Assert(d.Close(), IsNil)
41-
}
22+
s.repos = unpackFixtures(c, commitFixtures)
4223
}
4324

4425
var commitIterTests = []struct {

file.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package git
33
import (
44
"bytes"
55
"strings"
6-
7-
"gopkg.in/src-d/go-git.v3/core"
86
)
97

108
// File represents git file objects.
@@ -64,15 +62,9 @@ func (iter *FileIter) Next() (*File, error) {
6462
return nil, err
6563
}
6664

67-
if obj.Type() != core.BlobObject {
68-
// Skip non-blob objects
69-
continue
65+
if blob, ok := obj.(*Blob); ok {
66+
return newFile(name, blob), nil
7067
}
71-
72-
blob := &Blob{}
73-
blob.Decode(obj)
74-
75-
return newFile(name, blob), nil
7668
}
7769
}
7870

file_test.go

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package git
22

33
import (
44
"io"
5-
"os"
65

76
"gopkg.in/src-d/go-git.v3/core"
8-
"gopkg.in/src-d/go-git.v3/formats/packfile"
97

108
. "gopkg.in/check.v1"
119
)
@@ -18,28 +16,11 @@ var _ = Suite(&SuiteFile{})
1816

1917
// create the repositories of the fixtures
2018
func (s *SuiteFile) SetUpSuite(c *C) {
21-
fixtureRepos := [...]struct {
22-
url string
23-
packfile string
24-
}{
19+
fileFixtures := []packedFixture{
2520
{"https://github.com/tyba/git-fixture.git", "formats/packfile/fixtures/git-fixture.ofs-delta"},
2621
{"https://github.com/cpcs499/Final_Pres_P", "formats/packfile/fixtures/Final_Pres_P.ofs-delta"},
2722
}
28-
s.repos = make(map[string]*Repository, 0)
29-
for _, fixRepo := range fixtureRepos {
30-
s.repos[fixRepo.url] = NewPlainRepository()
31-
32-
d, err := os.Open(fixRepo.packfile)
33-
c.Assert(err, IsNil)
34-
35-
r := packfile.NewReader(d)
36-
r.Format = packfile.OFSDeltaFormat
37-
38-
_, err = r.Read(s.repos[fixRepo.url].Storage)
39-
c.Assert(err, IsNil)
40-
41-
c.Assert(d.Close(), IsNil)
42-
}
23+
s.repos = unpackFixtures(c, fileFixtures)
4324
}
4425

4526
type fileIterExpectedEntry struct {
@@ -77,6 +58,8 @@ func (s *SuiteFile) TestIter(c *C) {
7758
expected := t.files[k]
7859
file, err := iter.Next()
7960
c.Assert(err, IsNil, Commentf("subtest %d, iter %d, err=%v", i, k, err))
61+
c.Assert(file.Hash.IsZero(), Equals, false)
62+
c.Assert(file.Hash, Equals, file.ID())
8063
c.Assert(file.Name, Equals, expected.Name, Commentf("subtest %d, iter %d, name=%s, expected=%s", i, k, file.Name, expected.Hash))
8164
c.Assert(file.Hash.String(), Equals, expected.Hash, Commentf("subtest %d, iter %d, hash=%v, expected=%s", i, k, file.Hash.String(), expected.Hash))
8265
}

objects.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,36 @@ import (
1010
"gopkg.in/src-d/go-git.v3/core"
1111
)
1212

13+
var ErrUnsupportedObject = errors.New("unsupported object type")
14+
15+
// Object is a generic representation of any git object. It is implemented by
16+
// Commit, Tree, Blob and Tag, and includes the functions that are common to
17+
// them.
18+
//
19+
// Object is returned when an object could of any type. It is frequently used
20+
// with a type cast to acquire the specific type of object:
21+
//
22+
// func process(obj Object) {
23+
// switch o := obj.(type) {
24+
// case *Commit:
25+
// // o is a Commit
26+
// case *Tree:
27+
// // o is a Tree
28+
// case *Blob:
29+
// // o is a Blob
30+
// case *Tag:
31+
// // o is a Tag
32+
// }
33+
// }
34+
//
35+
// This interface is intentionally different from core.Object, which is a lower
36+
// level interface used by storage implementations to read and write objects.
37+
type Object interface {
38+
ID() core.Hash
39+
Type() core.ObjectType
40+
Decode(core.Object) error
41+
}
42+
1343
// Blob is used to store file data - it is generally a file.
1444
type Blob struct {
1545
Hash core.Hash
@@ -18,9 +48,22 @@ type Blob struct {
1848
obj core.Object
1949
}
2050

21-
var ErrUnsupportedObject = errors.New("unsupported object type")
51+
// ID returns the object ID of the blob. The returned value will always match
52+
// the current value of Blob.Hash.
53+
//
54+
// ID is present to fufill the Object interface.
55+
func (b *Blob) ID() core.Hash {
56+
return b.Hash
57+
}
58+
59+
// Type returns the type of object. It always returns core.BlobObject.
60+
//
61+
// Type is present to fufill the Object interface.
62+
func (b *Blob) Type() core.ObjectType {
63+
return core.BlobObject
64+
}
2265

23-
// Decode transform an core.Object into a Blob struct
66+
// Decode transforms a core.Object into a Blob struct.
2467
func (b *Blob) Decode(o core.Object) error {
2568
if o.Type() != core.BlobObject {
2669
return ErrUnsupportedObject

objects_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func (s *ObjectsSuite) TestNewCommit(c *C) {
2929
commit, err := s.r.Commit(hash)
3030
c.Assert(err, IsNil)
3131

32+
c.Assert(commit.Hash, Equals, commit.ID())
3233
c.Assert(commit.Hash.String(), Equals, "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69")
3334
c.Assert(commit.Tree().Hash.String(), Equals, "c2d30fa8ef288618f65f6eed6e168e0d514886f4")
3435

repository.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ func (r *Repository) Pull(remoteName, branch string) (err error) {
7575
req := &common.GitUploadPackRequest{}
7676
req.Want(ref)
7777

78+
// TODO: Provide "haves" for what's already in the repository's storage
79+
7880
reader, err := remote.Fetch(req)
7981
if err != nil {
8082
return err
@@ -155,3 +157,31 @@ func (r *Repository) Tag(h core.Hash) (*Tag, error) {
155157
func (r *Repository) Tags() *TagIter {
156158
return NewTagIter(r, r.Storage.Iter(core.TagObject))
157159
}
160+
161+
// Object returns an object with the given hash.
162+
func (r *Repository) Object(h core.Hash) (Object, error) {
163+
obj, err := r.Storage.Get(h)
164+
if err != nil {
165+
if err == core.ObjectNotFoundErr {
166+
return nil, ObjectNotFoundErr
167+
}
168+
return nil, err
169+
}
170+
171+
switch obj.Type() {
172+
case core.CommitObject:
173+
commit := &Commit{r: r}
174+
return commit, commit.Decode(obj)
175+
case core.TreeObject:
176+
tree := &Tree{r: r}
177+
return tree, tree.Decode(obj)
178+
case core.BlobObject:
179+
blob := &Blob{}
180+
return blob, blob.Decode(obj)
181+
case core.TagObject:
182+
tag := &Tag{r: r}
183+
return tag, tag.Decode(obj)
184+
default:
185+
return nil, core.ErrInvalidType
186+
}
187+
}

repository_test.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ type SuiteRepository struct {
1515

1616
var _ = Suite(&SuiteRepository{})
1717

18-
func (s *SuiteRepository) SetUpTest(c *C) {
19-
s.repos = unpackFixtures(c, tagFixtures)
18+
func (s *SuiteRepository) SetUpSuite(c *C) {
19+
s.repos = unpackFixtures(c, tagFixtures, treeWalkerFixtures)
2020
}
2121

2222
func (s *SuiteRepository) TestNewRepository(c *C) {
@@ -58,6 +58,9 @@ func (s *SuiteRepository) TestCommit(c *C) {
5858
c.Assert(err, IsNil)
5959

6060
c.Assert(commit.Hash.IsZero(), Equals, false)
61+
c.Assert(commit.Hash, Equals, commit.ID())
62+
c.Assert(commit.Hash, Equals, hash)
63+
c.Assert(commit.Type(), Equals, core.CommitObject)
6164
c.Assert(commit.Tree().Hash.IsZero(), Equals, false)
6265
c.Assert(commit.Author.Email, Equals, "[email protected]")
6366
}
@@ -79,6 +82,8 @@ func (s *SuiteRepository) TestCommits(c *C) {
7982

8083
count++
8184
c.Assert(commit.Hash.IsZero(), Equals, false)
85+
c.Assert(commit.Hash, Equals, commit.ID())
86+
c.Assert(commit.Type(), Equals, core.CommitObject)
8287
//c.Assert(commit.Tree.IsZero(), Equals, false)
8388
}
8489

@@ -90,10 +95,11 @@ func (s *SuiteRepository) TestTag(c *C) {
9095
r, ok := s.repos[t.repo]
9196
c.Assert(ok, Equals, true)
9297
k := 0
93-
for hash, expected := range t.tags {
94-
tag, err := r.Tag(core.NewHash(hash))
98+
for hashString, expected := range t.tags {
99+
hash := core.NewHash(hashString)
100+
tag, err := r.Tag(hash)
95101
c.Assert(err, IsNil)
96-
testTagExpected(c, tag, expected, fmt.Sprintf("subtest %d, tag %d: ", i, k))
102+
testTagExpected(c, tag, hash, expected, fmt.Sprintf("subtest %d, tag %d: ", i, k))
97103
k++
98104
}
99105
}
@@ -107,6 +113,22 @@ func (s *SuiteRepository) TestTags(c *C) {
107113
}
108114
}
109115

116+
func (s *SuiteRepository) TestObject(c *C) {
117+
for i, t := range treeWalkerTests {
118+
r, ok := s.repos[t.repo]
119+
c.Assert(ok, Equals, true)
120+
for k := 0; k < len(t.objs); k++ {
121+
comment := fmt.Sprintf("subtest %d, tag %d", i, k)
122+
info := t.objs[k]
123+
hash := core.NewHash(info.Hash)
124+
obj, err := r.Object(hash)
125+
c.Assert(err, IsNil, Commentf(comment))
126+
c.Assert(obj.Type(), Equals, info.Kind, Commentf(comment))
127+
c.Assert(obj.ID(), Equals, hash, Commentf(comment))
128+
}
129+
}
130+
}
131+
110132
func (s *SuiteRepository) TestCommitIterClosePanic(c *C) {
111133
r, err := NewRepository(RepositoryFixture, nil)
112134
r.Remotes["origin"].upSrv = &MockGitUploadPackService{}

0 commit comments

Comments
 (0)