diff options
author | Santiago M. Mola <santi@mola.io> | 2016-11-23 15:30:34 +0100 |
---|---|---|
committer | Máximo Cuadros <mcuadros@gmail.com> | 2016-11-23 15:38:12 +0100 |
commit | 08e08d771ef03df80248c80d81475fe7c5ea6fe7 (patch) | |
tree | d12e9befa22409e8cf50c5bbc4895e69fd8a5f48 /plumbing/transport/fetch_pack.go | |
parent | 844169a739fb8bf1f252d416f10d8c7034db9fe2 (diff) | |
download | go-git-08e08d771ef03df80248c80d81475fe7c5ea6fe7.tar.gz |
transport: create Client interface (#132)
* plumbing: move plumbing/client package to plumbing/transport.
* transport: create Client interface.
* A Client can instantiate any client transport service.
* InstallProtocol installs a Client for a given protocol,
instead of just a UploadPackService.
* A Client can open a session for fetch-pack or send-pack
for a specific Endpoint.
* Adapt ssh and http clients to the new client interface.
* updated doc
Diffstat (limited to 'plumbing/transport/fetch_pack.go')
-rw-r--r-- | plumbing/transport/fetch_pack.go | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/plumbing/transport/fetch_pack.go b/plumbing/transport/fetch_pack.go new file mode 100644 index 0000000..3b2a39c --- /dev/null +++ b/plumbing/transport/fetch_pack.go @@ -0,0 +1,156 @@ +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/packp" + "gopkg.in/src-d/go-git.v4/plumbing/format/packp/advrefs" + "gopkg.in/src-d/go-git.v4/plumbing/format/packp/pktline" + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/storage/memory" +) + +//TODO: Replace this by advrefs.AdvRefs. +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 := advrefs.NewDecoder(r) + ar := advrefs.New() + if err := d.Decode(ar); err != nil { + if err == advrefs.ErrEmpty { + return plumbing.NewPermanentError(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 *advrefs.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 *advrefs.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 *advrefs.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) 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()) +} |