123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- package packp
-
- import (
- "fmt"
- "sort"
- "strings"
-
- "github.com/go-git/go-git/v5/plumbing"
- "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
- "github.com/go-git/go-git/v5/plumbing/storer"
- "github.com/go-git/go-git/v5/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.
- type AdvRefs struct {
- // Prefix stores prefix payloads.
- //
- // When using this message 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 accommodate 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.
- Prefix [][]byte
- // Head stores the resolved HEAD reference if present.
- // This can be present with git-upload-pack, not with git-receive-pack.
- Head *plumbing.Hash
- // Capabilities are the capabilities.
- Capabilities *capability.List
- // References are the hash references.
- References map[string]plumbing.Hash
- // Peeled are the peeled hash references.
- Peeled map[string]plumbing.Hash
- // Shallows are the shallow object ids.
- 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 := a.addRefs(s); err != nil {
- return s, plumbing.NewUnexpectedError(err)
- }
-
- return s, nil
- }
-
- func (a *AdvRefs) addRefs(s storer.ReferenceStorer) error {
- for name, hash := range a.References {
- ref := plumbing.NewReferenceFromStrings(name, hash.String())
- if err := s.SetReference(ref); err != nil {
- return err
- }
- }
-
- if a.supportSymrefs() {
- return a.addSymbolicRefs(s)
- }
-
- return a.resolveHead(s)
- }
-
- // If the server does not support symrefs capability,
- // we need to guess the reference where HEAD is pointing to.
- //
- // Git versions prior to 1.8.4.3 has an special procedure to get
- // the reference where is pointing to HEAD:
- // - Check if a reference called master exists. If exists and it
- // has the same hash as HEAD hash, we can say that HEAD is pointing to master
- // - If master does not exists or does not have the same hash as HEAD,
- // order references and check in that order if that reference has the same
- // hash than HEAD. If yes, set HEAD pointing to that branch hash
- // - If no reference is found, throw an error
- func (a *AdvRefs) resolveHead(s storer.ReferenceStorer) error {
- if a.Head == nil {
- return nil
- }
-
- ref, err := s.Reference(plumbing.Master)
-
- // check first if HEAD is pointing to master
- if err == nil {
- ok, err := a.createHeadIfCorrectReference(ref, s)
- if err != nil {
- return err
- }
-
- if ok {
- return nil
- }
- }
-
- if err != nil && err != plumbing.ErrReferenceNotFound {
- return err
- }
-
- // From here we are trying to guess the branch that HEAD is pointing
- refIter, err := s.IterReferences()
- if err != nil {
- return err
- }
-
- var refNames []string
- err = refIter.ForEach(func(r *plumbing.Reference) error {
- refNames = append(refNames, string(r.Name()))
- return nil
- })
- if err != nil {
- return err
- }
-
- sort.Strings(refNames)
-
- var headSet bool
- for _, refName := range refNames {
- ref, err := s.Reference(plumbing.ReferenceName(refName))
- if err != nil {
- return err
- }
- ok, err := a.createHeadIfCorrectReference(ref, s)
- if err != nil {
- return err
- }
- if ok {
- headSet = true
- break
- }
- }
-
- if !headSet {
- return plumbing.ErrReferenceNotFound
- }
-
- return nil
- }
-
- func (a *AdvRefs) createHeadIfCorrectReference(
- reference *plumbing.Reference,
- s storer.ReferenceStorer) (bool, error) {
- if reference.Hash() == *a.Head {
- headRef := plumbing.NewSymbolicReference(plumbing.HEAD, reference.Name())
- if err := s.SetReference(headRef); err != nil {
- return false, err
- }
-
- return true, nil
- }
-
- return false, nil
- }
-
- func (a *AdvRefs) addSymbolicRefs(s storer.ReferenceStorer) error {
- for _, symref := range a.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 (a *AdvRefs) supportSymrefs() bool {
- return a.Capabilities.Supports(capability.SymRef)
- }
-
- // IsEmpty returns true if doesn't contain any reference.
- func (a *AdvRefs) IsEmpty() bool {
- return a.Head == nil &&
- len(a.References) == 0 &&
- len(a.Peeled) == 0 &&
- len(a.Shallows) == 0
- }
|