diff options
author | Máximo Cuadros <mcuadros@gmail.com> | 2017-07-27 18:04:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-27 18:04:00 +0200 |
commit | 86f33ed017b55898758bf8900a085f355b2793d0 (patch) | |
tree | 1e741bed0672a7afb62cda37ca0b1d86fad52889 /storage | |
parent | 7b08a3005480a50f0f4290aff8f3702085d5e30d (diff) | |
parent | 16b24f84e9342234ad90da27a6532887b05d1965 (diff) | |
download | go-git-86f33ed017b55898758bf8900a085f355b2793d0.tar.gz |
Merge pull request #515 from smola/reuse-packed-objects
storage: reuse deltas from packfiles
Diffstat (limited to 'storage')
-rw-r--r-- | storage/filesystem/deltaobject.go | 37 | ||||
-rw-r--r-- | storage/filesystem/object.go | 104 | ||||
-rw-r--r-- | storage/filesystem/object_test.go | 4 | ||||
-rw-r--r-- | storage/filesystem/storage_test.go | 9 | ||||
-rw-r--r-- | storage/test/storage_suite.go | 34 |
5 files changed, 179 insertions, 9 deletions
diff --git a/storage/filesystem/deltaobject.go b/storage/filesystem/deltaobject.go new file mode 100644 index 0000000..66cfb71 --- /dev/null +++ b/storage/filesystem/deltaobject.go @@ -0,0 +1,37 @@ +package filesystem + +import ( + "gopkg.in/src-d/go-git.v4/plumbing" +) + +type deltaObject struct { + plumbing.EncodedObject + base plumbing.Hash + hash plumbing.Hash + size int64 +} + +func newDeltaObject( + obj plumbing.EncodedObject, + hash plumbing.Hash, + base plumbing.Hash, + size int64) plumbing.DeltaObject { + return &deltaObject{ + EncodedObject: obj, + hash: hash, + base: base, + size: size, + } +} + +func (o *deltaObject) BaseHash() plumbing.Hash { + return o.base +} + +func (o *deltaObject) ActualSize() int64 { + return o.size +} + +func (o *deltaObject) ActualHash() plumbing.Hash { + return o.hash +} diff --git a/storage/filesystem/object.go b/storage/filesystem/object.go index 068abd3..5073a38 100644 --- a/storage/filesystem/object.go +++ b/storage/filesystem/object.go @@ -130,7 +130,27 @@ func (s *ObjectStorage) SetEncodedObject(o plumbing.EncodedObject) (plumbing.Has func (s *ObjectStorage) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (plumbing.EncodedObject, error) { obj, err := s.getFromUnpacked(h) if err == plumbing.ErrObjectNotFound { - obj, err = s.getFromPackfile(h) + obj, err = s.getFromPackfile(h, false) + } + + if err != nil { + return nil, err + } + + if plumbing.AnyObject != t && obj.Type() != t { + return nil, plumbing.ErrObjectNotFound + } + + return obj, nil +} + +// DeltaObject returns the object with the given hash, by searching for +// it in the packfile and the git object directories. +func (s *ObjectStorage) DeltaObject(t plumbing.ObjectType, + h plumbing.Hash) (plumbing.EncodedObject, error) { + obj, err := s.getFromUnpacked(h) + if err == plumbing.ErrObjectNotFound { + obj, err = s.getFromPackfile(h, true) } if err != nil { @@ -182,12 +202,14 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb // Get returns the object with the given hash, by searching for it in // the packfile. -func (s *ObjectStorage) getFromPackfile(h plumbing.Hash) (plumbing.EncodedObject, error) { +func (s *ObjectStorage) getFromPackfile(h plumbing.Hash, canBeDelta bool) ( + plumbing.EncodedObject, error) { + if err := s.requireIndex(); err != nil { return nil, err } - pack, offset := s.findObjectInPackfile(h) + pack, hash, offset := s.findObjectInPackfile(h) if offset == -1 { return nil, plumbing.ErrObjectNotFound } @@ -199,26 +221,94 @@ func (s *ObjectStorage) getFromPackfile(h plumbing.Hash) (plumbing.EncodedObject defer ioutil.CheckClose(f, &err) + idx := s.index[pack] + if canBeDelta { + return s.decodeDeltaObjectAt(f, idx, offset, hash) + } + + return s.decodeObjectAt(f, idx, offset) +} + +func (s *ObjectStorage) decodeObjectAt( + f billy.File, + idx *packfile.Index, + offset int64) (plumbing.EncodedObject, error) { + if _, err := f.Seek(0, io.SeekStart); err != nil { + return nil, err + } + p := packfile.NewScanner(f) + d, err := packfile.NewDecoder(p, memory.NewStorage()) if err != nil { return nil, err } + d.SetIndex(idx) d.DeltaBaseCache = s.DeltaBaseCache - d.SetIndex(s.index[pack]) obj, err := d.DecodeObjectAt(offset) return obj, err } -func (s *ObjectStorage) findObjectInPackfile(h plumbing.Hash) (plumbing.Hash, int64) { +func (s *ObjectStorage) decodeDeltaObjectAt( + f billy.File, + idx *packfile.Index, + offset int64, + hash plumbing.Hash) (plumbing.EncodedObject, error) { + if _, err := f.Seek(0, io.SeekStart); err != nil { + return nil, err + } + + p := packfile.NewScanner(f) + if _, err := p.SeekFromStart(offset); err != nil { + return nil, err + } + + header, err := p.NextObjectHeader() + if err != nil { + return nil, err + } + + var ( + base plumbing.Hash + ) + + switch header.Type { + case plumbing.REFDeltaObject: + base = header.Reference + case plumbing.OFSDeltaObject: + e, ok := idx.LookupOffset(uint64(header.OffsetReference)) + if !ok { + return nil, plumbing.ErrObjectNotFound + } + + base = e.Hash + default: + return s.decodeObjectAt(f, idx, offset) + } + + obj := &plumbing.MemoryObject{} + obj.SetType(header.Type) + w, err := obj.Writer() + if err != nil { + return nil, err + } + + if _, _, err := p.NextObject(w); err != nil { + return nil, err + } + + return newDeltaObject(obj, hash, base, header.Length), nil +} + +func (s *ObjectStorage) findObjectInPackfile(h plumbing.Hash) (plumbing.Hash, plumbing.Hash, int64) { for packfile, index := range s.index { if e, ok := index.LookupHash(h); ok { - return packfile, int64(e.Offset) + return packfile, e.Hash, int64(e.Offset) } } - return plumbing.ZeroHash, -1 + return plumbing.ZeroHash, plumbing.ZeroHash, -1 } // IterEncodedObjects returns an iterator for all the objects in the packfile diff --git a/storage/filesystem/object_test.go b/storage/filesystem/object_test.go index d741fa2..504bd45 100644 --- a/storage/filesystem/object_test.go +++ b/storage/filesystem/object_test.go @@ -52,12 +52,12 @@ func (s *FsSuite) TestGetFromPackfileMultiplePackfiles(c *C) { c.Assert(err, IsNil) expected := plumbing.NewHash("8d45a34641d73851e01d3754320b33bb5be3c4d3") - obj, err := o.getFromPackfile(expected) + obj, err := o.getFromPackfile(expected, false) c.Assert(err, IsNil) c.Assert(obj.Hash(), Equals, expected) expected = plumbing.NewHash("e9cfa4c9ca160546efd7e8582ec77952a27b17db") - obj, err = o.getFromPackfile(expected) + obj, err = o.getFromPackfile(expected, false) c.Assert(err, IsNil) c.Assert(obj.Hash(), Equals, expected) } diff --git a/storage/filesystem/storage_test.go b/storage/filesystem/storage_test.go index 22709f5..b165c5e 100644 --- a/storage/filesystem/storage_test.go +++ b/storage/filesystem/storage_test.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "testing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" "gopkg.in/src-d/go-git.v4/storage/test" . "gopkg.in/check.v1" @@ -25,6 +26,14 @@ func (s *StorageSuite) SetUpTest(c *C) { storage, err := NewStorage(osfs.New(s.dir)) c.Assert(err, IsNil) + // ensure that right interfaces are implemented + var _ storer.EncodedObjectStorer = storage + var _ storer.IndexStorer = storage + var _ storer.ReferenceStorer = storage + var _ storer.ShallowStorer = storage + var _ storer.DeltaObjectStorer = storage + var _ storer.PackfileWriter = storage + s.BaseStorageSuite = test.NewBaseStorageSuite(storage) s.BaseStorageSuite.SetUpTest(c) } diff --git a/storage/test/storage_suite.go b/storage/test/storage_suite.go index 7cb0fe3..624dc57 100644 --- a/storage/test/storage_suite.go +++ b/storage/test/storage_suite.go @@ -403,6 +403,40 @@ func (s *BaseStorageSuite) TestModule(c *C) { c.Assert(storer, NotNil) } +func (s *BaseStorageSuite) TestDeltaObjectStorer(c *C) { + dos, ok := s.Storer.(storer.DeltaObjectStorer) + if !ok { + c.Skip("not an DeltaObjectStorer") + } + + pwr, ok := s.Storer.(storer.PackfileWriter) + if !ok { + c.Skip("not a storer.PackWriter") + } + + pw, err := pwr.PackfileWriter() + c.Assert(err, IsNil) + + f := fixtures.Basic().One() + _, err = io.Copy(pw, f.Packfile()) + c.Assert(err, IsNil) + + err = pw.Close() + c.Assert(err, IsNil) + + h := plumbing.NewHash("32858aad3c383ed1ff0a0f9bdf231d54a00c9e88") + obj, err := dos.DeltaObject(plumbing.AnyObject, h) + c.Assert(err, IsNil) + c.Assert(obj.Type(), Equals, plumbing.BlobObject) + + h = plumbing.NewHash("aa9b383c260e1d05fbbf6b30a02914555e20c725") + obj, err = dos.DeltaObject(plumbing.AnyObject, h) + c.Assert(err, IsNil) + c.Assert(obj.Type(), Equals, plumbing.OFSDeltaObject) + _, ok = obj.(plumbing.DeltaObject) + c.Assert(ok, Equals, true) +} + func objectEquals(a plumbing.EncodedObject, b plumbing.EncodedObject) error { ha := a.Hash() hb := b.Hash() |