aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing
diff options
context:
space:
mode:
Diffstat (limited to 'plumbing')
-rw-r--r--plumbing/format/commitgraph/file.go6
-rw-r--r--plumbing/format/config/section.go2
-rw-r--r--plumbing/format/diff/patch.go10
-rw-r--r--plumbing/format/gitattributes/attributes.go2
-rw-r--r--plumbing/format/gitattributes/pattern.go5
-rw-r--r--plumbing/format/gitattributes/pattern_test.go6
-rw-r--r--plumbing/format/gitignore/dir.go25
-rw-r--r--plumbing/format/gitignore/dir_test.go17
-rw-r--r--plumbing/format/idxfile/decoder.go4
-rw-r--r--plumbing/format/index/encoder.go34
-rw-r--r--plumbing/format/index/encoder_test.go26
-rw-r--r--plumbing/format/index/index.go18
-rw-r--r--plumbing/format/objfile/reader.go17
-rw-r--r--plumbing/format/objfile/writer.go7
-rw-r--r--plumbing/format/packfile/common.go18
-rw-r--r--plumbing/format/packfile/diff_delta.go21
-rw-r--r--plumbing/format/packfile/fsobject.go15
-rw-r--r--plumbing/format/packfile/packfile.go34
-rw-r--r--plumbing/format/packfile/packfile_test.go17
-rw-r--r--plumbing/format/packfile/parser.go31
-rw-r--r--plumbing/format/packfile/parser_test.go41
-rw-r--r--plumbing/format/packfile/patch_delta.go17
-rw-r--r--plumbing/format/packfile/scanner.go54
-rw-r--r--plumbing/memory.go4
-rw-r--r--plumbing/object/change.go2
-rw-r--r--plumbing/object/change_adaptor.go4
-rw-r--r--plumbing/object/commit.go7
-rw-r--r--plumbing/object/common.go12
-rw-r--r--plumbing/object/patch.go4
-rw-r--r--plumbing/object/tag.go8
-rw-r--r--plumbing/object/tree.go8
-rw-r--r--plumbing/object/treenoder.go4
-rw-r--r--plumbing/protocol/packp/capability/capability.go15
-rw-r--r--plumbing/protocol/packp/capability/capability_test.go22
-rw-r--r--plumbing/protocol/packp/capability/list.go4
-rw-r--r--plumbing/protocol/packp/capability/list_test.go11
-rw-r--r--plumbing/protocol/packp/common.go1
-rw-r--r--plumbing/protocol/packp/srvresp.go28
-rw-r--r--plumbing/protocol/packp/srvresp_test.go17
-rw-r--r--plumbing/protocol/packp/ulreq.go2
-rw-r--r--plumbing/protocol/packp/ulreq_test.go2
-rw-r--r--plumbing/protocol/packp/updreq.go14
-rw-r--r--plumbing/protocol/packp/updreq_encode.go22
-rw-r--r--plumbing/protocol/packp/updreq_encode_test.go49
-rw-r--r--plumbing/protocol/packp/updreq_test.go4
-rw-r--r--plumbing/protocol/packp/uppackreq_test.go2
-rw-r--r--plumbing/protocol/packp/uppackresp.go3
-rw-r--r--plumbing/protocol/packp/uppackresp_test.go6
-rw-r--r--plumbing/reference.go27
-rw-r--r--plumbing/reference_test.go24
-rw-r--r--plumbing/revlist/revlist_test.go6
-rw-r--r--plumbing/storer/object.go8
-rw-r--r--plumbing/transport/client/client_test.go5
-rw-r--r--plumbing/transport/common.go2
-rw-r--r--plumbing/transport/git/common.go4
-rw-r--r--plumbing/transport/internal/common/common.go5
-rw-r--r--plumbing/transport/server/server.go4
-rw-r--r--plumbing/transport/ssh/auth_method.go46
-rw-r--r--plumbing/transport/ssh/common.go24
-rw-r--r--plumbing/transport/ssh/common_test.go3
60 files changed, 546 insertions, 294 deletions
diff --git a/plumbing/format/commitgraph/file.go b/plumbing/format/commitgraph/file.go
index 0ce7198..1d25238 100644
--- a/plumbing/format/commitgraph/file.go
+++ b/plumbing/format/commitgraph/file.go
@@ -14,14 +14,14 @@ import (
var (
// ErrUnsupportedVersion is returned by OpenFileIndex when the commit graph
// file version is not supported.
- ErrUnsupportedVersion = errors.New("Unsupported version")
+ ErrUnsupportedVersion = errors.New("unsupported version")
// ErrUnsupportedHash is returned by OpenFileIndex when the commit graph
// hash function is not supported. Currently only SHA-1 is defined and
// supported
- ErrUnsupportedHash = errors.New("Unsupported hash algorithm")
+ ErrUnsupportedHash = errors.New("unsupported hash algorithm")
// ErrMalformedCommitGraphFile is returned by OpenFileIndex when the commit
// graph file is corrupted.
- ErrMalformedCommitGraphFile = errors.New("Malformed commit graph file")
+ ErrMalformedCommitGraphFile = errors.New("malformed commit graph file")
commitFileSignature = []byte{'C', 'G', 'P', 'H'}
oidFanoutSignature = []byte{'O', 'I', 'D', 'F'}
diff --git a/plumbing/format/config/section.go b/plumbing/format/config/section.go
index 07f72f3..4625ac5 100644
--- a/plumbing/format/config/section.go
+++ b/plumbing/format/config/section.go
@@ -103,7 +103,7 @@ func (s *Section) RemoveSubsection(name string) *Section {
return s
}
-// Option return the value for the specified key. Empty string is returned if
+// Option returns the value for the specified key. Empty string is returned if
// key does not exists.
func (s *Section) Option(key string) string {
return s.Options.Get(key)
diff --git a/plumbing/format/diff/patch.go b/plumbing/format/diff/patch.go
index 39a66a1..c7678b0 100644
--- a/plumbing/format/diff/patch.go
+++ b/plumbing/format/diff/patch.go
@@ -9,7 +9,7 @@ import (
type Operation int
const (
- // Equal item represents a equals diff.
+ // Equal item represents an equals diff.
Equal Operation = iota
// Add item represents an insert diff.
Add
@@ -26,15 +26,15 @@ type Patch interface {
Message() string
}
-// FilePatch represents the necessary steps to transform one file to another.
+// FilePatch represents the necessary steps to transform one file into another.
type FilePatch interface {
// IsBinary returns true if this patch is representing a binary file.
IsBinary() bool
- // Files returns the from and to Files, with all the necessary metadata to
+ // Files returns the from and to Files, with all the necessary metadata
// about them. If the patch creates a new file, "from" will be nil.
// If the patch deletes a file, "to" will be nil.
Files() (from, to File)
- // Chunks returns a slice of ordered changes to transform "from" File to
+ // Chunks returns a slice of ordered changes to transform "from" File into
// "to" File. If the file is a binary one, Chunks will be empty.
Chunks() []Chunk
}
@@ -49,7 +49,7 @@ type File interface {
Path() string
}
-// Chunk represents a portion of a file transformation to another.
+// Chunk represents a portion of a file transformation into another.
type Chunk interface {
// Content contains the portion of the file.
Content() string
diff --git a/plumbing/format/gitattributes/attributes.go b/plumbing/format/gitattributes/attributes.go
index d13c2a9..329e667 100644
--- a/plumbing/format/gitattributes/attributes.go
+++ b/plumbing/format/gitattributes/attributes.go
@@ -15,7 +15,7 @@ const (
var (
ErrMacroNotAllowed = errors.New("macro not allowed")
- ErrInvalidAttributeName = errors.New("Invalid attribute name")
+ ErrInvalidAttributeName = errors.New("invalid attribute name")
)
type MatchAttribute struct {
diff --git a/plumbing/format/gitattributes/pattern.go b/plumbing/format/gitattributes/pattern.go
index d961aba..f101f47 100644
--- a/plumbing/format/gitattributes/pattern.go
+++ b/plumbing/format/gitattributes/pattern.go
@@ -52,6 +52,11 @@ func (p *pattern) Match(path []string) bool {
var match, doublestar bool
var err error
for _, part := range path {
+ // path is deeper than pattern
+ if len(pattern) == 0 {
+ return false
+ }
+
// skip empty
if pattern[0] == "" {
pattern = pattern[1:]
diff --git a/plumbing/format/gitattributes/pattern_test.go b/plumbing/format/gitattributes/pattern_test.go
index f95be6e..981d56f 100644
--- a/plumbing/format/gitattributes/pattern_test.go
+++ b/plumbing/format/gitattributes/pattern_test.go
@@ -174,6 +174,12 @@ func (s *PatternSuite) TestGlobMatch_tailingAsterisks_single(c *C) {
c.Assert(r, Equals, true)
}
+func (s *PatternSuite) TestGlobMatch_tailingAsterisk_single(c *C) {
+ p := ParsePattern("/*lue/*", nil)
+ r := p.Match([]string{"value", "volcano", "tail"})
+ c.Assert(r, Equals, false)
+}
+
func (s *PatternSuite) TestGlobMatch_tailingAsterisks_exactMatch(c *C) {
p := ParsePattern("/*lue/vol?ano/**", nil)
r := p.Match([]string{"value", "volcano"})
diff --git a/plumbing/format/gitignore/dir.go b/plumbing/format/gitignore/dir.go
index 7cea50c..15bc9c7 100644
--- a/plumbing/format/gitignore/dir.go
+++ b/plumbing/format/gitignore/dir.go
@@ -13,13 +13,14 @@ import (
)
const (
- commentPrefix = "#"
- coreSection = "core"
- excludesfile = "excludesfile"
- gitDir = ".git"
- gitignoreFile = ".gitignore"
- gitconfigFile = ".gitconfig"
- systemFile = "/etc/gitconfig"
+ commentPrefix = "#"
+ coreSection = "core"
+ excludesfile = "excludesfile"
+ gitDir = ".git"
+ gitignoreFile = ".gitignore"
+ gitconfigFile = ".gitconfig"
+ systemFile = "/etc/gitconfig"
+ infoExcludeFile = gitDir + "/info/exclude"
)
// readIgnoreFile reads a specific git ignore file.
@@ -42,10 +43,14 @@ func readIgnoreFile(fs billy.Filesystem, path []string, ignoreFile string) (ps [
return
}
-// ReadPatterns reads gitignore patterns recursively traversing through the directory
-// structure. The result is in the ascending order of priority (last higher).
+// ReadPatterns reads the .git/info/exclude and then the gitignore patterns
+// recursively traversing through the directory structure. The result is in
+// the ascending order of priority (last higher).
func ReadPatterns(fs billy.Filesystem, path []string) (ps []Pattern, err error) {
- ps, _ = readIgnoreFile(fs, path, gitignoreFile)
+ ps, _ = readIgnoreFile(fs, path, infoExcludeFile)
+
+ subps, _ := readIgnoreFile(fs, path, gitignoreFile)
+ ps = append(ps, subps...)
var fis []os.FileInfo
fis, err = fs.ReadDir(fs.Join(path...))
diff --git a/plumbing/format/gitignore/dir_test.go b/plumbing/format/gitignore/dir_test.go
index 94ed7be..facc36d 100644
--- a/plumbing/format/gitignore/dir_test.go
+++ b/plumbing/format/gitignore/dir_test.go
@@ -24,7 +24,17 @@ var _ = Suite(&MatcherSuite{})
func (s *MatcherSuite) SetUpTest(c *C) {
// setup generic git repository root
fs := memfs.New()
- f, err := fs.Create(".gitignore")
+
+ err := fs.MkdirAll(".git/info", os.ModePerm)
+ c.Assert(err, IsNil)
+ f, err := fs.Create(".git/info/exclude")
+ c.Assert(err, IsNil)
+ _, err = f.Write([]byte("exclude.crlf\r\n"))
+ c.Assert(err, IsNil)
+ err = f.Close()
+ c.Assert(err, IsNil)
+
+ f, err = fs.Create(".gitignore")
c.Assert(err, IsNil)
_, err = f.Write([]byte("vendor/g*/\n"))
c.Assert(err, IsNil)
@@ -44,6 +54,8 @@ func (s *MatcherSuite) SetUpTest(c *C) {
err = fs.MkdirAll("another", os.ModePerm)
c.Assert(err, IsNil)
+ err = fs.MkdirAll("exclude.crlf", os.ModePerm)
+ c.Assert(err, IsNil)
err = fs.MkdirAll("ignore.crlf", os.ModePerm)
c.Assert(err, IsNil)
err = fs.MkdirAll("vendor/github.com", os.ModePerm)
@@ -173,9 +185,10 @@ func (s *MatcherSuite) SetUpTest(c *C) {
func (s *MatcherSuite) TestDir_ReadPatterns(c *C) {
ps, err := ReadPatterns(s.GFS, nil)
c.Assert(err, IsNil)
- c.Assert(ps, HasLen, 3)
+ c.Assert(ps, HasLen, 4)
m := NewMatcher(ps)
+ c.Assert(m.Match([]string{"exclude.crlf"}, true), Equals, true)
c.Assert(m.Match([]string{"ignore.crlf"}, true), Equals, true)
c.Assert(m.Match([]string{"vendor", "gopkg.in"}, true), Equals, true)
c.Assert(m.Match([]string{"vendor", "github.com"}, true), Equals, false)
diff --git a/plumbing/format/idxfile/decoder.go b/plumbing/format/idxfile/decoder.go
index 7768bd6..51a3904 100644
--- a/plumbing/format/idxfile/decoder.go
+++ b/plumbing/format/idxfile/decoder.go
@@ -12,9 +12,9 @@ import (
var (
// ErrUnsupportedVersion is returned by Decode when the idx file version
// is not supported.
- ErrUnsupportedVersion = errors.New("Unsupported version")
+ ErrUnsupportedVersion = errors.New("unsupported version")
// ErrMalformedIdxFile is returned by Decode when the idx file is corrupted.
- ErrMalformedIdxFile = errors.New("Malformed IDX file")
+ ErrMalformedIdxFile = errors.New("malformed IDX file")
)
const (
diff --git a/plumbing/format/index/encoder.go b/plumbing/format/index/encoder.go
index 00d4e7a..2c94d93 100644
--- a/plumbing/format/index/encoder.go
+++ b/plumbing/format/index/encoder.go
@@ -14,7 +14,7 @@ import (
var (
// EncodeVersionSupported is the range of supported index versions
- EncodeVersionSupported uint32 = 2
+ EncodeVersionSupported uint32 = 3
// ErrInvalidTimestamp is returned by Encode if a Index with a Entry with
// negative timestamp values
@@ -36,9 +36,9 @@ func NewEncoder(w io.Writer) *Encoder {
// Encode writes the Index to the stream of the encoder.
func (e *Encoder) Encode(idx *Index) error {
- // TODO: support versions v3 and v4
+ // TODO: support v4
// TODO: support extensions
- if idx.Version != EncodeVersionSupported {
+ if idx.Version > EncodeVersionSupported {
return ErrUnsupportedVersion
}
@@ -68,8 +68,12 @@ func (e *Encoder) encodeEntries(idx *Index) error {
if err := e.encodeEntry(entry); err != nil {
return err
}
+ entryLength := entryHeaderLength
+ if entry.IntentToAdd || entry.SkipWorktree {
+ entryLength += 2
+ }
- wrote := entryHeaderLength + len(entry.Name)
+ wrote := entryLength + len(entry.Name)
if err := e.padEntry(wrote); err != nil {
return err
}
@@ -79,10 +83,6 @@ func (e *Encoder) encodeEntries(idx *Index) error {
}
func (e *Encoder) encodeEntry(entry *Entry) error {
- if entry.IntentToAdd || entry.SkipWorktree {
- return ErrUnsupportedVersion
- }
-
sec, nsec, err := e.timeToUint32(&entry.CreatedAt)
if err != nil {
return err
@@ -110,9 +110,25 @@ func (e *Encoder) encodeEntry(entry *Entry) error {
entry.GID,
entry.Size,
entry.Hash[:],
- flags,
}
+ flagsFlow := []interface{}{flags}
+
+ if entry.IntentToAdd || entry.SkipWorktree {
+ var extendedFlags uint16
+
+ if entry.IntentToAdd {
+ extendedFlags |= intentToAddMask
+ }
+ if entry.SkipWorktree {
+ extendedFlags |= skipWorkTreeMask
+ }
+
+ flagsFlow = []interface{}{flags | entryExtended, extendedFlags}
+ }
+
+ flow = append(flow, flagsFlow...)
+
if err := binary.Write(e.w, flow...); err != nil {
return err
}
diff --git a/plumbing/format/index/encoder_test.go b/plumbing/format/index/encoder_test.go
index b7a73cb..25c24f1 100644
--- a/plumbing/format/index/encoder_test.go
+++ b/plumbing/format/index/encoder_test.go
@@ -57,7 +57,7 @@ func (s *IndexSuite) TestEncode(c *C) {
}
func (s *IndexSuite) TestEncodeUnsupportedVersion(c *C) {
- idx := &Index{Version: 3}
+ idx := &Index{Version: 4}
buf := bytes.NewBuffer(nil)
e := NewEncoder(buf)
@@ -67,24 +67,40 @@ func (s *IndexSuite) TestEncodeUnsupportedVersion(c *C) {
func (s *IndexSuite) TestEncodeWithIntentToAddUnsupportedVersion(c *C) {
idx := &Index{
- Version: 2,
+ Version: 3,
Entries: []*Entry{{IntentToAdd: true}},
}
buf := bytes.NewBuffer(nil)
e := NewEncoder(buf)
err := e.Encode(idx)
- c.Assert(err, Equals, ErrUnsupportedVersion)
+ c.Assert(err, IsNil)
+
+ output := &Index{}
+ d := NewDecoder(buf)
+ err = d.Decode(output)
+ c.Assert(err, IsNil)
+
+ c.Assert(cmp.Equal(idx, output), Equals, true)
+ c.Assert(output.Entries[0].IntentToAdd, Equals, true)
}
func (s *IndexSuite) TestEncodeWithSkipWorktreeUnsupportedVersion(c *C) {
idx := &Index{
- Version: 2,
+ Version: 3,
Entries: []*Entry{{SkipWorktree: true}},
}
buf := bytes.NewBuffer(nil)
e := NewEncoder(buf)
err := e.Encode(idx)
- c.Assert(err, Equals, ErrUnsupportedVersion)
+ c.Assert(err, IsNil)
+
+ output := &Index{}
+ d := NewDecoder(buf)
+ err = d.Decode(output)
+ c.Assert(err, IsNil)
+
+ c.Assert(cmp.Equal(idx, output), Equals, true)
+ c.Assert(output.Entries[0].SkipWorktree, Equals, true)
}
diff --git a/plumbing/format/index/index.go b/plumbing/format/index/index.go
index 649416a..f4c7647 100644
--- a/plumbing/format/index/index.go
+++ b/plumbing/format/index/index.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"path/filepath"
+ "strings"
"time"
"github.com/go-git/go-git/v5/plumbing"
@@ -211,3 +212,20 @@ type EndOfIndexEntry struct {
// their contents).
Hash plumbing.Hash
}
+
+// SkipUnless applies patterns in the form of A, A/B, A/B/C
+// to the index to prevent the files from being checked out
+func (i *Index) SkipUnless(patterns []string) {
+ for _, e := range i.Entries {
+ var include bool
+ for _, pattern := range patterns {
+ if strings.HasPrefix(e.Name, pattern) {
+ include = true
+ break
+ }
+ }
+ if !include {
+ e.SkipWorktree = true
+ }
+ }
+}
diff --git a/plumbing/format/objfile/reader.go b/plumbing/format/objfile/reader.go
index b6b2ca0..d7932f4 100644
--- a/plumbing/format/objfile/reader.go
+++ b/plumbing/format/objfile/reader.go
@@ -1,13 +1,13 @@
package objfile
import (
- "compress/zlib"
"errors"
"io"
"strconv"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/packfile"
+ "github.com/go-git/go-git/v5/utils/sync"
)
var (
@@ -20,20 +20,22 @@ var (
// Reader implements io.ReadCloser. Close should be called when finished with
// the Reader. Close will not close the underlying io.Reader.
type Reader struct {
- multi io.Reader
- zlib io.ReadCloser
- hasher plumbing.Hasher
+ multi io.Reader
+ zlib io.Reader
+ zlibref sync.ZLibReader
+ hasher plumbing.Hasher
}
// NewReader returns a new Reader reading from r.
func NewReader(r io.Reader) (*Reader, error) {
- zlib, err := zlib.NewReader(r)
+ zlib, err := sync.GetZlibReader(r)
if err != nil {
return nil, packfile.ErrZLib.AddDetails(err.Error())
}
return &Reader{
- zlib: zlib,
+ zlib: zlib.Reader,
+ zlibref: zlib,
}, nil
}
@@ -110,5 +112,6 @@ func (r *Reader) Hash() plumbing.Hash {
// Close releases any resources consumed by the Reader. Calling Close does not
// close the wrapped io.Reader originally passed to NewReader.
func (r *Reader) Close() error {
- return r.zlib.Close()
+ sync.PutZlibReader(r.zlibref)
+ return nil
}
diff --git a/plumbing/format/objfile/writer.go b/plumbing/format/objfile/writer.go
index 2a96a43..0d0f154 100644
--- a/plumbing/format/objfile/writer.go
+++ b/plumbing/format/objfile/writer.go
@@ -7,6 +7,7 @@ import (
"strconv"
"github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/utils/sync"
)
var (
@@ -18,9 +19,9 @@ var (
// not close the underlying io.Writer.
type Writer struct {
raw io.Writer
- zlib io.WriteCloser
hasher plumbing.Hasher
multi io.Writer
+ zlib *zlib.Writer
closed bool
pending int64 // number of unwritten bytes
@@ -31,9 +32,10 @@ type Writer struct {
// The returned Writer implements io.WriteCloser. Close should be called when
// finished with the Writer. Close will not close the underlying io.Writer.
func NewWriter(w io.Writer) *Writer {
+ zlib := sync.GetZlibWriter(w)
return &Writer{
raw: w,
- zlib: zlib.NewWriter(w),
+ zlib: zlib,
}
}
@@ -100,6 +102,7 @@ func (w *Writer) Hash() plumbing.Hash {
// Calling Close does not close the wrapped io.Writer originally passed to
// NewWriter.
func (w *Writer) Close() error {
+ defer sync.PutZlibWriter(w.zlib)
if err := w.zlib.Close(); err != nil {
return err
}
diff --git a/plumbing/format/packfile/common.go b/plumbing/format/packfile/common.go
index df423ad..36c5ef5 100644
--- a/plumbing/format/packfile/common.go
+++ b/plumbing/format/packfile/common.go
@@ -1,10 +1,7 @@
package packfile
import (
- "bytes"
- "compress/zlib"
"io"
- "sync"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/utils/ioutil"
@@ -61,18 +58,3 @@ func WritePackfileToObjectStorage(
return err
}
-
-var bufPool = sync.Pool{
- New: func() interface{} {
- return bytes.NewBuffer(nil)
- },
-}
-
-var zlibInitBytes = []byte{0x78, 0x9c, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01}
-
-var zlibReaderPool = sync.Pool{
- New: func() interface{} {
- r, _ := zlib.NewReader(bytes.NewReader(zlibInitBytes))
- return r
- },
-}
diff --git a/plumbing/format/packfile/diff_delta.go b/plumbing/format/packfile/diff_delta.go
index 1951b34..2c7a335 100644
--- a/plumbing/format/packfile/diff_delta.go
+++ b/plumbing/format/packfile/diff_delta.go
@@ -5,6 +5,7 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/go-git/go-git/v5/utils/sync"
)
// See https://github.com/jelmer/dulwich/blob/master/dulwich/pack.py and
@@ -43,18 +44,16 @@ func getDelta(index *deltaIndex, base, target plumbing.EncodedObject) (o plumbin
defer ioutil.CheckClose(tr, &err)
- bb := bufPool.Get().(*bytes.Buffer)
- defer bufPool.Put(bb)
- bb.Reset()
+ bb := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(bb)
_, err = bb.ReadFrom(br)
if err != nil {
return nil, err
}
- tb := bufPool.Get().(*bytes.Buffer)
- defer bufPool.Put(tb)
- tb.Reset()
+ tb := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(tb)
_, err = tb.ReadFrom(tr)
if err != nil {
@@ -80,9 +79,8 @@ 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 := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(buf)
buf.Write(deltaEncodeSize(len(src)))
buf.Write(deltaEncodeSize(len(tgt)))
@@ -90,9 +88,8 @@ func diffDelta(index *deltaIndex, src []byte, tgt []byte) []byte {
index.init(src)
}
- ibuf := bufPool.Get().(*bytes.Buffer)
- defer bufPool.Put(ibuf)
- ibuf.Reset()
+ ibuf := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(ibuf)
for i := 0; i < len(tgt); i++ {
offset, l := index.findMatch(src, tgt, i)
diff --git a/plumbing/format/packfile/fsobject.go b/plumbing/format/packfile/fsobject.go
index a395d17..238339d 100644
--- a/plumbing/format/packfile/fsobject.go
+++ b/plumbing/format/packfile/fsobject.go
@@ -13,7 +13,6 @@ import (
// FSObject is an object from the packfile on the filesystem.
type FSObject struct {
hash plumbing.Hash
- h *ObjectHeader
offset int64
size int64
typ plumbing.ObjectType
@@ -118,17 +117,3 @@ func (o *FSObject) Type() plumbing.ObjectType {
func (o *FSObject) Writer() (io.WriteCloser, error) {
return nil, nil
}
-
-type objectReader struct {
- io.ReadCloser
- f billy.File
-}
-
-func (r *objectReader) Close() error {
- if err := r.ReadCloser.Close(); err != nil {
- _ = r.f.Close()
- return err
- }
-
- return r.f.Close()
-}
diff --git a/plumbing/format/packfile/packfile.go b/plumbing/format/packfile/packfile.go
index 8dd6041..6852702 100644
--- a/plumbing/format/packfile/packfile.go
+++ b/plumbing/format/packfile/packfile.go
@@ -2,7 +2,6 @@ package packfile
import (
"bytes"
- "compress/zlib"
"fmt"
"io"
"os"
@@ -13,6 +12,7 @@ import (
"github.com/go-git/go-git/v5/plumbing/format/idxfile"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/go-git/go-git/v5/utils/sync"
)
var (
@@ -138,9 +138,8 @@ func (p *Packfile) getObjectSize(h *ObjectHeader) (int64, error) {
case plumbing.CommitObject, plumbing.TreeObject, plumbing.BlobObject, plumbing.TagObject:
return h.Length, nil
case plumbing.REFDeltaObject, plumbing.OFSDeltaObject:
- buf := bufPool.Get().(*bytes.Buffer)
- defer bufPool.Put(buf)
- buf.Reset()
+ buf := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(buf)
if _, _, err := p.s.NextObject(buf); err != nil {
return 0, err
@@ -227,9 +226,9 @@ func (p *Packfile) getNextObject(h *ObjectHeader, hash plumbing.Hash) (plumbing.
// For delta objects we read the delta data and apply the small object
// 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()
+ buf := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(buf)
+
if _, _, err := p.s.NextObject(buf); err != nil {
return nil, err
}
@@ -290,14 +289,13 @@ func (p *Packfile) getObjectContent(offset int64) (io.ReadCloser, error) {
func asyncReader(p *Packfile) (io.ReadCloser, error) {
reader := ioutil.NewReaderUsingReaderAt(p.file, p.s.r.offset)
- zr := zlibReaderPool.Get().(io.ReadCloser)
-
- if err := zr.(zlib.Resetter).Reset(reader, nil); err != nil {
+ zr, err := sync.GetZlibReader(reader)
+ if err != nil {
return nil, fmt.Errorf("zlib reset error: %s", err)
}
- return ioutil.NewReadCloserWithCloser(zr, func() error {
- zlibReaderPool.Put(zr)
+ return ioutil.NewReadCloserWithCloser(zr.Reader, func() error {
+ sync.PutZlibReader(zr)
return nil
}), nil
@@ -373,9 +371,9 @@ func (p *Packfile) fillRegularObjectContent(obj plumbing.EncodedObject) (err err
}
func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plumbing.Hash) error {
- buf := bufPool.Get().(*bytes.Buffer)
- defer bufPool.Put(buf)
- buf.Reset()
+ buf := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(buf)
+
_, _, err := p.s.NextObject(buf)
if err != nil {
return err
@@ -417,9 +415,9 @@ 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()
+ buf := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(buf)
+
_, _, err := p.s.NextObject(buf)
if err != nil {
return err
diff --git a/plumbing/format/packfile/packfile_test.go b/plumbing/format/packfile/packfile_test.go
index 6af8817..2eb099d 100644
--- a/plumbing/format/packfile/packfile_test.go
+++ b/plumbing/format/packfile/packfile_test.go
@@ -8,7 +8,6 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/idxfile"
"github.com/go-git/go-git/v5/plumbing/format/packfile"
- "github.com/go-git/go-git/v5/plumbing/storer"
. "gopkg.in/check.v1"
)
@@ -236,22 +235,6 @@ var expectedHashes = []string{
"7e59600739c96546163833214c36459e324bad0a",
}
-func assertObjects(c *C, s storer.EncodedObjectStorer, expects []string) {
- i, err := s.IterEncodedObjects(plumbing.AnyObject)
- c.Assert(err, IsNil)
-
- var count int
- err = i.ForEach(func(plumbing.EncodedObject) error { count++; return nil })
- c.Assert(err, IsNil)
- c.Assert(count, Equals, len(expects))
-
- for _, exp := range expects {
- obt, err := s.EncodedObject(plumbing.AnyObject, plumbing.NewHash(exp))
- c.Assert(err, IsNil)
- c.Assert(obt.Hash().String(), Equals, exp)
- }
-}
-
func getIndexFromIdxFile(r io.Reader) idxfile.Index {
idx := idxfile.NewMemoryIndex()
if err := idxfile.NewDecoder(r).Decode(idx); err != nil {
diff --git a/plumbing/format/packfile/parser.go b/plumbing/format/packfile/parser.go
index 4b5a570..522c146 100644
--- a/plumbing/format/packfile/parser.go
+++ b/plumbing/format/packfile/parser.go
@@ -10,6 +10,7 @@ import (
"github.com/go-git/go-git/v5/plumbing/cache"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/go-git/go-git/v5/utils/sync"
)
var (
@@ -46,7 +47,6 @@ type Parser struct {
oi []*objectInfo
oiByHash map[plumbing.Hash]*objectInfo
oiByOffset map[int64]*objectInfo
- hashOffset map[plumbing.Hash]int64
checksum plumbing.Hash
cache *cache.BufferLRU
@@ -176,7 +176,8 @@ func (p *Parser) init() error {
}
func (p *Parser) indexObjects() error {
- buf := new(bytes.Buffer)
+ buf := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(buf)
for i := uint32(0); i < p.count; i++ {
buf.Reset()
@@ -220,6 +221,7 @@ func (p *Parser) indexObjects() error {
ota = newBaseObject(oh.Offset, oh.Length, t)
}
+ buf.Grow(int(oh.Length))
_, crc, err := p.scanner.NextObject(buf)
if err != nil {
return err
@@ -265,7 +267,9 @@ func (p *Parser) indexObjects() error {
}
func (p *Parser) resolveDeltas() error {
- buf := &bytes.Buffer{}
+ buf := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(buf)
+
for _, obj := range p.oi {
buf.Reset()
err := p.get(obj, buf)
@@ -287,6 +291,7 @@ func (p *Parser) resolveDeltas() error {
if err := p.resolveObject(stdioutil.Discard, child, content); err != nil {
return err
}
+ p.resolveExternalRef(child)
}
// Remove the delta from the cache.
@@ -299,6 +304,16 @@ func (p *Parser) resolveDeltas() error {
return nil
}
+func (p *Parser) resolveExternalRef(o *objectInfo) {
+ if ref, ok := p.oiByHash[o.SHA1]; ok && ref.ExternalRef {
+ p.oiByHash[o.SHA1] = o
+ o.Children = ref.Children
+ for _, c := range o.Children {
+ c.Parent = o
+ }
+ }
+}
+
func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) {
if !o.ExternalRef { // skip cache check for placeholder parents
b, ok := p.cache.Get(o.Offset)
@@ -336,9 +351,8 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) {
}
if o.DiskType.IsDelta() {
- b := bufPool.Get().(*bytes.Buffer)
- defer bufPool.Put(b)
- b.Reset()
+ b := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(b)
err := p.get(o.Parent, b)
if err != nil {
return err
@@ -372,9 +386,8 @@ func (p *Parser) resolveObject(
if !o.DiskType.IsDelta() {
return nil
}
- buf := bufPool.Get().(*bytes.Buffer)
- defer bufPool.Put(buf)
- buf.Reset()
+ buf := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(buf)
err := p.readData(buf, o)
if err != nil {
return err
diff --git a/plumbing/format/packfile/parser_test.go b/plumbing/format/packfile/parser_test.go
index b0b4af8..651d05f 100644
--- a/plumbing/format/packfile/parser_test.go
+++ b/plumbing/format/packfile/parser_test.go
@@ -10,8 +10,10 @@ import (
fixtures "github.com/go-git/go-git-fixtures/v4"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/plumbing/cache"
"github.com/go-git/go-git/v5/plumbing/format/packfile"
"github.com/go-git/go-git/v5/plumbing/storer"
+ "github.com/go-git/go-git/v5/storage/filesystem"
. "gopkg.in/check.v1"
)
@@ -132,6 +134,19 @@ func (s *ParserSuite) TestThinPack(c *C) {
}
+func (s *ParserSuite) TestResolveExternalRefsInThinPack(c *C) {
+ extRefsThinPack := fixtures.ByTag("codecommit").One()
+
+ scanner := packfile.NewScanner(extRefsThinPack.Packfile())
+
+ obs := new(testObserver)
+ parser, err := packfile.NewParser(scanner, obs)
+ c.Assert(err, IsNil)
+
+ _, err = parser.Parse()
+ c.Assert(err, IsNil)
+}
+
type observerObject struct {
hash string
otype plumbing.ObjectType
@@ -235,3 +250,29 @@ func BenchmarkParseBasic(b *testing.B) {
}
}
}
+
+func BenchmarkParser(b *testing.B) {
+ f := fixtures.Basic().One()
+ defer fixtures.Clean()
+
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ b.StopTimer()
+ scanner := packfile.NewScanner(f.Packfile())
+ fs := osfs.New(os.TempDir())
+ storage := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
+
+ parser, err := packfile.NewParserWithStorage(scanner, storage)
+ if err != nil {
+ b.Error(err)
+ }
+
+ b.StartTimer()
+ _, err = parser.Parse()
+
+ b.StopTimer()
+ if err != nil {
+ b.Error(err)
+ }
+ }
+}
diff --git a/plumbing/format/packfile/patch_delta.go b/plumbing/format/packfile/patch_delta.go
index 17da11e..f00562d 100644
--- a/plumbing/format/packfile/patch_delta.go
+++ b/plumbing/format/packfile/patch_delta.go
@@ -9,6 +9,7 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/go-git/go-git/v5/utils/sync"
)
// See https://github.com/git/git/blob/49fa3dc76179e04b0833542fa52d0f287a4955ac/delta.h
@@ -34,18 +35,16 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) {
defer ioutil.CheckClose(w, &err)
- buf := bufPool.Get().(*bytes.Buffer)
- defer bufPool.Put(buf)
- buf.Reset()
+ buf := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(buf)
_, err = buf.ReadFrom(r)
if err != nil {
return err
}
src := buf.Bytes()
- dst := bufPool.Get().(*bytes.Buffer)
- defer bufPool.Put(dst)
- dst.Reset()
+ dst := sync.GetBytesBuffer()
+ defer sync.PutBytesBuffer(dst)
err = patchDelta(dst, src, delta)
if err != nil {
return err
@@ -53,9 +52,9 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) {
target.SetSize(int64(dst.Len()))
- b := byteSlicePool.Get().([]byte)
- _, err = io.CopyBuffer(w, dst, b)
- byteSlicePool.Put(b)
+ b := sync.GetByteSlice()
+ _, err = io.CopyBuffer(w, dst, *b)
+ sync.PutByteSlice(b)
return err
}
diff --git a/plumbing/format/packfile/scanner.go b/plumbing/format/packfile/scanner.go
index 5d9e8fb..9ebb84a 100644
--- a/plumbing/format/packfile/scanner.go
+++ b/plumbing/format/packfile/scanner.go
@@ -3,17 +3,16 @@ package packfile
import (
"bufio"
"bytes"
- "compress/zlib"
"fmt"
"hash"
"hash/crc32"
"io"
stdioutil "io/ioutil"
- "sync"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/utils/binary"
"github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/go-git/go-git/v5/utils/sync"
)
var (
@@ -114,7 +113,7 @@ func (s *Scanner) Header() (version, objects uint32, err error) {
return
}
-// readSignature reads an returns the signature field in the packfile.
+// readSignature reads a returns the signature field in the packfile.
func (s *Scanner) readSignature() ([]byte, error) {
var sig = make([]byte, 4)
if _, err := io.ReadFull(s.r, sig); err != nil {
@@ -323,14 +322,14 @@ func (s *Scanner) NextObject(w io.Writer) (written int64, crc32 uint32, err erro
// ReadObject returns a reader for the object content and an error
func (s *Scanner) ReadObject() (io.ReadCloser, error) {
s.pendingObject = nil
- zr := zlibReaderPool.Get().(io.ReadCloser)
+ zr, err := sync.GetZlibReader(s.r)
- if err := zr.(zlib.Resetter).Reset(s.r, nil); err != nil {
+ if err != nil {
return nil, fmt.Errorf("zlib reset error: %s", err)
}
- return ioutil.NewReadCloserWithCloser(zr, func() error {
- zlibReaderPool.Put(zr)
+ return ioutil.NewReadCloserWithCloser(zr.Reader, func() error {
+ sync.PutZlibReader(zr)
return nil
}), nil
}
@@ -338,26 +337,20 @@ func (s *Scanner) ReadObject() (io.ReadCloser, error) {
// ReadRegularObject reads and write a non-deltified object
// from it zlib stream in an object entry in the packfile.
func (s *Scanner) copyObject(w io.Writer) (n int64, err error) {
- zr := zlibReaderPool.Get().(io.ReadCloser)
- defer zlibReaderPool.Put(zr)
+ zr, err := sync.GetZlibReader(s.r)
+ defer sync.PutZlibReader(zr)
- if err = zr.(zlib.Resetter).Reset(s.r, nil); err != nil {
+ if err != nil {
return 0, fmt.Errorf("zlib reset error: %s", err)
}
- defer ioutil.CheckClose(zr, &err)
- buf := byteSlicePool.Get().([]byte)
- n, err = io.CopyBuffer(w, zr, buf)
- byteSlicePool.Put(buf)
+ defer ioutil.CheckClose(zr.Reader, &err)
+ buf := sync.GetByteSlice()
+ n, err = io.CopyBuffer(w, zr.Reader, *buf)
+ sync.PutByteSlice(buf)
return
}
-var byteSlicePool = sync.Pool{
- New: func() interface{} {
- return make([]byte, 32*1024)
- },
-}
-
// SeekFromStart sets a new offset from start, returns the old position before
// the change.
func (s *Scanner) SeekFromStart(offset int64) (previous int64, err error) {
@@ -387,9 +380,10 @@ func (s *Scanner) Checksum() (plumbing.Hash, error) {
// Close reads the reader until io.EOF
func (s *Scanner) Close() error {
- buf := byteSlicePool.Get().([]byte)
- _, err := io.CopyBuffer(stdioutil.Discard, s.r, buf)
- byteSlicePool.Put(buf)
+ buf := sync.GetByteSlice()
+ _, err := io.CopyBuffer(stdioutil.Discard, s.r, *buf)
+ sync.PutByteSlice(buf)
+
return err
}
@@ -399,13 +393,13 @@ func (s *Scanner) Flush() error {
}
// scannerReader has the following characteristics:
-// - Provides an io.SeekReader impl for bufio.Reader, when the underlying
-// reader supports it.
-// - Keeps track of the current read position, for when the underlying reader
-// isn't an io.SeekReader, but we still want to know the current offset.
-// - Writes to the hash writer what it reads, with the aid of a smaller buffer.
-// The buffer helps avoid a performance penality for performing small writes
-// to the crc32 hash writer.
+// - Provides an io.SeekReader impl for bufio.Reader, when the underlying
+// reader supports it.
+// - Keeps track of the current read position, for when the underlying reader
+// isn't an io.SeekReader, but we still want to know the current offset.
+// - Writes to the hash writer what it reads, with the aid of a smaller buffer.
+// The buffer helps avoid a performance penalty for performing small writes
+// to the crc32 hash writer.
type scannerReader struct {
reader io.Reader
crc io.Writer
diff --git a/plumbing/memory.go b/plumbing/memory.go
index 21337cc..6d11271 100644
--- a/plumbing/memory.go
+++ b/plumbing/memory.go
@@ -25,13 +25,13 @@ func (o *MemoryObject) Hash() Hash {
return o.h
}
-// Type return the ObjectType
+// Type returns the ObjectType
func (o *MemoryObject) Type() ObjectType { return o.t }
// SetType sets the ObjectType
func (o *MemoryObject) SetType(t ObjectType) { o.t = t }
-// Size return the size of the object
+// Size returns the size of the object
func (o *MemoryObject) Size() int64 { return o.sz }
// SetSize set the object size, a content of the given size should be written
diff --git a/plumbing/object/change.go b/plumbing/object/change.go
index 8b119bc..3c619df 100644
--- a/plumbing/object/change.go
+++ b/plumbing/object/change.go
@@ -39,7 +39,7 @@ func (c *Change) Action() (merkletrie.Action, error) {
return merkletrie.Modify, nil
}
-// Files return the files before and after a change.
+// Files returns the files before and after a change.
// For insertions from will be nil. For deletions to will be nil.
func (c *Change) Files() (from, to *File, err error) {
action, err := c.Action()
diff --git a/plumbing/object/change_adaptor.go b/plumbing/object/change_adaptor.go
index f701188..b96ee84 100644
--- a/plumbing/object/change_adaptor.go
+++ b/plumbing/object/change_adaptor.go
@@ -16,11 +16,11 @@ func newChange(c merkletrie.Change) (*Change, error) {
var err error
if ret.From, err = newChangeEntry(c.From); err != nil {
- return nil, fmt.Errorf("From field: %s", err)
+ return nil, fmt.Errorf("from field: %s", err)
}
if ret.To, err = newChangeEntry(c.To); err != nil {
- return nil, fmt.Errorf("To field: %s", err)
+ return nil, fmt.Errorf("to field: %s", err)
}
return ret, nil
diff --git a/plumbing/object/commit.go b/plumbing/object/commit.go
index 7a1b8e5..d2f7184 100644
--- a/plumbing/object/commit.go
+++ b/plumbing/object/commit.go
@@ -1,7 +1,6 @@
package object
import (
- "bufio"
"bytes"
"context"
"errors"
@@ -14,6 +13,7 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/go-git/go-git/v5/utils/sync"
)
const (
@@ -180,9 +180,8 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
}
defer ioutil.CheckClose(reader, &err)
- r := bufPool.Get().(*bufio.Reader)
- defer bufPool.Put(r)
- r.Reset(reader)
+ r := sync.GetBufioReader(reader)
+ defer sync.PutBufioReader(r)
var message bool
var pgpsig bool
diff --git a/plumbing/object/common.go b/plumbing/object/common.go
deleted file mode 100644
index 3591f5f..0000000
--- a/plumbing/object/common.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package object
-
-import (
- "bufio"
- "sync"
-)
-
-var bufPool = sync.Pool{
- New: func() interface{} {
- return bufio.NewReader(nil)
- },
-}
diff --git a/plumbing/object/patch.go b/plumbing/object/patch.go
index 56b62c1..06bc35b 100644
--- a/plumbing/object/patch.go
+++ b/plumbing/object/patch.go
@@ -96,10 +96,6 @@ func filePatchWithContext(ctx context.Context, c *Change) (fdiff.FilePatch, erro
}
-func filePatch(c *Change) (fdiff.FilePatch, error) {
- return filePatchWithContext(context.Background(), c)
-}
-
func fileContent(f *File) (content string, isBinary bool, err error) {
if f == nil {
return
diff --git a/plumbing/object/tag.go b/plumbing/object/tag.go
index 216010d..84066f7 100644
--- a/plumbing/object/tag.go
+++ b/plumbing/object/tag.go
@@ -1,7 +1,6 @@
package object
import (
- "bufio"
"bytes"
"fmt"
"io"
@@ -13,6 +12,7 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/go-git/go-git/v5/utils/sync"
)
// Tag represents an annotated tag object. It points to a single git object of
@@ -93,9 +93,9 @@ func (t *Tag) Decode(o plumbing.EncodedObject) (err error) {
}
defer ioutil.CheckClose(reader, &err)
- r := bufPool.Get().(*bufio.Reader)
- defer bufPool.Put(r)
- r.Reset(reader)
+ r := sync.GetBufioReader(reader)
+ defer sync.PutBufioReader(r)
+
for {
var line []byte
line, err = r.ReadBytes('\n')
diff --git a/plumbing/object/tree.go b/plumbing/object/tree.go
index 5e6378c..e9f7666 100644
--- a/plumbing/object/tree.go
+++ b/plumbing/object/tree.go
@@ -1,7 +1,6 @@
package object
import (
- "bufio"
"context"
"errors"
"fmt"
@@ -14,6 +13,7 @@ import (
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/utils/ioutil"
+ "github.com/go-git/go-git/v5/utils/sync"
)
const (
@@ -230,9 +230,9 @@ func (t *Tree) Decode(o plumbing.EncodedObject) (err error) {
}
defer ioutil.CheckClose(reader, &err)
- r := bufPool.Get().(*bufio.Reader)
- defer bufPool.Put(r)
- r.Reset(reader)
+ r := sync.GetBufioReader(reader)
+ defer sync.PutBufioReader(r)
+
for {
str, err := r.ReadString(' ')
if err != nil {
diff --git a/plumbing/object/treenoder.go b/plumbing/object/treenoder.go
index b4891b9..6e7b334 100644
--- a/plumbing/object/treenoder.go
+++ b/plumbing/object/treenoder.go
@@ -38,6 +38,10 @@ func NewTreeRootNode(t *Tree) noder.Noder {
}
}
+func (t *treeNoder) Skip() bool {
+ return false
+}
+
func (t *treeNoder) isRoot() bool {
return t.name == ""
}
diff --git a/plumbing/protocol/packp/capability/capability.go b/plumbing/protocol/packp/capability/capability.go
index 8d6a56f..b52e8a4 100644
--- a/plumbing/protocol/packp/capability/capability.go
+++ b/plumbing/protocol/packp/capability/capability.go
@@ -1,6 +1,11 @@
// Package capability defines the server and client capabilities.
package capability
+import (
+ "fmt"
+ "os"
+)
+
// Capability describes a server or client capability.
type Capability string
@@ -238,7 +243,15 @@ const (
Filter Capability = "filter"
)
-const DefaultAgent = "go-git/4.x"
+const userAgent = "go-git/5.x"
+
+// DefaultAgent provides the user agent string.
+func DefaultAgent() string {
+ if envUserAgent, ok := os.LookupEnv("GO_GIT_USER_AGENT_EXTRA"); ok {
+ return fmt.Sprintf("%s %s", userAgent, envUserAgent)
+ }
+ return userAgent
+}
var known = map[Capability]bool{
MultiACK: true, MultiACKDetailed: true, NoDone: true, ThinPack: true,
diff --git a/plumbing/protocol/packp/capability/capability_test.go b/plumbing/protocol/packp/capability/capability_test.go
new file mode 100644
index 0000000..f1fd028
--- /dev/null
+++ b/plumbing/protocol/packp/capability/capability_test.go
@@ -0,0 +1,22 @@
+package capability
+
+import (
+ "fmt"
+ "os"
+
+ check "gopkg.in/check.v1"
+)
+
+var _ = check.Suite(&SuiteCapabilities{})
+
+func (s *SuiteCapabilities) TestDefaultAgent(c *check.C) {
+ os.Unsetenv("GO_GIT_USER_AGENT_EXTRA")
+ ua := DefaultAgent()
+ c.Assert(ua, check.Equals, userAgent)
+}
+
+func (s *SuiteCapabilities) TestEnvAgent(c *check.C) {
+ os.Setenv("GO_GIT_USER_AGENT_EXTRA", "abc xyz")
+ ua := DefaultAgent()
+ c.Assert(ua, check.Equals, fmt.Sprintf("%s %s", userAgent, "abc xyz"))
+}
diff --git a/plumbing/protocol/packp/capability/list.go b/plumbing/protocol/packp/capability/list.go
index f41ec79..553d81c 100644
--- a/plumbing/protocol/packp/capability/list.go
+++ b/plumbing/protocol/packp/capability/list.go
@@ -86,7 +86,9 @@ func (l *List) Get(capability Capability) []string {
// Set sets a capability removing the previous values
func (l *List) Set(capability Capability, values ...string) error {
- delete(l.m, capability)
+ if _, ok := l.m[capability]; ok {
+ l.m[capability].Values = l.m[capability].Values[:0]
+ }
return l.Add(capability, values...)
}
diff --git a/plumbing/protocol/packp/capability/list_test.go b/plumbing/protocol/packp/capability/list_test.go
index 61b0b13..71181cb 100644
--- a/plumbing/protocol/packp/capability/list_test.go
+++ b/plumbing/protocol/packp/capability/list_test.go
@@ -122,6 +122,17 @@ func (s *SuiteCapabilities) TestSetEmpty(c *check.C) {
c.Assert(cap.Get(Agent), check.HasLen, 1)
}
+func (s *SuiteCapabilities) TestSetDuplicate(c *check.C) {
+ cap := NewList()
+ err := cap.Set(Agent, "baz")
+ c.Assert(err, check.IsNil)
+
+ err = cap.Set(Agent, "bar")
+ c.Assert(err, check.IsNil)
+
+ c.Assert(cap.String(), check.Equals, "agent=bar")
+}
+
func (s *SuiteCapabilities) TestGetEmpty(c *check.C) {
cap := NewList()
c.Assert(cap.Get(Agent), check.HasLen, 0)
diff --git a/plumbing/protocol/packp/common.go b/plumbing/protocol/packp/common.go
index ab07ac8..fef50a4 100644
--- a/plumbing/protocol/packp/common.go
+++ b/plumbing/protocol/packp/common.go
@@ -19,7 +19,6 @@ var (
// common
sp = []byte(" ")
eol = []byte("\n")
- eq = []byte{'='}
// advertised-refs
null = []byte("\x00")
diff --git a/plumbing/protocol/packp/srvresp.go b/plumbing/protocol/packp/srvresp.go
index b3a7ee8..8cd0a72 100644
--- a/plumbing/protocol/packp/srvresp.go
+++ b/plumbing/protocol/packp/srvresp.go
@@ -21,11 +21,6 @@ type ServerResponse struct {
// Decode decodes the response into the struct, isMultiACK should be true, if
// the request was done with multi_ack or multi_ack_detailed capabilities.
func (r *ServerResponse) Decode(reader *bufio.Reader, isMultiACK bool) error {
- // TODO: implement support for multi_ack or multi_ack_detailed responses
- if isMultiACK {
- return errors.New("multi_ack and multi_ack_detailed are not supported")
- }
-
s := pktline.NewScanner(reader)
for s.Scan() {
@@ -48,7 +43,23 @@ func (r *ServerResponse) Decode(reader *bufio.Reader, isMultiACK bool) error {
}
}
- return s.Err()
+ // isMultiACK is true when the remote server advertises the related
+ // capabilities when they are not in transport.UnsupportedCapabilities.
+ //
+ // Users may decide to remove multi_ack and multi_ack_detailed from the
+ // unsupported capabilities list, which allows them to do initial clones
+ // from Azure DevOps.
+ //
+ // Follow-up fetches may error, therefore errors are wrapped with additional
+ // information highlighting that this capabilities are not supported by go-git.
+ //
+ // TODO: Implement support for multi_ack or multi_ack_detailed responses.
+ err := s.Err()
+ if err != nil && isMultiACK {
+ return fmt.Errorf("multi_ack and multi_ack_detailed are not supported: %w", err)
+ }
+
+ return err
}
// stopReading detects when a valid command such as ACK or NAK is found to be
@@ -113,8 +124,9 @@ func (r *ServerResponse) decodeACKLine(line []byte) error {
}
// Encode encodes the ServerResponse into a writer.
-func (r *ServerResponse) Encode(w io.Writer) error {
- if len(r.ACKs) > 1 {
+func (r *ServerResponse) Encode(w io.Writer, isMultiACK bool) error {
+ if len(r.ACKs) > 1 && !isMultiACK {
+ // For further information, refer to comments in the Decode func above.
return errors.New("multi_ack and multi_ack_detailed are not supported")
}
diff --git a/plumbing/protocol/packp/srvresp_test.go b/plumbing/protocol/packp/srvresp_test.go
index 02fab42..aa0af52 100644
--- a/plumbing/protocol/packp/srvresp_test.go
+++ b/plumbing/protocol/packp/srvresp_test.go
@@ -72,8 +72,21 @@ func (s *ServerResponseSuite) TestDecodeMalformed(c *C) {
c.Assert(err, NotNil)
}
+// multi_ack isn't fully implemented, this ensures that Decode ignores that fact,
+// as in some circumstances that's OK to assume so.
+//
+// TODO: Review as part of multi_ack implementation.
func (s *ServerResponseSuite) TestDecodeMultiACK(c *C) {
+ raw := "" +
+ "0031ACK 1111111111111111111111111111111111111111\n" +
+ "0031ACK 6ecf0ef2c2dffb796033e5a02219af86ec6584e5\n" +
+ "00080PACK\n"
+
sr := &ServerResponse{}
- err := sr.Decode(bufio.NewReader(bytes.NewBuffer(nil)), true)
- c.Assert(err, NotNil)
+ err := sr.Decode(bufio.NewReader(bytes.NewBufferString(raw)), true)
+ c.Assert(err, IsNil)
+
+ c.Assert(sr.ACKs, HasLen, 2)
+ c.Assert(sr.ACKs[0], Equals, plumbing.NewHash("1111111111111111111111111111111111111111"))
+ c.Assert(sr.ACKs[1], Equals, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
}
diff --git a/plumbing/protocol/packp/ulreq.go b/plumbing/protocol/packp/ulreq.go
index ddec06e..344f8c7 100644
--- a/plumbing/protocol/packp/ulreq.go
+++ b/plumbing/protocol/packp/ulreq.go
@@ -95,7 +95,7 @@ func NewUploadRequestFromCapabilities(adv *capability.List) *UploadRequest {
}
if adv.Supports(capability.Agent) {
- r.Capabilities.Set(capability.Agent, capability.DefaultAgent)
+ r.Capabilities.Set(capability.Agent, capability.DefaultAgent())
}
return r
diff --git a/plumbing/protocol/packp/ulreq_test.go b/plumbing/protocol/packp/ulreq_test.go
index 0b3b616..2797a4e 100644
--- a/plumbing/protocol/packp/ulreq_test.go
+++ b/plumbing/protocol/packp/ulreq_test.go
@@ -25,7 +25,7 @@ func (s *UlReqSuite) TestNewUploadRequestFromCapabilities(c *C) {
r := NewUploadRequestFromCapabilities(cap)
c.Assert(r.Capabilities.String(), Equals,
- "multi_ack_detailed side-band-64k thin-pack ofs-delta agent=go-git/4.x",
+ "multi_ack_detailed side-band-64k thin-pack ofs-delta agent=go-git/5.x",
)
}
diff --git a/plumbing/protocol/packp/updreq.go b/plumbing/protocol/packp/updreq.go
index 4d927d8..8f39b39 100644
--- a/plumbing/protocol/packp/updreq.go
+++ b/plumbing/protocol/packp/updreq.go
@@ -19,6 +19,7 @@ var (
type ReferenceUpdateRequest struct {
Capabilities *capability.List
Commands []*Command
+ Options []*Option
Shallow *plumbing.Hash
// Packfile contains an optional packfile reader.
Packfile io.ReadCloser
@@ -58,7 +59,7 @@ func NewReferenceUpdateRequestFromCapabilities(adv *capability.List) *ReferenceU
r := NewReferenceUpdateRequest()
if adv.Supports(capability.Agent) {
- r.Capabilities.Set(capability.Agent, capability.DefaultAgent)
+ r.Capabilities.Set(capability.Agent, capability.DefaultAgent())
}
if adv.Supports(capability.ReportStatus) {
@@ -86,9 +87,9 @@ type Action string
const (
Create Action = "create"
- Update = "update"
- Delete = "delete"
- Invalid = "invalid"
+ Update Action = "update"
+ Delete Action = "delete"
+ Invalid Action = "invalid"
)
type Command struct {
@@ -120,3 +121,8 @@ func (c *Command) validate() error {
return nil
}
+
+type Option struct {
+ Key string
+ Value string
+}
diff --git a/plumbing/protocol/packp/updreq_encode.go b/plumbing/protocol/packp/updreq_encode.go
index 2545e93..1205cfa 100644
--- a/plumbing/protocol/packp/updreq_encode.go
+++ b/plumbing/protocol/packp/updreq_encode.go
@@ -9,10 +9,6 @@ import (
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
)
-var (
- zeroHashString = plumbing.ZeroHash.String()
-)
-
// Encode writes the ReferenceUpdateRequest encoding to the stream.
func (req *ReferenceUpdateRequest) Encode(w io.Writer) error {
if err := req.validate(); err != nil {
@@ -29,6 +25,12 @@ func (req *ReferenceUpdateRequest) Encode(w io.Writer) error {
return err
}
+ if req.Capabilities.Supports(capability.PushOptions) {
+ if err := req.encodeOptions(e, req.Options); err != nil {
+ return err
+ }
+ }
+
if req.Packfile != nil {
if _, err := io.Copy(w, req.Packfile); err != nil {
return err
@@ -73,3 +75,15 @@ func formatCommand(cmd *Command) string {
n := cmd.New.String()
return fmt.Sprintf("%s %s %s", o, n, cmd.Name)
}
+
+func (req *ReferenceUpdateRequest) encodeOptions(e *pktline.Encoder,
+ opts []*Option) error {
+
+ for _, opt := range opts {
+ if err := e.Encodef("%s=%s", opt.Key, opt.Value); err != nil {
+ return err
+ }
+ }
+
+ return e.Flush()
+}
diff --git a/plumbing/protocol/packp/updreq_encode_test.go b/plumbing/protocol/packp/updreq_encode_test.go
index 5ad2b1b..4370b79 100644
--- a/plumbing/protocol/packp/updreq_encode_test.go
+++ b/plumbing/protocol/packp/updreq_encode_test.go
@@ -5,9 +5,11 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/pktline"
+ "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
- . "gopkg.in/check.v1"
"io/ioutil"
+
+ . "gopkg.in/check.v1"
)
type UpdReqEncodeSuite struct{}
@@ -142,3 +144,48 @@ func (s *UpdReqEncodeSuite) TestWithPackfile(c *C) {
s.testEncode(c, r, expected)
}
+
+func (s *UpdReqEncodeSuite) TestPushOptions(c *C) {
+ hash1 := plumbing.NewHash("1ecf0ef2c2dffb796033e5a02219af86ec6584e5")
+ hash2 := plumbing.NewHash("2ecf0ef2c2dffb796033e5a02219af86ec6584e5")
+ name := plumbing.ReferenceName("myref")
+
+ r := NewReferenceUpdateRequest()
+ r.Capabilities.Set(capability.PushOptions)
+ r.Commands = []*Command{
+ {Name: name, Old: hash1, New: hash2},
+ }
+ r.Options = []*Option{
+ {Key: "SomeKey", Value: "SomeValue"},
+ {Key: "AnotherKey", Value: "AnotherValue"},
+ }
+
+ expected := pktlines(c,
+ "1ecf0ef2c2dffb796033e5a02219af86ec6584e5 2ecf0ef2c2dffb796033e5a02219af86ec6584e5 myref\x00push-options",
+ pktline.FlushString,
+ "SomeKey=SomeValue",
+ "AnotherKey=AnotherValue",
+ pktline.FlushString,
+ )
+
+ s.testEncode(c, r, expected)
+}
+
+func (s *UpdReqEncodeSuite) TestPushAtomic(c *C) {
+ hash1 := plumbing.NewHash("1ecf0ef2c2dffb796033e5a02219af86ec6584e5")
+ hash2 := plumbing.NewHash("2ecf0ef2c2dffb796033e5a02219af86ec6584e5")
+ name := plumbing.ReferenceName("myref")
+
+ r := NewReferenceUpdateRequest()
+ r.Capabilities.Set(capability.Atomic)
+ r.Commands = []*Command{
+ {Name: name, Old: hash1, New: hash2},
+ }
+
+ expected := pktlines(c,
+ "1ecf0ef2c2dffb796033e5a02219af86ec6584e5 2ecf0ef2c2dffb796033e5a02219af86ec6584e5 myref\x00atomic",
+ pktline.FlushString,
+ )
+
+ s.testEncode(c, r, expected)
+}
diff --git a/plumbing/protocol/packp/updreq_test.go b/plumbing/protocol/packp/updreq_test.go
index c4ccbaf..80e03fb 100644
--- a/plumbing/protocol/packp/updreq_test.go
+++ b/plumbing/protocol/packp/updreq_test.go
@@ -23,14 +23,14 @@ func (s *UpdReqSuite) TestNewReferenceUpdateRequestFromCapabilities(c *C) {
r := NewReferenceUpdateRequestFromCapabilities(cap)
c.Assert(r.Capabilities.String(), Equals,
- "agent=go-git/4.x report-status",
+ "agent=go-git/5.x report-status",
)
cap = capability.NewList()
cap.Set(capability.Agent, "foo")
r = NewReferenceUpdateRequestFromCapabilities(cap)
- c.Assert(r.Capabilities.String(), Equals, "agent=go-git/4.x")
+ c.Assert(r.Capabilities.String(), Equals, "agent=go-git/5.x")
cap = capability.NewList()
diff --git a/plumbing/protocol/packp/uppackreq_test.go b/plumbing/protocol/packp/uppackreq_test.go
index f723e3c..5a6eb2c 100644
--- a/plumbing/protocol/packp/uppackreq_test.go
+++ b/plumbing/protocol/packp/uppackreq_test.go
@@ -18,7 +18,7 @@ func (s *UploadPackRequestSuite) TestNewUploadPackRequestFromCapabilities(c *C)
cap.Set(capability.Agent, "foo")
r := NewUploadPackRequestFromCapabilities(cap)
- c.Assert(r.Capabilities.String(), Equals, "agent=go-git/4.x")
+ c.Assert(r.Capabilities.String(), Equals, "agent=go-git/5.x")
}
func (s *UploadPackRequestSuite) TestIsEmpty(c *C) {
diff --git a/plumbing/protocol/packp/uppackresp.go b/plumbing/protocol/packp/uppackresp.go
index a9a7192..a485cb7 100644
--- a/plumbing/protocol/packp/uppackresp.go
+++ b/plumbing/protocol/packp/uppackresp.go
@@ -24,7 +24,6 @@ type UploadPackResponse struct {
r io.ReadCloser
isShallow bool
isMultiACK bool
- isOk bool
}
// NewUploadPackResponse create a new UploadPackResponse instance, the request
@@ -79,7 +78,7 @@ func (r *UploadPackResponse) Encode(w io.Writer) (err error) {
}
}
- if err := r.ServerResponse.Encode(w); err != nil {
+ if err := r.ServerResponse.Encode(w, r.isMultiACK); err != nil {
return err
}
diff --git a/plumbing/protocol/packp/uppackresp_test.go b/plumbing/protocol/packp/uppackresp_test.go
index 260dc57..3f87804 100644
--- a/plumbing/protocol/packp/uppackresp_test.go
+++ b/plumbing/protocol/packp/uppackresp_test.go
@@ -59,6 +59,10 @@ func (s *UploadPackResponseSuite) TestDecodeMalformed(c *C) {
c.Assert(err, NotNil)
}
+// multi_ack isn't fully implemented, this ensures that Decode ignores that fact,
+// as in some circumstances that's OK to assume so.
+//
+// TODO: Review as part of multi_ack implementation.
func (s *UploadPackResponseSuite) TestDecodeMultiACK(c *C) {
req := NewUploadPackRequest()
req.Capabilities.Set(capability.MultiACK)
@@ -67,7 +71,7 @@ func (s *UploadPackResponseSuite) TestDecodeMultiACK(c *C) {
defer res.Close()
err := res.Decode(ioutil.NopCloser(bytes.NewBuffer(nil)))
- c.Assert(err, NotNil)
+ c.Assert(err, IsNil)
}
func (s *UploadPackResponseSuite) TestReadNoDecode(c *C) {
diff --git a/plumbing/reference.go b/plumbing/reference.go
index 08e908f..eef11e8 100644
--- a/plumbing/reference.go
+++ b/plumbing/reference.go
@@ -168,22 +168,22 @@ func NewHashReference(n ReferenceName, h Hash) *Reference {
}
}
-// Type return the type of a reference
+// Type returns the type of a reference
func (r *Reference) Type() ReferenceType {
return r.t
}
-// Name return the name of a reference
+// Name returns the name of a reference
func (r *Reference) Name() ReferenceName {
return r.n
}
-// Hash return the hash of a hash reference
+// Hash returns the hash of a hash reference
func (r *Reference) Hash() Hash {
return r.h
}
-// Target return the target of a symbolic reference
+// Target returns the target of a symbolic reference
func (r *Reference) Target() ReferenceName {
return r.target
}
@@ -204,6 +204,21 @@ func (r *Reference) Strings() [2]string {
}
func (r *Reference) String() string {
- s := r.Strings()
- return fmt.Sprintf("%s %s", s[1], s[0])
+ ref := ""
+ switch r.Type() {
+ case HashReference:
+ ref = r.Hash().String()
+ case SymbolicReference:
+ ref = symrefPrefix + r.Target().String()
+ default:
+ return ""
+ }
+
+ name := r.Name().String()
+ var v strings.Builder
+ v.Grow(len(ref) + len(name) + 1)
+ v.WriteString(ref)
+ v.WriteString(" ")
+ v.WriteString(name)
+ return v.String()
}
diff --git a/plumbing/reference_test.go b/plumbing/reference_test.go
index b3ccf53..e69076f 100644
--- a/plumbing/reference_test.go
+++ b/plumbing/reference_test.go
@@ -1,6 +1,10 @@
package plumbing
-import . "gopkg.in/check.v1"
+import (
+ "testing"
+
+ . "gopkg.in/check.v1"
+)
type ReferenceSuite struct{}
@@ -98,3 +102,21 @@ func (s *ReferenceSuite) TestIsTag(c *C) {
r := ReferenceName("refs/tags/v3.1.")
c.Assert(r.IsTag(), Equals, true)
}
+
+func benchMarkReferenceString(r *Reference, b *testing.B) {
+ for n := 0; n < b.N; n++ {
+ r.String()
+ }
+}
+
+func BenchmarkReferenceStringSymbolic(b *testing.B) {
+ benchMarkReferenceString(NewSymbolicReference("v3.1.1", "refs/tags/v3.1.1"), b)
+}
+
+func BenchmarkReferenceStringHash(b *testing.B) {
+ benchMarkReferenceString(NewHashReference("v3.1.1", NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")), b)
+}
+
+func BenchmarkReferenceStringInvalid(b *testing.B) {
+ benchMarkReferenceString(&Reference{}, b)
+}
diff --git a/plumbing/revlist/revlist_test.go b/plumbing/revlist/revlist_test.go
index a1ee504..9f2f93b 100644
--- a/plumbing/revlist/revlist_test.go
+++ b/plumbing/revlist/revlist_test.go
@@ -55,12 +55,6 @@ func (s *RevListSuite) SetUpTest(c *C) {
s.Storer = sto
}
-func (s *RevListSuite) commit(c *C, h plumbing.Hash) *object.Commit {
- commit, err := object.GetCommit(s.Storer, h)
- c.Assert(err, IsNil)
- return commit
-}
-
func (s *RevListSuite) TestRevListObjects_Submodules(c *C) {
submodules := map[string]bool{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5": true,
diff --git a/plumbing/storer/object.go b/plumbing/storer/object.go
index dfe309d..d8a9c27 100644
--- a/plumbing/storer/object.go
+++ b/plumbing/storer/object.go
@@ -52,8 +52,8 @@ type DeltaObjectStorer interface {
DeltaObject(plumbing.ObjectType, plumbing.Hash) (plumbing.EncodedObject, error)
}
-// Transactioner is a optional method for ObjectStorer, it enable transaction
-// base write and read operations in the storage
+// Transactioner is a optional method for ObjectStorer, it enables transactional read and write
+// operations.
type Transactioner interface {
// Begin starts a transaction.
Begin() Transaction
@@ -87,8 +87,8 @@ type PackedObjectStorer interface {
DeleteOldObjectPackAndIndex(plumbing.Hash, time.Time) error
}
-// PackfileWriter is a optional method for ObjectStorer, it enable direct write
-// of packfile to the storage
+// PackfileWriter is an optional method for ObjectStorer, it enables directly writing
+// a packfile to storage.
type PackfileWriter interface {
// PackfileWriter returns a writer for writing a packfile to the storage
//
diff --git a/plumbing/transport/client/client_test.go b/plumbing/transport/client/client_test.go
index 9ebe113..92db525 100644
--- a/plumbing/transport/client/client_test.go
+++ b/plumbing/transport/client/client_test.go
@@ -1,7 +1,6 @@
package client
import (
- "fmt"
"net/http"
"testing"
@@ -68,7 +67,3 @@ func (*dummyClient) NewReceivePackSession(*transport.Endpoint, transport.AuthMet
transport.ReceivePackSession, error) {
return nil, nil
}
-
-func typeAsString(v interface{}) string {
- return fmt.Sprintf("%T", v)
-}
diff --git a/plumbing/transport/common.go b/plumbing/transport/common.go
index a9ee2ca..a2a78f0 100644
--- a/plumbing/transport/common.go
+++ b/plumbing/transport/common.go
@@ -112,7 +112,7 @@ type Endpoint struct {
Port int
// Path is the repository path.
Path string
- // InsecureSkipTLS skips ssl verify if protocal is https
+ // InsecureSkipTLS skips ssl verify if protocol is https
InsecureSkipTLS bool
// CaBundle specify additional ca bundle with system cert pool
CaBundle []byte
diff --git a/plumbing/transport/git/common.go b/plumbing/transport/git/common.go
index 306aae2..c18d600 100644
--- a/plumbing/transport/git/common.go
+++ b/plumbing/transport/git/common.go
@@ -77,14 +77,14 @@ func (c *command) StderrPipe() (io.Reader, error) {
return nil, nil
}
-// StdinPipe return the underlying connection as WriteCloser, wrapped to prevent
+// StdinPipe returns the underlying connection as WriteCloser, wrapped to prevent
// call to the Close function from the connection, a command execution in git
// protocol can't be closed or killed
func (c *command) StdinPipe() (io.WriteCloser, error) {
return ioutil.WriteNopCloser(c.conn), nil
}
-// StdoutPipe return the underlying connection as Reader
+// StdoutPipe returns the underlying connection as Reader
func (c *command) StdoutPipe() (io.Reader, error) {
return c.conn, nil
}
diff --git a/plumbing/transport/internal/common/common.go b/plumbing/transport/internal/common/common.go
index fdb148f..d0e9a29 100644
--- a/plumbing/transport/internal/common/common.go
+++ b/plumbing/transport/internal/common/common.go
@@ -428,11 +428,6 @@ func isRepoNotFoundError(s string) bool {
return false
}
-var (
- nak = []byte("NAK")
- eol = []byte("\n")
-)
-
// uploadPack implements the git-upload-pack protocol.
func uploadPack(w io.WriteCloser, r io.Reader, req *packp.UploadPackRequest) error {
// TODO support multi_ack mode
diff --git a/plumbing/transport/server/server.go b/plumbing/transport/server/server.go
index 8ab70fe..11fa0c8 100644
--- a/plumbing/transport/server/server.go
+++ b/plumbing/transport/server/server.go
@@ -189,7 +189,7 @@ func (s *upSession) objectsToUpload(req *packp.UploadPackRequest) ([]plumbing.Ha
}
func (*upSession) setSupportedCapabilities(c *capability.List) error {
- if err := c.Set(capability.Agent, capability.DefaultAgent); err != nil {
+ if err := c.Set(capability.Agent, capability.DefaultAgent()); err != nil {
return err
}
@@ -355,7 +355,7 @@ func (s *rpSession) reportStatus() *packp.ReportStatus {
}
func (*rpSession) setSupportedCapabilities(c *capability.List) error {
- if err := c.Set(capability.Agent, capability.DefaultAgent); err != nil {
+ if err := c.Set(capability.Agent, capability.DefaultAgent()); err != nil {
return err
}
diff --git a/plumbing/transport/ssh/auth_method.go b/plumbing/transport/ssh/auth_method.go
index 3514669..9d3bcd3 100644
--- a/plumbing/transport/ssh/auth_method.go
+++ b/plumbing/transport/ssh/auth_method.go
@@ -10,10 +10,9 @@ import (
"github.com/go-git/go-git/v5/plumbing/transport"
- "github.com/mitchellh/go-homedir"
+ "github.com/skeema/knownhosts"
sshagent "github.com/xanzy/ssh-agent"
"golang.org/x/crypto/ssh"
- "golang.org/x/crypto/ssh/knownhosts"
)
const DefaultUsername = "git"
@@ -44,7 +43,6 @@ const (
type KeyboardInteractive struct {
User string
Challenge ssh.KeyboardInteractiveChallenge
- HostKeyCallbackHelper
}
func (a *KeyboardInteractive) Name() string {
@@ -56,19 +54,18 @@ func (a *KeyboardInteractive) String() string {
}
func (a *KeyboardInteractive) ClientConfig() (*ssh.ClientConfig, error) {
- return a.SetHostKeyCallback(&ssh.ClientConfig{
+ return &ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{
a.Challenge,
},
- })
+ }, nil
}
// Password implements AuthMethod by using the given password.
type Password struct {
User string
Password string
- HostKeyCallbackHelper
}
func (a *Password) Name() string {
@@ -80,10 +77,10 @@ func (a *Password) String() string {
}
func (a *Password) ClientConfig() (*ssh.ClientConfig, error) {
- return a.SetHostKeyCallback(&ssh.ClientConfig{
+ return &ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.Password(a.Password)},
- })
+ }, nil
}
// PasswordCallback implements AuthMethod by using a callback
@@ -91,7 +88,6 @@ func (a *Password) ClientConfig() (*ssh.ClientConfig, error) {
type PasswordCallback struct {
User string
Callback func() (pass string, err error)
- HostKeyCallbackHelper
}
func (a *PasswordCallback) Name() string {
@@ -103,17 +99,16 @@ func (a *PasswordCallback) String() string {
}
func (a *PasswordCallback) ClientConfig() (*ssh.ClientConfig, error) {
- return a.SetHostKeyCallback(&ssh.ClientConfig{
+ return &ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PasswordCallback(a.Callback)},
- })
+ }, nil
}
// PublicKeys implements AuthMethod by using the given key pairs.
type PublicKeys struct {
User string
Signer ssh.Signer
- HostKeyCallbackHelper
}
// NewPublicKeys returns a PublicKeys from a PEM encoded private key. An
@@ -152,10 +147,10 @@ func (a *PublicKeys) String() string {
}
func (a *PublicKeys) ClientConfig() (*ssh.ClientConfig, error) {
- return a.SetHostKeyCallback(&ssh.ClientConfig{
+ return &ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PublicKeys(a.Signer)},
- })
+ }, nil
}
func username() (string, error) {
@@ -178,7 +173,6 @@ func username() (string, error) {
type PublicKeysCallback struct {
User string
Callback func() (signers []ssh.Signer, err error)
- HostKeyCallbackHelper
}
// NewSSHAgentAuth returns a PublicKeysCallback based on a SSH agent, it opens
@@ -213,10 +207,10 @@ func (a *PublicKeysCallback) String() string {
}
func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
- return a.SetHostKeyCallback(&ssh.ClientConfig{
+ return &ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Callback)},
- })
+ }, nil
}
// NewKnownHostsCallback returns ssh.HostKeyCallback based on a file based on a
@@ -224,12 +218,19 @@ func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
//
// If list of files is empty, then it will be read from the SSH_KNOWN_HOSTS
// environment variable, example:
-// /home/foo/custom_known_hosts_file:/etc/custom_known/hosts_file
+//
+// /home/foo/custom_known_hosts_file:/etc/custom_known/hosts_file
//
// If SSH_KNOWN_HOSTS is not set the following file locations will be used:
-// ~/.ssh/known_hosts
-// /etc/ssh/ssh_known_hosts
+//
+// ~/.ssh/known_hosts
+// /etc/ssh/ssh_known_hosts
func NewKnownHostsCallback(files ...string) (ssh.HostKeyCallback, error) {
+ kh, err := newKnownHosts(files...)
+ return ssh.HostKeyCallback(kh), err
+}
+
+func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
var err error
if len(files) == 0 {
@@ -251,7 +252,7 @@ func getDefaultKnownHostsFiles() ([]string, error) {
return files, nil
}
- homeDirPath, err := homedir.Dir()
+ homeDirPath, err := os.UserHomeDir()
if err != nil {
return nil, err
}
@@ -285,6 +286,9 @@ func filterKnownHostsFiles(files ...string) ([]string, error) {
// HostKeyCallbackHelper is a helper that provides common functionality to
// configure HostKeyCallback into a ssh.ClientConfig.
+// Deprecated in favor of SetConfigHostKeyFields (see common.go) which provides
+// a mechanism for also setting ClientConfig.HostKeyAlgorithms for a specific
+// host.
type HostKeyCallbackHelper struct {
// HostKeyCallback is the function type used for verifying server keys.
// If nil default callback will be create using NewKnownHostsCallback
diff --git a/plumbing/transport/ssh/common.go b/plumbing/transport/ssh/common.go
index 46e7913..4b9ac07 100644
--- a/plumbing/transport/ssh/common.go
+++ b/plumbing/transport/ssh/common.go
@@ -121,10 +121,15 @@ func (c *command) connect() error {
if err != nil {
return err
}
+ hostWithPort := c.getHostWithPort()
+ config, err = SetConfigHostKeyFields(config, hostWithPort)
+ if err != nil {
+ return err
+ }
overrideConfig(c.config, config)
- c.client, err = dial("tcp", c.getHostWithPort(), config)
+ c.client, err = dial("tcp", hostWithPort, config)
if err != nil {
return err
}
@@ -162,6 +167,23 @@ func dial(network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
return ssh.NewClient(c, chans, reqs), nil
}
+// SetConfigHostKeyFields sets cfg.HostKeyCallback and cfg.HostKeyAlgorithms
+// based on OpenSSH known_hosts. cfg is modified in-place. hostWithPort must be
+// supplied, since the algorithms will be set based on the known host keys for
+// that specific host. Otherwise, golang.org/x/crypto/ssh can return an error
+// upon connecting to a host whose *first* key is not known, even though other
+// keys (of different types) are known and match properly.
+// For background see https://github.com/go-git/go-git/issues/411 as well as
+// https://github.com/golang/go/issues/29286 for root cause.
+func SetConfigHostKeyFields(cfg *ssh.ClientConfig, hostWithPort string) (*ssh.ClientConfig, error) {
+ kh, err := newKnownHosts()
+ if err == nil {
+ cfg.HostKeyCallback = kh.HostKeyCallback()
+ cfg.HostKeyAlgorithms = kh.HostKeyAlgorithms(hostWithPort)
+ }
+ return cfg, err
+}
+
func (c *command) getHostWithPort() string {
if addr, found := c.doGetHostWithPortFromSSHConfig(); found {
return addr
diff --git a/plumbing/transport/ssh/common_test.go b/plumbing/transport/ssh/common_test.go
index e04a9c5..6d634d5 100644
--- a/plumbing/transport/ssh/common_test.go
+++ b/plumbing/transport/ssh/common_test.go
@@ -7,7 +7,6 @@ import (
"github.com/kevinburke/ssh_config"
"golang.org/x/crypto/ssh"
- stdssh "golang.org/x/crypto/ssh"
. "gopkg.in/check.v1"
)
@@ -99,7 +98,7 @@ func (s *SuiteCommon) TestIssue70(c *C) {
uploadPack.SetUpSuite(c)
config := &ssh.ClientConfig{
- HostKeyCallback: stdssh.InsecureIgnoreHostKey(),
+ HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
r := &runner{
config: config,