Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit 5a7b7af

Browse files
committed
dotgit: rewrite the way references are looked up
Now there's only two ways of getting a reference, by checking under refs/ directory or in packed-refs. refs/ directory is checked using a direct read by reference name and packed refs are cached until they have been changed. Signed-off-by: Miguel Molina <[email protected]>
1 parent 6f3b2d8 commit 5a7b7af

File tree

2 files changed

+76
-24
lines changed

2 files changed

+76
-24
lines changed

storage/filesystem/internal/dotgit/dotgit.go

+58-24
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
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.
5859
type 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").
6568
func 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

317362
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 {
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

342374
func (d *DotGit) rewritePackedRefsWithoutRef(name plumbing.ReferenceName) (err error) {
@@ -511,3 +543,5 @@ func isNum(b byte) bool {
511543
func isHexAlpha(b byte) bool {
512544
return b >= 'a' && b <= 'f' || b >= 'A' && b <= 'F'
513545
}
546+
547+
type refCache map[plumbing.ReferenceName]*plumbing.Reference

storage/filesystem/internal/dotgit/dotgit_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,24 @@ func (s *SuiteDotGit) TestRefsFromReferenceFile(c *C) {
134134

135135
}
136136

137+
func BenchmarkRefMultipleTimes(b *testing.B) {
138+
fs := fixtures.Basic().ByTag(".git").One().DotGit()
139+
refname := plumbing.ReferenceName("refs/remotes/origin/branch")
140+
141+
dir := New(fs)
142+
_, err := dir.Ref(refname)
143+
if err != nil {
144+
b.Fatalf("unexpected error: %s", err)
145+
}
146+
147+
for i := 0; i < b.N; i++ {
148+
_, err := dir.Ref(refname)
149+
if err != nil {
150+
b.Fatalf("unexpected error: %s", err)
151+
}
152+
}
153+
}
154+
137155
func (s *SuiteDotGit) TestRemoveRefFromReferenceFile(c *C) {
138156
fs := fixtures.Basic().ByTag(".git").One().DotGit()
139157
dir := New(fs)

0 commit comments

Comments
 (0)