Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Use advrefs in gituploadpackinfo #92

Merged
merged 28 commits into from
Oct 26, 2016
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2148aa4
add advrefs encoder and parser
alcortesm Oct 19, 2016
7132597
modify advrefs encoder to resemble json encoder
alcortesm Oct 19, 2016
52977eb
turn advrefs parser into a decoder
alcortesm Oct 19, 2016
6cfbf27
clean code
alcortesm Oct 19, 2016
3cee874
improve documentation
alcortesm Oct 19, 2016
8eb680e
improve documentation
alcortesm Oct 19, 2016
b5a7454
clean code
alcortesm Oct 19, 2016
ef3f004
Merge branch 'master' into use-advrefs-in-gituploadpackinfo
alcortesm Oct 20, 2016
9b4e8dd
upgrade to new pktline.Add and add Flush const to easy integration
alcortesm Oct 20, 2016
e88e8e6
gometalinter
alcortesm Oct 20, 2016
6c4306f
Use packp/advrefs for GitUploadPackInfo parsing
alcortesm Oct 21, 2016
9b1f318
clean advrefs test code
alcortesm Oct 21, 2016
e624ec4
clean advrefs test code
alcortesm Oct 21, 2016
b27bc9c
clean advrefs test code
alcortesm Oct 21, 2016
0e86933
gometalinter
alcortesm Oct 21, 2016
fe0746e
add pktline encoder
alcortesm Oct 21, 2016
ea279b2
change pktline.EncodeFlush to pktline.Flush
alcortesm Oct 21, 2016
3070114
make scanner tests use the encoder instead of Pktlines
alcortesm Oct 21, 2016
0e48e52
check errors on flush and clean constants
alcortesm Oct 21, 2016
401d447
ubstitute the PktLines type with a pktline.Encoder
alcortesm Oct 21, 2016
f28fdb4
use pktline.Encoder in all go-git
alcortesm Oct 21, 2016
99490ca
add example of pktline.Encodef()
alcortesm Oct 21, 2016
1683b16
add package overview
alcortesm Oct 21, 2016
2d11a06
documentation
alcortesm Oct 23, 2016
d6c6e69
support symbolic links other than HEAD
alcortesm Oct 23, 2016
ce4d701
simplify decoding of shallows
alcortesm Oct 23, 2016
a23e180
Merge branch 'master' into use-advrefs-in-gituploadpackinfo
alcortesm Oct 25, 2016
97b8650
packp: fix mcuadros comments
alcortesm Oct 26, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 62 additions & 206 deletions clients/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package common

