aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/protocol
diff options
context:
space:
mode:
authorSantiago M. Mola <santi@mola.io>2016-11-28 09:57:38 +0100
committerGitHub <noreply@github.com>2016-11-28 09:57:38 +0100
commit68893edf9ddc3de181431f1552e3b773cb66f080 (patch)
treef45e5fa18e10168a278b7d0ed7dea984ff9969f7 /plumbing/protocol
parentf9adb3565b36ba1573102f954d0ee916009efac2 (diff)
downloadgo-git-68893edf9ddc3de181431f1552e3b773cb66f080.tar.gz
remove old types from transport and use packp (#142)
* protocol: move UploadPackRequest to protocol. * UploadPackRequest is now defined as an embedding of UploadRequest and UploadHaves. * Move http encoding specific code from UploadPackRequest to transport/http. * rename UlReq to UploadRequest * packp: move AdvRefs Encoder/Decoder to Encode/Decode methods. * packp: move UploadRequest Encoder/Decoder to Encode/Decode methods. * packp: Remove transport.UploadPackInfo in favor of packp. AdvRefs.
Diffstat (limited to 'plumbing/protocol')
-rw-r--r--plumbing/protocol/packp/advrefs.go69
-rw-r--r--plumbing/protocol/packp/advrefs_decode.go51
-rw-r--r--plumbing/protocol/packp/advrefs_decode_test.go (renamed from plumbing/protocol/packp/advrefs_decoder_test.go)16
-rw-r--r--plumbing/protocol/packp/advrefs_encode.go (renamed from plumbing/protocol/packp/advrefs_encoder.go)37
-rw-r--r--plumbing/protocol/packp/advrefs_encode_test.go (renamed from plumbing/protocol/packp/advrefs_encoder_test.go)4
-rw-r--r--plumbing/protocol/packp/advrefs_test.go16
-rw-r--r--plumbing/protocol/packp/ulreq.go16
-rw-r--r--plumbing/protocol/packp/ulreq_decode.go (renamed from plumbing/protocol/packp/ulreq_decoder.go)47
-rw-r--r--plumbing/protocol/packp/ulreq_decode_test.go (renamed from plumbing/protocol/packp/ulreq_decoder_test.go)14
-rw-r--r--plumbing/protocol/packp/ulreq_encode.go (renamed from plumbing/protocol/packp/ulreq_encoder.go)37
-rw-r--r--plumbing/protocol/packp/ulreq_encode_test.go (renamed from plumbing/protocol/packp/ulreq_encoder_test.go)32
-rw-r--r--plumbing/protocol/packp/ulreq_test.go8
-rw-r--r--plumbing/protocol/packp/upload_pack_request.go75
-rw-r--r--plumbing/protocol/packp/upload_pack_request_test.go33
14 files changed, 319 insertions, 136 deletions
diff --git a/plumbing/protocol/packp/advrefs.go b/plumbing/protocol/packp/advrefs.go
index c54f9d8..b36b180 100644
--- a/plumbing/protocol/packp/advrefs.go
+++ b/plumbing/protocol/packp/advrefs.go
@@ -1,7 +1,16 @@
package packp
import (
+ "fmt"
+ "strings"
+
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/storage/memory"
+)
+
+const (
+ symref = "symref"
)
// AdvRefs values represent the information transmitted on an
@@ -38,3 +47,63 @@ func NewAdvRefs() *AdvRefs {
Shallows: []plumbing.Hash{},
}
}
+
+func (a *AdvRefs) AddReference(r *plumbing.Reference) error {
+ switch r.Type() {
+ case plumbing.SymbolicReference:
+ v := fmt.Sprintf("%s:%s", r.Name().String(), r.Target().String())
+ a.Capabilities.Add(symref, v)
+ case plumbing.HashReference:
+ a.References[r.Name().String()] = r.Hash()
+ default:
+ return plumbing.ErrInvalidType
+ }
+
+ return nil
+}
+
+func (a *AdvRefs) AllReferences() (memory.ReferenceStorage, error) {
+ s := memory.ReferenceStorage{}
+ if err := addRefs(s, a); err != nil {
+ return s, plumbing.NewUnexpectedError(err)
+ }
+
+ return s, nil
+}
+
+func addRefs(s storer.ReferenceStorer, ar *AdvRefs) error {
+ for name, hash := range ar.References {
+ ref := plumbing.NewReferenceFromStrings(name, hash.String())
+ if err := s.SetReference(ref); err != nil {
+ return err
+ }
+ }
+
+ return addSymbolicRefs(s, ar)
+}
+
+func addSymbolicRefs(s storer.ReferenceStorer, ar *AdvRefs) error {
+ if !hasSymrefs(ar) {
+ return nil
+ }
+
+ for _, symref := range ar.Capabilities.Get(symref).Values {
+ chunks := strings.Split(symref, ":")
+ if len(chunks) != 2 {
+ err := fmt.Errorf("bad number of `:` in symref value (%q)", symref)
+ return plumbing.NewUnexpectedError(err)
+ }
+ name := plumbing.ReferenceName(chunks[0])
+ target := plumbing.ReferenceName(chunks[1])
+ ref := plumbing.NewSymbolicReference(name, target)
+ if err := s.SetReference(ref); err != nil {
+ return nil
+ }
+ }
+
+ return nil
+}
+
+func hasSymrefs(ar *AdvRefs) bool {
+ return ar.Capabilities.Supports(symref)
+}
diff --git a/plumbing/protocol/packp/advrefs_decode.go b/plumbing/protocol/packp/advrefs_decode.go
index df824a9..8d37066 100644
--- a/plumbing/protocol/packp/advrefs_decode.go
+++ b/plumbing/protocol/packp/advrefs_decode.go
@@ -11,8 +11,14 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
)
-// A AdvRefsDecoder reads and decodes AdvRef values from an input stream.
-type AdvRefsDecoder struct {
+// Decode reads the next advertised-refs message form its input and
+// stores it in the AdvRefs.
+func (a *AdvRefs) Decode(r io.Reader) error {
+ d := newAdvRefsDecoder(r)
+ return d.Decode(a)
+}
+
+type advRefsDecoder struct {
s *pktline.Scanner // a pkt-line scanner from the input stream
line []byte // current pkt-line contents, use parser.nextLine() to make it advance
nLine int // current pkt-line number for debugging, begins at 1
@@ -21,21 +27,16 @@ type AdvRefsDecoder struct {
data *AdvRefs // parsed data is stored here
}
-// ErrEmpty is returned by Decode when there was no advertised-message at all
-var ErrEmpty = errors.New("empty advertised-ref message")
+// ErrEmptyAdvRefs is returned by Decode when there was no advertised-message at all
+var ErrEmptyAdvRefs = errors.New("empty advertised-ref message")
-// NewAdvRefsDecoder returns a new decoder that reads from r.
-//
-// Will not read more data from r than necessary.
-func NewAdvRefsDecoder(r io.Reader) *AdvRefsDecoder {
- return &AdvRefsDecoder{
+func newAdvRefsDecoder(r io.Reader) *advRefsDecoder {
+ return &advRefsDecoder{
s: pktline.NewScanner(r),
}
}
-// Decode reads the next advertised-refs message form its input and
-// stores it in the value pointed to by v.
-func (d *AdvRefsDecoder) Decode(v *AdvRefs) error {
+func (d *advRefsDecoder) Decode(v *AdvRefs) error {
d.data = v
for state := decodePrefix; state != nil; {
@@ -45,10 +46,10 @@ func (d *AdvRefsDecoder) Decode(v *AdvRefs) error {
return d.err
}
-type decoderStateFn func(*AdvRefsDecoder) decoderStateFn
+type decoderStateFn func(*advRefsDecoder) decoderStateFn
// fills out the parser stiky error
-func (d *AdvRefsDecoder) error(format string, a ...interface{}) {
+func (d *advRefsDecoder) error(format string, a ...interface{}) {
d.err = fmt.Errorf("pkt-line %d: %s", d.nLine,
fmt.Sprintf(format, a...))
}
@@ -57,7 +58,7 @@ func (d *AdvRefsDecoder) error(format string, a ...interface{}) {
// p.line and increments p.nLine. A successful invocation returns true,
// otherwise, false is returned and the sticky error is filled out
// accordingly. Trims eols at the end of the payloads.
-func (d *AdvRefsDecoder) nextLine() bool {
+func (d *advRefsDecoder) nextLine() bool {
d.nLine++
if !d.s.Scan() {
@@ -66,7 +67,7 @@ func (d *AdvRefsDecoder) nextLine() bool {
}
if d.nLine == 1 {
- d.err = ErrEmpty
+ d.err = ErrEmptyAdvRefs
return false
}
@@ -81,14 +82,14 @@ func (d *AdvRefsDecoder) nextLine() bool {
}
// The HTTP smart prefix is often followed by a flush-pkt.
-func decodePrefix(d *AdvRefsDecoder) decoderStateFn {
+func decodePrefix(d *advRefsDecoder) decoderStateFn {
if ok := d.nextLine(); !ok {
return nil
}
// If the repository is empty, we receive a flush here (SSH).
if isFlush(d.line) {
- d.err = ErrEmpty
+ d.err = ErrEmptyAdvRefs
return nil
}
@@ -122,10 +123,10 @@ func isFlush(payload []byte) bool {
// If the first hash is zero, then a no-refs is comming. Otherwise, a
// list-of-refs is comming, and the hash will be followed by the first
// advertised ref.
-func decodeFirstHash(p *AdvRefsDecoder) decoderStateFn {
+func decodeFirstHash(p *advRefsDecoder) decoderStateFn {
// If the repository is empty, we receive a flush here (HTTP).
if isFlush(p.line) {
- p.err = ErrEmpty
+ p.err = ErrEmptyAdvRefs
return nil
}
@@ -149,7 +150,7 @@ func decodeFirstHash(p *AdvRefsDecoder) decoderStateFn {
}
// Skips SP "capabilities^{}" NUL
-func decodeSkipNoRefs(p *AdvRefsDecoder) decoderStateFn {
+func decodeSkipNoRefs(p *advRefsDecoder) decoderStateFn {
if len(p.line) < len(noHeadMark) {
p.error("too short zero-id ref")
return nil
@@ -166,7 +167,7 @@ func decodeSkipNoRefs(p *AdvRefsDecoder) decoderStateFn {
}
// decode the refname, expectes SP refname NULL
-func decodeFirstRef(l *AdvRefsDecoder) decoderStateFn {
+func decodeFirstRef(l *advRefsDecoder) decoderStateFn {
if len(l.line) < 3 {
l.error("line too short after hash")
return nil
@@ -195,7 +196,7 @@ func decodeFirstRef(l *AdvRefsDecoder) decoderStateFn {
return decodeCaps
}
-func decodeCaps(p *AdvRefsDecoder) decoderStateFn {
+func decodeCaps(p *advRefsDecoder) decoderStateFn {
if len(p.line) == 0 {
return decodeOtherRefs
}
@@ -210,7 +211,7 @@ func decodeCaps(p *AdvRefsDecoder) decoderStateFn {
// The refs are either tips (obj-id SP refname) or a peeled (obj-id SP refname^{}).
// If there are no refs, then there might be a shallow or flush-ptk.
-func decodeOtherRefs(p *AdvRefsDecoder) decoderStateFn {
+func decodeOtherRefs(p *advRefsDecoder) decoderStateFn {
if ok := p.nextLine(); !ok {
return nil
}
@@ -253,7 +254,7 @@ func readRef(data []byte) (string, plumbing.Hash, error) {
}
// Keeps reading shallows until a flush-pkt is found
-func decodeShallow(p *AdvRefsDecoder) decoderStateFn {
+func decodeShallow(p *advRefsDecoder) decoderStateFn {
if !bytes.HasPrefix(p.line, shallow) {
p.error("malformed shallow prefix, found %q... instead", p.line[:len(shallow)])
return nil
diff --git a/plumbing/protocol/packp/advrefs_decoder_test.go b/plumbing/protocol/packp/advrefs_decode_test.go
index ee72d56..2b3da72 100644
--- a/plumbing/protocol/packp/advrefs_decoder_test.go
+++ b/plumbing/protocol/packp/advrefs_decode_test.go
@@ -18,10 +18,10 @@ var _ = Suite(&AdvRefsDecodeSuite{})
func (s *AdvRefsDecodeSuite) TestEmpty(c *C) {
ar := NewAdvRefs()
var buf bytes.Buffer
- d := NewAdvRefsDecoder(&buf)
+ d := newAdvRefsDecoder(&buf)
err := d.Decode(ar)
- c.Assert(err, Equals, ErrEmpty)
+ c.Assert(err, Equals, ErrEmptyAdvRefs)
}
func (s *AdvRefsDecodeSuite) TestEmptyFlush(c *C) {
@@ -30,10 +30,10 @@ func (s *AdvRefsDecodeSuite) TestEmptyFlush(c *C) {
e := pktline.NewEncoder(&buf)
e.Flush()
- d := NewAdvRefsDecoder(&buf)
+ d := newAdvRefsDecoder(&buf)
err := d.Decode(ar)
- c.Assert(err, Equals, ErrEmpty)
+ c.Assert(err, Equals, ErrEmptyAdvRefs)
}
func (s *AdvRefsDecodeSuite) TestEmptyPrefixFlush(c *C) {
@@ -44,10 +44,10 @@ func (s *AdvRefsDecodeSuite) TestEmptyPrefixFlush(c *C) {
e.Flush()
e.Flush()
- d := NewAdvRefsDecoder(&buf)
+ d := newAdvRefsDecoder(&buf)
err := d.Decode(ar)
- c.Assert(err, Equals, ErrEmpty)
+ c.Assert(err, Equals, ErrEmptyAdvRefs)
}
func (s *AdvRefsDecodeSuite) TestShortForHash(c *C) {
@@ -61,7 +61,7 @@ func (s *AdvRefsDecodeSuite) TestShortForHash(c *C) {
func (s *AdvRefsDecodeSuite) testDecoderErrorMatches(c *C, input io.Reader, pattern string) {
ar := NewAdvRefs()
- d := NewAdvRefsDecoder(input)
+ d := newAdvRefsDecoder(input)
err := d.Decode(ar)
c.Assert(err, ErrorMatches, pattern)
@@ -92,7 +92,7 @@ func (s *AdvRefsDecodeSuite) testDecodeOK(c *C, payloads []string) *AdvRefs {
c.Assert(err, IsNil)
ar := NewAdvRefs()
- d := NewAdvRefsDecoder(&buf)
+ d := newAdvRefsDecoder(&buf)
err = d.Decode(ar)
c.Assert(err, IsNil)
diff --git a/plumbing/protocol/packp/advrefs_encoder.go b/plumbing/protocol/packp/advrefs_encode.go
index cdba188..3c5df19 100644
--- a/plumbing/protocol/packp/advrefs_encoder.go
+++ b/plumbing/protocol/packp/advrefs_encode.go
@@ -9,26 +9,29 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
)
-// An AdvRefsEncoder writes AdvRefs values to an output stream.
-type AdvRefsEncoder struct {
+// Encode writes the AdvRefs encoding to a writer.
+//
+// All the payloads will end with a newline character. Capabilities,
+// references and shallows are writen in alphabetical order, except for
+// peeled references that always follow their corresponding references.
+func (a *AdvRefs) Encode(w io.Writer) error {
+ e := newAdvRefsEncoder(w)
+ return e.Encode(a)
+}
+
+type advRefsEncoder struct {
data *AdvRefs // data to encode
pe *pktline.Encoder // where to write the encoded data
err error // sticky error
}
-// NewAdvRefsEncoder returns a new encoder that writes to w.
-func NewAdvRefsEncoder(w io.Writer) *AdvRefsEncoder {
- return &AdvRefsEncoder{
+func newAdvRefsEncoder(w io.Writer) *advRefsEncoder {
+ return &advRefsEncoder{
pe: pktline.NewEncoder(w),
}
}
-// Encode writes the AdvRefs encoding of v to the stream.
-//
-// All the payloads will end with a newline character. Capabilities,
-// references and shallows are writen in alphabetical order, except for
-// peeled references that always follow their corresponding references.
-func (e *AdvRefsEncoder) Encode(v *AdvRefs) error {
+func (e *advRefsEncoder) Encode(v *AdvRefs) error {
e.data = v
for state := encodePrefix; state != nil; {
@@ -38,9 +41,9 @@ func (e *AdvRefsEncoder) Encode(v *AdvRefs) error {
return e.err
}
-type encoderStateFn func(*AdvRefsEncoder) encoderStateFn
+type encoderStateFn func(*advRefsEncoder) encoderStateFn
-func encodePrefix(e *AdvRefsEncoder) encoderStateFn {
+func encodePrefix(e *advRefsEncoder) encoderStateFn {
for _, p := range e.data.Prefix {
if bytes.Equal(p, pktline.Flush) {
if e.err = e.pe.Flush(); e.err != nil {
@@ -58,7 +61,7 @@ func encodePrefix(e *AdvRefsEncoder) encoderStateFn {
// Adds the first pkt-line payload: head hash, head ref and capabilities.
// Also handle the special case when no HEAD ref is found.
-func encodeFirstLine(e *AdvRefsEncoder) encoderStateFn {
+func encodeFirstLine(e *advRefsEncoder) encoderStateFn {
head := formatHead(e.data.Head)
separator := formatSeparator(e.data.Head)
capabilities := formatCaps(e.data.Capabilities)
@@ -98,7 +101,7 @@ func formatCaps(c *Capabilities) string {
// Adds the (sorted) refs: hash SP refname EOL
// and their peeled refs if any.
-func encodeRefs(e *AdvRefsEncoder) encoderStateFn {
+func encodeRefs(e *advRefsEncoder) encoderStateFn {
refs := sortRefs(e.data.References)
for _, r := range refs {
hash, _ := e.data.References[r]
@@ -127,7 +130,7 @@ func sortRefs(m map[string]plumbing.Hash) []string {
}
// Adds the (sorted) shallows: "shallow" SP hash EOL
-func encodeShallow(e *AdvRefsEncoder) encoderStateFn {
+func encodeShallow(e *advRefsEncoder) encoderStateFn {
sorted := sortShallows(e.data.Shallows)
for _, hash := range sorted {
if e.err = e.pe.Encodef("shallow %s\n", hash); e.err != nil {
@@ -148,7 +151,7 @@ func sortShallows(c []plumbing.Hash) []string {
return ret
}
-func encodeFlush(e *AdvRefsEncoder) encoderStateFn {
+func encodeFlush(e *advRefsEncoder) encoderStateFn {
e.err = e.pe.Flush()
return nil
}
diff --git a/plumbing/protocol/packp/advrefs_encoder_test.go b/plumbing/protocol/packp/advrefs_encode_test.go
index 222a267..207ce58 100644
--- a/plumbing/protocol/packp/advrefs_encoder_test.go
+++ b/plumbing/protocol/packp/advrefs_encode_test.go
@@ -16,7 +16,7 @@ var _ = Suite(&AdvRefsEncodeSuite{})
func testEncode(c *C, input *AdvRefs, expected []byte) {
var buf bytes.Buffer
- e := NewAdvRefsEncoder(&buf)
+ e := newAdvRefsEncoder(&buf)
err := e.Encode(input)
c.Assert(err, IsNil)
obtained := buf.Bytes()
@@ -231,7 +231,7 @@ func (s *AdvRefsEncodeSuite) TestErrorTooLong(c *C) {
}
var buf bytes.Buffer
- e := NewAdvRefsEncoder(&buf)
+ e := newAdvRefsEncoder(&buf)
err := e.Encode(ar)
c.Assert(err, ErrorMatches, ".*payload is too long.*")
}
diff --git a/plumbing/protocol/packp/advrefs_test.go b/plumbing/protocol/packp/advrefs_test.go
index 1a696d4..6d9f488 100644
--- a/plumbing/protocol/packp/advrefs_test.go
+++ b/plumbing/protocol/packp/advrefs_test.go
@@ -44,14 +44,10 @@ func (s *SuiteDecodeEncode) test(c *C, in []string, exp []string) {
var obtained []byte
{
ar := packp.NewAdvRefs()
- d := packp.NewAdvRefsDecoder(input)
- err = d.Decode(ar)
- c.Assert(err, IsNil)
+ c.Assert(ar.Decode(input), IsNil)
var buf bytes.Buffer
- e := packp.NewAdvRefsEncoder(&buf)
- err := e.Encode(ar)
- c.Assert(err, IsNil)
+ c.Assert(ar.Encode(&buf), IsNil)
obtained = buf.Bytes()
}
@@ -258,12 +254,9 @@ func ExampleDecoder_Decode() {
// Use the raw message as our input.
input := strings.NewReader(raw)
- // Create a Decoder reading from our input.
- d := packp.NewAdvRefsDecoder(input)
-
// Decode the input into a newly allocated AdvRefs value.
ar := packp.NewAdvRefs()
- _ = d.Decode(ar) // error check ignored for brevity
+ _ = ar.Decode(input) // error check ignored for brevity
// Do something interesting with the AdvRefs, e.g. print its contents.
fmt.Println("head =", ar.Head)
@@ -303,8 +296,7 @@ func ExampleEncoder_Encode() {
// You can encode into stdout too, but you will not be able
// see the '\x00' after "HEAD".
var buf bytes.Buffer
- e := packp.NewAdvRefsEncoder(&buf)
- _ = e.Encode(ar) // error checks ignored for brevity
+ _ = ar.Encode(&buf) // error checks ignored for brevity
// Print the contents of the buffer as a quoted string.
// Printing is as a non-quoted string will be prettier but you
diff --git a/plumbing/protocol/packp/ulreq.go b/plumbing/protocol/packp/ulreq.go
index 5870001..6ec5b96 100644
--- a/plumbing/protocol/packp/ulreq.go
+++ b/plumbing/protocol/packp/ulreq.go
@@ -6,10 +6,11 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing"
)
-// UlReq values represent the information transmitted on a
+// UploadRequest values represent the information transmitted on a
// upload-request message. Values from this type are not zero-value
// safe, use the New function instead.
-type UlReq struct {
+// This is a low level type, use UploadPackRequest instead.
+type UploadRequest struct {
Capabilities *Capabilities
Wants []plumbing.Hash
Shallows []plumbing.Hash
@@ -39,15 +40,20 @@ type DepthReference string
func (d DepthReference) isDepth() {}
-// NewUlReq returns a pointer to a new UlReq value, ready to be used. It has
+// NewUploadRequest returns a pointer to a new UlReq value, ready to be used. It has
// no capabilities, wants or shallows and an infinite depth. Please
// note that to encode an upload-request it has to have at least one
// wanted hash.
-func NewUlReq() *UlReq {
- return &UlReq{
+func NewUploadRequest() *UploadRequest {
+ return &UploadRequest{
Capabilities: NewCapabilities(),
Wants: []plumbing.Hash{},
Shallows: []plumbing.Hash{},
Depth: DepthCommits(0),
}
}
+
+// Want adds a hash reference to the 'wants' list.
+func (r *UploadRequest) Want(h ...plumbing.Hash) {
+ r.Wants = append(r.Wants, h...)
+}
diff --git a/plumbing/protocol/packp/ulreq_decoder.go b/plumbing/protocol/packp/ulreq_decode.go
index 67ba479..0124cd0 100644
--- a/plumbing/protocol/packp/ulreq_decoder.go
+++ b/plumbing/protocol/packp/ulreq_decode.go
@@ -12,27 +12,28 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
)
-// A UlReqDecoder reads and decodes AdvRef values from an input stream.
-type UlReqDecoder struct {
+// Decode reads the next upload-request form its input and
+// stores it in the UploadRequest.
+func (u *UploadRequest) Decode(r io.Reader) error {
+ d := newUlReqDecoder(r)
+ return d.Decode(u)
+}
+
+type ulReqDecoder struct {
s *pktline.Scanner // a pkt-line scanner from the input stream
line []byte // current pkt-line contents, use parser.nextLine() to make it advance
nLine int // current pkt-line number for debugging, begins at 1
err error // sticky error, use the parser.error() method to fill this out
- data *UlReq // parsed data is stored here
+ data *UploadRequest // parsed data is stored here
}
-// NewUlReqDecoder returns a new decoder that reads from r.
-//
-// Will not read more data from r than necessary.
-func NewUlReqDecoder(r io.Reader) *UlReqDecoder {
- return &UlReqDecoder{
+func newUlReqDecoder(r io.Reader) *ulReqDecoder {
+ return &ulReqDecoder{
s: pktline.NewScanner(r),
}
}
-// Decode reads the next upload-request form its input and
-// stores it in the value pointed to by v.
-func (d *UlReqDecoder) Decode(v *UlReq) error {
+func (d *ulReqDecoder) Decode(v *UploadRequest) error {
d.data = v
for state := d.decodeFirstWant; state != nil; {
@@ -43,7 +44,7 @@ func (d *UlReqDecoder) Decode(v *UlReq) error {
}
// fills out the parser stiky error
-func (d *UlReqDecoder) error(format string, a ...interface{}) {
+func (d *ulReqDecoder) error(format string, a ...interface{}) {
d.err = fmt.Errorf("pkt-line %d: %s", d.nLine,
fmt.Sprintf(format, a...))
}
@@ -52,7 +53,7 @@ func (d *UlReqDecoder) error(format string, a ...interface{}) {
// p.line and increments p.nLine. A successful invocation returns true,
// otherwise, false is returned and the sticky error is filled out
// accordingly. Trims eols at the end of the payloads.
-func (d *UlReqDecoder) nextLine() bool {
+func (d *ulReqDecoder) nextLine() bool {
d.nLine++
if !d.s.Scan() {
@@ -71,7 +72,7 @@ func (d *UlReqDecoder) nextLine() bool {
}
// Expected format: want <hash>[ capabilities]
-func (d *UlReqDecoder) decodeFirstWant() stateFn {
+func (d *ulReqDecoder) decodeFirstWant() stateFn {
if ok := d.nextLine(); !ok {
return nil
}
@@ -91,7 +92,7 @@ func (d *UlReqDecoder) decodeFirstWant() stateFn {
return d.decodeCaps
}
-func (d *UlReqDecoder) readHash() (plumbing.Hash, bool) {
+func (d *ulReqDecoder) readHash() (plumbing.Hash, bool) {
if len(d.line) < hashSize {
d.err = fmt.Errorf("malformed hash: %v", d.line)
return plumbing.ZeroHash, false
@@ -108,7 +109,7 @@ func (d *UlReqDecoder) readHash() (plumbing.Hash, bool) {
}
// Expected format: sp cap1 sp cap2 sp cap3...
-func (d *UlReqDecoder) decodeCaps() stateFn {
+func (d *ulReqDecoder) decodeCaps() stateFn {
if len(d.line) == 0 {
return d.decodeOtherWants
}
@@ -124,7 +125,7 @@ func (d *UlReqDecoder) decodeCaps() stateFn {
}
// Expected format: want <hash>
-func (d *UlReqDecoder) decodeOtherWants() stateFn {
+func (d *ulReqDecoder) decodeOtherWants() stateFn {
if ok := d.nextLine(); !ok {
return nil
}
@@ -157,7 +158,7 @@ func (d *UlReqDecoder) decodeOtherWants() stateFn {
}
// Expected format: shallow <hash>
-func (d *UlReqDecoder) decodeShallow() stateFn {
+func (d *ulReqDecoder) decodeShallow() stateFn {
if bytes.HasPrefix(d.line, deepen) {
return d.decodeDeepen
}
@@ -186,7 +187,7 @@ func (d *UlReqDecoder) decodeShallow() stateFn {
}
// Expected format: deepen <n> / deepen-since <ul> / deepen-not <ref>
-func (d *UlReqDecoder) decodeDeepen() stateFn {
+func (d *ulReqDecoder) decodeDeepen() stateFn {
if bytes.HasPrefix(d.line, deepenCommits) {
return d.decodeDeepenCommits
}
@@ -207,7 +208,7 @@ func (d *UlReqDecoder) decodeDeepen() stateFn {
return nil
}
-func (d *UlReqDecoder) decodeDeepenCommits() stateFn {
+func (d *ulReqDecoder) decodeDeepenCommits() stateFn {
d.line = bytes.TrimPrefix(d.line, deepenCommits)
var n int
@@ -223,7 +224,7 @@ func (d *UlReqDecoder) decodeDeepenCommits() stateFn {
return d.decodeFlush
}
-func (d *UlReqDecoder) decodeDeepenSince() stateFn {
+func (d *ulReqDecoder) decodeDeepenSince() stateFn {
d.line = bytes.TrimPrefix(d.line, deepenSince)
var secs int64
@@ -237,7 +238,7 @@ func (d *UlReqDecoder) decodeDeepenSince() stateFn {
return d.decodeFlush
}
-func (d *UlReqDecoder) decodeDeepenReference() stateFn {
+func (d *ulReqDecoder) decodeDeepenReference() stateFn {
d.line = bytes.TrimPrefix(d.line, deepenReference)
d.data.Depth = DepthReference(string(d.line))
@@ -245,7 +246,7 @@ func (d *UlReqDecoder) decodeDeepenReference() stateFn {
return d.decodeFlush
}
-func (d *UlReqDecoder) decodeFlush() stateFn {
+func (d *ulReqDecoder) decodeFlush() stateFn {
if ok := d.nextLine(); !ok {
return nil
}
diff --git a/plumbing/protocol/packp/ulreq_decoder_test.go b/plumbing/protocol/packp/ulreq_decode_test.go
index e90962a..eb12c90 100644
--- a/plumbing/protocol/packp/ulreq_decoder_test.go
+++ b/plumbing/protocol/packp/ulreq_decode_test.go
@@ -17,9 +17,9 @@ type UlReqDecodeSuite struct{}
var _ = Suite(&UlReqDecodeSuite{})
func (s *UlReqDecodeSuite) TestEmpty(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
var buf bytes.Buffer
- d := NewUlReqDecoder(&buf)
+ d := newUlReqDecoder(&buf)
err := d.Decode(ur)
c.Assert(err, ErrorMatches, "pkt-line 1: EOF")
@@ -35,8 +35,8 @@ func (s *UlReqDecodeSuite) TestNoWant(c *C) {
}
func (s *UlReqDecodeSuite) testDecoderErrorMatches(c *C, input io.Reader, pattern string) {
- ur := NewUlReq()
- d := NewUlReqDecoder(input)
+ ur := NewUploadRequest()
+ d := newUlReqDecoder(input)
err := d.Decode(ur)
c.Assert(err, ErrorMatches, pattern)
@@ -63,14 +63,14 @@ func (s *UlReqDecodeSuite) TestWantOK(c *C) {
})
}
-func (s *UlReqDecodeSuite) testDecodeOK(c *C, payloads []string) *UlReq {
+func (s *UlReqDecodeSuite) testDecodeOK(c *C, payloads []string) *UploadRequest {
var buf bytes.Buffer
e := pktline.NewEncoder(&buf)
err := e.EncodeString(payloads...)
c.Assert(err, IsNil)
- ur := NewUlReq()
- d := NewUlReqDecoder(&buf)
+ ur := NewUploadRequest()
+ d := newUlReqDecoder(&buf)
err = d.Decode(ur)
c.Assert(err, IsNil)
diff --git a/plumbing/protocol/packp/ulreq_encoder.go b/plumbing/protocol/packp/ulreq_encode.go
index 9ebc4b5..b422a5f 100644
--- a/plumbing/protocol/packp/ulreq_encoder.go
+++ b/plumbing/protocol/packp/ulreq_encode.go
@@ -10,27 +10,30 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
)
-// An UlReqEncoder writes UlReq values to an output stream.
-type UlReqEncoder struct {
+// Encode writes the UlReq encoding of u to the stream.
+//
+// All the payloads will end with a newline character. Wants and
+// shallows are sorted alphabetically. A depth of 0 means no depth
+// request is sent.
+func (u *UploadRequest) Encode(w io.Writer) error {
+ e := newUlReqEncoder(w)
+ return e.Encode(u)
+}
+
+type ulReqEncoder struct {
pe *pktline.Encoder // where to write the encoded data
- data *UlReq // the data to encode
+ data *UploadRequest // the data to encode
sortedWants []string
err error // sticky error
}
-// NewUlReqEncoder returns a new encoder that writes to w.
-func NewUlReqEncoder(w io.Writer) *UlReqEncoder {
- return &UlReqEncoder{
+func newUlReqEncoder(w io.Writer) *ulReqEncoder {
+ return &ulReqEncoder{
pe: pktline.NewEncoder(w),
}
}
-// Encode writes the UlReq encoding of v to the stream.
-//
-// All the payloads will end with a newline character. Wants and
-// shallows are sorted alphabetically. A depth of 0 means no depth
-// request is sent.
-func (e *UlReqEncoder) Encode(v *UlReq) error {
+func (e *ulReqEncoder) Encode(v *UploadRequest) error {
if len(v.Wants) == 0 {
return fmt.Errorf("empty wants provided")
}
@@ -55,7 +58,7 @@ func sortHashes(list []plumbing.Hash) []string {
return sorted
}
-func (e *UlReqEncoder) encodeFirstWant() stateFn {
+func (e *ulReqEncoder) encodeFirstWant() stateFn {
var err error
if e.data.Capabilities.IsEmpty() {
err = e.pe.Encodef("want %s\n", e.sortedWants[0])
@@ -75,7 +78,7 @@ func (e *UlReqEncoder) encodeFirstWant() stateFn {
return e.encodeAditionalWants
}
-func (e *UlReqEncoder) encodeAditionalWants() stateFn {
+func (e *ulReqEncoder) encodeAditionalWants() stateFn {
for _, w := range e.sortedWants[1:] {
if err := e.pe.Encodef("want %s\n", w); err != nil {
e.err = fmt.Errorf("encoding want %q: %s", w, err)
@@ -86,7 +89,7 @@ func (e *UlReqEncoder) encodeAditionalWants() stateFn {
return e.encodeShallows
}
-func (e *UlReqEncoder) encodeShallows() stateFn {
+func (e *ulReqEncoder) encodeShallows() stateFn {
sorted := sortHashes(e.data.Shallows)
for _, s := range sorted {
if err := e.pe.Encodef("shallow %s\n", s); err != nil {
@@ -98,7 +101,7 @@ func (e *UlReqEncoder) encodeShallows() stateFn {
return e.encodeDepth
}
-func (e *UlReqEncoder) encodeDepth() stateFn {
+func (e *ulReqEncoder) encodeDepth() stateFn {
switch depth := e.data.Depth.(type) {
case DepthCommits:
if depth != 0 {
@@ -128,7 +131,7 @@ func (e *UlReqEncoder) encodeDepth() stateFn {
return e.encodeFlush
}
-func (e *UlReqEncoder) encodeFlush() stateFn {
+func (e *ulReqEncoder) encodeFlush() stateFn {
if err := e.pe.Flush(); err != nil {
e.err = fmt.Errorf("encoding flush-pkt: %s", err)
return nil
diff --git a/plumbing/protocol/packp/ulreq_encoder_test.go b/plumbing/protocol/packp/ulreq_encode_test.go
index fb83653..1eb3175 100644
--- a/plumbing/protocol/packp/ulreq_encoder_test.go
+++ b/plumbing/protocol/packp/ulreq_encode_test.go
@@ -14,9 +14,9 @@ type UlReqEncodeSuite struct{}
var _ = Suite(&UlReqEncodeSuite{})
-func testUlReqEncode(c *C, ur *UlReq, expectedPayloads []string) {
+func testUlReqEncode(c *C, ur *UploadRequest, expectedPayloads []string) {
var buf bytes.Buffer
- e := NewUlReqEncoder(&buf)
+ e := newUlReqEncoder(&buf)
err := e.Encode(ur)
c.Assert(err, IsNil)
@@ -29,23 +29,23 @@ func testUlReqEncode(c *C, ur *UlReq, expectedPayloads []string) {
c.Assert(obtained, DeepEquals, expected, comment)
}
-func testUlReqEncodeError(c *C, ur *UlReq, expectedErrorRegEx string) {
+func testUlReqEncodeError(c *C, ur *UploadRequest, expectedErrorRegEx string) {
var buf bytes.Buffer
- e := NewUlReqEncoder(&buf)
+ e := newUlReqEncoder(&buf)
err := e.Encode(ur)
c.Assert(err, ErrorMatches, expectedErrorRegEx)
}
func (s *UlReqEncodeSuite) TestZeroValue(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
expectedErrorRegEx := ".*empty wants.*"
testUlReqEncodeError(c, ur, expectedErrorRegEx)
}
func (s *UlReqEncodeSuite) TestOneWant(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
expected := []string{
@@ -57,7 +57,7 @@ func (s *UlReqEncodeSuite) TestOneWant(c *C) {
}
func (s *UlReqEncodeSuite) TestOneWantWithCapabilities(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
ur.Capabilities.Add("sysref", "HEAD:/refs/heads/master")
ur.Capabilities.Add("multi_ack")
@@ -74,7 +74,7 @@ func (s *UlReqEncodeSuite) TestOneWantWithCapabilities(c *C) {
}
func (s *UlReqEncodeSuite) TestWants(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("4444444444444444444444444444444444444444"))
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333"))
@@ -94,7 +94,7 @@ func (s *UlReqEncodeSuite) TestWants(c *C) {
}
func (s *UlReqEncodeSuite) TestWantsWithCapabilities(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("4444444444444444444444444444444444444444"))
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333"))
@@ -120,7 +120,7 @@ func (s *UlReqEncodeSuite) TestWantsWithCapabilities(c *C) {
}
func (s *UlReqEncodeSuite) TestShallow(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
ur.Capabilities.Add("multi_ack")
ur.Shallows = append(ur.Shallows, plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
@@ -135,7 +135,7 @@ func (s *UlReqEncodeSuite) TestShallow(c *C) {
}
func (s *UlReqEncodeSuite) TestManyShallows(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
ur.Capabilities.Add("multi_ack")
ur.Shallows = append(ur.Shallows, plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"))
@@ -156,7 +156,7 @@ func (s *UlReqEncodeSuite) TestManyShallows(c *C) {
}
func (s *UlReqEncodeSuite) TestDepthCommits(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
ur.Depth = DepthCommits(1234)
@@ -170,7 +170,7 @@ func (s *UlReqEncodeSuite) TestDepthCommits(c *C) {
}
func (s *UlReqEncodeSuite) TestDepthSinceUTC(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
since := time.Date(2015, time.January, 2, 3, 4, 5, 0, time.UTC)
ur.Depth = DepthSince(since)
@@ -185,7 +185,7 @@ func (s *UlReqEncodeSuite) TestDepthSinceUTC(c *C) {
}
func (s *UlReqEncodeSuite) TestDepthSinceNonUTC(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
berlin, err := time.LoadLocation("Europe/Berlin")
c.Assert(err, IsNil)
@@ -204,7 +204,7 @@ func (s *UlReqEncodeSuite) TestDepthSinceNonUTC(c *C) {
}
func (s *UlReqEncodeSuite) TestDepthReference(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
ur.Depth = DepthReference("refs/heads/feature-foo")
@@ -218,7 +218,7 @@ func (s *UlReqEncodeSuite) TestDepthReference(c *C) {
}
func (s *UlReqEncodeSuite) TestAll(c *C) {
- ur := NewUlReq()
+ ur := NewUploadRequest()
ur.Wants = append(ur.Wants, plumbing.NewHash("4444444444444444444444444444444444444444"))
ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333"))
diff --git a/plumbing/protocol/packp/ulreq_test.go b/plumbing/protocol/packp/ulreq_test.go
index 19b6dd0..be02f9d 100644
--- a/plumbing/protocol/packp/ulreq_test.go
+++ b/plumbing/protocol/packp/ulreq_test.go
@@ -12,7 +12,7 @@ import (
func ExampleUlReqEncoder_Encode() {
// Create an empty UlReq with the contents you want...
- ur := NewUlReq()
+ ur := NewUploadRequest()
// Add a couple of wants
ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333"))
@@ -32,7 +32,7 @@ func ExampleUlReqEncoder_Encode() {
ur.Depth = DepthSince(since)
// Create a new Encode for the stdout...
- e := NewUlReqEncoder(os.Stdout)
+ e := newUlReqEncoder(os.Stdout)
// ...and encode the upload-request to it.
_ = e.Encode(ur) // ignoring errors for brevity
// Output:
@@ -60,10 +60,10 @@ func ExampleUlReqDecoder_Decode() {
input := strings.NewReader(raw)
// Create the Decoder reading from our input.
- d := NewUlReqDecoder(input)
+ d := newUlReqDecoder(input)
// Decode the input into a newly allocated UlReq value.
- ur := NewUlReq()
+ ur := NewUploadRequest()
_ = d.Decode(ur) // error check ignored for brevity
// Do something interesting with the UlReq, e.g. print its contents.
diff --git a/plumbing/protocol/packp/upload_pack_request.go b/plumbing/protocol/packp/upload_pack_request.go
new file mode 100644
index 0000000..42033b1
--- /dev/null
+++ b/plumbing/protocol/packp/upload_pack_request.go
@@ -0,0 +1,75 @@
+package packp
+
+import (
+ "fmt"
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
+)
+
+// UploadHaves is a message to signal the references that a client has in a
+// upload-pack. Do not use this directly. Use UploadPackRequest request instead.
+type UploadHaves struct {
+ Haves []plumbing.Hash
+}
+
+// Encode encodes the UploadHaves into the Writer.
+func (u *UploadHaves) Encode(w io.Writer) error {
+ e := pktline.NewEncoder(w)
+ for _, have := range u.Haves {
+ if err := e.Encodef("have %s\n", have); err != nil {
+ return fmt.Errorf("sending haves for %q: %s", have, err)
+ }
+ }
+
+ if len(u.Haves) != 0 {
+ if err := e.Flush(); err != nil {
+ return fmt.Errorf("sending flush-pkt after haves: %s", err)
+ }
+ }
+
+ return nil
+}
+
+// Have adds a hash reference to the 'haves' list.
+func (r *UploadHaves) Have(h ...plumbing.Hash) {
+ r.Haves = append(r.Haves, h...)
+}
+
+// UploadPackRequest represents a upload-pack request.
+// Zero-value is not safe, use NewUploadPackRequest instead.
+type UploadPackRequest struct {
+ *UploadRequest
+ *UploadHaves
+}
+
+// NewUploadPackRequest creates a new UploadPackRequest and returns a pointer.
+func NewUploadPackRequest() *UploadPackRequest {
+ return &UploadPackRequest{
+ UploadHaves: &UploadHaves{},
+ UploadRequest: NewUploadRequest(),
+ }
+}
+
+func (r *UploadPackRequest) IsEmpty() bool {
+ return isSubset(r.Wants, r.Haves)
+}
+
+func isSubset(needle []plumbing.Hash, haystack []plumbing.Hash) bool {
+ for _, h := range needle {
+ found := false
+ for _, oh := range haystack {
+ if h == oh {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ return false
+ }
+ }
+
+ return true
+}
diff --git a/plumbing/protocol/packp/upload_pack_request_test.go b/plumbing/protocol/packp/upload_pack_request_test.go
new file mode 100644
index 0000000..f321736
--- /dev/null
+++ b/plumbing/protocol/packp/upload_pack_request_test.go
@@ -0,0 +1,33 @@
+package packp
+
+import (
+ "gopkg.in/src-d/go-git.v4/plumbing"
+
+ . "gopkg.in/check.v1"
+)
+
+type UploadPackRequestSuite struct{}
+
+var _ = Suite(&UploadPackRequestSuite{})
+
+func (s *UploadPackRequestSuite) TestUploadPackRequest_IsEmpty(c *C) {
+ r := NewUploadPackRequest()
+ r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+ r.Want(plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989"))
+ r.Have(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+
+ c.Assert(r.IsEmpty(), Equals, false)
+
+ r = NewUploadPackRequest()
+ r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+ r.Want(plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989"))
+ r.Have(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+
+ c.Assert(r.IsEmpty(), Equals, false)
+
+ r = NewUploadPackRequest()
+ r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+ r.Have(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+
+ c.Assert(r.IsEmpty(), Equals, true)
+}