From 0b523020ef35b56c1f2481ca3773d974a7b80945 Mon Sep 17 00:00:00 2001 From: Antonio Jesus Navarro Perez Date: Mon, 26 Mar 2018 18:07:09 +0200 Subject: Resolve HEAD if symRefs capability is not supported Signed-off-by: Antonio Jesus Navarro Perez --- plumbing/protocol/packp/advrefs.go | 108 +++++++++++++++++++++++++++++--- plumbing/protocol/packp/advrefs_test.go | 73 +++++++++++++++++++++ 2 files changed, 172 insertions(+), 9 deletions(-) (limited to 'plumbing/protocol/packp') diff --git a/plumbing/protocol/packp/advrefs.go b/plumbing/protocol/packp/advrefs.go index 7d644bc..684e76a 100644 --- a/plumbing/protocol/packp/advrefs.go +++ b/plumbing/protocol/packp/advrefs.go @@ -2,6 +2,7 @@ package packp import ( "fmt" + "sort" "strings" "gopkg.in/src-d/go-git.v4/plumbing" @@ -68,30 +69,119 @@ func (a *AdvRefs) AddReference(r *plumbing.Reference) error { func (a *AdvRefs) AllReferences() (memory.ReferenceStorage, error) { s := memory.ReferenceStorage{} - if err := addRefs(s, a); err != nil { + if err := a.addRefs(s); err != nil { return s, plumbing.NewUnexpectedError(err) } return s, nil } -func addRefs(s storer.ReferenceStorer, ar *AdvRefs) error { - for name, hash := range ar.References { +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 } } - return addSymbolicRefs(s, ar) + if a.supportSymrefs() { + return a.addSymbolicRefs(s) + } + + return a.resolveHead(s) } -func addSymbolicRefs(s storer.ReferenceStorer, ar *AdvRefs) error { - if !hasSymrefs(ar) { +// 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.ReferenceName(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 } - for _, symref := range ar.Capabilities.Get(capability.SymRef) { + 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) @@ -108,6 +198,6 @@ func addSymbolicRefs(s storer.ReferenceStorer, ar *AdvRefs) error { return nil } -func hasSymrefs(ar *AdvRefs) bool { - return ar.Capabilities.Supports(capability.SymRef) +func (a *AdvRefs) supportSymrefs() bool { + return a.Capabilities.Supports(capability.SymRef) } diff --git a/plumbing/protocol/packp/advrefs_test.go b/plumbing/protocol/packp/advrefs_test.go index 0180fd3..bb8d032 100644 --- a/plumbing/protocol/packp/advrefs_test.go +++ b/plumbing/protocol/packp/advrefs_test.go @@ -79,6 +79,79 @@ func (s *AdvRefSuite) TestAllReferencesBadSymref(c *C) { c.Assert(err, NotNil) } +func (s *AdvRefSuite) TestNoSymRefCapabilityHeadToMaster(c *C) { + a := NewAdvRefs() + headHash := plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c") + a.Head = &headHash + ref := plumbing.NewHashReference(plumbing.Master, plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")) + + err := a.AddReference(ref) + c.Assert(err, IsNil) + + storage, err := a.AllReferences() + c.Assert(err, IsNil) + + head, err := storage.Reference(plumbing.HEAD) + c.Assert(err, IsNil) + c.Assert(head.Target(), Equals, ref.Name()) +} + +func (s *AdvRefSuite) TestNoSymRefCapabilityHeadToOtherThanMaster(c *C) { + a := NewAdvRefs() + headHash := plumbing.NewHash("0000000000000000000000000000000000000000") + a.Head = &headHash + ref1 := plumbing.NewHashReference(plumbing.Master, plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")) + ref2 := plumbing.NewHashReference("other/ref", plumbing.NewHash("0000000000000000000000000000000000000000")) + + err := a.AddReference(ref1) + c.Assert(err, IsNil) + err = a.AddReference(ref2) + c.Assert(err, IsNil) + + storage, err := a.AllReferences() + c.Assert(err, IsNil) + + head, err := storage.Reference(plumbing.HEAD) + c.Assert(err, IsNil) + c.Assert(head.Hash(), Equals, ref2.Hash()) +} + +func (s *AdvRefSuite) TestNoSymRefCapabilityHeadToNoRef(c *C) { + a := NewAdvRefs() + headHash := plumbing.NewHash("0000000000000000000000000000000000000000") + a.Head = &headHash + ref := plumbing.NewHashReference(plumbing.Master, plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")) + + err := a.AddReference(ref) + c.Assert(err, IsNil) + + _, err = a.AllReferences() + c.Assert(err, NotNil) +} + +func (s *AdvRefSuite) TestNoSymRefCapabilityHeadToNoMasterAlphabeticallyOrdered(c *C) { + a := NewAdvRefs() + headHash := plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c") + a.Head = &headHash + ref1 := plumbing.NewHashReference(plumbing.Master, plumbing.NewHash("0000000000000000000000000000000000000000")) + ref2 := plumbing.NewHashReference("aaaaaaaaaaaaaaa", plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")) + ref3 := plumbing.NewHashReference("bbbbbbbbbbbbbbb", plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")) + + err := a.AddReference(ref1) + c.Assert(err, IsNil) + err = a.AddReference(ref3) + c.Assert(err, IsNil) + err = a.AddReference(ref2) + c.Assert(err, IsNil) + + storage, err := a.AllReferences() + c.Assert(err, IsNil) + + head, err := storage.Reference(plumbing.HEAD) + c.Assert(err, IsNil) + c.Assert(head.Target(), Equals, ref2.Name()) +} + type AdvRefsDecodeEncodeSuite struct{} var _ = Suite(&AdvRefsDecodeEncodeSuite{}) -- cgit