import (
"bytes"
"errors"
"fmt"
"io"
Expand All @@ -11,6 +12,8 @@ import (
"strings"

"gopkg.in/src-d/go-git.v4/core"
"gopkg.in/src-d/go-git.v4/formats/packp"
"gopkg.in/src-d/go-git.v4/formats/packp/advrefs"
"gopkg.in/src-d/go-git.v4/formats/packp/pktline"
"gopkg.in/src-d/go-git.v4/storage/memory"
)
Expand Down Expand Up @@ -75,246 +78,100 @@ func (e *Endpoint) String() string {
return u.String()
}

// Capabilities contains all the server capabilities
// https://github.com/git/git/blob/master/Documentation/technical/protocol-capabilities.txt
type Capabilities struct {
m map[string]*Capability
o []string
}

// Capability represents a server capability
type Capability struct {
Name string
Values []string
type GitUploadPackInfo struct {
Capabilities *packp.Capabilities
Refs memory.ReferenceStorage
}

// NewCapabilities returns a new Capabilities struct
func NewCapabilities() *Capabilities {
return &Capabilities{
m: make(map[string]*Capability, 0),
}
func NewGitUploadPackInfo() *GitUploadPackInfo {
return &GitUploadPackInfo{Capabilities: packp.NewCapabilities()}
}

// Decode decodes a string
func (c *Capabilities) Decode(raw string) {
params := strings.Split(raw, " ")
for _, p := range params {
s := strings.SplitN(p, "=", 2)

var value string
if len(s) == 2 {
value = s[1]
func (i *GitUploadPackInfo) Decode(r io.Reader) error {
d := advrefs.NewDecoder(r)
ar := advrefs.New()
if err := d.Decode(ar); err != nil {
if err == advrefs.ErrEmpty {
return core.NewPermanentError(err)
}

c.Add(s[0], value)
}
}

// Get returns the values for a capability
func (c *Capabilities) Get(capability string) *Capability {
return c.m[capability]
}

// Set sets a capability removing the values
func (c *Capabilities) Set(capability string, values ...string) {
if _, ok := c.m[capability]; ok {
delete(c.m, capability)
return core.NewUnexpectedError(err)
}

c.Add(capability, values...)
}

// Add adds a capability, values are optional
func (c *Capabilities) Add(capability string, values ...string) {
if !c.Supports(capability) {
c.m[capability] = &Capability{Name: capability}
c.o = append(c.o, capability)
}
i.Capabilities = ar.Caps

if len(values) == 0 {
return
if err := i.addRefs(ar); err != nil {
return core.NewUnexpectedError(err)
}

c.m[capability].Values = append(c.m[capability].Values, values...)
}

// Supports returns true if capability is present
func (c *Capabilities) Supports(capability string) bool {
_, ok := c.m[capability]
return ok
return nil
}

// SymbolicReference returns the reference for a given symbolic reference
func (c *Capabilities) SymbolicReference(sym string) string {
if !c.Supports("symref") {
return ""
}

for _, symref := range c.Get("symref").Values {
parts := strings.Split(symref, ":")
if len(parts) != 2 {
continue
}

if parts[0] == sym {
return parts[1]
}
func (i *GitUploadPackInfo) addRefs(ar *advrefs.AdvRefs) error {
i.Refs = make(memory.ReferenceStorage, 0)
for name, hash := range ar.Refs {
ref := core.NewReferenceFromStrings(name, hash.String())
i.Refs.Set(ref)
}

return ""
return i.addSymbolicRefs(ar)
}

func (c *Capabilities) String() string {
if len(c.o) == 0 {
return ""
}

var o string
for _, key := range c.o {
cap := c.m[key]

added := false
for _, value := range cap.Values {
if value == "" {
continue
}

added = true
o += fmt.Sprintf("%s=%s ", key, value)
}

if len(cap.Values) == 0 || !added {
o += key + " "
}
}

if len(o) == 0 {
return o
func (i *GitUploadPackInfo) addSymbolicRefs(ar *advrefs.AdvRefs) error {
if !hasSymrefs(ar) {
return nil
}

return o[:len(o)-1]
}

type GitUploadPackInfo struct {
Capabilities *Capabilities
Refs memory.ReferenceStorage
}

func NewGitUploadPackInfo() *GitUploadPackInfo {
return &GitUploadPackInfo{Capabilities: NewCapabilities()}
}

func (r *GitUploadPackInfo) Decode(s *pktline.Scanner) error {
if err := r.read(s); err != nil {
if err == ErrEmptyGitUploadPack {
return core.NewPermanentError(err)
for _, symref := range ar.Caps.Get("symref").Values {
chunks := strings.Split(symref, ":")
if len(chunks) != 2 {
err := fmt.Errorf("bad number of `:` in symref value (%q)", symref)
return core.NewUnexpectedError(err)
}

return core.NewUnexpectedError(err)
name := core.ReferenceName(chunks[0])
target := core.ReferenceName(chunks[1])
ref := core.NewSymbolicReference(name, target)
i.Refs.Set(ref)
}

return nil
}

func (r *GitUploadPackInfo) read(s *pktline.Scanner) error {
isEmpty := true
r.Refs = make(memory.ReferenceStorage, 0)
smartCommentIgnore := false
for s.Scan() {
line := string(s.Bytes())

if smartCommentIgnore {
// some servers like Github add a flush-pkt after the smart http comment
// that we must ignore to prevent a premature termination of the read.
if len(line) == 0 {
continue
}
smartCommentIgnore = false
}

// exit on first flush-pkt
if len(line) == 0 {
break
}

if isSmartHttpComment(line) {
smartCommentIgnore = true
continue
}

if err := r.readLine(line); err != nil {
return err
}

isEmpty = false
}

if isEmpty {
return ErrEmptyGitUploadPack
}

return s.Err()
func hasSymrefs(ar *advrefs.AdvRefs) bool {
return ar.Caps.Supports("symref")
}

func isSmartHttpComment(line string) bool {
return line[0] == '#'
func (i *GitUploadPackInfo) Head() *core.Reference {
ref, _ := core.ResolveReference(i.Refs, core.HEAD)
return ref
}

func (r *GitUploadPackInfo) readLine(line string) error {
hashEnd := strings.Index(line, " ")
hash := line[:hashEnd]

zeroID := strings.Index(line, string([]byte{0}))
if zeroID == -1 {
name := line[hashEnd+1 : len(line)-1]
ref := core.NewReferenceFromStrings(name, hash)
return r.Refs.Set(ref)
}

name := line[hashEnd+1 : zeroID]
r.Capabilities.Decode(line[zeroID+1 : len(line)-1])
if !r.Capabilities.Supports("symref") {
ref := core.NewReferenceFromStrings(name, hash)
return r.Refs.Set(ref)
}

target := r.Capabilities.SymbolicReference(name)
ref := core.NewSymbolicReference(core.ReferenceName(name), core.ReferenceName(target))
return r.Refs.Set(ref)
func (i *GitUploadPackInfo) String() string {
return string(i.Bytes())
}

func (r *GitUploadPackInfo) Head() *core.Reference {
ref, _ := core.ResolveReference(r.Refs, core.HEAD)
return ref
}
func (i *GitUploadPackInfo) Bytes() []byte {
var buf bytes.Buffer
e := pktline.NewEncoder(&buf)

func (r *GitUploadPackInfo) String() string {
return string(r.Bytes())
}
_ = e.EncodeString("# service=git-upload-pack\n")

func (r *GitUploadPackInfo) Bytes() []byte {
p := pktline.New()
_ = p.AddString("# service=git-upload-pack\n")
// inserting a flush-pkt here violates the protocol spec, but some
// servers do it, like Github.com
p.AddFlush()
e.Flush()

firstLine := fmt.Sprintf("%s HEAD\x00%s\n", r.Head().Hash(), r.Capabilities.String())
_ = p.AddString(firstLine)
_ = e.Encodef("%s HEAD\x00%s\n", i.Head().Hash(), i.Capabilities.String())

for _, ref := range r.Refs {
for _, ref := range i.Refs {
if ref.Type() != core.HashReference {
continue
}

ref := fmt.Sprintf("%s %s\n", ref.Hash(), ref.Name())
_ = p.AddString(ref)
_ = e.Encodef("%s %s\n", ref.Hash(), ref.Name())
}

p.AddFlush()
b, _ := ioutil.ReadAll(p)
e.Flush()

return b
return buf.Bytes()
}

type GitUploadPackRequest struct {
Expand All @@ -337,24 +194,23 @@ func (r *GitUploadPackRequest) String() string {
}

func (r *GitUploadPackRequest) Reader() *strings.Reader {
p := pktline.New()
var buf bytes.Buffer
e := pktline.NewEncoder(&buf)

for _, want := range r.Wants {
_ = p.AddString(fmt.Sprintf("want %s\n", want))
_ = e.Encodef("want %s\n", want)
}

for _, have := range r.Haves {
_ = p.AddString(fmt.Sprintf("have %s\n", have))
_ = e.Encodef("have %s\n", have)
}

if r.Depth != 0 {
_ = p.AddString(fmt.Sprintf("deepen %d\n", r.Depth))
_ = e.Encodef("deepen %d\n", r.Depth)
}

p.AddFlush()
_ = p.AddString("done\n")

b, _ := ioutil.ReadAll(p)
_ = e.Flush()
_ = e.EncodeString("done\n")

return strings.NewReader(string(b))
return strings.NewReader(buf.String())
}
Loading