aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/format
diff options
context:
space:
mode:
Diffstat (limited to 'plumbing/format')
-rw-r--r--plumbing/format/packfile/common_test.go36
-rw-r--r--plumbing/format/packfile/decoder_test.go3
-rw-r--r--plumbing/format/packfile/delta_selector.go169
-rw-r--r--plumbing/format/packfile/delta_selector_test.go199
-rw-r--r--plumbing/format/packfile/delta_test.go16
-rw-r--r--plumbing/format/packfile/diff_delta.go21
-rw-r--r--plumbing/format/packfile/encoder.go90
-rw-r--r--plumbing/format/packfile/encoder_test.go44
-rw-r--r--plumbing/format/packfile/object_pack.go6
9 files changed, 473 insertions, 111 deletions
diff --git a/plumbing/format/packfile/common_test.go b/plumbing/format/packfile/common_test.go
new file mode 100644
index 0000000..387c0d1
--- /dev/null
+++ b/plumbing/format/packfile/common_test.go
@@ -0,0 +1,36 @@
+package packfile
+
+import (
+ "testing"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+
+ . "gopkg.in/check.v1"
+)
+
+func Test(t *testing.T) { TestingT(t) }
+
+func newObject(t plumbing.ObjectType, cont []byte) plumbing.EncodedObject {
+ o := plumbing.MemoryObject{}
+ o.SetType(t)
+ o.SetSize(int64(len(cont)))
+ o.Write(cont)
+
+ return &o
+}
+
+type piece struct {
+ val string
+ times int
+}
+
+func genBytes(elements []piece) []byte {
+ var result []byte
+ for _, e := range elements {
+ for i := 0; i < e.times; i++ {
+ result = append(result, e.val...)
+ }
+ }
+
+ return result
+}
diff --git a/plumbing/format/packfile/decoder_test.go b/plumbing/format/packfile/decoder_test.go
index fdf4c96..eeb1e3d 100644
--- a/plumbing/format/packfile/decoder_test.go
+++ b/plumbing/format/packfile/decoder_test.go
@@ -2,7 +2,6 @@ package packfile_test
import (
"io"
- "testing"
"gopkg.in/src-d/go-git.v4/fixtures"
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -16,8 +15,6 @@ import (
. "gopkg.in/check.v1"
)
-func Test(t *testing.T) { TestingT(t) }
-
type ReaderSuite struct {
fixtures.Suite
}
diff --git a/plumbing/format/packfile/delta_selector.go b/plumbing/format/packfile/delta_selector.go
new file mode 100644
index 0000000..a73a209
--- /dev/null
+++ b/plumbing/format/packfile/delta_selector.go
@@ -0,0 +1,169 @@
+package packfile
+
+import (
+ "sort"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+)
+
+const (
+ // deltas based on deltas, how many steps we can do.
+ // 50 is the default value used in JGit
+ maxDepth = int64(50)
+)
+
+// applyDelta is the set of object types that we should apply deltas
+var applyDelta = map[plumbing.ObjectType]bool{
+ plumbing.BlobObject: true,
+ plumbing.TreeObject: true,
+}
+
+type deltaSelector struct {
+ storer storer.EncodedObjectStorer
+}
+
+func newDeltaSelector(s storer.EncodedObjectStorer) *deltaSelector {
+ return &deltaSelector{s}
+}
+
+// ObjectsToPack creates a list of ObjectToPack from the hashes provided,
+// creating deltas if it's suitable, using an specific internal logic
+func (dw *deltaSelector) ObjectsToPack(hashes []plumbing.Hash) ([]*ObjectToPack, error) {
+ otp, err := dw.objectsToPack(hashes)
+ if err != nil {
+ return nil, err
+ }
+
+ dw.sort(otp)
+
+ if err := dw.walk(otp); err != nil {
+ return nil, err
+ }
+
+ return otp, nil
+}
+
+func (dw *deltaSelector) objectsToPack(hashes []plumbing.Hash) ([]*ObjectToPack, error) {
+ var objectsToPack []*ObjectToPack
+ for _, h := range hashes {
+ o, err := dw.storer.EncodedObject(plumbing.AnyObject, h)
+ if err != nil {
+ return nil, err
+ }
+
+ objectsToPack = append(objectsToPack, newObjectToPack(o))
+ }
+
+ return objectsToPack, nil
+}
+
+func (dw *deltaSelector) sort(objectsToPack []*ObjectToPack) {
+ sort.Sort(byTypeAndSize(objectsToPack))
+}
+
+func (dw *deltaSelector) walk(objectsToPack []*ObjectToPack) error {
+ for i := 0; i < len(objectsToPack); i++ {
+ target := objectsToPack[i]
+
+ // We only want to create deltas from specific types
+ if !applyDelta[target.Original.Type()] {
+ continue
+ }
+
+ for j := i - 1; j >= 0; j-- {
+ base := objectsToPack[j]
+ // Objects must use only the same type as their delta base.
+ if base.Original.Type() != target.Original.Type() {
+ break
+ }
+
+ if err := dw.tryToDeltify(base, target); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+func (dw *deltaSelector) tryToDeltify(base, target *ObjectToPack) error {
+ // If the sizes are radically different, this is a bad pairing.
+ if target.Original.Size() < base.Original.Size()>>4 {
+ return nil
+ }
+
+ msz := dw.deltaSizeLimit(
+ target.Object.Size(),
+ base.Depth,
+ target.Depth,
+ target.IsDelta(),
+ )
+
+ // Nearly impossible to fit useful delta.
+ if msz <= 8 {
+ return nil
+ }
+
+ // If we have to insert a lot to make this work, find another.
+ if base.Original.Size()-target.Object.Size() > msz {
+ return nil
+ }
+
+ // Now we can generate the delta using originals
+ delta, err := GetDelta(base.Original, target.Original)
+ if err != nil {
+ return err
+ }
+
+ // if delta better than target
+ if delta.Size() < msz {
+ target.SetDelta(base, delta)
+ }
+
+ return nil
+}
+
+func (dw *deltaSelector) deltaSizeLimit(targetSize int64, baseDepth int,
+ targetDepth int, targetDelta bool) int64 {
+ if !targetDelta {
+ // Any delta should be no more than 50% of the original size
+ // (for text files deflate of whole form should shrink 50%).
+ n := targetSize >> 1
+
+ // Evenly distribute delta size limits over allowed depth.
+ // If src is non-delta (depth = 0), delta <= 50% of original.
+ // If src is almost at limit (9/10), delta <= 10% of original.
+ return n * (maxDepth - int64(baseDepth)) / maxDepth
+ }
+
+ // With a delta base chosen any new delta must be "better".
+ // Retain the distribution described above.
+ d := int64(targetDepth)
+ n := targetSize
+
+ // If src is whole (depth=0) and base is near limit (depth=9/10)
+ // any delta using src can be 10x larger and still be better.
+ //
+ // If src is near limit (depth=9/10) and base is whole (depth=0)
+ // a new delta dependent on src must be 1/10th the size.
+ return n * (maxDepth - int64(baseDepth)) / (maxDepth - d)
+}
+
+type byTypeAndSize []*ObjectToPack
+
+func (a byTypeAndSize) Len() int { return len(a) }
+
+func (a byTypeAndSize) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+func (a byTypeAndSize) Less(i, j int) bool {
+ if a[i].Object.Type() < a[j].Object.Type() {
+ return false
+ }
+
+ if a[i].Object.Type() > a[j].Object.Type() {
+ return true
+ }
+
+ return a[i].Object.Size() > a[j].Object.Size()
+}
diff --git a/plumbing/format/packfile/delta_selector_test.go b/plumbing/format/packfile/delta_selector_test.go
new file mode 100644
index 0000000..9a8833f
--- /dev/null
+++ b/plumbing/format/packfile/delta_selector_test.go
@@ -0,0 +1,199 @@
+package packfile
+
+import (
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/storage/memory"
+
+ . "gopkg.in/check.v1"
+)
+
+type DeltaSelectorSuite struct {
+ ds *deltaSelector
+ store *memory.Storage
+ hashes map[string]plumbing.Hash
+}
+
+var _ = Suite(&DeltaSelectorSuite{})
+
+func (s *DeltaSelectorSuite) SetUpTest(c *C) {
+ s.store = memory.NewStorage()
+ s.createTestObjects()
+ s.ds = newDeltaSelector(s.store)
+}
+
+func (s *DeltaSelectorSuite) TestSort(c *C) {
+ var o1 = newObjectToPack(newObject(plumbing.BlobObject, []byte("00000")))
+ var o4 = newObjectToPack(newObject(plumbing.BlobObject, []byte("0000")))
+ var o6 = newObjectToPack(newObject(plumbing.BlobObject, []byte("00")))
+ var o9 = newObjectToPack(newObject(plumbing.BlobObject, []byte("0")))
+ var o8 = newObjectToPack(newObject(plumbing.TreeObject, []byte("000")))
+ var o2 = newObjectToPack(newObject(plumbing.TreeObject, []byte("00")))
+ var o3 = newObjectToPack(newObject(plumbing.TreeObject, []byte("0")))
+ var o5 = newObjectToPack(newObject(plumbing.CommitObject, []byte("0000")))
+ var o7 = newObjectToPack(newObject(plumbing.CommitObject, []byte("00")))
+
+ toSort := []*ObjectToPack{o1, o2, o3, o4, o5, o6, o7, o8, o9}
+ s.ds.sort(toSort)
+ expected := []*ObjectToPack{o1, o4, o6, o9, o8, o2, o3, o5, o7}
+ c.Assert(toSort, DeepEquals, expected)
+}
+
+type testObject struct {
+ id string
+ object plumbing.EncodedObject
+}
+
+var testObjects []*testObject = []*testObject{{
+ id: "base",
+ object: newObject(plumbing.BlobObject,
+ genBytes([]piece{{
+ times: 1000,
+ val: "a",
+ }, {
+ times: 1000,
+ val: "b",
+ }})),
+}, {
+ id: "smallBase",
+ object: newObject(plumbing.BlobObject,
+ genBytes([]piece{{
+ times: 1,
+ val: "a",
+ }, {
+ times: 1,
+ val: "b",
+ }, {
+ times: 6,
+ val: "c",
+ }})),
+}, {
+ id: "smallTarget",
+ object: newObject(plumbing.BlobObject,
+ genBytes([]piece{{
+ times: 1,
+ val: "a",
+ }, {
+ times: 1,
+ val: "c",
+ }})),
+}, {
+ id: "target",
+ object: newObject(plumbing.BlobObject,
+ genBytes([]piece{{
+ times: 1000,
+ val: "a",
+ }, {
+ times: 1000,
+ val: "b",
+ }, {
+ times: 1000,
+ val: "c",
+ }})),
+}, {
+ id: "o1",
+ object: newObject(plumbing.BlobObject,
+ genBytes([]piece{{
+ times: 1000,
+ val: "a",
+ }, {
+ times: 1000,
+ val: "b",
+ }})),
+}, {
+ id: "o2",
+ object: newObject(plumbing.BlobObject,
+ genBytes([]piece{{
+ times: 1000,
+ val: "a",
+ }, {
+ times: 500,
+ val: "b",
+ }})),
+}, {
+ id: "o3",
+ object: newObject(plumbing.BlobObject,
+ genBytes([]piece{{
+ times: 1000,
+ val: "a",
+ }, {
+ times: 499,
+ val: "b",
+ }})),
+}, {
+ id: "bigBase",
+ object: newObject(plumbing.BlobObject,
+ genBytes([]piece{{
+ times: 1000000,
+ val: "a",
+ }})),
+}, {
+ id: "treeType",
+ object: newObject(plumbing.TreeObject,
+ []byte("I am a tree!")),
+}}
+
+func (s *DeltaSelectorSuite) createTestObjects() {
+ s.hashes = make(map[string]plumbing.Hash)
+ for _, o := range testObjects {
+ h, err := s.store.SetEncodedObject(o.object)
+ if err != nil {
+ panic(err)
+ }
+ s.hashes[o.id] = h
+ }
+}
+
+func (s *DeltaSelectorSuite) TestObjectsToPack(c *C) {
+ // Different type
+ hashes := []plumbing.Hash{s.hashes["base"], s.hashes["treeType"]}
+ otp, err := s.ds.ObjectsToPack(hashes)
+ c.Assert(err, IsNil)
+ c.Assert(len(otp), Equals, 2)
+ c.Assert(otp[0].Object, Equals, s.store.Objects[s.hashes["base"]])
+ c.Assert(otp[1].Object, Equals, s.store.Objects[s.hashes["treeType"]])
+
+ // Size radically different
+ hashes = []plumbing.Hash{s.hashes["bigBase"], s.hashes["target"]}
+ otp, err = s.ds.ObjectsToPack(hashes)
+ c.Assert(err, IsNil)
+ c.Assert(len(otp), Equals, 2)
+ c.Assert(otp[0].Object, Equals, s.store.Objects[s.hashes["bigBase"]])
+ c.Assert(otp[1].Object, Equals, s.store.Objects[s.hashes["target"]])
+
+ // Delta Size Limit with no best delta yet
+ hashes = []plumbing.Hash{s.hashes["smallBase"], s.hashes["smallTarget"]}
+ otp, err = s.ds.ObjectsToPack(hashes)
+ c.Assert(err, IsNil)
+ c.Assert(len(otp), Equals, 2)
+ c.Assert(otp[0].Object, Equals, s.store.Objects[s.hashes["smallBase"]])
+ c.Assert(otp[1].Object, Equals, s.store.Objects[s.hashes["smallTarget"]])
+
+ // It will create the delta
+ hashes = []plumbing.Hash{s.hashes["base"], s.hashes["target"]}
+ otp, err = s.ds.ObjectsToPack(hashes)
+ c.Assert(err, IsNil)
+ c.Assert(len(otp), Equals, 2)
+ c.Assert(otp[0].Object, Equals, s.store.Objects[s.hashes["target"]])
+ c.Assert(otp[0].IsDelta(), Equals, false)
+ c.Assert(otp[1].Original, Equals, s.store.Objects[s.hashes["base"]])
+ c.Assert(otp[1].IsDelta(), Equals, true)
+ c.Assert(otp[1].Depth, Equals, 1)
+
+ // If our base is another delta, the depth will increase by one
+ hashes = []plumbing.Hash{
+ s.hashes["o1"],
+ s.hashes["o2"],
+ s.hashes["o3"],
+ }
+ otp, err = s.ds.ObjectsToPack(hashes)
+ c.Assert(err, IsNil)
+ c.Assert(len(otp), Equals, 3)
+ c.Assert(otp[0].Object, Equals, s.store.Objects[s.hashes["o1"]])
+ c.Assert(otp[0].IsDelta(), Equals, false)
+ c.Assert(otp[1].Original, Equals, s.store.Objects[s.hashes["o2"]])
+ c.Assert(otp[1].IsDelta(), Equals, true)
+ c.Assert(otp[1].Depth, Equals, 1)
+ c.Assert(otp[2].Original, Equals, s.store.Objects[s.hashes["o3"]])
+ c.Assert(otp[2].IsDelta(), Equals, true)
+ c.Assert(otp[2].Depth, Equals, 2)
+}
diff --git a/plumbing/format/packfile/delta_test.go b/plumbing/format/packfile/delta_test.go
index 4ef9b70..43253b0 100644
--- a/plumbing/format/packfile/delta_test.go
+++ b/plumbing/format/packfile/delta_test.go
@@ -12,11 +12,6 @@ type DeltaSuite struct {
var _ = Suite(&DeltaSuite{})
-type piece struct {
- val string
- times int
-}
-
type deltaTest struct {
description string
base []piece
@@ -80,14 +75,3 @@ func (s *DeltaSuite) TestAddDelta(c *C) {
c.Assert(result, DeepEquals, targetBuf)
}
}
-
-func genBytes(elements []piece) []byte {
- var result []byte
- for _, e := range elements {
- for i := 0; i < e.times; i++ {
- result = append(result, e.val...)
- }
- }
-
- return result
-}
diff --git a/plumbing/format/packfile/diff_delta.go b/plumbing/format/packfile/diff_delta.go
index bc4fafa..e3607bf 100644
--- a/plumbing/format/packfile/diff_delta.go
+++ b/plumbing/format/packfile/diff_delta.go
@@ -1,7 +1,6 @@
package packfile
import (
- "fmt"
"io/ioutil"
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -15,23 +14,9 @@ const (
maxCopyLen = 0xffff
)
-// GetOFSDelta returns an offset delta that knows the way of how to transform
+// GetDelta returns an offset delta that knows the way of how to transform
// base object to target object
-func GetOFSDelta(base, target plumbing.EncodedObject) (plumbing.EncodedObject, error) {
- return getDelta(base, target, plumbing.OFSDeltaObject)
-}
-
-// GetRefDelta returns a reference delta that knows the way of how to transform
-// base object to target object
-func GetRefDelta(base, target plumbing.EncodedObject) (plumbing.EncodedObject, error) {
- return getDelta(base, target, plumbing.REFDeltaObject)
-}
-
-func getDelta(base, target plumbing.EncodedObject, t plumbing.ObjectType) (plumbing.EncodedObject, error) {
- if t != plumbing.OFSDeltaObject && t != plumbing.REFDeltaObject {
- return nil, fmt.Errorf("Type not supported: %v", t)
- }
-
+func GetDelta(base, target plumbing.EncodedObject) (plumbing.EncodedObject, error) {
br, err := base.Reader()
if err != nil {
return nil, err
@@ -59,7 +44,7 @@ func getDelta(base, target plumbing.EncodedObject, t plumbing.ObjectType) (plumb
}
delta.SetSize(int64(len(db)))
- delta.SetType(t)
+ delta.SetType(plumbing.OFSDeltaObject)
return delta, nil
}
diff --git a/plumbing/format/packfile/encoder.go b/plumbing/format/packfile/encoder.go
index 847e9e1..a11ba61 100644
--- a/plumbing/format/packfile/encoder.go
+++ b/plumbing/format/packfile/encoder.go
@@ -14,16 +14,17 @@ import (
// Encoder gets the data from the storage and write it into the writer in PACK
// format
type Encoder struct {
- storage storer.EncodedObjectStorer
- w *offsetWriter
- zw *zlib.Writer
- hasher plumbing.Hasher
- offsets map[plumbing.Hash]int64
+ selector *deltaSelector
+ w *offsetWriter
+ zw *zlib.Writer
+ hasher plumbing.Hasher
+ offsets map[plumbing.Hash]int64
+ useRefDeltas bool
}
// NewEncoder creates a new packfile encoder using a specific Writer and
// ObjectStorer
-func NewEncoder(w io.Writer, s storer.EncodedObjectStorer) *Encoder {
+func NewEncoder(w io.Writer, s storer.EncodedObjectStorer, useRefDeltas bool) *Encoder {
h := plumbing.Hasher{
Hash: sha1.New(),
}
@@ -31,29 +32,26 @@ func NewEncoder(w io.Writer, s storer.EncodedObjectStorer) *Encoder {
ow := newOffsetWriter(mw)
zw := zlib.NewWriter(mw)
return &Encoder{
- storage: s,
- w: ow,
- zw: zw,
- hasher: h,
- offsets: make(map[plumbing.Hash]int64),
+ selector: newDeltaSelector(s),
+ w: ow,
+ zw: zw,
+ hasher: h,
+ offsets: make(map[plumbing.Hash]int64),
+ useRefDeltas: useRefDeltas,
}
}
// Encode creates a packfile containing all the objects referenced in hashes
// and writes it to the writer in the Encoder.
func (e *Encoder) Encode(hashes []plumbing.Hash) (plumbing.Hash, error) {
- var objects []*ObjectToPack
- for _, h := range hashes {
- o, err := e.storage.EncodedObject(plumbing.AnyObject, h)
- if err != nil {
- return plumbing.ZeroHash, err
- }
- // TODO delta selection logic
- objects = append(objects, newObjectToPack(o))
+ objects, err := e.selector.ObjectsToPack(hashes)
+ if err != nil {
+ return plumbing.ZeroHash, err
}
return e.encode(objects)
}
+
func (e *Encoder) encode(objects []*ObjectToPack) (plumbing.Hash, error) {
if err := e.head(len(objects)); err != nil {
return plumbing.ZeroHash, err
@@ -67,6 +65,7 @@ func (e *Encoder) encode(objects []*ObjectToPack) (plumbing.Hash, error) {
return e.footer()
}
+
func (e *Encoder) head(numEntries int) error {
return binary.Write(
e.w,
@@ -79,17 +78,19 @@ func (e *Encoder) head(numEntries int) error {
func (e *Encoder) entry(o *ObjectToPack) error {
offset := e.w.Offset()
- if err := e.entryHead(o.Object.Type(), o.Object.Size()); err != nil {
- return err
+ if o.IsDelta() {
+ if err := e.writeDeltaHeader(o, offset); err != nil {
+ return err
+ }
+ } else {
+ if err := e.entryHead(o.Object.Type(), o.Object.Size()); err != nil {
+ return err
+ }
}
// Save the position using the original hash, maybe a delta will need it
e.offsets[o.Original.Hash()] = offset
- if err := e.writeDeltaHeaderIfAny(o, offset); err != nil {
- return err
- }
-
e.zw.Reset(e.w)
or, err := o.Object.Reader()
if err != nil {
@@ -103,33 +104,34 @@ func (e *Encoder) entry(o *ObjectToPack) error {
return e.zw.Close()
}
-func (e *Encoder) writeDeltaHeaderIfAny(o *ObjectToPack, offset int64) error {
- if o.IsDelta() {
- switch o.Object.Type() {
- case plumbing.OFSDeltaObject:
- if err := e.writeOfsDeltaHeader(offset, o.Base.Original.Hash()); err != nil {
- return err
- }
- case plumbing.REFDeltaObject:
- if err := e.writeRefDeltaHeader(o.Base.Original.Hash()); err != nil {
- return err
- }
- }
+func (e *Encoder) writeDeltaHeader(o *ObjectToPack, offset int64) error {
+ // Write offset deltas by default
+ t := plumbing.OFSDeltaObject
+ if e.useRefDeltas {
+ t = plumbing.REFDeltaObject
}
- return nil
+ if err := e.entryHead(t, o.Object.Size()); err != nil {
+ return err
+ }
+
+ if e.useRefDeltas {
+ return e.writeRefDeltaHeader(o.Base.Original.Hash())
+ } else {
+ return e.writeOfsDeltaHeader(offset, o.Base.Original.Hash())
+ }
}
-func (e *Encoder) writeRefDeltaHeader(source plumbing.Hash) error {
- return binary.Write(e.w, source)
+func (e *Encoder) writeRefDeltaHeader(base plumbing.Hash) error {
+ return binary.Write(e.w, base)
}
-func (e *Encoder) writeOfsDeltaHeader(deltaOffset int64, source plumbing.Hash) error {
- // because it is an offset delta, we need the source
+func (e *Encoder) writeOfsDeltaHeader(deltaOffset int64, base plumbing.Hash) error {
+ // because it is an offset delta, we need the base
// object position
- offset, ok := e.offsets[source]
+ offset, ok := e.offsets[base]
if !ok {
- return fmt.Errorf("delta source not found. Hash: %v", source)
+ return fmt.Errorf("delta base not found. Hash: %v", base)
}
return binary.WriteVariableWidthInt(e.w, deltaOffset-offset)
diff --git a/plumbing/format/packfile/encoder_test.go b/plumbing/format/packfile/encoder_test.go
index 1a94d16..dde8570 100644
--- a/plumbing/format/packfile/encoder_test.go
+++ b/plumbing/format/packfile/encoder_test.go
@@ -22,7 +22,7 @@ var _ = Suite(&EncoderSuite{})
func (s *EncoderSuite) SetUpTest(c *C) {
s.buf = bytes.NewBuffer(nil)
s.store = memory.NewStorage()
- s.enc = NewEncoder(s.buf, s.store)
+ s.enc = NewEncoder(s.buf, s.store, false)
}
func (s *EncoderSuite) TestCorrectPackHeader(c *C) {
@@ -149,26 +149,30 @@ func (s *EncoderSuite) TestDecodeEncodeDecode(c *C) {
}
func (s *EncoderSuite) TestDecodeEncodeWithDeltaDecodeREF(c *C) {
- s.simpleDeltaTest(c, plumbing.REFDeltaObject)
+ s.enc = NewEncoder(s.buf, s.store, true)
+ s.simpleDeltaTest(c)
}
func (s *EncoderSuite) TestDecodeEncodeWithDeltaDecodeOFS(c *C) {
- s.simpleDeltaTest(c, plumbing.OFSDeltaObject)
+ s.enc = NewEncoder(s.buf, s.store, false)
+ s.simpleDeltaTest(c)
}
func (s *EncoderSuite) TestDecodeEncodeWithDeltasDecodeREF(c *C) {
- s.deltaOverDeltaTest(c, plumbing.REFDeltaObject)
+ s.enc = NewEncoder(s.buf, s.store, true)
+ s.deltaOverDeltaTest(c)
}
func (s *EncoderSuite) TestDecodeEncodeWithDeltasDecodeOFS(c *C) {
- s.deltaOverDeltaTest(c, plumbing.OFSDeltaObject)
+ s.enc = NewEncoder(s.buf, s.store, false)
+ s.deltaOverDeltaTest(c)
}
-func (s *EncoderSuite) simpleDeltaTest(c *C, t plumbing.ObjectType) {
+func (s *EncoderSuite) simpleDeltaTest(c *C) {
srcObject := newObject(plumbing.BlobObject, []byte("0"))
targetObject := newObject(plumbing.BlobObject, []byte("01"))
- deltaObject, err := delta(srcObject, targetObject, t)
+ deltaObject, err := GetDelta(srcObject, targetObject)
c.Assert(err, IsNil)
srcToPack := newObjectToPack(srcObject)
@@ -196,16 +200,16 @@ func (s *EncoderSuite) simpleDeltaTest(c *C, t plumbing.ObjectType) {
c.Assert(decTarget, DeepEquals, targetObject)
}
-func (s *EncoderSuite) deltaOverDeltaTest(c *C, t plumbing.ObjectType) {
+func (s *EncoderSuite) deltaOverDeltaTest(c *C) {
srcObject := newObject(plumbing.BlobObject, []byte("0"))
targetObject := newObject(plumbing.BlobObject, []byte("01"))
otherTargetObject := newObject(plumbing.BlobObject, []byte("011111"))
- deltaObject, err := delta(srcObject, targetObject, t)
+ deltaObject, err := GetDelta(srcObject, targetObject)
c.Assert(err, IsNil)
c.Assert(deltaObject.Hash(), Not(Equals), plumbing.ZeroHash)
- otherDeltaObject, err := delta(targetObject, otherTargetObject, t)
+ otherDeltaObject, err := GetDelta(targetObject, otherTargetObject)
c.Assert(err, IsNil)
c.Assert(otherDeltaObject.Hash(), Not(Equals), plumbing.ZeroHash)
@@ -238,23 +242,3 @@ func (s *EncoderSuite) deltaOverDeltaTest(c *C, t plumbing.ObjectType) {
c.Assert(err, IsNil)
c.Assert(decOtherTarget, DeepEquals, otherTargetObject)
}
-
-func delta(base, target plumbing.EncodedObject, t plumbing.ObjectType) (plumbing.EncodedObject, error) {
- switch t {
- case plumbing.OFSDeltaObject:
- return GetOFSDelta(base, target)
- case plumbing.REFDeltaObject:
- return GetRefDelta(base, target)
- default:
- panic("delta type not found")
- }
-}
-
-func newObject(t plumbing.ObjectType, cont []byte) plumbing.EncodedObject {
- o := plumbing.MemoryObject{}
- o.SetType(t)
- o.SetSize(int64(len(cont)))
- o.Write(cont)
-
- return &o
-}
diff --git a/plumbing/format/packfile/object_pack.go b/plumbing/format/packfile/object_pack.go
index dfe9bb2..a3e99c0 100644
--- a/plumbing/format/packfile/object_pack.go
+++ b/plumbing/format/packfile/object_pack.go
@@ -46,3 +46,9 @@ func (o *ObjectToPack) IsDelta() bool {
return false
}
+
+func (o *ObjectToPack) SetDelta(base *ObjectToPack, delta plumbing.EncodedObject) {
+ o.Object = delta
+ o.Base = base
+ o.Depth = base.Depth + 1
+}