8
8
stdioutil "io/ioutil"
9
9
"os"
10
10
"strings"
11
+ "time"
11
12
12
13
"gopkg.in/src-d/go-git.v4/plumbing"
13
14
"gopkg.in/src-d/go-git.v4/utils/ioutil"
@@ -56,14 +57,16 @@ var (
56
57
// The DotGit type represents a local git repository on disk. This
57
58
// type is not zero-value-safe, use the New function to initialize it.
58
59
type DotGit struct {
59
- fs billy.Filesystem
60
+ fs billy.Filesystem
61
+ cachedPackedRefs refCache
62
+ packedRefsLastMod time.Time
60
63
}
61
64
62
65
// New returns a DotGit value ready to be used. The path argument must
63
66
// be the absolute path of a git repository directory (e.g.
64
67
// "/foo/bar/.git").
65
68
func New (fs billy.Filesystem ) * DotGit {
66
- return & DotGit {fs : fs }
69
+ return & DotGit {fs : fs , cachedPackedRefs : make ( refCache ) }
67
70
}
68
71
69
72
// Initialize creates all the folder scaffolding.
@@ -285,15 +288,57 @@ func (d *DotGit) Ref(name plumbing.ReferenceName) (*plumbing.Reference, error) {
285
288
return ref , nil
286
289
}
287
290
288
- refs , err := d .Refs ()
291
+ return d .packedRef (name )
292
+ }
293
+
294
+ func (d * DotGit ) syncPackedRefs () error {
295
+ fi , err := d .fs .Stat (packedRefsPath )
296
+ if os .IsNotExist (err ) {
297
+ return nil
298
+ }
299
+
289
300
if err != nil {
290
- return nil , err
301
+ return err
291
302
}
292
303
293
- for _ , ref := range refs {
294
- if ref .Name () == name {
295
- return ref , nil
304
+ if d .packedRefsLastMod .Before (fi .ModTime ()) {
305
+ d .cachedPackedRefs = make (refCache )
306
+ f , err := d .fs .Open (packedRefsPath )
307
+ if err != nil {
308
+ if os .IsNotExist (err ) {
309
+ return nil
310
+ }
311
+ return err
296
312
}
313
+ defer ioutil .CheckClose (f , & err )
314
+
315
+ s := bufio .NewScanner (f )
316
+ for s .Scan () {
317
+ ref , err := d .processLine (s .Text ())
318
+ if err != nil {
319
+ return err
320
+ }
321
+
322
+ if ref != nil {
323
+ d .cachedPackedRefs [ref .Name ()] = ref
324
+ }
325
+ }
326
+
327
+ d .packedRefsLastMod = fi .ModTime ()
328
+
329
+ return s .Err ()
330
+ }
331
+
332
+ return nil
333
+ }
334
+
335
+ func (d * DotGit ) packedRef (name plumbing.ReferenceName ) (* plumbing.Reference , error ) {
336
+ if err := d .syncPackedRefs (); err != nil {
337
+ return nil , err
338
+ }
339
+
340
+ if ref , ok := d .cachedPackedRefs [name ]; ok {
341
+ return ref , nil
297
342
}
298
343
299
344
return nil , plumbing .ErrReferenceNotFound
@@ -315,28 +360,15 @@ func (d *DotGit) RemoveRef(name plumbing.ReferenceName) error {
315
360
}
316
361
317
362
func (d * DotGit ) addRefsFromPackedRefs (refs * []* plumbing.Reference ) (err error ) {
318
- f , err := d .fs .Open (packedRefsPath )
319
- if err != nil {
320
- if os .IsNotExist (err ) {
321
- return nil
322
- }
363
+ if err := d .syncPackedRefs (); err != nil {
323
364
return err
324
365
}
325
- defer ioutil .CheckClose (f , & err )
326
366
327
- s := bufio .NewScanner (f )
328
- for s .Scan () {
329
- ref , err := d .processLine (s .Text ())
330
- if err != nil {
331
- return err
332
- }
333
-
334
- if ref != nil {
335
- * refs = append (* refs , ref )
336
- }
367
+ for _ , ref := range d .cachedPackedRefs {
368
+ * refs = append (* refs , ref )
337
369
}
338
370
339
- return s . Err ()
371
+ return nil
340
372
}
341
373
342
374
func (d * DotGit ) rewritePackedRefsWithoutRef (name plumbing.ReferenceName ) (err error ) {
@@ -511,3 +543,5 @@ func isNum(b byte) bool {
511
543
func isHexAlpha (b byte ) bool {
512
544
return b >= 'a' && b <= 'f' || b >= 'A' && b <= 'F'
513
545
}
546
+
547
+ type refCache map [plumbing.ReferenceName ]* plumbing.Reference
0 commit comments