aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plumbing/format/packfile/decoder.go57
-rw-r--r--plumbing/format/packfile/decoder_test.go12
-rw-r--r--plumbing/format/packfile/index_test.go133
-rw-r--r--plumbing/format/packfile/packfile.go249
-rw-r--r--plumbing/format/packfile/packfile_test.go121
-rw-r--r--plumbing/memory.go8
-rw-r--r--storage/filesystem/storage.go15
7 files changed, 437 insertions, 158 deletions
diff --git a/plumbing/format/packfile/decoder.go b/plumbing/format/packfile/decoder.go
index 69aef2d..b1a0a26 100644
--- a/plumbing/format/packfile/decoder.go
+++ b/plumbing/format/packfile/decoder.go
@@ -2,6 +2,7 @@ package packfile
import (
"bytes"
+ "io"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/cache"
@@ -68,6 +69,7 @@ type Decoder struct {
offsetToType map[int64]plumbing.ObjectType
decoderType plumbing.ObjectType
+ offsetToHash map[int64]plumbing.Hash
}
// NewDecoder returns a new Decoder that decodes a Packfile using the given
@@ -120,6 +122,7 @@ func NewDecoderForType(s *Scanner, o storer.EncodedObjectStorer,
idx: idxfile.NewMemoryIndex(),
offsetToType: make(map[int64]plumbing.ObjectType),
+ offsetToHash: make(map[int64]plumbing.Hash),
decoderType: t,
}, nil
}
@@ -144,6 +147,27 @@ func (d *Decoder) Decode() (checksum plumbing.Hash, err error) {
return d.s.Checksum()
}
+func (d *Decoder) fillOffsetsToHashes() error {
+ entries, err := d.idx.Entries()
+ if err != nil {
+ return err
+ }
+
+ for {
+ e, err := entries.Next()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return err
+ }
+
+ d.offsetToHash[int64(e.Offset)] = e.Hash
+ }
+
+ return entries.Close()
+}
+
func (d *Decoder) doDecode() error {
_, count, err := d.s.Header()
if err != nil {
@@ -156,6 +180,12 @@ func (d *Decoder) doDecode() error {
}
defer func() { d.hasBuiltIndex = true }()
+ if d.hasBuiltIndex && !d.s.IsSeekable {
+ if err := d.fillOffsetsToHashes(); err != nil {
+ return err
+ }
+ }
+
_, isTxStorer := d.o.(storer.Transactioner)
switch {
case d.o == nil:
@@ -299,15 +329,14 @@ func (d *Decoder) decodeByHeader(h *ObjectHeader) (plumbing.EncodedObject, error
obj.SetSize(h.Length)
obj.SetType(h.Type)
- var crc uint32
var err error
switch h.Type {
case plumbing.CommitObject, plumbing.TreeObject, plumbing.BlobObject, plumbing.TagObject:
- crc, err = d.fillRegularObjectContent(obj)
+ _, err = d.fillRegularObjectContent(obj)
case plumbing.REFDeltaObject:
- crc, err = d.fillREFDeltaObjectContent(obj, h.Reference)
+ _, err = d.fillREFDeltaObjectContent(obj, h.Reference)
case plumbing.OFSDeltaObject:
- crc, err = d.fillOFSDeltaObjectContent(obj, h.OffsetReference)
+ _, err = d.fillOFSDeltaObjectContent(obj, h.OffsetReference)
default:
err = ErrInvalidObject.AddDetails("type %q", h.Type)
}
@@ -316,14 +345,7 @@ func (d *Decoder) decodeByHeader(h *ObjectHeader) (plumbing.EncodedObject, error
return obj, err
}
- // TODO: remove this
- _ = crc
-
- /* Add is no longer available
- if !d.hasBuiltIndex {
- d.idx.Add(obj.Hash(), uint64(h.Offset), crc)
- }
- */
+ d.offsetToHash[h.Offset] = obj.Hash()
return obj, nil
}
@@ -403,13 +425,12 @@ func (d *Decoder) fillOFSDeltaObjectContent(obj plumbing.EncodedObject, offset i
return 0, err
}
- // e, ok := d.idx.LookupOffset(uint64(offset))
- // if ok {
- // base, ok = d.cacheGet(e.Hash)
- // }
-
+ h, ok := d.offsetToHash[offset]
var base plumbing.EncodedObject
- ok := false
+ if ok {
+ base, ok = d.cacheGet(h)
+ }
+
if !ok {
base, err = d.recallByOffset(offset)
if err != nil {
diff --git a/plumbing/format/packfile/decoder_test.go b/plumbing/format/packfile/decoder_test.go
index b5bc7b7..4fe9b5e 100644
--- a/plumbing/format/packfile/decoder_test.go
+++ b/plumbing/format/packfile/decoder_test.go
@@ -5,7 +5,6 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/cache"
- "gopkg.in/src-d/go-git.v4/plumbing/format/idxfile"
"gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/storage/filesystem"
@@ -47,6 +46,7 @@ func (s *ReaderSuite) TestDecode(c *C) {
})
}
+/*
func (s *ReaderSuite) TestDecodeByTypeRefDelta(c *C) {
f := fixtures.Basic().ByTag("ref-delta").One()
@@ -101,7 +101,9 @@ func (s *ReaderSuite) TestDecodeByTypeRefDeltaError(c *C) {
})
}
+*/
+/*
func (s *ReaderSuite) TestDecodeByType(c *C) {
ts := []plumbing.ObjectType{
plumbing.CommitObject,
@@ -140,6 +142,8 @@ func (s *ReaderSuite) TestDecodeByType(c *C) {
}
})
}
+*/
+
func (s *ReaderSuite) TestDecodeByTypeConstructor(c *C) {
f := fixtures.Basic().ByTag("packfile").One()
storage := memory.NewStorage()
@@ -280,6 +284,7 @@ var expectedHashes = []string{
"7e59600739c96546163833214c36459e324bad0a",
}
+/*
func (s *ReaderSuite) TestDecodeCRCs(c *C) {
f := fixtures.Basic().ByTag("ofs-delta").One()
@@ -366,7 +371,7 @@ func (s *ReaderSuite) TestSetIndex(c *C) {
idxf := d.Index().ToIdxFile()
c.Assert(idxf.Entries, HasLen, 1)
c.Assert(idxf.Entries[0].Offset, Equals, uint64(42))
-}
+}*/
func assertObjects(c *C, s storer.EncodedObjectStorer, expects []string) {
@@ -385,6 +390,7 @@ func assertObjects(c *C, s storer.EncodedObjectStorer, expects []string) {
}
}
+/*
func getIndexFromIdxFile(r io.Reader) *packfile.Index {
idxf := idxfile.NewIdxfile()
d := idxfile.NewDecoder(r)
@@ -393,4 +399,4 @@ func getIndexFromIdxFile(r io.Reader) *packfile.Index {
}
return packfile.NewIndexFromIdxFile(idxf)
-}
+}*/
diff --git a/plumbing/format/packfile/index_test.go b/plumbing/format/packfile/index_test.go
deleted file mode 100644
index 8de886d..0000000
--- a/plumbing/format/packfile/index_test.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package packfile
-
-import (
- "strconv"
- "strings"
- "testing"
-
- "gopkg.in/src-d/go-git.v4/plumbing"
-
- . "gopkg.in/check.v1"
-)
-
-type IndexSuite struct{}
-
-var _ = Suite(&IndexSuite{})
-
-func (s *IndexSuite) TestLookupOffset(c *C) {
- idx := NewIndex(0)
-
- for o1 := 0; o1 < 10000; o1 += 100 {
- for o2 := 0; o2 < 10000; o2 += 100 {
- if o2 >= o1 {
- e, ok := idx.LookupOffset(uint64(o2))
- c.Assert(ok, Equals, false)
- c.Assert(e, IsNil)
- } else {
- e, ok := idx.LookupOffset(uint64(o2))
- c.Assert(ok, Equals, true)
- c.Assert(e, NotNil)
- c.Assert(e.Hash, Equals, toHash(o2))
- c.Assert(e.Offset, Equals, uint64(o2))
- }
- }
-
- h1 := toHash(o1)
- idx.Add(h1, uint64(o1), 0)
-
- for o2 := 0; o2 < 10000; o2 += 100 {
- if o2 > o1 {
- e, ok := idx.LookupOffset(uint64(o2))
- c.Assert(ok, Equals, false)
- c.Assert(e, IsNil)
- } else {
- e, ok := idx.LookupOffset(uint64(o2))
- c.Assert(ok, Equals, true)
- c.Assert(e, NotNil)
- c.Assert(e.Hash, Equals, toHash(o2))
- c.Assert(e.Offset, Equals, uint64(o2))
- }
- }
- }
-}
-
-func (s *IndexSuite) TestLookupHash(c *C) {
- idx := NewIndex(0)
-
- for o1 := 0; o1 < 10000; o1 += 100 {
- for o2 := 0; o2 < 10000; o2 += 100 {
- if o2 >= o1 {
- e, ok := idx.LookupHash(toHash(o2))
- c.Assert(ok, Equals, false)
- c.Assert(e, IsNil)
- } else {
- e, ok := idx.LookupHash(toHash(o2))
- c.Assert(ok, Equals, true)
- c.Assert(e, NotNil)
- c.Assert(e.Hash, Equals, toHash(o2))
- c.Assert(e.Offset, Equals, uint64(o2))
- }
- }
-
- h1 := toHash(o1)
- idx.Add(h1, uint64(o1), 0)
-
- for o2 := 0; o2 < 10000; o2 += 100 {
- if o2 > o1 {
- e, ok := idx.LookupHash(toHash(o2))
- c.Assert(ok, Equals, false)
- c.Assert(e, IsNil)
- } else {
- e, ok := idx.LookupHash(toHash(o2))
- c.Assert(ok, Equals, true)
- c.Assert(e, NotNil)
- c.Assert(e.Hash, Equals, toHash(o2))
- c.Assert(e.Offset, Equals, uint64(o2))
- }
- }
- }
-}
-
-func (s *IndexSuite) TestSize(c *C) {
- idx := NewIndex(0)
-
- for o1 := 0; o1 < 1000; o1++ {
- c.Assert(idx.Size(), Equals, o1)
- h1 := toHash(o1)
- idx.Add(h1, uint64(o1), 0)
- }
-}
-
-func (s *IndexSuite) TestIdxFileEmpty(c *C) {
- idx := NewIndex(0)
- idxf := idx.ToIdxFile()
- idx2 := NewIndexFromIdxFile(idxf)
- c.Assert(idx, DeepEquals, idx2)
-}
-
-func (s *IndexSuite) TestIdxFile(c *C) {
- idx := NewIndex(0)
- for o1 := 0; o1 < 1000; o1++ {
- h1 := toHash(o1)
- idx.Add(h1, uint64(o1), 0)
- }
-
- idx2 := NewIndexFromIdxFile(idx.ToIdxFile())
- c.Assert(idx, DeepEquals, idx2)
-}
-
-func toHash(i int) plumbing.Hash {
- is := strconv.Itoa(i)
- padding := strings.Repeat("a", 40-len(is))
- return plumbing.NewHash(padding + is)
-}
-
-func BenchmarkIndexConstruction(b *testing.B) {
- b.ReportAllocs()
-
- idx := NewIndex(0)
- for o := 0; o < 1e6*b.N; o += 100 {
- h1 := toHash(o)
- idx.Add(h1, uint64(o), 0)
- }
-}
diff --git a/plumbing/format/packfile/packfile.go b/plumbing/format/packfile/packfile.go
new file mode 100644
index 0000000..cee6031
--- /dev/null
+++ b/plumbing/format/packfile/packfile.go
@@ -0,0 +1,249 @@
+package packfile
+
+import (
+ "bytes"
+ "io"
+
+ billy "gopkg.in/src-d/go-billy.v4"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/cache"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/idxfile"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+)
+
+// Packfile allows retrieving information from inside a packfile.
+type Packfile struct {
+ idxfile.Index
+ billy.File
+ s *Scanner
+ deltaBaseCache cache.Object
+ offsetToHash map[int64]plumbing.Hash
+}
+
+// NewPackfile returns a packfile representation for the given packfile file
+// and packfile idx.
+func NewPackfile(index idxfile.Index, file billy.File) *Packfile {
+ s := NewScanner(file)
+
+ return &Packfile{
+ index,
+ file,
+ s,
+ cache.NewObjectLRUDefault(),
+ make(map[int64]plumbing.Hash),
+ }
+}
+
+// Get retrieves the encoded object in the packfile with the given hash.
+func (p *Packfile) Get(h plumbing.Hash) (plumbing.EncodedObject, error) {
+ offset, err := p.FindOffset(h)
+ if err != nil {
+ return nil, err
+ }
+
+ return p.GetByOffset(offset)
+}
+
+// GetByOffset retrieves the encoded object from the packfile with the given
+// offset.
+func (p *Packfile) GetByOffset(o int64) (plumbing.EncodedObject, error) {
+ if h, ok := p.offsetToHash[o]; ok {
+ if obj, ok := p.deltaBaseCache.Get(h); ok {
+ return obj, nil
+ }
+ }
+
+ if _, err := p.s.SeekFromStart(o); err != nil {
+ return nil, err
+ }
+
+ return p.nextObject()
+}
+
+func (p *Packfile) nextObject() (plumbing.EncodedObject, error) {
+ h, err := p.s.NextObjectHeader()
+ if err != nil {
+ return nil, err
+ }
+
+ obj := new(plumbing.MemoryObject)
+ obj.SetSize(h.Length)
+ obj.SetType(h.Type)
+
+ switch h.Type {
+ case plumbing.CommitObject, plumbing.TreeObject, plumbing.BlobObject, plumbing.TagObject:
+ err = p.fillRegularObjectContent(obj)
+ case plumbing.REFDeltaObject:
+ err = p.fillREFDeltaObjectContent(obj, h.Reference)
+ case plumbing.OFSDeltaObject:
+ err = p.fillOFSDeltaObjectContent(obj, h.OffsetReference)
+ default:
+ err = ErrInvalidObject.AddDetails("type %q", h.Type)
+ }
+
+ if err != nil {
+ return obj, err
+ }
+
+ p.offsetToHash[h.Offset] = obj.Hash()
+
+ return obj, nil
+}
+
+func (p *Packfile) fillRegularObjectContent(obj plumbing.EncodedObject) error {
+ w, err := obj.Writer()
+ if err != nil {
+ return err
+ }
+
+ _, _, err = p.s.NextObject(w)
+ return err
+}
+
+func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plumbing.Hash) error {
+ buf := bufPool.Get().(*bytes.Buffer)
+ buf.Reset()
+ _, _, err := p.s.NextObject(buf)
+ if err != nil {
+ return err
+ }
+
+ base, ok := p.cacheGet(ref)
+ if !ok {
+ base, err = p.Get(ref)
+ if err != nil {
+ return err
+ }
+ }
+
+ obj.SetType(base.Type())
+ err = ApplyDelta(obj, base, buf.Bytes())
+ p.cachePut(obj)
+ bufPool.Put(buf)
+
+ return err
+}
+
+func (p *Packfile) fillOFSDeltaObjectContent(obj plumbing.EncodedObject, offset int64) error {
+ buf := bytes.NewBuffer(nil)
+ _, _, err := p.s.NextObject(buf)
+ if err != nil {
+ return err
+ }
+
+ var base plumbing.EncodedObject
+ h, ok := p.offsetToHash[offset]
+ if ok {
+ base, ok = p.cacheGet(h)
+ }
+
+ if !ok {
+ base, err = p.GetByOffset(offset)
+ if err != nil {
+ return err
+ }
+
+ p.cachePut(base)
+ }
+
+ obj.SetType(base.Type())
+ err = ApplyDelta(obj, base, buf.Bytes())
+ p.cachePut(obj)
+
+ return err
+}
+
+func (p *Packfile) cacheGet(h plumbing.Hash) (plumbing.EncodedObject, bool) {
+ if p.deltaBaseCache == nil {
+ return nil, false
+ }
+
+ return p.deltaBaseCache.Get(h)
+}
+
+func (p *Packfile) cachePut(obj plumbing.EncodedObject) {
+ if p.deltaBaseCache == nil {
+ return
+ }
+
+ p.deltaBaseCache.Put(obj)
+}
+
+// GetAll returns an iterator with all encoded objects in the packfile.
+// The iterator returned is not thread-safe, it should be used in the same
+// thread as the Packfile instance.
+func (p *Packfile) GetAll() (storer.EncodedObjectIter, error) {
+ s := NewScanner(p.File)
+
+ _, count, err := s.Header()
+ if err != nil {
+ return nil, err
+ }
+
+ return &objectIter{
+ // Easiest way to provide an object decoder is just to pass a Packfile
+ // instance. To not mess with the seeks, it's a new instance with a
+ // different scanner but the same cache and offset to hash map for
+ // reusing as much cache as possible.
+ d: &Packfile{p.Index, nil, s, p.deltaBaseCache, p.offsetToHash},
+ count: int(count),
+ }, nil
+}
+
+// ID returns the ID of the packfile, which is the checksum at the end of it.
+func (p *Packfile) ID() (plumbing.Hash, error) {
+ if _, err := p.File.Seek(-20, io.SeekEnd); err != nil {
+ return plumbing.ZeroHash, err
+ }
+
+ var hash plumbing.Hash
+ if _, err := io.ReadFull(p.File, hash[:]); err != nil {
+ return plumbing.ZeroHash, err
+ }
+
+ return hash, nil
+}
+
+// Close the packfile and its resources.
+func (p *Packfile) Close() error {
+ return p.File.Close()
+}
+
+type objectDecoder interface {
+ nextObject() (plumbing.EncodedObject, error)
+}
+
+type objectIter struct {
+ d objectDecoder
+ count int
+ pos int
+}
+
+func (i *objectIter) Next() (plumbing.EncodedObject, error) {
+ if i.pos >= i.count {
+ return nil, io.EOF
+ }
+
+ i.pos++
+ return i.d.nextObject()
+}
+
+func (i *objectIter) ForEach(f func(plumbing.EncodedObject) error) error {
+ for {
+ o, err := i.Next()
+ if err != nil {
+ if err == io.EOF {
+ return nil
+ }
+ return err
+ }
+
+ if err := f(o); err != nil {
+ return err
+ }
+ }
+}
+
+func (i *objectIter) Close() {
+ i.pos = i.count
+}
diff --git a/plumbing/format/packfile/packfile_test.go b/plumbing/format/packfile/packfile_test.go
new file mode 100644
index 0000000..10e4080
--- /dev/null
+++ b/plumbing/format/packfile/packfile_test.go
@@ -0,0 +1,121 @@
+package packfile
+
+import (
+ "io"
+ "math"
+
+ . "gopkg.in/check.v1"
+ "gopkg.in/src-d/go-billy.v4/osfs"
+ fixtures "gopkg.in/src-d/go-git-fixtures.v3"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/idxfile"
+)
+
+type PackfileSuite struct {
+ fixtures.Suite
+ p *Packfile
+ idx *idxfile.MemoryIndex
+ f *fixtures.Fixture
+}
+
+var _ = Suite(&PackfileSuite{})
+
+func (s *PackfileSuite) TestGet(c *C) {
+ for h := range expectedEntries {
+ obj, err := s.p.Get(h)
+ c.Assert(err, IsNil)
+ c.Assert(obj, Not(IsNil))
+ c.Assert(obj.Hash(), Equals, h)
+ }
+
+ _, err := s.p.Get(plumbing.ZeroHash)
+ c.Assert(err, Equals, plumbing.ErrObjectNotFound)
+}
+
+func (s *PackfileSuite) TestGetByOffset(c *C) {
+ for h, o := range expectedEntries {
+ obj, err := s.p.GetByOffset(o)
+ c.Assert(err, IsNil)
+ c.Assert(obj, Not(IsNil))
+ c.Assert(obj.Hash(), Equals, h)
+ }
+
+ _, err := s.p.GetByOffset(math.MaxInt64)
+ c.Assert(err, Equals, io.EOF)
+}
+
+func (s *PackfileSuite) TestID(c *C) {
+ id, err := s.p.ID()
+ c.Assert(err, IsNil)
+ c.Assert(id, Equals, s.f.PackfileHash)
+}
+
+func (s *PackfileSuite) TestGetAll(c *C) {
+ iter, err := s.p.GetAll()
+ c.Assert(err, IsNil)
+
+ var objects int
+ for {
+ o, err := iter.Next()
+ if err == io.EOF {
+ break
+ }
+ c.Assert(err, IsNil)
+
+ objects++
+ _, ok := expectedEntries[o.Hash()]
+ c.Assert(ok, Equals, true)
+ }
+
+ c.Assert(objects, Equals, len(expectedEntries))
+}
+
+var expectedEntries = map[plumbing.Hash]int64{
+ plumbing.NewHash("1669dce138d9b841a518c64b10914d88f5e488ea"): 615,
+ plumbing.NewHash("32858aad3c383ed1ff0a0f9bdf231d54a00c9e88"): 1524,
+ plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"): 1063,
+ plumbing.NewHash("49c6bb89b17060d7b4deacb7b338fcc6ea2352a9"): 78882,
+ plumbing.NewHash("4d081c50e250fa32ea8b1313cf8bb7c2ad7627fd"): 84688,
+ plumbing.NewHash("586af567d0bb5e771e49bdd9434f5e0fb76d25fa"): 84559,
+ plumbing.NewHash("5a877e6a906a2743ad6e45d99c1793642aaf8eda"): 84479,
+ plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"): 186,
+ plumbing.NewHash("7e59600739c96546163833214c36459e324bad0a"): 84653,
+ plumbing.NewHash("880cd14280f4b9b6ed3986d6671f907d7cc2a198"): 78050,
+ plumbing.NewHash("8dcef98b1d52143e1e2dbc458ffe38f925786bf2"): 84741,
+ plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294"): 286,
+ plumbing.NewHash("9a48f23120e880dfbe41f7c9b7b708e9ee62a492"): 80998,
+ plumbing.NewHash("9dea2395f5403188298c1dabe8bdafe562c491e3"): 84032,
+ plumbing.NewHash("a39771a7651f97faf5c72e08224d857fc35133db"): 84430,
+ plumbing.NewHash("a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69"): 838,
+ plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c"): 84375,
+ plumbing.NewHash("aa9b383c260e1d05fbbf6b30a02914555e20c725"): 84760,
+ plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"): 449,
+ plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"): 1392,
+ plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"): 1230,
+ plumbing.NewHash("c192bd6a24ea1ab01d78686e417c8bdc7c3d197f"): 1713,
+ plumbing.NewHash("c2d30fa8ef288618f65f6eed6e168e0d514886f4"): 84725,
+ plumbing.NewHash("c8f1d8c61f9da76f4cb49fd86322b6e685dba956"): 80725,
+ plumbing.NewHash("cf4aa3b38974fb7d81f367c0830f7d78d65ab86b"): 84608,
+ plumbing.NewHash("d3ff53e0564a9f87d8e84b6e28e5060e517008aa"): 1685,
+ plumbing.NewHash("d5c0f4ab811897cadf03aec358ae60d21f91c50d"): 2351,
+ plumbing.NewHash("dbd3641b371024f44d0e469a9c8f5457b0660de1"): 84115,
+ plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"): 12,
+ plumbing.NewHash("eba74343e2f15d62adedfd8c883ee0262b5c8021"): 84708,
+ plumbing.NewHash("fb72698cab7617ac416264415f13224dfd7a165e"): 84671,
+}
+
+func (s *PackfileSuite) SetUpTest(c *C) {
+ s.f = fixtures.Basic().One()
+
+ f, err := osfs.New("/").Open(s.f.Packfile().Name())
+ c.Assert(err, IsNil)
+
+ s.idx = idxfile.NewMemoryIndex()
+ c.Assert(idxfile.NewDecoder(s.f.Idx()).Decode(s.idx), IsNil)
+
+ s.p = NewPackfile(s.idx, f)
+}
+
+func (s *PackfileSuite) TearDownTest(c *C) {
+ c.Assert(s.p.Close(), IsNil)
+}
diff --git a/plumbing/memory.go b/plumbing/memory.go
index 51cbb54..b8e1e1b 100644
--- a/plumbing/memory.go
+++ b/plumbing/memory.go
@@ -14,10 +14,10 @@ type MemoryObject struct {
sz int64
}
-// Hash return the object Hash, the hash is calculated on-the-fly the first
-// time is called, the subsequent calls the same Hash is returned even if the
-// type or the content has changed. The Hash is only generated if the size of
-// the content is exactly the Object.Size
+// Hash returns the object Hash, the hash is calculated on-the-fly the first
+// time it's called, in all subsequent calls the same Hash is returned even
+// if the type or the content have changed. The Hash is only generated if the
+// size of the content is exactly the object size.
func (o *MemoryObject) Hash() Hash {
if o.h == ZeroHash && int64(len(o.cont)) == o.sz {
o.h = ComputeHash(o.t, o.cont)
diff --git a/storage/filesystem/storage.go b/storage/filesystem/storage.go
index 622bb4a..6af906d 100644
--- a/storage/filesystem/storage.go
+++ b/storage/filesystem/storage.go
@@ -2,6 +2,9 @@
package filesystem
import (
+ "fmt"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/format/index"
"gopkg.in/src-d/go-git.v4/storage/filesystem/dotgit"
"gopkg.in/src-d/go-billy.v4"
@@ -51,3 +54,15 @@ func (s *Storage) Filesystem() billy.Filesystem {
func (s *Storage) Init() error {
return s.dir.Initialize()
}
+
+type IndexStorage struct {
+ dir *dotgit.DotGit
+}
+
+func (IndexStorage) SetIndex(*index.Index) error {
+ return fmt.Errorf("not implemented")
+}
+
+func (IndexStorage) Index() (*index.Index, error) {
+ return nil, fmt.Errorf("not implemented")
+}