package packp import ( "fmt" "strings" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability" "gopkg.in/src-d/go-git.v4/plumbing/storer" "gopkg.in/src-d/go-git.v4/storage/memory" ) // AdvRefs values represent the information transmitted on an // advertised-refs message. Values from this type are not zero-value // safe, use the New function instead. // // When using this messages over (smart) HTTP, you have to add a pktline // before the whole thing with the following payload: // // '# service=$servicename" LF // // Moreover, some (all) git HTTP smart servers will send a flush-pkt // just after the first pkt-line. // // To accomodate both situations, the Prefix field allow you to store // any data you want to send before the actual pktlines. It will also // be filled up with whatever is found on the line. type AdvRefs struct { Prefix [][]byte // payloads of the prefix Head *plumbing.Hash Capabilities *capability.List References map[string]plumbing.Hash Peeled map[string]plumbing.Hash Shallows []plumbing.Hash } // NewAdvRefs returns a pointer to a new AdvRefs value, ready to be used. func NewAdvRefs() *AdvRefs { return &AdvRefs{ Prefix: [][]byte{}, Capabilities: capability.NewList(), References: make(map[string]plumbing.Hash), Peeled: make(map[string]plumbing.Hash), 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(capability.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(capability.SymRef) { 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(capability.SymRef) }