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

Commit 2f15838

Browse files
authored
Merge pull request #941 from jfontan/improvement/static-mode
plumbing/storer: add ExclusiveAccess option to Storer
2 parents 8e76874 + 659ec44 commit 2f15838

File tree

6 files changed

+260
-9
lines changed

6 files changed

+260
-9
lines changed

repository.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,8 @@ func PlainOpen(path string) (*Repository, error) {
235235
return PlainOpenWithOptions(path, &PlainOpenOptions{})
236236
}
237237

238-
// PlainOpen opens a git repository from the given path. It detects if the
239-
// repository is bare or a normal one. If the path doesn't contain a valid
240-
// repository ErrRepositoryNotExists is returned
238+
// PlainOpenWithOptions opens a git repository from the given path with specific
239+
// options. See PlainOpen for more info.
241240
func PlainOpenWithOptions(path string, o *PlainOpenOptions) (*Repository, error) {
242241
dot, wt, err := dotGitToOSFilesystems(path, o.DetectDotGit)
243242
if err != nil {

storage/filesystem/dotgit/dotgit.go

Lines changed: 181 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,43 @@ var (
5757
ErrSymRefTargetNotFound = errors.New("symbolic reference target not found")
5858
)
5959

60+
// Options holds configuration for the storage.
61+
type Options struct {
62+
// ExclusiveAccess means that the filesystem is not modified externally
63+
// while the repo is open.
64+
ExclusiveAccess bool
65+
}
66+
6067
// The DotGit type represents a local git repository on disk. This
6168
// type is not zero-value-safe, use the New function to initialize it.
6269
type DotGit struct {
63-
fs billy.Filesystem
70+
options Options
71+
fs billy.Filesystem
6472

6573
// incoming object directory information
6674
incomingChecked bool
6775
incomingDirName string
76+
77+
objectList []plumbing.Hash
78+
objectMap map[plumbing.Hash]struct{}
79+
packList []plumbing.Hash
80+
packMap map[plumbing.Hash]struct{}
6881
}
6982

7083
// New returns a DotGit value ready to be used. The path argument must
7184
// be the absolute path of a git repository directory (e.g.
7285
// "/foo/bar/.git").
7386
func New(fs billy.Filesystem) *DotGit {
74-
return &DotGit{fs: fs}
87+
return NewWithOptions(fs, Options{})
88+
}
89+
90+
// NewWithOptions creates a new DotGit and sets non default configuration
91+
// options. See New for complete help.
92+
func NewWithOptions(fs billy.Filesystem, o Options) *DotGit {
93+
return &DotGit{
94+
options: o,
95+
fs: fs,
96+
}
7597
}
7698

7799
// Initialize creates all the folder scaffolding.
@@ -143,11 +165,25 @@ func (d *DotGit) Shallow() (billy.File, error) {
143165
// NewObjectPack return a writer for a new packfile, it saves the packfile to
144166
// disk and also generates and save the index for the given packfile.
145167
func (d *DotGit) NewObjectPack() (*PackWriter, error) {
168+
d.cleanPackList()
146169
return newPackWrite(d.fs)
147170
}
148171

149172
// ObjectPacks returns the list of availables packfiles
150173
func (d *DotGit) ObjectPacks() ([]plumbing.Hash, error) {
174+
if !d.options.ExclusiveAccess {
175+
return d.objectPacks()
176+
}
177+
178+
err := d.genPackList()
179+
if err != nil {
180+
return nil, err
181+
}
182+
183+
return d.packList, nil
184+
}
185+
186+
func (d *DotGit) objectPacks() ([]plumbing.Hash, error) {
151187
packDir := d.fs.Join(objectsPath, packPath)
152188
files, err := d.fs.ReadDir(packDir)
153189
if err != nil {
@@ -181,6 +217,11 @@ func (d *DotGit) objectPackPath(hash plumbing.Hash, extension string) string {
181217
}
182218

183219
func (d *DotGit) objectPackOpen(hash plumbing.Hash, extension string) (billy.File, error) {
220+
err := d.hasPack(hash)
221+
if err != nil {
222+
return nil, err
223+
}
224+
184225
pack, err := d.fs.Open(d.objectPackPath(hash, extension))
185226
if err != nil {
186227
if os.IsNotExist(err) {
@@ -195,15 +236,27 @@ func (d *DotGit) objectPackOpen(hash plumbing.Hash, extension string) (billy.Fil
195236

196237
// ObjectPack returns a fs.File of the given packfile
197238
func (d *DotGit) ObjectPack(hash plumbing.Hash) (billy.File, error) {
239+
err := d.hasPack(hash)
240+
if err != nil {
241+
return nil, err
242+
}
243+
198244
return d.objectPackOpen(hash, `pack`)
199245
}
200246

201247
// ObjectPackIdx returns a fs.File of the index file for a given packfile
202248
func (d *DotGit) ObjectPackIdx(hash plumbing.Hash) (billy.File, error) {
249+
err := d.hasPack(hash)
250+
if err != nil {
251+
return nil, err
252+
}
253+
203254
return d.objectPackOpen(hash, `idx`)
204255
}
205256

206257
func (d *DotGit) DeleteOldObjectPackAndIndex(hash plumbing.Hash, t time.Time) error {
258+
d.cleanPackList()
259+
207260
path := d.objectPackPath(hash, `pack`)
208261
if !t.IsZero() {
209262
fi, err := d.fs.Stat(path)
@@ -224,12 +277,23 @@ func (d *DotGit) DeleteOldObjectPackAndIndex(hash plumbing.Hash, t time.Time) er
224277

225278
// NewObject return a writer for a new object file.
226279
func (d *DotGit) NewObject() (*ObjectWriter, error) {
280+
d.cleanObjectList()
281+
227282
return newObjectWriter(d.fs)
228283
}
229284

230285
// Objects returns a slice with the hashes of objects found under the
231286
// .git/objects/ directory.
232287
func (d *DotGit) Objects() ([]plumbing.Hash, error) {
288+
if d.options.ExclusiveAccess {
289+
err := d.genObjectList()
290+
if err != nil {
291+
return nil, err
292+
}
293+
294+
return d.objectList, nil
295+
}
296+
233297
var objects []plumbing.Hash
234298
err := d.ForEachObjectHash(func(hash plumbing.Hash) error {
235299
objects = append(objects, hash)
@@ -241,9 +305,29 @@ func (d *DotGit) Objects() ([]plumbing.Hash, error) {
241305
return objects, nil
242306
}
243307

244-
// Objects returns a slice with the hashes of objects found under the
245-
// .git/objects/ directory.
308+
// ForEachObjectHash iterates over the hashes of objects found under the
309+
// .git/objects/ directory and executes the provided function.
246310
func (d *DotGit) ForEachObjectHash(fun func(plumbing.Hash) error) error {
311+
if !d.options.ExclusiveAccess {
312+
return d.forEachObjectHash(fun)
313+
}
314+
315+
err := d.genObjectList()
316+
if err != nil {
317+
return err
318+
}
319+
320+
for _, h := range d.objectList {
321+
err := fun(h)
322+
if err != nil {
323+
return err
324+
}
325+
}
326+
327+
return nil
328+
}
329+
330+
func (d *DotGit) forEachObjectHash(fun func(plumbing.Hash) error) error {
247331
files, err := d.fs.ReadDir(objectsPath)
248332
if err != nil {
249333
if os.IsNotExist(err) {
@@ -278,6 +362,87 @@ func (d *DotGit) ForEachObjectHash(fun func(plumbing.Hash) error) error {
278362
return nil
279363
}
280364

365+
func (d *DotGit) cleanObjectList() {
366+
d.objectMap = nil
367+
d.objectList = nil
368+
}
369+
370+
func (d *DotGit) genObjectList() error {
371+
if d.objectMap != nil {
372+
return nil
373+
}
374+
375+
d.objectMap = make(map[plumbing.Hash]struct{})
376+
return d.forEachObjectHash(func(h plumbing.Hash) error {
377+
d.objectList = append(d.objectList, h)
378+
d.objectMap[h] = struct{}{}
379+
380+
return nil
381+
})
382+
}
383+
384+
func (d *DotGit) hasObject(h plumbing.Hash) error {
385+
if !d.options.ExclusiveAccess {
386+
return nil
387+
}
388+
389+
err := d.genObjectList()
390+
if err != nil {
391+
return err
392+
}
393+
394+
_, ok := d.objectMap[h]
395+
if !ok {
396+
return plumbing.ErrObjectNotFound
397+
}
398+
399+
return nil
400+
}
401+
402+
func (d *DotGit) cleanPackList() {
403+
d.packMap = nil
404+
d.packList = nil
405+
}
406+
407+
func (d *DotGit) genPackList() error {
408+
if d.packMap != nil {
409+
return nil
410+
}
411+
412+
op, err := d.objectPacks()
413+
if err != nil {
414+
return err
415+
}
416+
417+
d.packMap = make(map[plumbing.Hash]struct{})
418+
d.packList = nil
419+
420+
for _, h := range op {
421+
d.packList = append(d.packList, h)
422+
d.packMap[h] = struct{}{}
423+
}
424+
425+
return nil
426+
}
427+
428+
func (d *DotGit) hasPack(h plumbing.Hash) error {
429+
if !d.options.ExclusiveAccess {
430+
return nil
431+
}
432+
433+
err := d.genPackList()
434+
if err != nil {
435+
return err
436+
}
437+
438+
_, ok := d.packMap[h]
439+
if !ok {
440+
return ErrPackfileNotFound
441+
}
442+
443+
return nil
444+
}
445+
281446
func (d *DotGit) objectPath(h plumbing.Hash) string {
282447
hash := h.String()
283448
return d.fs.Join(objectsPath, hash[0:2], hash[2:40])
@@ -322,6 +487,11 @@ func (d *DotGit) hasIncomingObjects() bool {
322487

323488
// Object returns a fs.File pointing the object file, if exists
324489
func (d *DotGit) Object(h plumbing.Hash) (billy.File, error) {
490+
err := d.hasObject(h)
491+
if err != nil {
492+
return nil, err
493+
}
494+
325495
obj1, err1 := d.fs.Open(d.objectPath(h))
326496
if os.IsNotExist(err1) && d.hasIncomingObjects() {
327497
obj2, err2 := d.fs.Open(d.incomingObjectPath(h))
@@ -335,6 +505,11 @@ func (d *DotGit) Object(h plumbing.Hash) (billy.File, error) {
335505

336506
// ObjectStat returns a os.FileInfo pointing the object file, if exists
337507
func (d *DotGit) ObjectStat(h plumbing.Hash) (os.FileInfo, error) {
508+
err := d.hasObject(h)
509+
if err != nil {
510+
return nil, err
511+
}
512+
338513
obj1, err1 := d.fs.Stat(d.objectPath(h))
339514
if os.IsNotExist(err1) && d.hasIncomingObjects() {
340515
obj2, err2 := d.fs.Stat(d.incomingObjectPath(h))
@@ -348,6 +523,8 @@ func (d *DotGit) ObjectStat(h plumbing.Hash) (os.FileInfo, error) {
348523

349524
// ObjectDelete removes the object file, if exists
350525
func (d *DotGit) ObjectDelete(h plumbing.Hash) error {
526+
d.cleanObjectList()
527+
351528
err1 := d.fs.Remove(d.objectPath(h))
352529
if os.IsNotExist(err1) && d.hasIncomingObjects() {
353530
err2 := d.fs.Remove(d.incomingObjectPath(h))

storage/filesystem/dotgit/dotgit_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010
"testing"
1111

12+
"gopkg.in/src-d/go-billy.v4"
1213
"gopkg.in/src-d/go-git.v4/plumbing"
1314

1415
. "gopkg.in/check.v1"
@@ -424,6 +425,18 @@ func (s *SuiteDotGit) TestObjectPacks(c *C) {
424425
fs := f.DotGit()
425426
dir := New(fs)
426427

428+
testObjectPacks(c, fs, dir, f)
429+
}
430+
431+
func (s *SuiteDotGit) TestObjectPacksExclusive(c *C) {
432+
f := fixtures.Basic().ByTag(".git").One()
433+
fs := f.DotGit()
434+
dir := NewWithOptions(fs, Options{ExclusiveAccess: true})
435+
436+
testObjectPacks(c, fs, dir, f)
437+
}
438+
439+
func testObjectPacks(c *C, fs billy.Filesystem, dir *DotGit, f *fixtures.Fixture) {
427440
hashes, err := dir.ObjectPacks()
428441
c.Assert(err, IsNil)
429442
c.Assert(hashes, HasLen, 1)
@@ -506,6 +519,17 @@ func (s *SuiteDotGit) TestObjects(c *C) {
506519
fs := fixtures.ByTag(".git").ByTag("unpacked").One().DotGit()
507520
dir := New(fs)
508521

522+
testObjects(c, fs, dir)
523+
}
524+
525+
func (s *SuiteDotGit) TestObjectsExclusive(c *C) {
526+
fs := fixtures.ByTag(".git").ByTag("unpacked").One().DotGit()
527+
dir := NewWithOptions(fs, Options{ExclusiveAccess: true})
528+
529+
testObjects(c, fs, dir)
530+
}
531+
532+
func testObjects(c *C, fs billy.Filesystem, dir *DotGit) {
509533
hashes, err := dir.Objects()
510534
c.Assert(err, IsNil)
511535
c.Assert(hashes, HasLen, 187)

storage/filesystem/object.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
)
1919

2020
type ObjectStorage struct {
21+
options Options
22+
2123
// deltaBaseCache is an object cache uses to cache delta's bases when
2224
deltaBaseCache cache.Object
2325

@@ -27,7 +29,17 @@ type ObjectStorage struct {
2729

2830
// NewObjectStorage creates a new ObjectStorage with the given .git directory.
2931
func NewObjectStorage(dir *dotgit.DotGit) (ObjectStorage, error) {
32+
return NewObjectStorageWithOptions(dir, Options{})
33+
}
34+
35+
// NewObjectStorageWithOptions creates a new ObjectStorage with the given .git
36+
// directory and sets its options.
37+
func NewObjectStorageWithOptions(
38+
dir *dotgit.DotGit,
39+
ops Options,
40+
) (ObjectStorage, error) {
3041
s := ObjectStorage{
42+
options: ops,
3143
deltaBaseCache: cache.NewObjectLRUDefault(),
3244
dir: dir,
3345
}

0 commit comments

Comments
 (0)