diff options
Diffstat (limited to 'plumbing/format')
-rw-r--r-- | plumbing/format/commitgraph/encoder.go | 14 | ||||
-rw-r--r-- | plumbing/format/commitgraph/file.go | 2 | ||||
-rw-r--r-- | plumbing/format/commitgraph/memory.go | 2 | ||||
-rw-r--r-- | plumbing/format/diff/unified_encoder.go | 25 | ||||
-rw-r--r-- | plumbing/format/diff/unified_encoder_test.go | 90 | ||||
-rw-r--r-- | plumbing/format/gitattributes/pattern.go | 2 | ||||
-rw-r--r-- | plumbing/format/idxfile/decoder.go | 6 | ||||
-rw-r--r-- | plumbing/format/idxfile/writer.go | 2 | ||||
-rw-r--r-- | plumbing/format/index/decoder_test.go | 2 | ||||
-rw-r--r-- | plumbing/format/index/doc.go | 4 | ||||
-rw-r--r-- | plumbing/format/index/encoder_test.go | 6 | ||||
-rw-r--r-- | plumbing/format/index/index.go | 2 | ||||
-rw-r--r-- | plumbing/format/packfile/diff_delta.go | 13 | ||||
-rw-r--r-- | plumbing/format/packfile/packfile.go | 8 | ||||
-rw-r--r-- | plumbing/format/packfile/parser.go | 99 | ||||
-rw-r--r-- | plumbing/format/packfile/patch_delta.go | 53 | ||||
-rw-r--r-- | plumbing/format/packfile/scanner_test.go | 1 |
17 files changed, 202 insertions, 129 deletions
diff --git a/plumbing/format/commitgraph/encoder.go b/plumbing/format/commitgraph/encoder.go index a06871c..615e833 100644 --- a/plumbing/format/commitgraph/encoder.go +++ b/plumbing/format/commitgraph/encoder.go @@ -24,8 +24,6 @@ func NewEncoder(w io.Writer) *Encoder { // Encode writes an index into the commit-graph file
func (e *Encoder) Encode(idx Index) error {
- var err error
-
// Get all the hashes in the input index
hashes := idx.Hashes()
@@ -39,26 +37,26 @@ func (e *Encoder) Encode(idx Index) error { chunkSizes = append(chunkSizes, uint64(extraEdgesCount)*4)
}
- if err = e.encodeFileHeader(len(chunkSignatures)); err != nil {
+ if err := e.encodeFileHeader(len(chunkSignatures)); err != nil {
return err
}
- if err = e.encodeChunkHeaders(chunkSignatures, chunkSizes); err != nil {
+ if err := e.encodeChunkHeaders(chunkSignatures, chunkSizes); err != nil {
return err
}
- if err = e.encodeFanout(fanout); err != nil {
+ if err := e.encodeFanout(fanout); err != nil {
return err
}
- if err = e.encodeOidLookup(hashes); err != nil {
+ if err := e.encodeOidLookup(hashes); err != nil {
return err
}
if extraEdges, err := e.encodeCommitData(hashes, hashToIndex, idx); err == nil {
if err = e.encodeExtraEdges(extraEdges); err != nil {
return err
}
- }
- if err != nil {
+ } else {
return err
}
+
return e.encodeChecksum()
}
diff --git a/plumbing/format/commitgraph/file.go b/plumbing/format/commitgraph/file.go index 175d279..1f82abd 100644 --- a/plumbing/format/commitgraph/file.go +++ b/plumbing/format/commitgraph/file.go @@ -249,7 +249,7 @@ func (fi *fileIndex) getHashesFromIndexes(indexes []int) ([]plumbing.Hash, error // Hashes returns all the hashes that are available in the index
func (fi *fileIndex) Hashes() []plumbing.Hash {
hashes := make([]plumbing.Hash, fi.fanout[0xff])
- for i := 0; i < int(fi.fanout[0xff]); i++ {
+ for i := 0; i < fi.fanout[0xff]; i++ {
offset := fi.oidLookupOffset + int64(i)*20
if n, err := fi.reader.ReadAt(hashes[i][:], offset); err != nil || n < 20 {
return nil
diff --git a/plumbing/format/commitgraph/memory.go b/plumbing/format/commitgraph/memory.go index a4a96e9..f5afd4c 100644 --- a/plumbing/format/commitgraph/memory.go +++ b/plumbing/format/commitgraph/memory.go @@ -31,7 +31,7 @@ func (mi *MemoryIndex) GetIndexByHash(h plumbing.Hash) (int, error) { // GetCommitDataByIndex gets the commit node from the commit graph using index
// obtained from child node, if available
func (mi *MemoryIndex) GetCommitDataByIndex(i int) (*CommitData, error) {
- if int(i) >= len(mi.commitData) {
+ if i >= len(mi.commitData) {
return nil, plumbing.ErrObjectNotFound
}
diff --git a/plumbing/format/diff/unified_encoder.go b/plumbing/format/diff/unified_encoder.go index 8bd6d8a..ce3bc7c 100644 --- a/plumbing/format/diff/unified_encoder.go +++ b/plumbing/format/diff/unified_encoder.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io" + "regexp" "strings" "gopkg.in/src-d/go-git.v4/plumbing" @@ -25,9 +26,10 @@ const ( tPath = "+++ %s\n" binary = "Binary files %s and %s differ\n" - addLine = "+%s\n" - deleteLine = "-%s\n" - equalLine = " %s\n" + addLine = "+%s%s" + deleteLine = "-%s%s" + equalLine = " %s%s" + noNewLine = "\n\\ No newline at end of file\n" oldMode = "old mode %o\n" newMode = "new mode %o\n" @@ -94,7 +96,7 @@ func (e *UnifiedEncoder) printMessage(message string) { isEmpty := message == "" hasSuffix := strings.HasSuffix(message, "\n") if !isEmpty && !hasSuffix { - message = message + "\n" + message += "\n" } e.buf.WriteString(message) @@ -216,7 +218,7 @@ func (c *hunksGenerator) processHunk(i int, op Operation) { linesBefore = c.ctxLines } - c.current = &hunk{ctxPrefix: ctxPrefix} + c.current = &hunk{ctxPrefix: strings.TrimSuffix(ctxPrefix, "\n")} c.current.AddOp(Equal, c.beforeContext...) switch op { @@ -279,12 +281,13 @@ func (c *hunksGenerator) processEqualsLines(ls []string, i int) { } } +var splitLinesRE = regexp.MustCompile(`[^\n]*(\n|$)`) + func splitLines(s string) []string { - out := strings.Split(s, "\n") + out := splitLinesRE.FindAllString(s, -1) if out[len(out)-1] == "" { out = out[:len(out)-1] } - return out } @@ -346,7 +349,7 @@ type op struct { } func (o *op) String() string { - var prefix string + var prefix, suffix string switch o.t { case Add: prefix = addLine @@ -355,6 +358,10 @@ func (o *op) String() string { case Equal: prefix = equalLine } + n := len(o.text) + if n > 0 && o.text[n-1] != '\n' { + suffix = noNewLine + } - return fmt.Sprintf(prefix, o.text) + return fmt.Sprintf(prefix, o.text, suffix) } diff --git a/plumbing/format/diff/unified_encoder_test.go b/plumbing/format/diff/unified_encoder_test.go index 7736af1..091a96a 100644 --- a/plumbing/format/diff/unified_encoder_test.go +++ b/plumbing/format/diff/unified_encoder_test.go @@ -83,7 +83,7 @@ var oneChunkPatch Patch = testPatch{ content: "A\n", op: Delete, }, { - content: "B\nC\nD\nE\nF\nG", + content: "B\nC\nD\nE\nF\nG\n", op: Equal, }, { content: "H\n", @@ -125,7 +125,7 @@ var oneChunkPatchInverted Patch = testPatch{ content: "A\n", op: Add, }, { - content: "B\nC\nD\nE\nF\nG", + content: "B\nC\nD\nE\nF\nG\n", op: Equal, }, { content: "H\n", @@ -164,13 +164,13 @@ var fixtures []*fixture = []*fixture{{ seed: "hello\nbug\n", }, chunks: []testChunk{{ - content: "hello", + content: "hello\n", op: Equal, }, { - content: "world", + content: "world\n", op: Delete, }, { - content: "bug", + content: "bug\n", op: Add, }}, }}, @@ -239,18 +239,18 @@ rename to test1.txt from: &testFile{ mode: filemode.Regular, path: "test.txt", - seed: "test", + seed: "test\n", }, to: &testFile{ mode: filemode.Regular, path: "test1.txt", - seed: "test1", + seed: "test1\n", }, chunks: []testChunk{{ - content: "test", + content: "test\n", op: Delete, }, { - content: "test1", + content: "test1\n", op: Add, }}, }}, @@ -260,7 +260,7 @@ rename to test1.txt diff: `diff --git a/test.txt b/test1.txt rename from test.txt rename to test1.txt -index 30d74d258442c7c65512eafab474568dd706c430..f079749c42ffdcc5f52ed2d3a6f15b09307e975e 100644 +index 9daeafb9864cf43055ae93beb0afd6c7d144bfa4..a5bce3fd2565d8f458555a0c6f42d0504a848bd5 100644 --- a/test.txt +++ b/test1.txt @@ -1 +1 @@ @@ -299,19 +299,19 @@ rename to test1.txt from: &testFile{ mode: filemode.Regular, path: "test.txt", - seed: "test", + seed: "test\n", }, to: &testFile{ mode: filemode.Regular, path: "test.txt", - seed: "test2", + seed: "test2\n", }, chunks: []testChunk{{ - content: "test", + content: "test\n", op: Delete, }, { - content: "test2", + content: "test2\n", op: Add, }}, }}, @@ -320,7 +320,7 @@ rename to test1.txt desc: "one line change", context: 1, diff: `diff --git a/test.txt b/test.txt -index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d55b2ae9d 100644 +index 9daeafb9864cf43055ae93beb0afd6c7d144bfa4..180cf8328022becee9aaa2577a8f84ea2b9f3827 100644 --- a/test.txt +++ b/test.txt @@ -1 +1 @@ @@ -334,19 +334,19 @@ index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d from: &testFile{ mode: filemode.Regular, path: "test.txt", - seed: "test", + seed: "test\n", }, to: &testFile{ mode: filemode.Regular, path: "test.txt", - seed: "test2", + seed: "test2\n", }, chunks: []testChunk{{ - content: "test", + content: "test\n", op: Delete, }, { - content: "test2", + content: "test2\n", op: Add, }}, }}, @@ -356,7 +356,7 @@ index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d context: 1, diff: `this is the message diff --git a/test.txt b/test.txt -index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d55b2ae9d 100644 +index 9daeafb9864cf43055ae93beb0afd6c7d144bfa4..180cf8328022becee9aaa2577a8f84ea2b9f3827 100644 --- a/test.txt +++ b/test.txt @@ -1 +1 @@ @@ -397,7 +397,9 @@ index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d +++ b/test.txt @@ -1 +1 @@ -test +\ No newline at end of file +test2 +\ No newline at end of file `, }, { patch: testPatch{ @@ -407,7 +409,7 @@ index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d to: &testFile{ mode: filemode.Regular, path: "new.txt", - seed: "test\ntest2\test3", + seed: "test\ntest2\ntest3", }, chunks: []testChunk{{ @@ -421,13 +423,14 @@ index 30d74d258442c7c65512eafab474568dd706c430..d606037cb232bfda7788a8322492312d context: 1, diff: `diff --git a/new.txt b/new.txt new file mode 100644 -index 0000000000000000000000000000000000000000..65c8dd02a42273038658a22b1cb29c8d9457ca12 +index 0000000000000000000000000000000000000000..3ceaab5442b64a0c2b33dd25fae67ccdb4fd1ea8 --- /dev/null +++ b/new.txt @@ -0,0 +1,3 @@ +test +test2 +test3 +\ No newline at end of file `, }, { patch: testPatch{ @@ -456,6 +459,7 @@ index 30d74d258442c7c65512eafab474568dd706c430..00000000000000000000000000000000 +++ /dev/null @@ -1 +0,0 @@ -test +\ No newline at end of file `, }, { patch: oneChunkPatch, @@ -548,6 +552,7 @@ index ab5eed5d4a2c33aeef67e0188ee79bed666bde6f..0adddcde4fd38042c354518351820eb0 X Y Z +\ No newline at end of file `, }, { patch: oneChunkPatch, @@ -813,6 +818,47 @@ index 0adddcde4fd38042c354518351820eb06c417c82..553ae669c7a9303cf848fcc749a25692 +++ b/onechunk.txt @@ -23 +22,0 @@ Y -Z +\ No newline at end of file +`, +}, { + patch: testPatch{ + message: "", + filePatches: []testFilePatch{{ + from: &testFile{ + mode: filemode.Regular, + path: "onechunk.txt", + seed: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\nY\nZ", + }, + to: &testFile{ + mode: filemode.Regular, + path: "onechunk.txt", + seed: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\nY", + }, + + chunks: []testChunk{{ + content: "B\nC\nD\nE\nF\nG\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nV\nW\nX\n", + op: Equal, + }, { + content: "Y\nZ", + op: Delete, + }, { + content: "Y", + op: Add, + }}, + }}, + }, + desc: "remove last letter and no newline at end of file", + context: 0, + diff: `diff --git a/onechunk.txt b/onechunk.txt +index 0adddcde4fd38042c354518351820eb06c417c82..d39ae38aad7ba9447b5e7998b2e4714f26c9218d 100644 +--- a/onechunk.txt ++++ b/onechunk.txt +@@ -22,2 +21 @@ X +-Y +-Z +\ No newline at end of file ++Y +\ No newline at end of file `, }} diff --git a/plumbing/format/gitattributes/pattern.go b/plumbing/format/gitattributes/pattern.go index c5ca0c7..d961aba 100644 --- a/plumbing/format/gitattributes/pattern.go +++ b/plumbing/format/gitattributes/pattern.go @@ -66,7 +66,7 @@ func (p *pattern) Match(path []string) bool { doublestar = true } - switch true { + switch { case strings.Contains(pattern[0], "**"): return false diff --git a/plumbing/format/idxfile/decoder.go b/plumbing/format/idxfile/decoder.go index 5b92782..d1a8a2c 100644 --- a/plumbing/format/idxfile/decoder.go +++ b/plumbing/format/idxfile/decoder.go @@ -12,7 +12,7 @@ import ( var ( // ErrUnsupportedVersion is returned by Decode when the idx file version // is not supported. - ErrUnsupportedVersion = errors.New("Unsuported version") + ErrUnsupportedVersion = errors.New("Unsupported version") // ErrMalformedIdxFile is returned by Decode when the idx file is corrupted. ErrMalformedIdxFile = errors.New("Malformed IDX file") ) @@ -110,10 +110,6 @@ func readObjectNames(idx *MemoryIndex, r io.Reader) error { continue } - if buckets < 0 { - return ErrMalformedIdxFile - } - idx.FanoutMapping[k] = len(idx.Names) nameLen := int(buckets * objectIDLength) diff --git a/plumbing/format/idxfile/writer.go b/plumbing/format/idxfile/writer.go index aa919e7..fcc78c5 100644 --- a/plumbing/format/idxfile/writer.go +++ b/plumbing/format/idxfile/writer.go @@ -147,7 +147,7 @@ func (w *Writer) createIndex() (*MemoryIndex, error) { idx.Offset32[bucket] = append(idx.Offset32[bucket], buf.Bytes()...) buf.Truncate(0) - binary.WriteUint32(buf, uint32(o.CRC32)) + binary.WriteUint32(buf, o.CRC32) idx.CRC32[bucket] = append(idx.CRC32[bucket], buf.Bytes()...) } diff --git a/plumbing/format/index/decoder_test.go b/plumbing/format/index/decoder_test.go index 7468ad0..92d312d 100644 --- a/plumbing/format/index/decoder_test.go +++ b/plumbing/format/index/decoder_test.go @@ -115,7 +115,7 @@ func (s *IndexSuite) TestDecodeMergeConflict(c *C) { {TheirMode, "14f8e368114f561c38e134f6e68ea6fea12d77ed"}, } - // stagged files + // staged files for i, e := range idx.Entries[4:7] { c.Assert(e.Stage, Equals, expected[i].Stage) c.Assert(e.CreatedAt.IsZero(), Equals, true) diff --git a/plumbing/format/index/doc.go b/plumbing/format/index/doc.go index f2b3d76..39ae6ad 100644 --- a/plumbing/format/index/doc.go +++ b/plumbing/format/index/doc.go @@ -320,7 +320,7 @@ // == End of Index Entry // // The End of Index Entry (EOIE) is used to locate the end of the variable -// length index entries and the begining of the extensions. Code can take +// length index entries and the beginning of the extensions. Code can take // advantage of this to quickly locate the index extensions without having // to parse through all of the index entries. // @@ -353,7 +353,7 @@ // // - A number of index offset entries each consisting of: // -// - 32-bit offset from the begining of the file to the first cache entry +// - 32-bit offset from the beginning of the file to the first cache entry // in this block of entries. // // - 32-bit count of cache entries in this blockpackage index diff --git a/plumbing/format/index/encoder_test.go b/plumbing/format/index/encoder_test.go index 78cbbba..ea121fc 100644 --- a/plumbing/format/index/encoder_test.go +++ b/plumbing/format/index/encoder_test.go @@ -55,7 +55,7 @@ func (s *IndexSuite) TestEncode(c *C) { } -func (s *IndexSuite) TestEncodeUnsuportedVersion(c *C) { +func (s *IndexSuite) TestEncodeUnsupportedVersion(c *C) { idx := &Index{Version: 3} buf := bytes.NewBuffer(nil) @@ -64,7 +64,7 @@ func (s *IndexSuite) TestEncodeUnsuportedVersion(c *C) { c.Assert(err, Equals, ErrUnsupportedVersion) } -func (s *IndexSuite) TestEncodeWithIntentToAddUnsuportedVersion(c *C) { +func (s *IndexSuite) TestEncodeWithIntentToAddUnsupportedVersion(c *C) { idx := &Index{ Version: 2, Entries: []*Entry{{IntentToAdd: true}}, @@ -76,7 +76,7 @@ func (s *IndexSuite) TestEncodeWithIntentToAddUnsuportedVersion(c *C) { c.Assert(err, Equals, ErrUnsupportedVersion) } -func (s *IndexSuite) TestEncodeWithSkipWorktreeUnsuportedVersion(c *C) { +func (s *IndexSuite) TestEncodeWithSkipWorktreeUnsupportedVersion(c *C) { idx := &Index{ Version: 2, Entries: []*Entry{{SkipWorktree: true}}, diff --git a/plumbing/format/index/index.go b/plumbing/format/index/index.go index 6c4b7ca..6653c91 100644 --- a/plumbing/format/index/index.go +++ b/plumbing/format/index/index.go @@ -198,7 +198,7 @@ type ResolveUndoEntry struct { } // EndOfIndexEntry is the End of Index Entry (EOIE) is used to locate the end of -// the variable length index entries and the begining of the extensions. Code +// the variable length index entries and the beginning of the extensions. Code // can take advantage of this to quickly locate the index extensions without // having to parse through all of the index entries. // diff --git a/plumbing/format/packfile/diff_delta.go b/plumbing/format/packfile/diff_delta.go index d35e78a..43f87a0 100644 --- a/plumbing/format/packfile/diff_delta.go +++ b/plumbing/format/packfile/diff_delta.go @@ -40,8 +40,8 @@ func getDelta(index *deltaIndex, base, target plumbing.EncodedObject) (plumbing. defer tr.Close() bb := bufPool.Get().(*bytes.Buffer) - bb.Reset() defer bufPool.Put(bb) + bb.Reset() _, err = bb.ReadFrom(br) if err != nil { @@ -49,8 +49,8 @@ func getDelta(index *deltaIndex, base, target plumbing.EncodedObject) (plumbing. } tb := bufPool.Get().(*bytes.Buffer) - tb.Reset() defer bufPool.Put(tb) + tb.Reset() _, err = tb.ReadFrom(tr) if err != nil { @@ -77,6 +77,7 @@ func DiffDelta(src, tgt []byte) []byte { func diffDelta(index *deltaIndex, src []byte, tgt []byte) []byte { buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) buf.Reset() buf.Write(deltaEncodeSize(len(src))) buf.Write(deltaEncodeSize(len(tgt))) @@ -86,6 +87,7 @@ func diffDelta(index *deltaIndex, src []byte, tgt []byte) []byte { } ibuf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(ibuf) ibuf.Reset() for i := 0; i < len(tgt); i++ { offset, l := index.findMatch(src, tgt, i) @@ -127,12 +129,9 @@ func diffDelta(index *deltaIndex, src []byte, tgt []byte) []byte { } encodeInsertOperation(ibuf, buf) - bytes := buf.Bytes() - - bufPool.Put(buf) - bufPool.Put(ibuf) - return bytes + // buf.Bytes() is only valid until the next modifying operation on the buffer. Copy it. + return append([]byte{}, buf.Bytes()...) } func encodeInsertOperation(ibuf, buf *bytes.Buffer) { diff --git a/plumbing/format/packfile/packfile.go b/plumbing/format/packfile/packfile.go index f528073..21a15de 100644 --- a/plumbing/format/packfile/packfile.go +++ b/plumbing/format/packfile/packfile.go @@ -133,8 +133,8 @@ func (p *Packfile) getObjectSize(h *ObjectHeader) (int64, error) { return h.Length, nil case plumbing.REFDeltaObject, plumbing.OFSDeltaObject: buf := bufPool.Get().(*bytes.Buffer) - buf.Reset() defer bufPool.Put(buf) + buf.Reset() if _, _, err := p.s.NextObject(buf); err != nil { return 0, err @@ -222,11 +222,11 @@ func (p *Packfile) getNextObject(h *ObjectHeader, hash plumbing.Hash) (plumbing. // optimization only if the expanded version of the object still meets // the small object threshold condition. buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) buf.Reset() if _, _, err := p.s.NextObject(buf); err != nil { return nil, err } - defer bufPool.Put(buf) size = p.getDeltaObjectSize(buf) if size <= smallObjectThreshold { @@ -321,12 +321,12 @@ func (p *Packfile) fillRegularObjectContent(obj plumbing.EncodedObject) error { func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plumbing.Hash) error { buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) buf.Reset() _, _, err := p.s.NextObject(buf) if err != nil { return err } - defer bufPool.Put(buf) return p.fillREFDeltaObjectContentWithBuffer(obj, ref, buf) } @@ -351,12 +351,12 @@ func (p *Packfile) fillREFDeltaObjectContentWithBuffer(obj plumbing.EncodedObjec func (p *Packfile) fillOFSDeltaObjectContent(obj plumbing.EncodedObject, offset int64) error { buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) buf.Reset() _, _, err := p.s.NextObject(buf) if err != nil { return err } - defer bufPool.Put(buf) return p.fillOFSDeltaObjectContentWithBuffer(obj, offset, buf) } diff --git a/plumbing/format/packfile/parser.go b/plumbing/format/packfile/parser.go index 71cbba9..d8c0f75 100644 --- a/plumbing/format/packfile/parser.go +++ b/plumbing/format/packfile/parser.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "io" + "io/ioutil" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/cache" @@ -263,11 +264,14 @@ func (p *Parser) indexObjects() error { } func (p *Parser) resolveDeltas() error { + buf := &bytes.Buffer{} for _, obj := range p.oi { - content, err := p.get(obj) + buf.Reset() + err := p.get(obj, buf) if err != nil { return err } + content := buf.Bytes() if err := p.onInflatedObjectHeader(obj.Type, obj.Length, obj.Offset); err != nil { return err @@ -279,7 +283,7 @@ func (p *Parser) resolveDeltas() error { if !obj.IsDelta() && len(obj.Children) > 0 { for _, child := range obj.Children { - if _, err := p.resolveObject(child, content); err != nil { + if err := p.resolveObject(ioutil.Discard, child, content); err != nil { return err } } @@ -294,82 +298,87 @@ func (p *Parser) resolveDeltas() error { return nil } -func (p *Parser) get(o *objectInfo) (b []byte, err error) { - var ok bool +func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) error { if !o.ExternalRef { // skip cache check for placeholder parents - b, ok = p.cache.Get(o.Offset) + b, ok := p.cache.Get(o.Offset) + if ok { + _, err := buf.Write(b) + return err + } } // If it's not on the cache and is not a delta we can try to find it in the // storage, if there's one. External refs must enter here. - if !ok && p.storage != nil && !o.Type.IsDelta() { + if p.storage != nil && !o.Type.IsDelta() { e, err := p.storage.EncodedObject(plumbing.AnyObject, o.SHA1) if err != nil { - return nil, err + return err } o.Type = e.Type() r, err := e.Reader() if err != nil { - return nil, err - } - - b = make([]byte, e.Size()) - if _, err = r.Read(b); err != nil { - return nil, err + return err } - } - if b != nil { - return b, nil + _, err = buf.ReadFrom(io.LimitReader(r, e.Size())) + return err } if o.ExternalRef { // we were not able to resolve a ref in a thin pack - return nil, ErrReferenceDeltaNotFound + return ErrReferenceDeltaNotFound } - var data []byte if o.DiskType.IsDelta() { - base, err := p.get(o.Parent) + b := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(b) + b.Reset() + err := p.get(o.Parent, b) if err != nil { - return nil, err + return err } + base := b.Bytes() - data, err = p.resolveObject(o, base) + err = p.resolveObject(buf, o, base) if err != nil { - return nil, err + return err } } else { - data, err = p.readData(o) + err := p.readData(buf, o) if err != nil { - return nil, err + return err } } if len(o.Children) > 0 { + data := make([]byte, buf.Len()) + copy(data, buf.Bytes()) p.cache.Put(o.Offset, data) } - - return data, nil + return nil } func (p *Parser) resolveObject( + w io.Writer, o *objectInfo, base []byte, -) ([]byte, error) { +) error { if !o.DiskType.IsDelta() { - return nil, nil + return nil } - - data, err := p.readData(o) + buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) + buf.Reset() + err := p.readData(buf, o) if err != nil { - return nil, err + return err } + data := buf.Bytes() data, err = applyPatchBase(o, data, base) if err != nil { - return nil, err + return err } if p.storage != nil { @@ -377,37 +386,35 @@ func (p *Parser) resolveObject( obj.SetSize(o.Size()) obj.SetType(o.Type) if _, err := obj.Write(data); err != nil { - return nil, err + return err } if _, err := p.storage.SetEncodedObject(obj); err != nil { - return nil, err + return err } } - - return data, nil + _, err = w.Write(data) + return err } -func (p *Parser) readData(o *objectInfo) ([]byte, error) { +func (p *Parser) readData(w io.Writer, o *objectInfo) error { if !p.scanner.IsSeekable && o.DiskType.IsDelta() { data, ok := p.deltas[o.Offset] if !ok { - return nil, ErrDeltaNotCached + return ErrDeltaNotCached } - - return data, nil + _, err := w.Write(data) + return err } if _, err := p.scanner.SeekObjectHeader(o.Offset); err != nil { - return nil, err + return err } - buf := new(bytes.Buffer) - if _, _, err := p.scanner.NextObject(buf); err != nil { - return nil, err + if _, _, err := p.scanner.NextObject(w); err != nil { + return err } - - return buf.Bytes(), nil + return nil } func applyPatchBase(ota *objectInfo, data, base []byte) ([]byte, error) { diff --git a/plumbing/format/packfile/patch_delta.go b/plumbing/format/packfile/patch_delta.go index a972f1c..e1a5141 100644 --- a/plumbing/format/packfile/patch_delta.go +++ b/plumbing/format/packfile/patch_delta.go @@ -1,8 +1,9 @@ package packfile import ( + "bytes" "errors" - "io/ioutil" + "io" "gopkg.in/src-d/go-git.v4/plumbing" ) @@ -26,19 +27,29 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) error { return err } - src, err := ioutil.ReadAll(r) + buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) + buf.Reset() + _, err = buf.ReadFrom(r) if err != nil { return err } + src := buf.Bytes() - dst, err := PatchDelta(src, delta) + dst := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(dst) + dst.Reset() + err = patchDelta(dst, src, delta) if err != nil { return err } - target.SetSize(int64(len(dst))) - _, err = w.Write(dst) + target.SetSize(int64(dst.Len())) + + b := byteSlicePool.Get().([]byte) + _, err = io.CopyBuffer(w, dst, b) + byteSlicePool.Put(b) return err } @@ -51,23 +62,31 @@ var ( // An error will be returned if delta is corrupted (ErrDeltaLen) or an action command // is not copy from source or copy from delta (ErrDeltaCmd). func PatchDelta(src, delta []byte) ([]byte, error) { + b := &bytes.Buffer{} + if err := patchDelta(b, src, delta); err != nil { + return nil, err + } + return b.Bytes(), nil +} + +func patchDelta(dst *bytes.Buffer, src, delta []byte) error { if len(delta) < deltaSizeMin { - return nil, ErrInvalidDelta + return ErrInvalidDelta } srcSz, delta := decodeLEB128(delta) if srcSz != uint(len(src)) { - return nil, ErrInvalidDelta + return ErrInvalidDelta } targetSz, delta := decodeLEB128(delta) remainingTargetSz := targetSz var cmd byte - dest := make([]byte, 0, targetSz) + dst.Grow(int(targetSz)) for { if len(delta) == 0 { - return nil, ErrInvalidDelta + return ErrInvalidDelta } cmd = delta[0] @@ -77,35 +96,35 @@ func PatchDelta(src, delta []byte) ([]byte, error) { var err error offset, delta, err = decodeOffset(cmd, delta) if err != nil { - return nil, err + return err } sz, delta, err = decodeSize(cmd, delta) if err != nil { - return nil, err + return err } if invalidSize(sz, targetSz) || invalidOffsetSize(offset, sz, srcSz) { break } - dest = append(dest, src[offset:offset+sz]...) + dst.Write(src[offset:offset+sz]) remainingTargetSz -= sz } else if isCopyFromDelta(cmd) { sz := uint(cmd) // cmd is the size itself if invalidSize(sz, targetSz) { - return nil, ErrInvalidDelta + return ErrInvalidDelta } if uint(len(delta)) < sz { - return nil, ErrInvalidDelta + return ErrInvalidDelta } - dest = append(dest, delta[0:sz]...) + dst.Write(delta[0:sz]) remainingTargetSz -= sz delta = delta[sz:] } else { - return nil, ErrDeltaCmd + return ErrDeltaCmd } if remainingTargetSz <= 0 { @@ -113,7 +132,7 @@ func PatchDelta(src, delta []byte) ([]byte, error) { } } - return dest, nil + return nil } // Decodes a number encoded as an unsigned LEB128 at the start of some diff --git a/plumbing/format/packfile/scanner_test.go b/plumbing/format/packfile/scanner_test.go index a401d6d..3078477 100644 --- a/plumbing/format/packfile/scanner_test.go +++ b/plumbing/format/packfile/scanner_test.go @@ -140,6 +140,7 @@ func (s *ScannerSuite) TestReaderReset(c *C) { p := NewScanner(r) version, objects, err := p.Header() + c.Assert(err, IsNil) c.Assert(version, Equals, VersionSupported) c.Assert(objects, Equals, uint32(31)) |