diff options
author | Antonio Jesus Navarro Perez <antnavper@gmail.com> | 2017-12-18 18:11:38 +0100 |
---|---|---|
committer | Antonio Jesus Navarro Perez <antnavper@gmail.com> | 2017-12-20 12:06:36 +0100 |
commit | c932bd462fed2ff470d9ff40f15102237a3287f8 (patch) | |
tree | 49aecd1f4d1f356f5f482f5d9eaa801c3907ef37 /plumbing/format/packfile/encoder.go | |
parent | fbe632ef8d41c17caa76b6ed3f1d404e1f047299 (diff) | |
download | go-git-c932bd462fed2ff470d9ff40f15102237a3287f8.tar.gz |
Improve delta reutilization
- Remove wrong 'if' on delta selector that causes poor delta reutilizations
- packfile.Encoder now can write deltas and objects in a non specific order
- ObjectToPack now saves the Offset on the packfile to be able to obtain base
offset in a recursive manner and write them before the delta itself
- Added encoder test to check cyclic delta chains
- Check the output packfile hash in all encoder tests
Signed-off-by: Antonio Jesus Navarro Perez <antnavper@gmail.com>
Diffstat (limited to 'plumbing/format/packfile/encoder.go')
-rw-r--r-- | plumbing/format/packfile/encoder.go | 57 |
1 files changed, 40 insertions, 17 deletions
diff --git a/plumbing/format/packfile/encoder.go b/plumbing/format/packfile/encoder.go index 7ee6546..6686dd5 100644 --- a/plumbing/format/packfile/encoder.go +++ b/plumbing/format/packfile/encoder.go @@ -18,10 +18,7 @@ type Encoder struct { w *offsetWriter zw *zlib.Writer hasher plumbing.Hasher - // offsets is a map of object hashes to corresponding offsets in the packfile. - // It is used to determine offset of the base of a delta when a OFS_DELTA is - // used. - offsets map[plumbing.Hash]int64 + useRefDeltas bool } @@ -40,7 +37,6 @@ func NewEncoder(w io.Writer, s storer.EncodedObjectStorer, useRefDeltas bool) *E w: ow, zw: zw, hasher: h, - offsets: make(map[plumbing.Hash]int64), useRefDeltas: useRefDeltas, } } @@ -85,11 +81,34 @@ func (e *Encoder) head(numEntries int) error { } func (e *Encoder) entry(o *ObjectToPack) error { - offset := e.w.Offset() - e.offsets[o.Hash()] = offset + if o.WantWrite() { + // A cycle exists in this delta chain. This should only occur if a + // selected object representation disappeared during writing + // (for example due to a concurrent repack) and a different base + // was chosen, forcing a cycle. Select something other than a + // delta, and write this object. + o.BackToOriginal() + } + + if o.IsWritten() { + return nil + } + + o.MarkWantWrite() + + if err := e.writeBaseIfDelta(o); err != nil { + return err + } + + // We need to check if we already write that object due a cyclic delta chain + if o.IsWritten() { + return nil + } + + o.Offset = e.w.Offset() if o.IsDelta() { - if err := e.writeDeltaHeader(o, offset); err != nil { + if err := e.writeDeltaHeader(o); err != nil { return err } } else { @@ -112,7 +131,16 @@ func (e *Encoder) entry(o *ObjectToPack) error { return e.zw.Close() } -func (e *Encoder) writeDeltaHeader(o *ObjectToPack, offset int64) error { +func (e *Encoder) writeBaseIfDelta(o *ObjectToPack) error { + if o.IsDelta() && !o.Base.IsWritten() { + // We must write base first + return e.entry(o.Base) + } + + return nil +} + +func (e *Encoder) writeDeltaHeader(o *ObjectToPack) error { // Write offset deltas by default t := plumbing.OFSDeltaObject if e.useRefDeltas { @@ -126,7 +154,7 @@ func (e *Encoder) writeDeltaHeader(o *ObjectToPack, offset int64) error { if e.useRefDeltas { return e.writeRefDeltaHeader(o.Base.Hash()) } else { - return e.writeOfsDeltaHeader(offset, o.Base.Hash()) + return e.writeOfsDeltaHeader(o) } } @@ -134,15 +162,10 @@ func (e *Encoder) writeRefDeltaHeader(base plumbing.Hash) error { return binary.Write(e.w, base) } -func (e *Encoder) writeOfsDeltaHeader(deltaOffset int64, base plumbing.Hash) error { - baseOffset, ok := e.offsets[base] - if !ok { - return fmt.Errorf("base for delta not found, base hash: %v", base) - } - +func (e *Encoder) writeOfsDeltaHeader(o *ObjectToPack) error { // for OFS_DELTA, offset of the base is interpreted as negative offset // relative to the type-byte of the header of the ofs-delta entry. - relativeOffset := deltaOffset - baseOffset + relativeOffset := o.Offset - o.Base.Offset if relativeOffset <= 0 { return fmt.Errorf("bad offset for OFS_DELTA entry: %d", relativeOffset) } |