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

Commit a989631

Browse files
committed
Merge pull request #20 from scjalliance/generic-object-storage
Iterable ObjectStorage interface for use in Repository struct
2 parents 1931dfb + e82d491 commit a989631

File tree

7 files changed

+274
-101
lines changed

7 files changed

+274
-101
lines changed

commit.go

Lines changed: 10 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,7 @@ func (c *Commit) Tree() *Tree {
3434
}
3535

3636
func (c *Commit) Parents() *CommitIter {
37-
i := NewCommitIter(c.r)
38-
go func() {
39-
defer i.Close()
40-
for _, hash := range c.parents {
41-
obj, _ := c.r.Storage.Get(hash)
42-
i.Add(obj)
43-
}
44-
}()
45-
46-
return i
37+
return NewCommitIter(c.r, core.NewObjectLookupIter(c.r.Storage, c.parents))
4738
}
4839

4940
// NumParents returns the number of parents in a commit.
@@ -106,52 +97,24 @@ func (c *Commit) String() string {
10697
}
10798

10899
type CommitIter struct {
109-
iter
100+
core.ObjectIter
101+
r *Repository
110102
}
111103

112-
func NewCommitIter(r *Repository) *CommitIter {
113-
return &CommitIter{newIter(r)}
104+
func NewCommitIter(r *Repository, iter core.ObjectIter) *CommitIter {
105+
return &CommitIter{iter, r}
114106
}
115107

116-
func (i *CommitIter) Next() (*Commit, error) {
117-
obj := <-i.ch
118-
if obj == nil {
119-
return nil, io.EOF
108+
func (iter *CommitIter) Next() (*Commit, error) {
109+
obj, err := iter.ObjectIter.Next()
110+
if err != nil {
111+
return nil, err
120112
}
121113

122-
commit := &Commit{r: i.r}
114+
commit := &Commit{r: iter.r}
123115
return commit, commit.Decode(obj)
124116
}
125117

126-
type iter struct {
127-
ch chan core.Object
128-
r *Repository
129-
130-
IsClosed bool
131-
}
132-
133-
func newIter(r *Repository) iter {
134-
ch := make(chan core.Object, 1)
135-
return iter{ch: ch, r: r}
136-
}
137-
138-
func (i *iter) Add(o core.Object) {
139-
if i.IsClosed {
140-
return
141-
}
142-
143-
i.ch <- o
144-
}
145-
146-
func (i *iter) Close() {
147-
if i.IsClosed {
148-
return
149-
}
150-
151-
defer func() { i.IsClosed = true }()
152-
close(i.ch)
153-
}
154-
155118
type commitSorterer struct {
156119
l []*Commit
157120
}

commit_test.go

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package git
22

33
import (
4+
"io"
45
"os"
56

67
"gopkg.in/src-d/go-git.v2/core"
@@ -40,10 +41,73 @@ func (s *SuiteCommit) SetUpSuite(c *C) {
4041
}
4142
}
4243

43-
func (s *SuiteCommit) TestIterClose(c *C) {
44-
i := &iter{ch: make(chan core.Object, 1)}
45-
i.Close()
46-
i.Close()
44+
var iterTests = []struct {
45+
repo string // the repo name in the test suite's map of fixtures
46+
commits []string // the commit hashes to iterate over in the test
47+
}{
48+
{"https://github.com/tyba/git-fixture.git", []string{
49+
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
50+
"918c48b83bd081e863dbe1b80f8998f058cd8294",
51+
"af2d6a6954d532f8ffb47615169c8fdf9d383a1a",
52+
"1669dce138d9b841a518c64b10914d88f5e488ea",
53+
"35e85108805c84807bc66a02d91535e1e24b38b9",
54+
"b029517f6300c2da0f4b651b8642506cd6aaf45d",
55+
"a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69",
56+
"b029517f6300c2da0f4b651b8642506cd6aaf45d", // Intentional duplicate
57+
"b8e471f58bcbca63b07bda20e428190409c2db47",
58+
"b029517f6300c2da0f4b651b8642506cd6aaf45d"}}, // Intentional duplicate
59+
}
60+
61+
func (s *SuiteCommit) TestIterSlice(c *C) {
62+
for i, t := range iterTests {
63+
r := s.repos[t.repo]
64+
iter := NewCommitIter(r, core.NewObjectSliceIter(makeObjectSlice(t.commits, r.Storage)))
65+
s.checkIter(c, r, i, iter, t.commits)
66+
}
67+
}
68+
69+
func (s *SuiteCommit) TestIterLookup(c *C) {
70+
for i, t := range iterTests {
71+
r := s.repos[t.repo]
72+
iter := NewCommitIter(r, core.NewObjectLookupIter(r.Storage, makeHashSlice(t.commits)))
73+
s.checkIter(c, r, i, iter, t.commits)
74+
}
75+
}
76+
77+
func (s *SuiteCommit) checkIter(c *C, r *Repository, subtest int, iter *CommitIter, commits []string) {
78+
for k := 0; k < len(commits); k++ {
79+
commit, err := iter.Next()
80+
c.Assert(err, IsNil, Commentf("subtest %d, iter %d, err=%v", subtest, k, err))
81+
c.Assert(commit.Hash.String(), Equals, commits[k], Commentf("subtest %d, iter %d, hash=%v, expected=%s", subtest, k, commit.Hash.String(), commits[k]))
82+
}
83+
_, err := iter.Next()
84+
c.Assert(err, Equals, io.EOF)
85+
}
86+
87+
func (s *SuiteCommit) TestIterSliceClose(c *C) {
88+
for i, t := range iterTests {
89+
r := s.repos[t.repo]
90+
iter := NewCommitIter(r, core.NewObjectSliceIter(makeObjectSlice(t.commits, r.Storage)))
91+
s.checkIterClose(c, i, iter)
92+
}
93+
}
94+
95+
func (s *SuiteCommit) TestIterLookupClose(c *C) {
96+
for i, t := range iterTests {
97+
r := s.repos[t.repo]
98+
iter := NewCommitIter(r, core.NewObjectLookupIter(r.Storage, makeHashSlice(t.commits)))
99+
s.checkIterClose(c, i, iter)
100+
}
101+
}
102+
103+
func (s *SuiteCommit) checkIterClose(c *C, subtest int, iter *CommitIter) {
104+
iter.Close()
105+
_, err := iter.Next()
106+
c.Assert(err, Equals, io.EOF, Commentf("subtest %d, close 1, err=%v", subtest, err))
107+
108+
iter.Close()
109+
_, err = iter.Next()
110+
c.Assert(err, Equals, io.EOF, Commentf("subtest %d, close 2, err=%v", subtest, err))
47111
}
48112

49113
var fileTests = []struct {
@@ -102,3 +166,22 @@ func (s *SuiteCommit) TestFile(c *C) {
102166
}
103167
}
104168
}
169+
170+
func makeObjectSlice(hashes []string, storage core.ObjectStorage) []core.Object {
171+
series := make([]core.Object, 0, len(hashes))
172+
for _, member := range hashes {
173+
obj, err := storage.Get(core.NewHash(member))
174+
if err == nil {
175+
series = append(series, obj)
176+
}
177+
}
178+
return series
179+
}
180+
181+
func makeHashSlice(hashes []string) []core.Hash {
182+
series := make([]core.Hash, 0, len(hashes))
183+
for _, member := range hashes {
184+
series = append(series, core.NewHash(member))
185+
}
186+
return series
187+
}

core/object.go

Lines changed: 122 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ package core
33

44
import (
55
"bytes"
6+
"errors"
67
"io"
78
)
89

10+
var (
11+
ObjectNotFoundErr = errors.New("object not found")
12+
)
13+
914
// Object is a generic representation of any git object
1015
type Object interface {
1116
Type() ObjectType
@@ -19,9 +24,10 @@ type Object interface {
1924

2025
// ObjectStorage generic storage of objects
2126
type ObjectStorage interface {
22-
New() Object
23-
Set(Object) Hash
24-
Get(Hash) (Object, bool)
27+
New() (Object, error)
28+
Set(Object) (Hash, error)
29+
Get(Hash) (Object, error)
30+
Iter(ObjectType) ObjectIter
2531
}
2632

2733
// ObjectType internal object type's
@@ -59,6 +65,89 @@ func (t ObjectType) Bytes() []byte {
5965
return []byte(t.String())
6066
}
6167

68+
// ObjectIter is a generic closable interface for iterating over objects.
69+
type ObjectIter interface {
70+
Next() (Object, error)
71+
Close()
72+
}
73+
74+
// ObjectLookupIter implements ObjectIter. It iterates over a series of object
75+
// hashes and yields their associated objects by retrieving each one from
76+
// object storage. The retrievals are lazy and only occur when the iterator
77+
// moves forward with a call to Next().
78+
//
79+
// The ObjectLookupIter must be closed with a call to Close() when it is no
80+
// longer needed.
81+
type ObjectLookupIter struct {
82+
storage ObjectStorage
83+
series []Hash
84+
pos int
85+
}
86+
87+
// NewObjectLookupIter returns an object iterator given an object storage and
88+
// a slice of object hashes.
89+
func NewObjectLookupIter(storage ObjectStorage, series []Hash) *ObjectLookupIter {
90+
return &ObjectLookupIter{
91+
storage: storage,
92+
series: series,
93+
}
94+
}
95+
96+
// Next returns the next object from the iterator. If the iterator has reached
97+
// the end it will return io.EOF as an error. If the object can't be found in
98+
// the object storage, it will return ObjectNotFoundErr as an error. If the
99+
// object is retreieved successfully error will be nil.
100+
func (iter *ObjectLookupIter) Next() (Object, error) {
101+
if iter.pos >= len(iter.series) {
102+
return nil, io.EOF
103+
}
104+
hash := iter.series[iter.pos]
105+
obj, err := iter.storage.Get(hash)
106+
if err == nil {
107+
iter.pos++
108+
}
109+
return obj, err
110+
}
111+
112+
// Close releases any resources used by the iterator.
113+
func (iter *ObjectLookupIter) Close() {
114+
iter.pos = len(iter.series)
115+
}
116+
117+
// ObjectSliceIter implements ObjectIter. It iterates over a series of objects
118+
// stored in a slice and yields each one in turn when Next() is called.
119+
//
120+
// The ObjectSliceIter must be closed with a call to Close() when it is no
121+
// longer needed.
122+
type ObjectSliceIter struct {
123+
series []Object
124+
pos int
125+
}
126+
127+
// NewObjectSliceIter returns an object iterator for the given slice of objects.
128+
func NewObjectSliceIter(series []Object) *ObjectSliceIter {
129+
return &ObjectSliceIter{
130+
series: series,
131+
}
132+
}
133+
134+
// Next returns the next object from the iterator. If the iterator has reached
135+
// the end it will return io.EOF as an error. If the object is retreieved
136+
// successfully error will be nil.
137+
func (iter *ObjectSliceIter) Next() (Object, error) {
138+
if iter.pos >= len(iter.series) {
139+
return nil, io.EOF
140+
}
141+
obj := iter.series[iter.pos]
142+
iter.pos++
143+
return obj, nil
144+
}
145+
146+
// Close releases any resources used by the iterator.
147+
func (iter *ObjectSliceIter) Close() {
148+
iter.pos = len(iter.series)
149+
}
150+
62151
type RAWObject struct {
63152
b []byte
64153
t ObjectType
@@ -93,11 +182,11 @@ func NewRAWObjectStorage() *RAWObjectStorage {
93182
}
94183
}
95184

96-
func (o *RAWObjectStorage) New() Object {
97-
return &RAWObject{}
185+
func (o *RAWObjectStorage) New() (Object, error) {
186+
return &RAWObject{}, nil
98187
}
99188

100-
func (o *RAWObjectStorage) Set(obj Object) Hash {
189+
func (o *RAWObjectStorage) Set(obj Object) (Hash, error) {
101190
h := obj.Hash()
102191
o.Objects[h] = obj
103192

@@ -110,11 +199,35 @@ func (o *RAWObjectStorage) Set(obj Object) Hash {
110199
o.Blobs[h] = o.Objects[h]
111200
}
112201

113-
return h
202+
return h, nil
114203
}
115204

116-
func (o *RAWObjectStorage) Get(h Hash) (Object, bool) {
205+
func (o *RAWObjectStorage) Get(h Hash) (Object, error) {
117206
obj, ok := o.Objects[h]
207+
if !ok {
208+
return nil, ObjectNotFoundErr
209+
}
210+
211+
return obj, nil
212+
}
118213

119-
return obj, ok
214+
func (o *RAWObjectStorage) Iter(t ObjectType) ObjectIter {
215+
var series []Object
216+
switch t {
217+
case CommitObject:
218+
series = flattenObjectMap(o.Commits)
219+
case TreeObject:
220+
series = flattenObjectMap(o.Trees)
221+
case BlobObject:
222+
series = flattenObjectMap(o.Blobs)
223+
}
224+
return NewObjectSliceIter(series)
225+
}
226+
227+
func flattenObjectMap(m map[Hash]Object) []Object {
228+
objects := make([]Object, 0, len(m))
229+
for _, obj := range m {
230+
objects = append(objects, obj)
231+
}
232+
return objects
120233
}

0 commit comments

Comments
 (0)