88 stdioutil "io/ioutil"
99 "os"
1010 "strings"
11+ "time"
1112
1213 "gopkg.in/src-d/go-git.v4/plumbing"
1314 "gopkg.in/src-d/go-git.v4/utils/ioutil"
@@ -56,14 +57,16 @@ var (
5657// The DotGit type represents a local git repository on disk. This
5758// type is not zero-value-safe, use the New function to initialize it.
5859type DotGit struct {
59- fs billy.Filesystem
60+ fs billy.Filesystem
61+ cachedPackedRefs refCache
62+ packedRefsLastMod time.Time
6063}
6164
6265// New returns a DotGit value ready to be used. The path argument must
6366// be the absolute path of a git repository directory (e.g.
6467// "/foo/bar/.git").
6568func New (fs billy.Filesystem ) * DotGit {
66- return & DotGit {fs : fs }
69+ return & DotGit {fs : fs , cachedPackedRefs : make ( refCache ) }
6770}
6871
6972// Initialize creates all the folder scaffolding.
@@ -285,15 +288,57 @@ func (d *DotGit) Ref(name plumbing.ReferenceName) (*plumbing.Reference, error) {
285288 return ref , nil
286289 }
287290
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+
289300 if err != nil {
290- return nil , err
301+ return err
291302 }
292303
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
296312 }
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
297342 }
298343
299344 return nil , plumbing .ErrReferenceNotFound
@@ -315,28 +360,15 @@ func (d *DotGit) RemoveRef(name plumbing.ReferenceName) error {
315360}
316361
317362func (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 {
323364 return err
324365 }
325- defer ioutil .CheckClose (f , & err )
326366
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 )
337369 }
338370
339- return s . Err ()
371+ return nil
340372}
341373
342374func (d * DotGit ) rewritePackedRefsWithoutRef (name plumbing.ReferenceName ) (err error ) {
@@ -511,3 +543,5 @@ func isNum(b byte) bool {
511543func isHexAlpha (b byte ) bool {
512544 return b >= 'a' && b <= 'f' || b >= 'A' && b <= 'F'
513545}
546+
547+ type refCache map [plumbing.ReferenceName ]* plumbing.Reference
0 commit comments