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

add Blobs, Trees and Objects iters. #114

Merged
merged 1 commit into from
Nov 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions blobs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package git

import (
"io"

"gopkg.in/src-d/go-git.v4/core"
)

// Blob is used to store file data - it is generally a file.
type Blob struct {
Hash core.Hash
Size int64

obj core.Object
}

// ID returns the object ID of the blob. The returned value will always match
// the current value of Blob.Hash.
//
// ID is present to fulfill the Object interface.
func (b *Blob) ID() core.Hash {
return b.Hash
}

// Type returns the type of object. It always returns core.BlobObject.
//
// Type is present to fulfill the Object interface.
func (b *Blob) Type() core.ObjectType {
return core.BlobObject
}

// Decode transforms a core.Object into a Blob struct.
func (b *Blob) Decode(o core.Object) error {
if o.Type() != core.BlobObject {
return ErrUnsupportedObject
}

b.Hash = o.Hash()
b.Size = o.Size()
b.obj = o

return nil
}

// Encode transforms a Blob into a core.Object.
func (b *Blob) Encode(o core.Object) error {
w, err := o.Writer()
if err != nil {
return err
}
defer checkClose(w, &err)
r, err := b.Reader()
if err != nil {
return err
}
defer checkClose(r, &err)
_, err = io.Copy(w, r)
o.SetType(core.BlobObject)
return err
}

// Reader returns a reader allow the access to the content of the blob
func (b *Blob) Reader() (core.ObjectReader, error) {
return b.obj.Reader()
}

// BlobIter provides an iterator for a set of blobs.
type BlobIter struct {
core.ObjectIter
r *Repository
}

// NewBlobIter returns a CommitIter for the given repository and underlying
// object iterator.
//
// The returned BlobIter will automatically skip over non-blob objects.
func NewBlobIter(r *Repository, iter core.ObjectIter) *BlobIter {
return &BlobIter{iter, r}
}

// Next moves the iterator to the next blob and returns a pointer to it. If it
// has reached the end of the set it will return io.EOF.
func (iter *BlobIter) Next() (*Blob, error) {
for {
obj, err := iter.ObjectIter.Next()
if err != nil {
return nil, err
}

if obj.Type() != core.BlobObject {
continue
}

blob := &Blob{}
return blob, blob.Decode(obj)
}
}

// ForEach call the cb function for each blob contained on this iter until
// an error happens or the end of the iter is reached. If ErrStop is sent
// the iteration is stop but no error is returned. The iterator is closed.
func (iter *BlobIter) ForEach(cb func(*Blob) error) error {
return iter.ObjectIter.ForEach(func(obj core.Object) error {
if obj.Type() != core.BlobObject {
return nil
}

blob := &Blob{}
if err := blob.Decode(obj); err != nil {
return err
}

return cb(blob)
})
}
94 changes: 94 additions & 0 deletions blobs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package git

import (
"io"
"io/ioutil"

"gopkg.in/src-d/go-git.v4/core"

. "gopkg.in/check.v1"
)

type BlobsSuite struct {
BaseSuite
}

var _ = Suite(&BlobsSuite{})

func (s *BlobsSuite) TestBlobHash(c *C) {
o := &core.MemoryObject{}
o.SetType(core.BlobObject)
o.SetSize(3)

writer, err := o.Writer()
c.Assert(err, IsNil)
defer func() { c.Assert(writer.Close(), IsNil) }()

writer.Write([]byte{'F', 'O', 'O'})

blob := &Blob{}
c.Assert(blob.Decode(o), IsNil)

c.Assert(blob.Size, Equals, int64(3))
c.Assert(blob.Hash.String(), Equals, "d96c7efbfec2814ae0301ad054dc8d9fc416c9b5")

reader, err := blob.Reader()
c.Assert(err, IsNil)
defer func() { c.Assert(reader.Close(), IsNil) }()

data, err := ioutil.ReadAll(reader)
c.Assert(err, IsNil)
c.Assert(string(data), Equals, "FOO")
}

func (s *BlobsSuite) TestBlobDecodeEncodeIdempotent(c *C) {
var objects []*core.MemoryObject
for _, str := range []string{"foo", "foo\n"} {
obj := &core.MemoryObject{}
obj.Write([]byte(str))
obj.SetType(core.BlobObject)
obj.Hash()
objects = append(objects, obj)
}
for _, object := range objects {
blob := &Blob{}
err := blob.Decode(object)
c.Assert(err, IsNil)
newObject := &core.MemoryObject{}
err = blob.Encode(newObject)
c.Assert(err, IsNil)
newObject.Hash() // Ensure Hash is pre-computed before deep comparison
c.Assert(newObject, DeepEquals, object)
}
}

