fork of go-git with some jj specific features

Merge pull request #792 from ajnavarro/fix/support-no-symref-capability

Resolve HEAD if symRefs capability is not supported

authored by Máximo Cuadros and committed by GitHub 2cbff8d8 32931400

Changed files
+172 -9
plumbing
protocol
+99 -9
plumbing/protocol/packp/advrefs.go
··· 2 2 3 3 import ( 4 4 "fmt" 5 + "sort" 5 6 "strings" 6 7 7 8 "gopkg.in/src-d/go-git.v4/plumbing" ··· 68 69 69 70 func (a *AdvRefs) AllReferences() (memory.ReferenceStorage, error) { 70 71 s := memory.ReferenceStorage{} 71 - if err := addRefs(s, a); err != nil { 72 + if err := a.addRefs(s); err != nil { 72 73 return s, plumbing.NewUnexpectedError(err) 73 74 } 74 75 75 76 return s, nil 76 77 } 77 78 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 { 80 81 ref := plumbing.NewReferenceFromStrings(name, hash.String()) 81 82 if err := s.SetReference(ref); err != nil { 82 83 return err 83 84 } 84 85 } 85 86 86 - return addSymbolicRefs(s, ar) 87 + if a.supportSymrefs() { 88 + return a.addSymbolicRefs(s) 89 + } 90 + 91 + return a.resolveHead(s) 87 92 } 88 93 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 { 91 107 return nil 92 108 } 93 109 94 - for _, symref := range ar.Capabilities.Get(capability.SymRef) { 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())) 137 + 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 163 + } 164 + 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) { 95 185 chunks := strings.Split(symref, ":") 96 186 if len(chunks) != 2 { 97 187 err := fmt.Errorf("bad number of `:` in symref value (%q)", symref) ··· 108 198 return nil 109 199 } 110 200 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) 113 203 }
+73
plumbing/protocol/packp/advrefs_test.go
··· 79 79 c.Assert(err, NotNil) 80 80 } 81 81 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 + 82 155 type AdvRefsDecodeEncodeSuite struct{} 83 156 84 157 var _ = Suite(&AdvRefsDecodeEncodeSuite{})