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

Commit 07b8ede

Browse files
smolamcuadros
authored andcommitted
add Blobs, Trees and Objects iters. (#114)
* Now every object type as an iterator in Repository. * old TreeIter is TreeWalker again, TreeIter now matches the same behaviour as other iterators.
1 parent 03c4b83 commit 07b8ede

File tree

9 files changed

+440
-119
lines changed

9 files changed

+440
-119
lines changed

blobs.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package git
2+
3+
import (
4+
"io"
5+
6+
"gopkg.in/src-d/go-git.v4/core"
7+
)
8+
9+
// Blob is used to store file data - it is generally a file.
10+
type Blob struct {
11+
Hash core.Hash
12+
Size int64
13+
14+
obj core.Object
15+
}
16+
17+
// ID returns the object ID of the blob. The returned value will always match
18+
// the current value of Blob.Hash.
19+
//
20+
// ID is present to fulfill the Object interface.
21+
func (b *Blob) ID() core.Hash {
22+
return b.Hash
23+
}
24+
25+
// Type returns the type of object. It always returns core.BlobObject.
26+
//
27+
// Type is present to fulfill the Object interface.
28+
func (b *Blob) Type() core.ObjectType {
29+
return core.BlobObject
30+
}
31+
32+
// Decode transforms a core.Object into a Blob struct.
33+
func (b *Blob) Decode(o core.Object) error {
34+
if o.Type() != core.BlobObject {
35+
return ErrUnsupportedObject
36+
}
37+
38+
b.Hash = o.Hash()
39+
b.Size = o.Size()
40+
b.obj = o
41+
42+
return nil
43+
}
44+
45+
// Encode transforms a Blob into a core.Object.
46+
func (b *Blob) Encode(o core.Object) error {
47+
w, err := o.Writer()
48+
if err != nil {
49+
return err
50+
}
51+
defer checkClose(w, &err)
52+
r, err := b.Reader()
53+
if err != nil {
54+
return err
55+
}
56+
defer checkClose(r, &err)
57+
_, err = io.Copy(w, r)
58+
o.SetType(core.BlobObject)
59+
return err
60+
}
61+
62+
// Reader returns a reader allow the access to the content of the blob
63+
func (b *Blob) Reader() (core.ObjectReader, error) {
64+
return b.obj.Reader()
65+
}
66+
67+
// BlobIter provides an iterator for a set of blobs.
68+
type BlobIter struct {
69+
core.ObjectIter
70+
r *Repository
71+
}
72+
73+
// NewBlobIter returns a CommitIter for the given repository and underlying
74+
// object iterator.
75+
//
76+
// The returned BlobIter will automatically skip over non-blob objects.
77+
func NewBlobIter(r *Repository, iter core.ObjectIter) *BlobIter {
78+
return &BlobIter{iter, r}
79+
}
80+
81+
// Next moves the iterator to the next blob and returns a pointer to it. If it
82+
// has reached the end of the set it will return io.EOF.
83+
func (iter *BlobIter) Next() (*Blob, error) {
84+
for {
85+
obj, err := iter.ObjectIter.Next()
86+
if err != nil {
87+
return nil, err
88+
}
89+
90+
if obj.Type() != core.BlobObject {
91+
continue
92+
}
93+
94+
blob := &Blob{}
95+
return blob, blob.Decode(obj)
96+
}
97+
}
98+
99+
// ForEach call the cb function for each blob contained on this iter until
100+
// an error happens or the end of the iter is reached. If ErrStop is sent
101+
// the iteration is stop but no error is returned. The iterator is closed.
102+
func (iter *BlobIter) ForEach(cb func(*Blob) error) error {
103+
return iter.ObjectIter.ForEach(func(obj core.Object) error {
104+
if obj.Type() != core.BlobObject {
105+
return nil
106+
}
107+
108+
blob := &Blob{}
109+
if err := blob.Decode(obj); err != nil {
110+
return err
111+
}
112+
113+
return cb(blob)
114+
})
115+
}

blobs_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package git
2+
3+
import (
4+
"io"
5+
"io/ioutil"
6+
7+
"gopkg.in/src-d/go-git.v4/core"
8+
9+
. "gopkg.in/check.v1"
10+
)
11+
12+
type BlobsSuite struct {
13+
BaseSuite
14+
}
15+
16+
var _ = Suite(&BlobsSuite{})
17+
18+
func (s *BlobsSuite) TestBlobHash(c *C) {
19+
o := &core.MemoryObject{}
20+
o.SetType(core.BlobObject)
21+
o.SetSize(3)
22+
23+
writer, err := o.Writer()
24+
c.Assert(err, IsNil)
25+
defer func() { c.Assert(writer.Close(), IsNil) }()
26+
27+
writer.Write([]byte{'F', 'O', 'O'})
28+
29+
blob := &Blob{}
30+
c.Assert(blob.Decode(o), IsNil)
31+
32+
c.Assert(blob.Size, Equals, int64(3))
33+
c.Assert(blob.Hash.String(), Equals, "d96c7efbfec2814ae0301ad054dc8d9fc416c9b5")
34+
35+
reader, err := blob.Reader()
36+
c.Assert(err, IsNil)
37+
defer func() { c.Assert(reader.Close(), IsNil) }()
38+
39+
data, err := ioutil.ReadAll(reader)
40+
c.Assert(err, IsNil)
41+
c.Assert(string(data), Equals, "FOO")
42+
}
43+
44+
func (s *BlobsSuite) TestBlobDecodeEncodeIdempotent(c *C) {
45+
var objects []*core.MemoryObject
46+
for _, str := range []string{"foo", "foo\n"} {
47+
obj := &core.MemoryObject{}
48+
obj.Write([]byte(str))
49+
obj.SetType(core.BlobObject)
50+
obj.Hash()
51+
objects = append(objects, obj)
52+
}
53+
for _, object := range objects {
54+
blob := &Blob{}
55+
err := blob.Decode(object)
56+
c.Assert(err, IsNil)
57+
newObject := &core.MemoryObject{}
58+
err = blob.Encode(newObject)
59+
c.Assert(err, IsNil)
60+
newObject.Hash() // Ensure Hash is pre-computed before deep comparison
61+
c.Assert(newObject, DeepEquals, object)
62+
}
63+
}
64+
65+
func (s *BlobsSuite) TestBlobIter(c *C) {
66+
iter, err := s.Repository.Blobs()
67+
c.Assert(err, IsNil)
68+
69+
blobs := []*Blob{}
70+
iter.ForEach(func(b *Blob) error {
71+
blobs = append(blobs, b)
72+
return nil
73+
})
74+
75+
c.Assert(len(blobs) > 0, Equals, true)
76+
iter.Close()
77+
78+
iter, err = s.Repository.Blobs()
79+
c.Assert(err, IsNil)
80+
81+
i := 0
82+
for {
83+
b, err := iter.Next()
84+
if err == io.EOF {
85+
break
86+
}
87+
88+
c.Assert(err, IsNil)
89+
c.Assert(b, DeepEquals, blobs[i])
90+
i += 1
91+
}
92+
93+
iter.Close()
94+
}

file.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ func (f *File) Lines() ([]string, error) {
5555

5656
type FileIter struct {
5757
r *Repository
58-
w TreeIter
58+
w TreeWalker
5959
}
6060

6161
func NewFileIter(r *Repository, t *Tree) *FileIter {
62-
return &FileIter{r: r, w: *NewTreeIter(r, t, true)}
62+
return &FileIter{r: r, w: *NewTreeWalker(r, t, true)}
6363
}
6464

6565
func (iter *FileIter) Next() (*File, error) {

objects.go

Lines changed: 71 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -43,64 +43,6 @@ type Object interface {
4343
Encode(core.Object) error
4444
}
4545

46-
// Blob is used to store file data - it is generally a file.
47-
type Blob struct {
48-
Hash core.Hash
49-
Size int64
50-
51-
obj core.Object
52-
}
53-
54-
// ID returns the object ID of the blob. The returned value will always match
55-
// the current value of Blob.Hash.
56-
//
57-
// ID is present to fulfill the Object interface.
58-
func (b *Blob) ID() core.Hash {
59-
return b.Hash
60-
}
61-
62-
// Type returns the type of object. It always returns core.BlobObject.
63-
//
64-
// Type is present to fulfill the Object interface.
65-
func (b *Blob) Type() core.ObjectType {
66-
return core.BlobObject
67-
}
68-
69-
// Decode transforms a core.Object into a Blob struct.
70-
func (b *Blob) Decode(o core.Object) error {
71-
if o.Type() != core.BlobObject {
72-
return ErrUnsupportedObject
73-
}
74-
75-
b.Hash = o.Hash()
76-
b.Size = o.Size()
77-
b.obj = o
78-
79-
return nil
80-
}
81-
82-
// Encode transforms a Blob into a core.Object.
83-
func (b *Blob) Encode(o core.Object) error {
84-
w, err := o.Writer()
85-
if err != nil {
86-
return err
87-
}
88-
defer checkClose(w, &err)
89-
r, err := b.Reader()
90-
if err != nil {
91-
return err
92-
}
93-
defer checkClose(r, &err)
94-
_, err = io.Copy(w, r)
95-
o.SetType(core.BlobObject)
96-
return err
97-
}
98-
99-
// Reader returns a reader allow the access to the content of the blob
100-
func (b *Blob) Reader() (core.ObjectReader, error) {
101-
return b.obj.Reader()
102-
}
103-
10446
// Signature represents an action signed by a person
10547
type Signature struct {
10648
Name string
@@ -171,3 +113,74 @@ func (s *Signature) encodeTimeAndTimeZone(w io.Writer) error {
171113
func (s *Signature) String() string {
172114
return fmt.Sprintf("%s <%s>", s.Name, s.Email)
173115
}
116+
117+
// ObjectIter provides an iterator for a set of objects.
118+
type ObjectIter struct {
119+
core.ObjectIter
120+
r *Repository
121+
}
122+
123+
// NewObjectIter returns a ObjectIter for the given repository and underlying
124+
// object iterator.
125+
func NewObjectIter(r *Repository, iter core.ObjectIter) *ObjectIter {
126+
return &ObjectIter{iter, r}
127+
}
128+
129+
// Next moves the iterator to the next object and returns a pointer to it. If it
130+
// has reached the end of the set it will return io.EOF.
131+
func (iter *ObjectIter) Next() (Object, error) {
132+
for {
133+
obj, err := iter.ObjectIter.Next()
134+
if err != nil {
135+
return nil, err
136+
}
137+
138+
o, err := iter.toObject(obj)
139+
if err == core.ErrInvalidType {
140+
continue
141+
}
142+
143+
if err != nil {
144+
return nil, err
145+
}
146+
147+
return o, nil
148+
}
149+
}
150+
151+
// ForEach call the cb function for each object contained on this iter until
152+
// an error happens or the end of the iter is reached. If ErrStop is sent
153+
// the iteration is stop but no error is returned. The iterator is closed.
154+
func (iter *ObjectIter) ForEach(cb func(Object) error) error {
155+
return iter.ObjectIter.ForEach(func(obj core.Object) error {
156+
o, err := iter.toObject(obj)
157+
if err == core.ErrInvalidType {
158+
return nil
159+
}
160+
161+
if err != nil {
162+
return err
163+
}
164+
165+
return cb(o)
166+
})
167+
}
168+
169+
func (iter *ObjectIter) toObject(obj core.Object) (Object, error) {
170+
switch obj.Type() {
171+
case core.BlobObject:
172+
blob := &Blob{}
173+
return blob, blob.Decode(obj)
174+
case core.TreeObject:
175+
tree := &Tree{r: iter.r}
176+
return tree, tree.Decode(obj)
177+
case core.CommitObject:
178+
commit := &Commit{}
179+
return commit, commit.Decode(obj)
180+
case core.TagObject:
181+
tag := &Tag{}
182+
return tag, tag.Decode(obj)
183+
default:
184+
return nil, core.ErrInvalidType
185+
}
186+
}

0 commit comments

Comments
 (0)