2
2
package common
3
3
4
4
import (
5
+ "bytes"
5
6
"errors"
6
7
"fmt"
7
8
"io"
@@ -11,6 +12,8 @@ import (
11
12
"strings"
12
13
13
14
"gopkg.in/src-d/go-git.v4/core"
15
+ "gopkg.in/src-d/go-git.v4/formats/packp"
16
+ "gopkg.in/src-d/go-git.v4/formats/packp/advrefs"
14
17
"gopkg.in/src-d/go-git.v4/formats/packp/pktline"
15
18
"gopkg.in/src-d/go-git.v4/storage/memory"
16
19
)
@@ -75,246 +78,100 @@ func (e *Endpoint) String() string {
75
78
return u .String ()
76
79
}
77
80
78
- // Capabilities contains all the server capabilities
79
- // https://github.com/git/git/blob/master/Documentation/technical/protocol-capabilities.txt
80
- type Capabilities struct {
81
- m map [string ]* Capability
82
- o []string
83
- }
84
-
85
- // Capability represents a server capability
86
- type Capability struct {
87
- Name string
88
- Values []string
81
+ type GitUploadPackInfo struct {
82
+ Capabilities * packp.Capabilities
83
+ Refs memory.ReferenceStorage
89
84
}
90
85
91
- // NewCapabilities returns a new Capabilities struct
92
- func NewCapabilities () * Capabilities {
93
- return & Capabilities {
94
- m : make (map [string ]* Capability , 0 ),
95
- }
86
+ func NewGitUploadPackInfo () * GitUploadPackInfo {
87
+ return & GitUploadPackInfo {Capabilities : packp .NewCapabilities ()}
96
88
}
97
89
98
- // Decode decodes a string
99
- func (c * Capabilities ) Decode (raw string ) {
100
- params := strings .Split (raw , " " )
101
- for _ , p := range params {
102
- s := strings .SplitN (p , "=" , 2 )
103
-
104
- var value string
105
- if len (s ) == 2 {
106
- value = s [1 ]
90
+ func (i * GitUploadPackInfo ) Decode (r io.Reader ) error {
91
+ d := advrefs .NewDecoder (r )
92
+ ar := advrefs .New ()
93
+ if err := d .Decode (ar ); err != nil {
94
+ if err == advrefs .ErrEmpty {
95
+ return core .NewPermanentError (err )
107
96
}
108
-
109
- c .Add (s [0 ], value )
110
- }
111
- }
112
-
113
- // Get returns the values for a capability
114
- func (c * Capabilities ) Get (capability string ) * Capability {
115
- return c .m [capability ]
116
- }
117
-
118
- // Set sets a capability removing the values
119
- func (c * Capabilities ) Set (capability string , values ... string ) {
120
- if _ , ok := c .m [capability ]; ok {
121
- delete (c .m , capability )
97
+ return core .NewUnexpectedError (err )
122
98
}
123
99
124
- c .Add (capability , values ... )
125
- }
126
-
127
- // Add adds a capability, values are optional
128
- func (c * Capabilities ) Add (capability string , values ... string ) {
129
- if ! c .Supports (capability ) {
130
- c .m [capability ] = & Capability {Name : capability }
131
- c .o = append (c .o , capability )
132
- }
100
+ i .Capabilities = ar .Capabilities
133
101
134
- if len ( values ) == 0 {
135
- return
102
+ if err := i . addRefs ( ar ); err != nil {
103
+ return core . NewUnexpectedError ( err )
136
104
}
137
105
138
- c .m [capability ].Values = append (c .m [capability ].Values , values ... )
139
- }
140
-
141
- // Supports returns true if capability is present
142
- func (c * Capabilities ) Supports (capability string ) bool {
143
- _ , ok := c .m [capability ]
144
- return ok
106
+ return nil
145
107
}
146
108
147
- // SymbolicReference returns the reference for a given symbolic reference
148
- func (c * Capabilities ) SymbolicReference (sym string ) string {
149
- if ! c .Supports ("symref" ) {
150
- return ""
151
- }
152
-
153
- for _ , symref := range c .Get ("symref" ).Values {
154
- parts := strings .Split (symref , ":" )
155
- if len (parts ) != 2 {
156
- continue
157
- }
158
-
159
- if parts [0 ] == sym {
160
- return parts [1 ]
161
- }
109
+ func (i * GitUploadPackInfo ) addRefs (ar * advrefs.AdvRefs ) error {
110
+ i .Refs = make (memory.ReferenceStorage , 0 )
111
+ for name , hash := range ar .References {
112
+ ref := core .NewReferenceFromStrings (name , hash .String ())
113
+ i .Refs .Set (ref )
162
114
}
163
115
164
- return ""
116
+ return i . addSymbolicRefs ( ar )
165
117
}
166
118
167
- func (c * Capabilities ) String () string {
168
- if len (c .o ) == 0 {
169
- return ""
170
- }
171
-
172
- var o string
173
- for _ , key := range c .o {
174
- cap := c .m [key ]
175
-
176
- added := false
177
- for _ , value := range cap .Values {
178
- if value == "" {
179
- continue
180
- }
181
-
182
- added = true
183
- o += fmt .Sprintf ("%s=%s " , key , value )
184
- }
185
-
186
- if len (cap .Values ) == 0 || ! added {
187
- o += key + " "
188
- }
189
- }
190
-
191
- if len (o ) == 0 {
192
- return o
119
+ func (i * GitUploadPackInfo ) addSymbolicRefs (ar * advrefs.AdvRefs ) error {
120
+ if ! hasSymrefs (ar ) {
121
+ return nil
193
122
}
194
123
195
- return o [:len (o )- 1 ]
196
- }
197
-
198
- type GitUploadPackInfo struct {
199
- Capabilities * Capabilities
200
- Refs memory.ReferenceStorage
201
- }
202
-
203
- func NewGitUploadPackInfo () * GitUploadPackInfo {
204
- return & GitUploadPackInfo {Capabilities : NewCapabilities ()}
205
- }
206
-
207
- func (r * GitUploadPackInfo ) Decode (s * pktline.Scanner ) error {
208
- if err := r .read (s ); err != nil {
209
- if err == ErrEmptyGitUploadPack {
210
- return core .NewPermanentError (err )
124
+ for _ , symref := range ar .Capabilities .Get ("symref" ).Values {
125
+ chunks := strings .Split (symref , ":" )
126
+ if len (chunks ) != 2 {
127
+ err := fmt .Errorf ("bad number of `:` in symref value (%q)" , symref )
128
+ return core .NewUnexpectedError (err )
211
129
}
212
-
213
- return core .NewUnexpectedError (err )
130
+ name := core .ReferenceName (chunks [0 ])
131
+ target := core .ReferenceName (chunks [1 ])
132
+ ref := core .NewSymbolicReference (name , target )
133
+ i .Refs .Set (ref )
214
134
}
215
135
216
136
return nil
217
137
}
218
138
219
- func (r * GitUploadPackInfo ) read (s * pktline.Scanner ) error {
220
- isEmpty := true
221
- r .Refs = make (memory.ReferenceStorage , 0 )
222
- smartCommentIgnore := false
223
- for s .Scan () {
224
- line := string (s .Bytes ())
225
-
226
- if smartCommentIgnore {
227
- // some servers like Github add a flush-pkt after the smart http comment
228
- // that we must ignore to prevent a premature termination of the read.
229
- if len (line ) == 0 {
230
- continue
231
- }
232
- smartCommentIgnore = false
233
- }
234
-
235
- // exit on first flush-pkt
236
- if len (line ) == 0 {
237
- break
238
- }
239
-
240
- if isSmartHttpComment (line ) {
241
- smartCommentIgnore = true
242
- continue
243
- }
244
-
245
- if err := r .readLine (line ); err != nil {
246
- return err
247
- }
248
-
249
- isEmpty = false
250
- }
251
-
252
- if isEmpty {
253
- return ErrEmptyGitUploadPack
254
- }
255
-
256
- return s .Err ()
139
+ func hasSymrefs (ar * advrefs.AdvRefs ) bool {
140
+ return ar .Capabilities .Supports ("symref" )
257
141
}
258
142
259
- func isSmartHttpComment (line string ) bool {
260
- return line [0 ] == '#'
143
+ func (i * GitUploadPackInfo ) Head () * core.Reference {
144
+ ref , _ := core .ResolveReference (i .Refs , core .HEAD )
145
+ return ref
261
146
}
262
147
263
- func (r * GitUploadPackInfo ) readLine (line string ) error {
264
- hashEnd := strings .Index (line , " " )
265
- hash := line [:hashEnd ]
266
-
267
- zeroID := strings .Index (line , string ([]byte {0 }))
268
- if zeroID == - 1 {
269
- name := line [hashEnd + 1 : len (line )- 1 ]
270
- ref := core .NewReferenceFromStrings (name , hash )
271
- return r .Refs .Set (ref )
272
- }
273
-
274
- name := line [hashEnd + 1 : zeroID ]
275
- r .Capabilities .Decode (line [zeroID + 1 : len (line )- 1 ])
276
- if ! r .Capabilities .Supports ("symref" ) {
277
- ref := core .NewReferenceFromStrings (name , hash )
278
- return r .Refs .Set (ref )
279
- }
280
-
281
- target := r .Capabilities .SymbolicReference (name )
282
- ref := core .NewSymbolicReference (core .ReferenceName (name ), core .ReferenceName (target ))
283
- return r .Refs .Set (ref )
148
+ func (i * GitUploadPackInfo ) String () string {
149
+ return string (i .Bytes ())
284
150
}
285
151
286
- func (r * GitUploadPackInfo ) Head () * core.Reference {
287
- ref , _ := core .ResolveReference (r .Refs , core .HEAD )
288
- return ref
289
- }
152
+ func (i * GitUploadPackInfo ) Bytes () []byte {
153
+ var buf bytes.Buffer
154
+ e := pktline .NewEncoder (& buf )
290
155
291
- func (r * GitUploadPackInfo ) String () string {
292
- return string (r .Bytes ())
293
- }
156
+ _ = e .EncodeString ("# service=git-upload-pack\n " )
294
157
295
- func (r * GitUploadPackInfo ) Bytes () []byte {
296
- p := pktline .New ()
297
- _ = p .AddString ("# service=git-upload-pack\n " )
298
158
// inserting a flush-pkt here violates the protocol spec, but some
299
159
// servers do it, like Github.com
300
- p . AddFlush ()
160
+ e . Flush ()
301
161
302
- firstLine := fmt .Sprintf ("%s HEAD\x00 %s\n " , r .Head ().Hash (), r .Capabilities .String ())
303
- _ = p .AddString (firstLine )
162
+ _ = e .Encodef ("%s HEAD\x00 %s\n " , i .Head ().Hash (), i .Capabilities .String ())
304
163
305
- for _ , ref := range r .Refs {
164
+ for _ , ref := range i .Refs {
306
165
if ref .Type () != core .HashReference {
307
166
continue
308
167
}
309
168
310
- ref := fmt .Sprintf ("%s %s\n " , ref .Hash (), ref .Name ())
311
- _ = p .AddString (ref )
169
+ _ = e .Encodef ("%s %s\n " , ref .Hash (), ref .Name ())
312
170
}
313
171
314
- p .AddFlush ()
315
- b , _ := ioutil .ReadAll (p )
172
+ e .Flush ()
316
173
317
- return b
174
+ return buf . Bytes ()
318
175
}
319
176
320
177
type GitUploadPackRequest struct {
@@ -337,24 +194,23 @@ func (r *GitUploadPackRequest) String() string {
337
194
}
338
195
339
196
func (r * GitUploadPackRequest ) Reader () * strings.Reader {
340
- p := pktline .New ()
197
+ var buf bytes.Buffer
198
+ e := pktline .NewEncoder (& buf )
341
199
342
200
for _ , want := range r .Wants {
343
- _ = p . AddString ( fmt . Sprintf ( "want %s\n " , want ) )
201
+ _ = e . Encodef ( "want %s\n " , want )
344
202
}
345
203
346
204
for _ , have := range r .Haves {
347
- _ = p . AddString ( fmt . Sprintf ( "have %s\n " , have ) )
205
+ _ = e . Encodef ( "have %s\n " , have )
348
206
}
349
207
350
208
if r .Depth != 0 {
351
- _ = p . AddString ( fmt . Sprintf ( "deepen %d\n " , r .Depth ) )
209
+ _ = e . Encodef ( "deepen %d\n " , r .Depth )
352
210
}
353
211
354
- p .AddFlush ()
355
- _ = p .AddString ("done\n " )
356
-
357
- b , _ := ioutil .ReadAll (p )
212
+ _ = e .Flush ()
213
+ _ = e .EncodeString ("done\n " )
358
214
359
- return strings .NewReader (string ( b ))
215
+ return strings .NewReader (buf . String ( ))
360
216
}
0 commit comments