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

Commit 2cbff8d

Browse files
authored
Merge pull request #792 from ajnavarro/fix/support-no-symref-capability
Resolve HEAD if symRefs capability is not supported
2 parents 3293140 + 0b52302 commit 2cbff8d

File tree

2 files changed

+172
-9
lines changed

2 files changed

+172
-9
lines changed

plumbing/protocol/packp/advrefs.go

+99-9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package packp
22

33
import (
44
"fmt"
5+
"sort"
56
"strings"
67

78
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -68,30 +69,119 @@ func (a *AdvRefs) AddReference(r *plumbing.Reference) error {
6869

6970
func (a *AdvRefs) AllReferences() (memory.ReferenceStorage, error) {
7071
s := memory.ReferenceStorage{}
71-
if err := addRefs(s, a); err != nil {
72+
if err := a.addRefs(s); err != nil {
7273
return s, plumbing.NewUnexpectedError(err)
7374
}
7475

7576
return s, nil
7677
}
7778

78-
func addRefs(s storer.ReferenceStorer, ar *AdvRefs) error {
79-
for name, hash := range ar.References {
79+
func (a *AdvRefs) addRefs(s storer.ReferenceStorer) error {
80+
for name, hash := range a.References {
8081
ref := plumbing.NewReferenceFromStrings(name, hash.String())
8182
if err := s.SetReference(ref); err != nil {
8283
return err
8384
}
8485
}
8586

86-
return addSymbolicRefs(s, ar)
87+
if a.supportSymrefs() {
88+
return a.addSymbolicRefs(s)
89+
}
90+
91+
return a.resolveHead(s)
8792
}
8893

89-
func addSymbolicRefs(s storer.ReferenceStorer, ar *AdvRefs) error {
90-
if !hasSymrefs(ar) {
94+
// If the server does not support symrefs capability,
95+
// we need to guess the reference where HEAD is pointing to.
96+
//
97+
// Git versions prior to 1.8.4.3 has an special procedure to get
98+
// the reference where is pointing to HEAD:
99+
// - Check if a reference called master exists. If exists and it
100+
// has the same hash as HEAD hash, we can say that HEAD is pointing to master
101+
// - If master does not exists or does not have the same hash as HEAD,
102+
// order references and check in that order if that reference has the same
103+
// hash than HEAD. If yes, set HEAD pointing to that branch hash
104+
// - If no reference is found, throw an error
105+
func (a *AdvRefs) resolveHead(s storer.ReferenceStorer) error {
106+
if a.Head == nil {
107+
return nil
108+
}
109+
110+
ref, err := s.Reference(plumbing.ReferenceName(plumbing.Master))
111+
112+
// check first if HEAD is pointing to master
113+
if err == nil {
114+
ok, err := a.createHeadIfCorrectReference(ref, s)
115+
if err != nil {
116+
return err
117+
}
118+
119+
if ok {
120+
return nil
121+
}
122+
}
123+
124+
if err != nil && err != plumbing.ErrReferenceNotFound {
125+
return err
126+
}
127+
128+
// From here we are trying to guess the branch that HEAD is pointing
129+
refIter, err := s.IterReferences()
130+
if err != nil {
131+
return err
132+
}
133+
134+
var refNames []string
135+
err = refIter.ForEach(func(r *plumbing.Reference) error {
136+
refNames = append(refNames, string(r.Name()))
91137
return nil
138+
})
139+
if err != nil {
140+
return err
141+
}
142+
143+
sort.Strings(refNames)
144+
145+
var headSet bool
146+
for _, refName := range refNames {
147+
ref, err := s.Reference(plumbing.ReferenceName(refName))
148+
if err != nil {
149+
return err
150+
}
151+
ok, err := a.createHeadIfCorrectReference(ref, s)
152+
if err != nil {
153+
return err
154+
}
155+
if ok {
156+
headSet = true
157+
break
158+
}
159+
}
160+
161+
if !headSet {
162+
return plumbing.ErrReferenceNotFound
92163
}
93164

94-
for _, symref := range ar.Capabilities.Get(capability.SymRef) {
165+
return nil
166+
}
167+
168+
func (a *AdvRefs) createHeadIfCorrectReference(
169+
reference *plumbing.Reference,
170+
s storer.ReferenceStorer) (bool, error) {
171+
if reference.Hash() == *a.Head {
172+
headRef := plumbing.NewSymbolicReference(plumbing.HEAD, reference.Name())
173+
if err := s.SetReference(headRef); err != nil {
174+
return false, err
175+
}
176+
177+
return true, nil
178+
}
179+
180+
return false, nil
181+
}
182+
183+
func (a *AdvRefs) addSymbolicRefs(s storer.ReferenceStorer) error {
184+
for _, symref := range a.Capabilities.Get(capability.SymRef) {
95185
chunks := strings.Split(symref, ":")
96186
if len(chunks) != 2 {
97187
err := fmt.Errorf("bad number of `:` in symref value (%q)", symref)
@@ -108,6 +198,6 @@ func addSymbolicRefs(s storer.ReferenceStorer, ar *AdvRefs) error {
108198
return nil
109199
}
110200

111-
func hasSymrefs(ar *AdvRefs) bool {
112-
return ar.Capabilities.Supports(capability.SymRef)
201+
func (a *AdvRefs) supportSymrefs() bool {
202+
return a.Capabilities.Supports(capability.SymRef)
113203
}

plumbing/protocol/packp/advrefs_test.go

+73
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,79 @@ func (s *AdvRefSuite) TestAllReferencesBadSymref(c *C) {
7979
c.Assert(err, NotNil)
8080
}
8181

82+
func (s *AdvRefSuite) TestNoSymRefCapabilityHeadToMaster(c *C) {
83+
a := NewAdvRefs()
84+
headHash := plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")
85+
a.Head = &headHash
86+
ref := plumbing.NewHashReference(plumbing.Master, plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"))
87+
88+
err := a.AddReference(ref)
89+
c.Assert(err, IsNil)
90+
91+
storage, err := a.AllReferences()
92+
c.Assert(err, IsNil)
93+
94+
head, err := storage.Reference(plumbing.HEAD)
95+
c.Assert(err, IsNil)
96+
c.Assert(head.Target(), Equals, ref.Name())
97+
}
98+
99+
func (s *AdvRefSuite) TestNoSymRefCapabilityHeadToOtherThanMaster(c *C) {
100+
a := NewAdvRefs()
101+
headHash := plumbing.NewHash("0000000000000000000000000000000000000000")
102+
a.Head = &headHash
103+
ref1 := plumbing.NewHashReference(plumbing.Master, plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"))
104+
ref2 := plumbing.NewHashReference("other/ref", plumbing.NewHash("0000000000000000000000000000000000000000"))
105+
106+
err := a.AddReference(ref1)
107+
c.Assert(err, IsNil)
108+
err = a.AddReference(ref2)
109+
c.Assert(err, IsNil)
110+
111+
storage, err := a.AllReferences()
112+
c.Assert(err, IsNil)
113+
114+
head, err := storage.Reference(plumbing.HEAD)
115+
c.Assert(err, IsNil)
116+
c.Assert(head.Hash(), Equals, ref2.Hash())
117+
}
118+
119+
func (s *AdvRefSuite) TestNoSymRefCapabilityHeadToNoRef(c *C) {
120+
a := NewAdvRefs()
121+
headHash := plumbing.NewHash("0000000000000000000000000000000000000000")
122+
a.Head = &headHash
123+
ref := plumbing.NewHashReference(plumbing.Master, plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"))
124+
125+
err := a.AddReference(ref)
126+
c.Assert(err, IsNil)
127+
128+
_, err = a.AllReferences()
129+
c.Assert(err, NotNil)
130+
}
131+
132+
func (s *AdvRefSuite) TestNoSymRefCapabilityHeadToNoMasterAlphabeticallyOrdered(c *C) {
133+
a := NewAdvRefs()
134+
headHash := plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")
135+
a.Head = &headHash
136+
ref1 := plumbing.NewHashReference(plumbing.Master, plumbing.NewHash("0000000000000000000000000000000000000000"))
137+
ref2 := plumbing.NewHashReference("aaaaaaaaaaaaaaa", plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"))
138+
ref3 := plumbing.NewHashReference("bbbbbbbbbbbbbbb", plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"))
139+
140+
err := a.AddReference(ref1)
141+
c.Assert(err, IsNil)
142+
err = a.AddReference(ref3)
143+
c.Assert(err, IsNil)
144+
err = a.AddReference(ref2)
145+
c.Assert(err, IsNil)
146+
147+
storage, err := a.AllReferences()
148+
c.Assert(err, IsNil)
149+
150+
head, err := storage.Reference(plumbing.HEAD)
151+
c.Assert(err, IsNil)
152+
c.Assert(head.Target(), Equals, ref2.Name())
153+
}
154+
82155
type AdvRefsDecodeEncodeSuite struct{}
83156

84157
var _ = Suite(&AdvRefsDecodeEncodeSuite{})

0 commit comments

Comments
 (0)