aboutsummaryrefslogblamecommitdiffstats
path: root/plumbing/protocol/packp/advrefs.go
blob: a0587ab295e6b1072faec7cc54ca147d91300dc1 (plain) (tree)
1
2
3
4
5
6
7
8
             

        


                 
                                           
                                                                     



                                                  
















                                                                        
                                   
                                     


                                             

 

                                                                         

                                         
                                                   


                                                             

         




                                                                                 
                                                        

































                                                                            
                                                                       
















                                                                                           
                                                          
 
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)
}