aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/protocol
diff options
context:
space:
mode:
Diffstat (limited to 'plumbing/protocol')
-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
16 files changed, 172 insertions, 30 deletions
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) {