func (s *BlobsSuite) TestBlobIter(c *C) {
iter, err := s.Repository.Blobs()
c.Assert(err, IsNil)

blobs := []*Blob{}
iter.ForEach(func(b *Blob) error {
blobs = append(blobs, b)
return nil
})

c.Assert(len(blobs) > 0, Equals, true)
iter.Close()

iter, err = s.Repository.Blobs()
c.Assert(err, IsNil)

i := 0
for {
b, err := iter.Next()
if err == io.EOF {
break
}

c.Assert(err, IsNil)
c.Assert(b, DeepEquals, blobs[i])
i += 1
}

iter.Close()
}
4 changes: 2 additions & 2 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ func (f *File) Lines() ([]string, error) {

type FileIter struct {
r *Repository
w TreeIter
w TreeWalker
}

func NewFileIter(r *Repository, t *Tree) *FileIter {
return &FileIter{r: r, w: *NewTreeIter(r, t, true)}
return &FileIter{r: r, w: *NewTreeWalker(r, t, true)}
}

func (iter *FileIter) Next() (*File, error) {
Expand Down
129 changes: 71 additions & 58 deletions objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,64 +43,6 @@ type Object interface {
Encode(core.Object) error
}

// Blob is used to store file data - it is generally a file.
type Blob struct {
Hash core.Hash
Size int64

obj core.Object
}

// ID returns the object ID of the blob. The returned value will always match
// the current value of Blob.Hash.
//
// ID is present to fulfill the Object interface.
func (b *Blob) ID() core.Hash {
return b.Hash
}

// Type returns the type of object. It always returns core.BlobObject.
//
// Type is present to fulfill the Object interface.
func (b *Blob) Type() core.ObjectType {
return core.BlobObject
}

// Decode transforms a core.Object into a Blob struct.
func (b *Blob) Decode(o core.Object) error {
if o.Type() != core.BlobObject {
return ErrUnsupportedObject
}

b.Hash = o.Hash()
b.Size = o.Size()
b.obj = o

return nil
}

// Encode transforms a Blob into a core.Object.
func (b *Blob) Encode(o core.Object) error {
w, err := o.Writer()
if err != nil {
return err
}
defer checkClose(w, &err)
r, err := b.Reader()
if err != nil {
return err
}
defer checkClose(r, &err)
_, err = io.Copy(w, r)
o.SetType(core.BlobObject)
return err
}

// Reader returns a reader allow the access to the content of the blob
func (b *Blob) Reader() (core.ObjectReader, error) {
return b.obj.Reader()
}

// Signature represents an action signed by a person
type Signature struct {
Name string
Expand Down Expand Up @@ -171,3 +113,74 @@ func (s *Signature) encodeTimeAndTimeZone(w io.Writer) error {
func (s *Signature) String() string {
return fmt.Sprintf("%s <%s>", s.Name, s.Email)
}

// ObjectIter provides an iterator for a set of objects.
type ObjectIter struct {
core.ObjectIter
r *Repository
}

// NewObjectIter returns a ObjectIter for the given repository and underlying
// object iterator.
func NewObjectIter(r *Repository, iter core.ObjectIter) *ObjectIter {
return &ObjectIter{iter, r}
}

// Next moves the iterator to the next object and returns a pointer to it. If it
// has reached the end of the set it will return io.EOF.
func (iter *ObjectIter) Next() (Object, error) {
for {
obj, err := iter.ObjectIter.Next()
if err != nil {
return nil, err
}

o, err := iter.toObject(obj)
if err == core.ErrInvalidType {
continue
}

if err != nil {
return nil, err
}

return o, nil
}
}

// ForEach call the cb function for each object contained on this iter until
// an error happens or the end of the iter is reached. If ErrStop is sent
// the iteration is stop but no error is returned. The iterator is closed.
func (iter *ObjectIter) ForEach(cb func(Object) error) error {
return iter.ObjectIter.ForEach(func(obj core.Object) error {
o, err := iter.toObject(obj)
if err == core.ErrInvalidType {
return nil
}

if err != nil {
return err
}

return cb(o)
})
}

func (iter *ObjectIter) toObject(obj core.Object) (Object, error) {
switch obj.Type() {
case core.BlobObject:
blob := &Blob{}
return blob, blob.Decode(obj)
case core.TreeObject:
tree := &Tree{r: iter.r}
return tree, tree.Decode(obj)
case core.CommitObject:
commit := &Commit{}
return commit, commit.Decode(obj)
case core.TagObject:
tag := &Tag{}
return tag, tag.Decode(obj)
default:
return nil, core.ErrInvalidType
}
}
Loading