diff options
27 files changed, 465 insertions, 532 deletions
diff --git a/common_test.go b/common_test.go index 62896a1..be86b4e 100644 --- a/common_test.go +++ b/common_test.go @@ -93,8 +93,7 @@ func (c *MockFetchPackSession) SetAuth(auth transport.AuthMethod) error { return nil } -func (c *MockFetchPackSession) AdvertisedReferences() ( - *transport.UploadPackInfo, error) { +func (c *MockFetchPackSession) AdvertisedReferences() (*packp.AdvRefs, error) { h := fixtures.ByURL(c.endpoint.String()).One().Head @@ -105,19 +104,19 @@ func (c *MockFetchPackSession) AdvertisedReferences() ( branch := plumbing.ReferenceName("refs/heads/branch") tag := plumbing.ReferenceName("refs/tags/v1.0.0") - return &transport.UploadPackInfo{ - Capabilities: cap, - Refs: map[plumbing.ReferenceName]*plumbing.Reference{ - plumbing.HEAD: plumbing.NewSymbolicReference(plumbing.HEAD, ref), - ref: plumbing.NewHashReference(ref, h), - tag: plumbing.NewHashReference(tag, h), - branch: plumbing.NewHashReference(branch, plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881")), - }, - }, nil + a := packp.NewAdvRefs() + a.Capabilities = cap + a.Head = &h + a.AddReference(plumbing.NewSymbolicReference(plumbing.HEAD, ref)) + a.AddReference(plumbing.NewHashReference(ref, h)) + a.AddReference(plumbing.NewHashReference(tag, h)) + a.AddReference(plumbing.NewHashReference(branch, plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"))) + + return a, nil } func (c *MockFetchPackSession) FetchPack( - r *transport.UploadPackRequest) (io.ReadCloser, error) { + r *packp.UploadPackRequest) (io.ReadCloser, error) { f := fixtures.ByURL(c.endpoint.String()) 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) +} diff --git a/plumbing/transport/common.go b/plumbing/transport/common.go index 8329069..2f6fcee 100644 --- a/plumbing/transport/common.go +++ b/plumbing/transport/common.go @@ -20,6 +20,7 @@ import ( "regexp" "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" ) var ( @@ -63,10 +64,10 @@ type FetchPackSession interface { // AdvertisedReferences retrieves the advertised references for a // repository. It should be called before FetchPack, and it cannot be // called after FetchPack. - AdvertisedReferences() (*UploadPackInfo, error) + AdvertisedReferences() (*packp.AdvRefs, error) // FetchPack takes a request and returns a reader for the packfile // received from the server. - FetchPack(req *UploadPackRequest) (io.ReadCloser, error) + FetchPack(req *packp.UploadPackRequest) (io.ReadCloser, error) } // FetchPackSession represents a git-send-pack session. @@ -78,7 +79,7 @@ type SendPackSession interface { // AdvertisedReferences retrieves the advertised references for a // repository. It should be called before FetchPack, and it cannot be // called after FetchPack. - AdvertisedReferences() (*UploadPackInfo, error) + AdvertisedReferences() (*packp.AdvRefs, error) // UpdateReferences sends an update references request and returns a // writer to be used for packfile writing. //TODO: Complete signature. diff --git a/plumbing/transport/fetch_pack.go b/plumbing/transport/fetch_pack.go deleted file mode 100644 index 14d3e19..0000000 --- a/plumbing/transport/fetch_pack.go +++ /dev/null @@ -1,177 +0,0 @@ -package transport - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "strings" - - "gopkg.in/src-d/go-git.v4/plumbing" - "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" - "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" - "gopkg.in/src-d/go-git.v4/plumbing/storer" - "gopkg.in/src-d/go-git.v4/storage/memory" -) - -//TODO: Replace this by packpAdvRefs. -type UploadPackInfo struct { - Capabilities *packp.Capabilities - Refs memory.ReferenceStorage -} - -func NewUploadPackInfo() *UploadPackInfo { - return &UploadPackInfo{ - Capabilities: packp.NewCapabilities(), - Refs: make(memory.ReferenceStorage, 0), - } -} - -func (i *UploadPackInfo) Decode(r io.Reader) error { - d := packp.NewAdvRefsDecoder(r) - ar := packp.NewAdvRefs() - if err := d.Decode(ar); err != nil { - if err == packp.ErrEmpty { - return err - } - return plumbing.NewUnexpectedError(err) - } - - i.Capabilities = ar.Capabilities - - if err := i.addRefs(ar); err != nil { - return plumbing.NewUnexpectedError(err) - } - - return nil -} - -func (i *UploadPackInfo) addRefs(ar *packp.AdvRefs) error { - for name, hash := range ar.References { - ref := plumbing.NewReferenceFromStrings(name, hash.String()) - i.Refs.SetReference(ref) - } - - return i.addSymbolicRefs(ar) -} - -func (i *UploadPackInfo) addSymbolicRefs(ar *packp.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) - i.Refs.SetReference(ref) - } - - return nil -} - -func hasSymrefs(ar *packp.AdvRefs) bool { - return ar.Capabilities.Supports("symref") -} - -func (i *UploadPackInfo) Head() *plumbing.Reference { - ref, _ := storer.ResolveReference(i.Refs, plumbing.HEAD) - return ref -} - -func (i *UploadPackInfo) String() string { - return string(i.Bytes()) -} - -func (i *UploadPackInfo) Bytes() []byte { - var buf bytes.Buffer - e := pktline.NewEncoder(&buf) - - _ = e.EncodeString("# service=git-upload-pack\n") - - // inserting a flush-pkt here violates the protocol spec, but some - // servers do it, like Github.com - e.Flush() - - _ = e.Encodef("%s HEAD\x00%s\n", i.Head().Hash(), i.Capabilities.String()) - - for _, ref := range i.Refs { - if ref.Type() != plumbing.HashReference { - continue - } - - _ = e.Encodef("%s %s\n", ref.Hash(), ref.Name()) - } - - e.Flush() - - return buf.Bytes() -} - -type UploadPackRequest struct { - Wants []plumbing.Hash - Haves []plumbing.Hash - Depth int -} - -func (r *UploadPackRequest) Want(h ...plumbing.Hash) { - r.Wants = append(r.Wants, h...) -} - -func (r *UploadPackRequest) Have(h ...plumbing.Hash) { - r.Haves = append(r.Haves, h...) -} - -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 -} - -func (r *UploadPackRequest) String() string { - b, _ := ioutil.ReadAll(r.Reader()) - return string(b) -} - -func (r *UploadPackRequest) Reader() *strings.Reader { - var buf bytes.Buffer - e := pktline.NewEncoder(&buf) - - for _, want := range r.Wants { - _ = e.Encodef("want %s\n", want) - } - - for _, have := range r.Haves { - _ = e.Encodef("have %s\n", have) - } - - if r.Depth != 0 { - _ = e.Encodef("deepen %d\n", r.Depth) - } - - _ = e.Flush() - _ = e.EncodeString("done\n") - - return strings.NewReader(buf.String()) -} diff --git a/plumbing/transport/fetch_pack_test.go b/plumbing/transport/fetch_pack_test.go deleted file mode 100644 index fc77fe2..0000000 --- a/plumbing/transport/fetch_pack_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package transport - -import ( - "bytes" - "encoding/base64" - - "gopkg.in/src-d/go-git.v4/plumbing" - "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" - - . "gopkg.in/check.v1" -) - -type UploadPackSuite struct{} - -var _ = Suite(&UploadPackSuite{}) - -const UploadPackInfoFixture = "MDAxZSMgc2VydmljZT1naXQtdXBsb2FkLXBhY2sKMDAwMDAxMGM2ZWNmMGVmMmMyZGZmYjc5NjAzM2U1YTAyMjE5YWY4NmVjNjU4NGU1IEhFQUQAbXVsdGlfYWNrIHRoaW4tcGFjayBzaWRlLWJhbmQgc2lkZS1iYW5kLTY0ayBvZnMtZGVsdGEgc2hhbGxvdyBuby1wcm9ncmVzcyBpbmNsdWRlLXRhZyBtdWx0aV9hY2tfZGV0YWlsZWQgbm8tZG9uZSBzeW1yZWY9SEVBRDpyZWZzL2hlYWRzL21hc3RlciBhZ2VudD1naXQvMjoyLjQuOH5kYnVzc2luay1maXgtZW50ZXJwcmlzZS10b2tlbnMtY29tcGlsYXRpb24tMTE2Ny1nYzcwMDZjZgowMDNmZThkM2ZmYWI1NTI4OTVjMTliOWZjZjdhYTI2NGQyNzdjZGUzMzg4MSByZWZzL2hlYWRzL2JyYW5jaAowMDNmNmVjZjBlZjJjMmRmZmI3OTYwMzNlNWEwMjIxOWFmODZlYzY1ODRlNSByZWZzL2hlYWRzL21hc3RlcgowMDNlYjhlNDcxZjU4YmNiY2E2M2IwN2JkYTIwZTQyODE5MDQwOWMyZGI0NyByZWZzL3B1bGwvMS9oZWFkCjAwMDA=" - -func (s *UploadPackSuite) TestUploadPackInfo(c *C) { - b, _ := base64.StdEncoding.DecodeString(UploadPackInfoFixture) - - i := NewUploadPackInfo() - err := i.Decode(bytes.NewBuffer(b)) - c.Assert(err, IsNil) - - name := i.Capabilities.SymbolicReference("HEAD") - c.Assert(name, Equals, "refs/heads/master") - c.Assert(i.Refs, HasLen, 4) - - ref := i.Refs[plumbing.ReferenceName(name)] - c.Assert(ref, NotNil) - c.Assert(ref.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - - ref = i.Refs[plumbing.HEAD] - c.Assert(ref, NotNil) - c.Assert(ref.Target(), Equals, plumbing.ReferenceName(name)) -} - -const UploadPackInfoNoHEADFixture = "MDAxZSMgc2VydmljZT1naXQtdXBsb2FkLXBhY2sKMDAwMDAwYmNkN2UxZmVlMjYxMjM0YmIzYTQzYzA5NmY1NTg3NDhhNTY5ZDc5ZWZmIHJlZnMvaGVhZHMvdjQAbXVsdGlfYWNrIHRoaW4tcGFjayBzaWRlLWJhbmQgc2lkZS1iYW5kLTY0ayBvZnMtZGVsdGEgc2hhbGxvdyBuby1wcm9ncmVzcyBpbmNsdWRlLXRhZyBtdWx0aV9hY2tfZGV0YWlsZWQgbm8tZG9uZSBhZ2VudD1naXQvMS45LjEKMDAwMA==" - -func (s *UploadPackSuite) TestUploadPackInfoNoHEAD(c *C) { - b, _ := base64.StdEncoding.DecodeString(UploadPackInfoNoHEADFixture) - - i := NewUploadPackInfo() - err := i.Decode(bytes.NewBuffer(b)) - c.Assert(err, IsNil) - - name := i.Capabilities.SymbolicReference("HEAD") - c.Assert(name, Equals, "") - c.Assert(i.Refs, HasLen, 1) - - ref := i.Refs["refs/heads/v4"] - c.Assert(ref, NotNil) - c.Assert(ref.Hash().String(), Equals, "d7e1fee261234bb3a43c096f558748a569d79eff") -} - -func (s *UploadPackSuite) TestUploadPackInfoEmpty(c *C) { - b := bytes.NewBuffer(nil) - - i := NewUploadPackInfo() - err := i.Decode(b) - c.Assert(err, Equals, packp.ErrEmpty) -} - -func (s *UploadPackSuite) TestUploadPackEncode(c *C) { - info := NewUploadPackInfo() - info.Capabilities.Add("symref", "HEAD:refs/heads/master") - - ref := plumbing.ReferenceName("refs/heads/master") - hash := plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - info.Refs = map[plumbing.ReferenceName]*plumbing.Reference{ - plumbing.HEAD: plumbing.NewSymbolicReference(plumbing.HEAD, ref), - ref: plumbing.NewHashReference(ref, hash), - } - - c.Assert(info.Head(), NotNil) - c.Assert(info.String(), Equals, - "001e# service=git-upload-pack\n"+ - "000000506ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:refs/heads/master\n"+ - "003f6ecf0ef2c2dffb796033e5a02219af86ec6584e5 refs/heads/master\n"+ - "0000", - ) -} - -func (s *UploadPackSuite) TestUploadPackRequest(c *C) { - r := &UploadPackRequest{} - r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) - r.Want(plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989")) - r.Have(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) - - c.Assert(r.String(), Equals, - "0032want d82f291cde9987322c8a0c81a325e1ba6159684c\n"+ - "0032want 2b41ef280fdb67a9b250678686a0c3e03b0a9989\n"+ - "0032have 6ecf0ef2c2dffb796033e5a02219af86ec6584e5\n0000"+ - "0009done\n", - ) -} - -func (s *UploadPackSuite) TestUploadPackRequest_IsEmpty(c *C) { - r := &UploadPackRequest{} - r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) - r.Want(plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989")) - r.Have(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) - - c.Assert(r.IsEmpty(), Equals, false) - - r = &UploadPackRequest{} - r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) - r.Want(plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989")) - r.Have(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) - - c.Assert(r.IsEmpty(), Equals, false) - - r = &UploadPackRequest{} - r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) - r.Have(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) - - c.Assert(r.IsEmpty(), Equals, true) -} diff --git a/plumbing/transport/http/fetch_pack.go b/plumbing/transport/http/fetch_pack.go index 29e385b..40c3a7f 100644 --- a/plumbing/transport/http/fetch_pack.go +++ b/plumbing/transport/http/fetch_pack.go @@ -31,8 +31,7 @@ func newFetchPackSession(c *http.Client, } } -func (s *fetchPackSession) AdvertisedReferences() (*transport.UploadPackInfo, - error) { +func (s *fetchPackSession) AdvertisedReferences() (*packp.AdvRefs, error) { if s.advRefsRun { return nil, transport.ErrAdvertistedReferencesAlreadyCalled } @@ -62,19 +61,19 @@ func (s *fetchPackSession) AdvertisedReferences() (*transport.UploadPackInfo, return nil, transport.ErrAuthorizationRequired } - i := transport.NewUploadPackInfo() - if err := i.Decode(res.Body); err != nil { - if err == packp.ErrEmpty { + ar := packp.NewAdvRefs() + if err := ar.Decode(res.Body); err != nil { + if err == packp.ErrEmptyAdvRefs { err = transport.ErrEmptyRemoteRepository } return nil, err } - return i, nil + return ar, nil } -func (s *fetchPackSession) FetchPack(r *transport.UploadPackRequest) (io.ReadCloser, error) { +func (s *fetchPackSession) FetchPack(r *packp.UploadPackRequest) (io.ReadCloser, error) { if r.IsEmpty() { return nil, transport.ErrEmptyUploadPackRequest } @@ -84,7 +83,12 @@ func (s *fetchPackSession) FetchPack(r *transport.UploadPackRequest) (io.ReadClo s.endpoint.String(), transport.UploadPackServiceName, ) - res, err := s.doRequest("POST", url, r.Reader()) + content, err := uploadPackRequestToReader(r) + if err != nil { + return nil, err + } + + res, err := s.doRequest("POST", url, content) if err != nil { return nil, err } @@ -161,3 +165,32 @@ func (s *fetchPackSession) applyHeadersToRequest(req *http.Request, content *str req.Header.Add("Content-Length", string(content.Len())) } } + +func uploadPackRequestToReader(r *packp.UploadPackRequest) (*strings.Reader, error) { + var buf bytes.Buffer + e := pktline.NewEncoder(&buf) + + for _, want := range r.Wants { + _ = e.Encodef("want %s\n", want) + } + + for _, have := range r.Haves { + _ = e.Encodef("have %s\n", have) + } + + if r.Depth != nil { + depth, ok := r.Depth.(packp.DepthCommits) + if !ok { + return nil, fmt.Errorf("only commit depth is supported") + } + + if depth != 0 { + _ = e.Encodef("deepen %d\n", depth) + } + } + + _ = e.Flush() + _ = e.EncodeString("done\n") + + return strings.NewReader(buf.String()), nil +} diff --git a/plumbing/transport/http/fetch_pack_test.go b/plumbing/transport/http/fetch_pack_test.go index c7666c8..7471208 100644 --- a/plumbing/transport/http/fetch_pack_test.go +++ b/plumbing/transport/http/fetch_pack_test.go @@ -1,6 +1,10 @@ package http import ( + "io/ioutil" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/plumbing/transport/test" @@ -36,3 +40,20 @@ func (s *FetchPackSuite) TestInfoNotExists(c *C) { c.Assert(err, Equals, transport.ErrAuthorizationRequired) c.Assert(info, IsNil) } + +func (s *FetchPackSuite) TestuploadPackRequestToReader(c *C) { + r := packp.NewUploadPackRequest() + r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) + r.Want(plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989")) + r.Have(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) + + sr, err := uploadPackRequestToReader(r) + c.Assert(err, IsNil) + b, _ := ioutil.ReadAll(sr) + c.Assert(string(b), Equals, + "0032want d82f291cde9987322c8a0c81a325e1ba6159684c\n"+ + "0032want 2b41ef280fdb67a9b250678686a0c3e03b0a9989\n"+ + "0032have 6ecf0ef2c2dffb796033e5a02219af86ec6584e5\n0000"+ + "0009done\n", + ) +} diff --git a/plumbing/transport/http/send_pack.go b/plumbing/transport/http/send_pack.go index 39be95c..5e3b2bb 100644 --- a/plumbing/transport/http/send_pack.go +++ b/plumbing/transport/http/send_pack.go @@ -5,12 +5,13 @@ import ( "io" "net/http" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" "gopkg.in/src-d/go-git.v4/plumbing/transport" ) var errSendPackNotSupported = errors.New("send-pack not supported yet") -type sendPackSession struct{ +type sendPackSession struct { *session } @@ -18,8 +19,7 @@ func newSendPackSession(c *http.Client, ep transport.Endpoint) transport.SendPac return &sendPackSession{&session{}} } -func (s *sendPackSession) AdvertisedReferences() (*transport.UploadPackInfo, - error) { +func (s *sendPackSession) AdvertisedReferences() (*packp.AdvRefs, error) { return nil, errSendPackNotSupported } diff --git a/plumbing/transport/ssh/fetch_pack.go b/plumbing/transport/ssh/fetch_pack.go index decd9c4..a0f52f1 100644 --- a/plumbing/transport/ssh/fetch_pack.go +++ b/plumbing/transport/ssh/fetch_pack.go @@ -35,7 +35,7 @@ func newFetchPackSession(ep transport.Endpoint) (*fetchPackSession, error) { return s, nil } -func (s *fetchPackSession) AdvertisedReferences() (*transport.UploadPackInfo, error) { +func (s *fetchPackSession) AdvertisedReferences() (*packp.AdvRefs, error) { if s.advRefsRun { return nil, transport.ErrAdvertistedReferencesAlreadyCalled } @@ -46,9 +46,9 @@ func (s *fetchPackSession) AdvertisedReferences() (*transport.UploadPackInfo, er return nil, err } - i := transport.NewUploadPackInfo() - if err := i.Decode(s.stdout); err != nil { - if err != packp.ErrEmpty { + ar := packp.NewAdvRefs() + if err := ar.Decode(s.stdout); err != nil { + if err != packp.ErrEmptyAdvRefs { return nil, err } @@ -65,12 +65,12 @@ func (s *fetchPackSession) AdvertisedReferences() (*transport.UploadPackInfo, er return nil, err } - return i, nil + return ar, nil } // FetchPack returns a packfile for a given upload request. // Closing the returned reader will close the SSH session. -func (s *fetchPackSession) FetchPack(req *transport.UploadPackRequest) ( +func (s *fetchPackSession) FetchPack(req *packp.UploadPackRequest) ( io.ReadCloser, error) { if req.IsEmpty() { @@ -161,13 +161,13 @@ var ( // TODO support acks for common objects // TODO build a proper state machine for all these processing options func fetchPack(w io.WriteCloser, r io.Reader, - req *transport.UploadPackRequest) error { + req *packp.UploadPackRequest) error { - if err := sendUlReq(w, req); err != nil { + if err := req.UploadRequest.Encode(w); err != nil { return fmt.Errorf("sending upload-req message: %s", err) } - if err := sendHaves(w, req); err != nil { + if err := req.UploadHaves.Encode(w); err != nil { return fmt.Errorf("sending haves message: %s", err) } @@ -186,32 +186,6 @@ func fetchPack(w io.WriteCloser, r io.Reader, return nil } -func sendUlReq(w io.Writer, req *transport.UploadPackRequest) error { - ur := packp.NewUlReq() - ur.Wants = req.Wants - ur.Depth = packp.DepthCommits(req.Depth) - e := packp.NewUlReqEncoder(w) - - return e.Encode(ur) -} - -func sendHaves(w io.Writer, req *transport.UploadPackRequest) error { - e := pktline.NewEncoder(w) - for _, have := range req.Haves { - if err := e.Encodef("have %s\n", have); err != nil { - return fmt.Errorf("sending haves for %q: %s", have, err) - } - } - - if len(req.Haves) != 0 { - if err := e.Flush(); err != nil { - return fmt.Errorf("sending flush-pkt after haves: %s", err) - } - } - - return nil -} - func sendDone(w io.Writer) error { e := pktline.NewEncoder(w) diff --git a/plumbing/transport/ssh/send_pack.go b/plumbing/transport/ssh/send_pack.go index afe7510..adf67bb 100644 --- a/plumbing/transport/ssh/send_pack.go +++ b/plumbing/transport/ssh/send_pack.go @@ -4,6 +4,7 @@ import ( "errors" "io" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" "gopkg.in/src-d/go-git.v4/plumbing/transport" ) @@ -19,8 +20,7 @@ func newSendPackSession(ep transport.Endpoint) (transport.SendPackSession, return &sendPackSession{&session{}}, nil } -func (s *sendPackSession) AdvertisedReferences() (*transport.UploadPackInfo, - error) { +func (s *sendPackSession) AdvertisedReferences() (*packp.AdvRefs, error) { return nil, errSendPackNotSupported } diff --git a/plumbing/transport/test/common.go b/plumbing/transport/test/common.go index 059c0c9..7818fbb 100644 --- a/plumbing/transport/test/common.go +++ b/plumbing/transport/test/common.go @@ -10,6 +10,7 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/format/packfile" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/storage/memory" @@ -40,7 +41,7 @@ func (s *FetchPackSuite) TestInfoNotExists(c *C) { r, err = s.Client.NewFetchPackSession(s.NonExistentEndpoint) c.Assert(err, IsNil) - req := &transport.UploadPackRequest{} + req := packp.NewUploadPackRequest() req.Want(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) reader, err := r.FetchPack(req) c.Assert(err, Equals, transport.ErrRepositoryNotFound) @@ -85,7 +86,7 @@ func (s *FetchPackSuite) TestFullFetchPack(c *C) { c.Assert(err, IsNil) c.Assert(info, NotNil) - req := &transport.UploadPackRequest{} + req := packp.NewUploadPackRequest() req.Want(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) reader, err := r.FetchPack(req) @@ -99,7 +100,7 @@ func (s *FetchPackSuite) TestFetchPack(c *C) { c.Assert(err, IsNil) defer func() { c.Assert(r.Close(), IsNil) }() - req := &transport.UploadPackRequest{} + req := packp.NewUploadPackRequest() req.Want(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) reader, err := r.FetchPack(req) @@ -113,7 +114,7 @@ func (s *FetchPackSuite) TestFetchPackNoChanges(c *C) { c.Assert(err, IsNil) defer func() { c.Assert(r.Close(), IsNil) }() - req := &transport.UploadPackRequest{} + req := packp.NewUploadPackRequest() req.Want(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) req.Have(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) @@ -127,7 +128,7 @@ func (s *FetchPackSuite) TestFetchPackMulti(c *C) { c.Assert(err, IsNil) defer func() { c.Assert(r.Close(), IsNil) }() - req := &transport.UploadPackRequest{} + req := packp.NewUploadPackRequest() req.Want(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) req.Want(plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881")) @@ -141,7 +142,7 @@ func (s *FetchPackSuite) TestFetchError(c *C) { r, err := s.Client.NewFetchPackSession(s.Endpoint) c.Assert(err, IsNil) - req := &transport.UploadPackRequest{} + req := packp.NewUploadPackRequest() req.Want(plumbing.NewHash("1111111111111111111111111111111111111111")) reader, err := r.FetchPack(req) @@ -12,6 +12,7 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing/storer" "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/plumbing/transport/client" + "gopkg.in/src-d/go-git.v4/storage/memory" ) var NoErrAlreadyUpToDate = errors.New("already up-to-date") @@ -25,7 +26,8 @@ type Remote struct { endpoint transport.Endpoint client transport.Client fetchSession transport.FetchPackSession - upInfo *transport.UploadPackInfo + advRefs *packp.AdvRefs + refs memory.ReferenceStorage } func newRemote(s Storer, c *config.RemoteConfig) *Remote { @@ -49,7 +51,7 @@ func (r *Remote) Connect() error { return nil } - return r.retrieveUpInfo() + return r.retrieveAdvertisedReferences() } func (r *Remote) initClient() error { @@ -71,20 +73,25 @@ func (r *Remote) initClient() error { return nil } -func (r *Remote) retrieveUpInfo() error { +func (r *Remote) retrieveAdvertisedReferences() error { var err error - r.upInfo, err = r.fetchSession.AdvertisedReferences() + r.advRefs, err = r.fetchSession.AdvertisedReferences() + if err != nil { + return err + } + + r.refs, err = r.advRefs.AllReferences() return err } -// Info returns the git-upload-pack info -func (r *Remote) Info() *transport.UploadPackInfo { - return r.upInfo +// AdvertisedReferences returns the git-upload-pack advertised references. +func (r *Remote) AdvertisedReferences() *packp.AdvRefs { + return r.advRefs } // Capabilities returns the remote capabilities func (r *Remote) Capabilities() *packp.Capabilities { - return r.upInfo.Capabilities + return r.advRefs.Capabilities } // Fetch returns a reader using the request @@ -126,7 +133,7 @@ func (r *Remote) Fetch(o *FetchOptions) (err error) { func (r *Remote) getWantedReferences(spec []config.RefSpec) ([]*plumbing.Reference, error) { var refs []*plumbing.Reference - iter, err := r.Refs() + iter, err := r.References() if err != nil { return refs, err } @@ -162,9 +169,9 @@ func (r *Remote) getWantedReferences(spec []config.RefSpec) ([]*plumbing.Referen func (r *Remote) buildRequest( s storer.ReferenceStorer, o *FetchOptions, refs []*plumbing.Reference, -) (*transport.UploadPackRequest, error) { - req := &transport.UploadPackRequest{} - req.Depth = o.Depth +) (*packp.UploadPackRequest, error) { + req := packp.NewUploadPackRequest() + req.Depth = packp.DepthCommits(o.Depth) for _, ref := range refs { req.Want(ref.Hash()) @@ -232,7 +239,7 @@ func (r *Remote) updateLocalReferenceStorage(specs []config.RefSpec, refs []*plu } func (r *Remote) buildFetchedTags() error { - iter, err := r.Refs() + iter, err := r.References() if err != nil { return err } @@ -257,26 +264,31 @@ func (r *Remote) buildFetchedTags() error { // Head returns the Reference of the HEAD func (r *Remote) Head() *plumbing.Reference { - return r.upInfo.Head() + ref, err := storer.ResolveReference(r.refs, plumbing.HEAD) + if err != nil { + return nil + } + + return ref } -// Ref returns the Hash pointing the given refName -func (r *Remote) Ref(name plumbing.ReferenceName, resolved bool) (*plumbing.Reference, error) { +// Reference returns a Reference for a ReferenceName. +func (r *Remote) Reference(name plumbing.ReferenceName, resolved bool) (*plumbing.Reference, error) { if resolved { - return storer.ResolveReference(r.upInfo.Refs, name) + return storer.ResolveReference(r.refs, name) } - return r.upInfo.Refs.Reference(name) + return r.refs.Reference(name) } -// Refs returns a map with all the References -func (r *Remote) Refs() (storer.ReferenceIter, error) { - return r.upInfo.Refs.IterReferences() +// References returns an iterator for all references. +func (r *Remote) References() (storer.ReferenceIter, error) { + return r.refs.IterReferences() } // Disconnect from the remote and save the config func (r *Remote) Disconnect() error { - r.upInfo = nil + r.advRefs = nil return r.fetchSession.Close() } diff --git a/remote_test.go b/remote_test.go index 4df8767..ff543f2 100644 --- a/remote_test.go +++ b/remote_test.go @@ -54,10 +54,10 @@ func (s *RemoteSuite) TestInfo(c *C) { r := newRemote(nil, &config.RemoteConfig{Name: "foo", URL: RepositoryFixture}) r.client = &MockClient{} - c.Assert(r.Info(), IsNil) + c.Assert(r.AdvertisedReferences(), IsNil) c.Assert(r.Connect(), IsNil) - c.Assert(r.Info(), NotNil) - c.Assert(r.Info().Capabilities.Get("ofs-delta"), NotNil) + c.Assert(r.AdvertisedReferences(), NotNil) + c.Assert(r.AdvertisedReferences().Capabilities.Get("ofs-delta"), NotNil) } func (s *RemoteSuite) TestDefaultBranch(c *C) { @@ -180,11 +180,11 @@ func (s *RemoteSuite) TestRef(c *C) { err := r.Connect() c.Assert(err, IsNil) - ref, err := r.Ref(plumbing.HEAD, false) + ref, err := r.Reference(plumbing.HEAD, false) c.Assert(err, IsNil) c.Assert(ref.Name(), Equals, plumbing.HEAD) - ref, err = r.Ref(plumbing.HEAD, true) + ref, err = r.Reference(plumbing.HEAD, true) c.Assert(err, IsNil) c.Assert(ref.Name(), Equals, plumbing.ReferenceName("refs/heads/master")) } @@ -196,7 +196,7 @@ func (s *RemoteSuite) TestRefs(c *C) { err := r.Connect() c.Assert(err, IsNil) - iter, err := r.Refs() + iter, err := r.References() c.Assert(err, IsNil) c.Assert(iter, NotNil) } diff --git a/repository.go b/repository.go index ec3a1f9..a1a5cae 100644 --- a/repository.go +++ b/repository.go @@ -160,7 +160,7 @@ func (r *Repository) Clone(o *CloneOptions) error { return err } - head, err := remote.Ref(o.ReferenceName, true) + head, err := remote.Reference(o.ReferenceName, true) if err != nil { return err } @@ -177,7 +177,12 @@ func (r *Repository) updateRemoteConfig( return nil } - head, err := storer.ResolveReference(remote.Info().Refs, o.ReferenceName) + refs, err := remote.AdvertisedReferences().AllReferences() + if err != nil { + return err + } + + head, err := storer.ResolveReference(refs, o.ReferenceName) if err != nil { return err } @@ -242,7 +247,7 @@ func (r *Repository) Pull(o *PullOptions) error { defer remote.Disconnect() - head, err := remote.Ref(o.ReferenceName, true) + head, err := remote.Reference(o.ReferenceName, true) if err != nil { return err } |