@@ -225,3 +225,107 @@ func resolveReference(s ReferenceStorage, r *Reference, recursion int) (*Referen
225
225
recursion ++
226
226
return resolveReference (s , t , recursion )
227
227
}
228
+
229
+ const (
230
+ refSpecWildcard = "*"
231
+ refSpecForce = "+"
232
+ refSpecSeparator = ":"
233
+ )
234
+
235
+ // RefSpec is a mapping from local branches to remote references
236
+ // The format of the refspec is an optional +, followed by <src>:<dst>, where
237
+ // <src> is the pattern for references on the remote side and <dst> is where
238
+ // those references will be written locally. The + tells Git to update the
239
+ // reference even if it isn’t a fast-forward.
240
+ // eg.: "+refs/*/*:refs/remotes/origin/*"
241
+ //
242
+ // https://git-scm.com/book/es/v2/Git-Internals-The-Refspec
243
+ type RefSpec string
244
+
245
+ // IsValid validates the RefSpec
246
+ func (s RefSpec ) IsValid () bool {
247
+ spec := string (s )
248
+ if strings .Count (spec , refSpecSeparator ) != 1 {
249
+ return false
250
+ }
251
+
252
+ sep := strings .Index (spec , refSpecSeparator )
253
+ if sep == len (spec ) {
254
+ return false
255
+ }
256
+
257
+ ws := strings .Count (spec [0 :sep ], refSpecWildcard )
258
+ wd := strings .Count (spec [sep + 1 :len (spec )], refSpecWildcard )
259
+ return ws == wd && ws < 2 && wd < 2
260
+ }
261
+
262
+ // IsForceUpdate returns if update is allowed in non fast-forward merges
263
+ func (s RefSpec ) IsForceUpdate () bool {
264
+ if s [0 ] == refSpecForce [0 ] {
265
+ return true
266
+ }
267
+
268
+ return false
269
+ }
270
+
271
+ // Src return the src side
272
+ func (s RefSpec ) Src () string {
273
+ spec := string (s )
274
+ start := strings .Index (spec , refSpecForce ) + 1
275
+ end := strings .Index (spec , refSpecSeparator )
276
+
277
+ return spec [start :end ]
278
+ }
279
+
280
+ // Match match the given ReferenceName against the source
281
+ func (s RefSpec ) Match (n ReferenceName ) bool {
282
+ if ! s .isGlob () {
283
+ return s .matchExact (n )
284
+ }
285
+
286
+ return s .matchGlob (n )
287
+ }
288
+
289
+ func (s RefSpec ) isGlob () bool {
290
+ return strings .Index (string (s ), refSpecWildcard ) != - 1
291
+ }
292
+
293
+ func (s RefSpec ) matchExact (n ReferenceName ) bool {
294
+ return s .Src () == n .String ()
295
+ }
296
+
297
+ func (s RefSpec ) matchGlob (n ReferenceName ) bool {
298
+ src := s .Src ()
299
+ name := n .String ()
300
+ wildcard := strings .Index (src , refSpecWildcard )
301
+
302
+ var prefix , suffix string
303
+ prefix = src [0 :wildcard ]
304
+ if len (src ) < wildcard {
305
+ suffix = src [wildcard + 1 : len (suffix )]
306
+ }
307
+
308
+ return len (name ) > len (prefix )+ len (suffix ) &&
309
+ strings .HasPrefix (name , prefix ) &&
310
+ strings .HasSuffix (name , suffix )
311
+ }
312
+
313
+ // Dst returns the destination for the given remote reference
314
+ func (s RefSpec ) Dst (n ReferenceName ) ReferenceName {
315
+ spec := string (s )
316
+ start := strings .Index (spec , refSpecSeparator ) + 1
317
+ dst := spec [start :len (spec )]
318
+ src := s .Src ()
319
+
320
+ if ! s .isGlob () {
321
+ return ReferenceName (dst )
322
+ }
323
+
324
+ name := n .String ()
325
+ ws := strings .Index (src , refSpecWildcard )
326
+ wd := strings .Index (dst , refSpecWildcard )
327
+ match := name [ws : len (name )- (len (src )- (ws + 1 ))]
328
+
329
+ return ReferenceName (dst [0 :wd ] + match + dst [wd + 1 :len (dst )])
330
+
331
+ }
0 commit comments