aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/storer
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2016-11-08 23:46:38 +0100
committerGitHub <noreply@github.com>2016-11-08 23:46:38 +0100
commitac095bb12c4d29722b60ba9f20590fa7cfa6bc7d (patch)
tree223f36f336ba3414b1e45cac8af6c4744a5d7ef6 /plumbing/storer
parente523701393598f4fa241dd407af9ff8925507a1a (diff)
downloadgo-git-ac095bb12c4d29722b60ba9f20590fa7cfa6bc7d.tar.gz
new plumbing package (#118)
* plumbing: now core was renamed to core, and formats and clients moved inside
Diffstat (limited to 'plumbing/storer')
-rw-r--r--plumbing/storer/index.go9
-rw-r--r--plumbing/storer/object.go241
-rw-r--r--plumbing/storer/object_test.go150
-rw-r--r--plumbing/storer/reference.go109
-rw-r--r--plumbing/storer/reference_test.go67
5 files changed, 576 insertions, 0 deletions
diff --git a/plumbing/storer/index.go b/plumbing/storer/index.go
new file mode 100644
index 0000000..e087296
--- /dev/null
+++ b/plumbing/storer/index.go
@@ -0,0 +1,9 @@
+package storer
+
+import "gopkg.in/src-d/go-git.v4/plumbing/format/index"
+
+// IndexStorer generic storage of index.Index
+type IndexStorer interface {
+ SetIndex(*index.Index) error
+ Index() (*index.Index, error)
+}
diff --git a/plumbing/storer/object.go b/plumbing/storer/object.go
new file mode 100644
index 0000000..c7841b6
--- /dev/null
+++ b/plumbing/storer/object.go
@@ -0,0 +1,241 @@
+package storer
+
+import (
+ "errors"
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+)
+
+var (
+ //ErrStop is used to stop a ForEach function in an Iter
+ ErrStop = errors.New("stop iter")
+)
+
+// ObjectStorer generic storage of objects
+type ObjectStorer interface {
+ // NewObject returns a new plumbing.Object, the real type of the object can
+ // be a custom implementation or the defaul one, plumbing.MemoryObject
+ NewObject() plumbing.Object
+ // SetObject save an object into the storage, the object shuld be create
+ // with the NewObject, method, and file if the type is not supported.
+ SetObject(plumbing.Object) (plumbing.Hash, error)
+ // Object get an object by hash with the given plumbing.ObjectType.
+ // Implementors should return (nil, plumbing.ErrObjectNotFound) if an object
+ // doesn't exist with both the given hash and object type.
+ //
+ // Valid plumbing.ObjectType values are CommitObject, BlobObject, TagObject,
+ // TreeObject and AnyObject. If plumbing.AnyObject is given, the object must
+ // be looked up regardless of its type.
+ Object(plumbing.ObjectType, plumbing.Hash) (plumbing.Object, error)
+ // IterObjects returns a custom ObjectIter over all the object on the
+ // storage.
+ //
+ // Valid plumbing.ObjectType values are CommitObject, BlobObject, TagObject,
+ IterObjects(plumbing.ObjectType) (ObjectIter, error)
+}
+
+// Transactioner is a optional method for ObjectStorer, it enable transaction
+// base write and read operations in the storage
+type Transactioner interface {
+ // Begin starts a transaction.
+ Begin() Transaction
+}
+
+// PackfileWriter is a optional method for ObjectStorer, it enable direct write
+// of packfile to the storage
+type PackfileWriter interface {
+ // PackfileWriter retuns a writer for writing a packfile to the storage
+ //
+ // If the Storer not implements PackfileWriter the objects should be written
+ // using the Set method.
+ PackfileWriter() (io.WriteCloser, error)
+}
+
+// ObjectIter is a generic closable interface for iterating over objects.
+type ObjectIter interface {
+ Next() (plumbing.Object, error)
+ ForEach(func(plumbing.Object) error) error
+ Close()
+}
+
+// Transaction is an in-progress storage transaction. A transaction must end
+// with a call to Commit or Rollback.
+type Transaction interface {
+ SetObject(plumbing.Object) (plumbing.Hash, error)
+ Object(plumbing.ObjectType, plumbing.Hash) (plumbing.Object, error)
+ Commit() error
+ Rollback() error
+}
+
+// ObjectLookupIter implements ObjectIter. It iterates over a series of object
+// hashes and yields their associated objects by retrieving each one from
+// object storage. The retrievals are lazy and only occur when the iterator
+// moves forward with a call to Next().
+//
+// The ObjectLookupIter must be closed with a call to Close() when it is no
+// longer needed.
+type ObjectLookupIter struct {
+ storage ObjectStorer
+ series []plumbing.Hash
+ t plumbing.ObjectType
+ pos int
+}
+
+// NewObjectLookupIter returns an object iterator given an object storage and
+// a slice of object hashes.
+func NewObjectLookupIter(
+ storage ObjectStorer, t plumbing.ObjectType, series []plumbing.Hash) *ObjectLookupIter {
+ return &ObjectLookupIter{
+ storage: storage,
+ series: series,
+ t: t,
+ }
+}
+
+// Next returns the next object from the iterator. If the iterator has reached
+// the end it will return io.EOF as an error. If the object can't be found in
+// the object storage, it will return plumbing.ErrObjectNotFound as an error.
+// If the object is retreieved successfully error will be nil.
+func (iter *ObjectLookupIter) Next() (plumbing.Object, error) {
+ if iter.pos >= len(iter.series) {
+ return nil, io.EOF
+ }
+
+ hash := iter.series[iter.pos]
+ obj, err := iter.storage.Object(iter.t, hash)
+ if err == nil {
+ iter.pos++
+ }
+
+ return obj, err
+}
+
+// ForEach call the cb function for each object contained on this iter until
+// an error happends 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 *ObjectLookupIter) ForEach(cb func(plumbing.Object) error) error {
+ return ForEachIterator(iter, cb)
+}
+
+// Close releases any resources used by the iterator.
+func (iter *ObjectLookupIter) Close() {
+ iter.pos = len(iter.series)
+}
+
+// ObjectSliceIter implements ObjectIter. It iterates over a series of objects
+// stored in a slice and yields each one in turn when Next() is called.
+//
+// The ObjectSliceIter must be closed with a call to Close() when it is no
+// longer needed.
+type ObjectSliceIter struct {
+ series []plumbing.Object
+ pos int
+}
+
+// NewObjectSliceIter returns an object iterator for the given slice of objects.
+func NewObjectSliceIter(series []plumbing.Object) *ObjectSliceIter {
+ return &ObjectSliceIter{
+ series: series,
+ }
+}
+
+// Next returns the next object from the iterator. If the iterator has reached
+// the end it will return io.EOF as an error. If the object is retreieved
+// successfully error will be nil.
+func (iter *ObjectSliceIter) Next() (plumbing.Object, error) {
+ if len(iter.series) == 0 {
+ return nil, io.EOF
+ }
+
+ obj := iter.series[0]
+ iter.series = iter.series[1:]
+
+ return obj, nil
+}
+
+// ForEach call the cb function for each object contained on this iter until
+// an error happends 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 *ObjectSliceIter) ForEach(cb func(plumbing.Object) error) error {
+ return ForEachIterator(iter, cb)
+}
+
+// Close releases any resources used by the iterator.
+func (iter *ObjectSliceIter) Close() {
+ iter.series = []plumbing.Object{}
+}
+
+// MultiObjectIter implements ObjectIter. It iterates over several ObjectIter,
+//
+// The MultiObjectIter must be closed with a call to Close() when it is no
+// longer needed.
+type MultiObjectIter struct {
+ iters []ObjectIter
+ pos int
+}
+
+// NewMultiObjectIter returns an object iterator for the given slice of objects.
+func NewMultiObjectIter(iters []ObjectIter) ObjectIter {
+ return &MultiObjectIter{iters: iters}
+}
+
+// Next returns the next object from the iterator, if one iterator reach io.EOF
+// is removed and the next one is used.
+func (iter *MultiObjectIter) Next() (plumbing.Object, error) {
+ if len(iter.iters) == 0 {
+ return nil, io.EOF
+ }
+
+ obj, err := iter.iters[0].Next()
+ if err == io.EOF {
+ iter.iters[0].Close()
+ iter.iters = iter.iters[1:]
+ return iter.Next()
+ }
+
+ return obj, err
+}
+
+// ForEach call the cb function for each object contained on this iter until
+// an error happends 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 *MultiObjectIter) ForEach(cb func(plumbing.Object) error) error {
+ return ForEachIterator(iter, cb)
+}
+
+// Close releases any resources used by the iterator.
+func (iter *MultiObjectIter) Close() {
+ for _, i := range iter.iters {
+ i.Close()
+ }
+}
+
+type bareIterator interface {
+ Next() (plumbing.Object, error)
+ Close()
+}
+
+// ForEachIterator is a helper function to build iterators without need to
+// rewrite the same ForEach function each time.
+func ForEachIterator(iter bareIterator, cb func(plumbing.Object) error) error {
+ defer iter.Close()
+ for {
+ obj, err := iter.Next()
+ if err != nil {
+ if err == io.EOF {
+ return nil
+ }
+
+ return err
+ }
+
+ if err := cb(obj); err != nil {
+ if err == ErrStop {
+ return nil
+ }
+
+ return err
+ }
+ }
+}
diff --git a/plumbing/storer/object_test.go b/plumbing/storer/object_test.go
new file mode 100644
index 0000000..a0a7755
--- /dev/null
+++ b/plumbing/storer/object_test.go
@@ -0,0 +1,150 @@
+package storer
+
+import (
+ "fmt"
+ "testing"
+
+ . "gopkg.in/check.v1"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+)
+
+func Test(t *testing.T) { TestingT(t) }
+
+type ObjectSuite struct {
+ Objects []plumbing.Object
+ Hash []plumbing.Hash
+}
+
+var _ = Suite(&ObjectSuite{})
+
+func (s *ObjectSuite) SetUpSuite(c *C) {
+ s.Objects = []plumbing.Object{
+ s.buildObject([]byte("foo")),
+ s.buildObject([]byte("bar")),
+ }
+
+ for _, o := range s.Objects {
+ s.Hash = append(s.Hash, o.Hash())
+ }
+}
+
+func (s *ObjectSuite) TestMultiObjectIterNext(c *C) {
+ expected := []plumbing.Object{
+ &plumbing.MemoryObject{},
+ &plumbing.MemoryObject{},
+ &plumbing.MemoryObject{},
+ &plumbing.MemoryObject{},
+ &plumbing.MemoryObject{},
+ &plumbing.MemoryObject{},
+ }
+
+ iter := NewMultiObjectIter([]ObjectIter{
+ NewObjectSliceIter(expected[0:2]),
+ NewObjectSliceIter(expected[2:4]),
+ NewObjectSliceIter(expected[4:5]),
+ })
+
+ var i int
+ iter.ForEach(func(o plumbing.Object) error {
+ c.Assert(o, Equals, expected[i])
+ i++
+ return nil
+ })
+
+ iter.Close()
+}
+
+func (s *ObjectSuite) buildObject(content []byte) plumbing.Object {
+ o := &plumbing.MemoryObject{}
+ o.Write(content)
+
+ return o
+}
+
+func (s *ObjectSuite) TestObjectLookupIter(c *C) {
+ var count int
+
+ storage := &MockObjectStorage{s.Objects}
+ i := NewObjectLookupIter(storage, plumbing.CommitObject, s.Hash)
+ err := i.ForEach(func(o plumbing.Object) error {
+ c.Assert(o, NotNil)
+ c.Assert(o.Hash().String(), Equals, s.Hash[count].String())
+ count++
+ return nil
+ })
+
+ c.Assert(err, IsNil)
+ i.Close()
+}
+
+func (s *ObjectSuite) TestObjectSliceIter(c *C) {
+ var count int
+
+ i := NewObjectSliceIter(s.Objects)
+ err := i.ForEach(func(o plumbing.Object) error {
+ c.Assert(o, NotNil)
+ c.Assert(o.Hash().String(), Equals, s.Hash[count].String())
+ count++
+ return nil
+ })
+
+ c.Assert(count, Equals, 2)
+ c.Assert(err, IsNil)
+ c.Assert(i.series, HasLen, 0)
+}
+
+func (s *ObjectSuite) TestObjectSliceIterStop(c *C) {
+ i := NewObjectSliceIter(s.Objects)
+
+ var count = 0
+ err := i.ForEach(func(o plumbing.Object) error {
+ c.Assert(o, NotNil)
+ c.Assert(o.Hash().String(), Equals, s.Hash[count].String())
+ count++
+ return ErrStop
+ })
+
+ c.Assert(count, Equals, 1)
+ c.Assert(err, IsNil)
+}
+
+func (s *ObjectSuite) TestObjectSliceIterError(c *C) {
+ i := NewObjectSliceIter([]plumbing.Object{
+ s.buildObject([]byte("foo")),
+ })
+
+ err := i.ForEach(func(plumbing.Object) error {
+ return fmt.Errorf("a random error")
+ })
+
+ c.Assert(err, NotNil)
+}
+
+type MockObjectStorage struct {
+ db []plumbing.Object
+}
+
+func (o *MockObjectStorage) NewObject() plumbing.Object {
+ return nil
+}
+
+func (o *MockObjectStorage) SetObject(obj plumbing.Object) (plumbing.Hash, error) {
+ return plumbing.ZeroHash, nil
+}
+
+func (o *MockObjectStorage) Object(t plumbing.ObjectType, h plumbing.Hash) (plumbing.Object, error) {
+ for _, o := range o.db {
+ if o.Hash() == h {
+ return o, nil
+ }
+ }
+ return nil, plumbing.ErrObjectNotFound
+}
+
+func (o *MockObjectStorage) IterObjects(t plumbing.ObjectType) (ObjectIter, error) {
+ return nil, nil
+}
+
+func (o *MockObjectStorage) Begin() Transaction {
+ return nil
+}
diff --git a/plumbing/storer/reference.go b/plumbing/storer/reference.go
new file mode 100644
index 0000000..5e818c6
--- /dev/null
+++ b/plumbing/storer/reference.go
@@ -0,0 +1,109 @@
+package storer
+
+import (
+ "errors"
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+)
+
+const MaxResolveRecursion = 1024
+
+// ErrMaxResolveRecursion is returned by ResolveReference is MaxResolveRecursion
+// is exceeded
+var ErrMaxResolveRecursion = errors.New("max. recursion level reached")
+
+// ReferenceStorer generic storage of references
+type ReferenceStorer interface {
+ SetReference(*plumbing.Reference) error
+ Reference(plumbing.ReferenceName) (*plumbing.Reference, error)
+ IterReferences() (ReferenceIter, error)
+}
+
+// ReferenceIter is a generic closable interface for iterating over references
+type ReferenceIter interface {
+ Next() (*plumbing.Reference, error)
+ ForEach(func(*plumbing.Reference) error) error
+ Close()
+}
+
+// ReferenceSliceIter implements ReferenceIter. It iterates over a series of
+// references stored in a slice and yields each one in turn when Next() is
+// called.
+//
+// The ReferenceSliceIter must be closed with a call to Close() when it is no
+// longer needed.
+type ReferenceSliceIter struct {
+ series []*plumbing.Reference
+ pos int
+}
+
+// NewReferenceSliceIter returns a reference iterator for the given slice of
+// objects.
+func NewReferenceSliceIter(series []*plumbing.Reference) *ReferenceSliceIter {
+ return &ReferenceSliceIter{
+ series: series,
+ }
+}
+
+// Next returns the next reference from the iterator. If the iterator has
+// reached the end it will return io.EOF as an error.
+func (iter *ReferenceSliceIter) Next() (*plumbing.Reference, error) {
+ if iter.pos >= len(iter.series) {
+ return nil, io.EOF
+ }
+
+ obj := iter.series[iter.pos]
+ iter.pos++
+ return obj, nil
+}
+
+// ForEach call the cb function for each reference contained on this iter until
+// an error happends 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 *ReferenceSliceIter) ForEach(cb func(*plumbing.Reference) error) error {
+ defer iter.Close()
+ for _, r := range iter.series {
+ if err := cb(r); err != nil {
+ if err == ErrStop {
+ return nil
+ }
+
+ return nil
+ }
+ }
+
+ return nil
+}
+
+// Close releases any resources used by the iterator.
+func (iter *ReferenceSliceIter) Close() {
+ iter.pos = len(iter.series)
+}
+
+// ResolveReference resolve a SymbolicReference to a HashReference
+func ResolveReference(s ReferenceStorer, n plumbing.ReferenceName) (*plumbing.Reference, error) {
+ r, err := s.Reference(n)
+ if err != nil || r == nil {
+ return r, err
+ }
+ return resolveReference(s, r, 0)
+}
+
+func resolveReference(s ReferenceStorer, r *plumbing.Reference, recursion int) (*plumbing.Reference, error) {
+ if r.Type() != plumbing.SymbolicReference {
+ return r, nil
+ }
+
+ if recursion > MaxResolveRecursion {
+ return nil, ErrMaxResolveRecursion
+ }
+
+ t, err := s.Reference(r.Target())
+ if err != nil {
+ return nil, err
+ }
+
+ recursion++
+ return resolveReference(s, t, recursion)
+}
diff --git a/plumbing/storer/reference_test.go b/plumbing/storer/reference_test.go
new file mode 100644
index 0000000..3014df5
--- /dev/null
+++ b/plumbing/storer/reference_test.go
@@ -0,0 +1,67 @@
+package storer
+
+import (
+ "io"
+
+ . "gopkg.in/check.v1"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+)
+
+type ReferenceSuite struct{}
+
+var _ = Suite(&ReferenceSuite{})
+
+func (s *ReferenceSuite) TestReferenceSliceIterNext(c *C) {
+ slice := []*plumbing.Reference{
+ plumbing.NewReferenceFromStrings("foo", "foo"),
+ plumbing.NewReferenceFromStrings("bar", "bar"),
+ }
+
+ i := NewReferenceSliceIter(slice)
+ foo, err := i.Next()
+ c.Assert(err, IsNil)
+ c.Assert(foo == slice[0], Equals, true)
+
+ bar, err := i.Next()
+ c.Assert(err, IsNil)
+ c.Assert(bar == slice[1], Equals, true)
+
+ empty, err := i.Next()
+ c.Assert(err, Equals, io.EOF)
+ c.Assert(empty, IsNil)
+}
+
+func (s *ReferenceSuite) TestReferenceSliceIterForEach(c *C) {
+ slice := []*plumbing.Reference{
+ plumbing.NewReferenceFromStrings("foo", "foo"),
+ plumbing.NewReferenceFromStrings("bar", "bar"),
+ }
+
+ i := NewReferenceSliceIter(slice)
+ var count int
+ i.ForEach(func(r *plumbing.Reference) error {
+ c.Assert(r == slice[count], Equals, true)
+ count++
+ return nil
+ })
+
+ c.Assert(count, Equals, 2)
+}
+
+func (s *ReferenceSuite) TestReferenceSliceIterForEachStop(c *C) {
+ slice := []*plumbing.Reference{
+ plumbing.NewReferenceFromStrings("foo", "foo"),
+ plumbing.NewReferenceFromStrings("bar", "bar"),
+ }
+
+ i := NewReferenceSliceIter(slice)
+
+ var count int
+ i.ForEach(func(r *plumbing.Reference) error {
+ c.Assert(r == slice[count], Equals, true)
+ count++
+ return ErrStop
+ })
+
+ c.Assert(count, Equals, 1)
+}