diff options
Diffstat (limited to 'plumbing/protocol/packp')
-rw-r--r-- | plumbing/protocol/packp/advrefs.go | 69 | ||||
-rw-r--r-- | plumbing/protocol/packp/advrefs_decode.go | 51 | ||||
-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.go | 16 | ||||
-rw-r--r-- | plumbing/protocol/packp/ulreq.go | 16 | ||||
-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.go | 8 | ||||
-rw-r--r-- | plumbing/protocol/packp/upload_pack_request.go | 75 | ||||
-rw-r--r-- | plumbing/protocol/packp/upload_pack_request_test.go | 33 |
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) +} |