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

Commit 3932973

Browse files
committed
storage/worktree: filesystem, support common directory/linked repositories.
Signed-off-by: Arran Walker <[email protected]>
1 parent 923642a commit 3932973

File tree

5 files changed

+162
-11
lines changed

5 files changed

+162
-11
lines changed

repository.go

+40-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ var (
4646

4747
ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch")
4848
ErrRepositoryNotExists = errors.New("repository does not exist")
49+
ErrRepositoryIncomplete = errors.New("repository's commondir path does not exist")
4950
ErrRepositoryAlreadyExists = errors.New("repository already exists")
5051
ErrRemoteNotFound = errors.New("remote not found")
5152
ErrRemoteExists = errors.New("remote already exists")
@@ -252,7 +253,13 @@ func PlainOpenWithOptions(path string, o *PlainOpenOptions) (*Repository, error)
252253
return nil, err
253254
}
254255

255-
s := filesystem.NewStorage(dot, cache.NewObjectLRUDefault())
256+
options := filesystem.Options{}
257+
options.CommonDir, err = dotGitCommonDirectory(dot)
258+
if err != nil {
259+
return nil, err
260+
}
261+
262+
s := filesystem.NewStorageWithOptions(dot, cache.NewObjectLRUDefault(), options)
256263

257264
return Open(s, wt)
258265
}
@@ -327,6 +334,38 @@ func dotGitFileToOSFilesystem(path string, fs billy.Filesystem) (bfs billy.Files
327334
return osfs.New(fs.Join(path, gitdir)), nil
328335
}
329336

337+
func dotGitCommonDirectory(fs billy.Filesystem) (commonDir billy.Filesystem, err error) {
338+
f, err := fs.Open("commondir")
339+
if os.IsNotExist(err) {
340+
return nil, nil
341+
}
342+
if err != nil {
343+
return nil, err
344+
}
345+
346+
b, err := stdioutil.ReadAll(f)
347+
if err != nil {
348+
return nil, err
349+
}
350+
if len(b) > 0 {
351+
path := strings.TrimSpace(string(b))
352+
if filepath.IsAbs(path) {
353+
commonDir = osfs.New(path)
354+
} else {
355+
commonDir = osfs.New(filepath.Join(fs.Root(), path))
356+
}
357+
if _, err := commonDir.Stat(""); err != nil {
358+
if os.IsNotExist(err) {
359+
return nil, ErrRepositoryIncomplete
360+
}
361+
362+
return nil, err
363+
}
364+
}
365+
366+
return commonDir, nil
367+
}
368+
330369
// PlainClone a repository into the path with the given options, isBare defines
331370
// if the new repository will be bare or normal. If the path is not empty
332371
// ErrRepositoryAlreadyExists is returned.

storage/filesystem/dotgit/dotgit.go

+21-2
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,17 @@ type Options struct {
6666
// KeepDescriptors makes the file descriptors to be reused but they will
6767
// need to be manually closed calling Close().
6868
KeepDescriptors bool
69+
// CommonDir sets the directory used for accessing non-worktree files that
70+
// would normally be taken from the root directory.
71+
CommonDir billy.Filesystem
6972
}
7073

7174
// The DotGit type represents a local git repository on disk. This
7275
// type is not zero-value-safe, use the New function to initialize it.
7376
type DotGit struct {
7477
options Options
7578
fs billy.Filesystem
79+
localfs billy.Filesystem
7680

7781
// incoming object directory information
7882
incomingChecked bool
@@ -96,9 +100,14 @@ func New(fs billy.Filesystem) *DotGit {
96100
// NewWithOptions sets non default configuration options.
97101
// See New for complete help.
98102
func NewWithOptions(fs billy.Filesystem, o Options) *DotGit {
103+
if o.CommonDir == nil {
104+
o.CommonDir = fs
105+
}
106+
99107
return &DotGit{
100108
options: o,
101-
fs: fs,
109+
fs: o.CommonDir,
110+
localfs: fs,
102111
}
103112
}
104113

@@ -923,7 +932,8 @@ func (d *DotGit) addRefFromHEAD(refs *[]*plumbing.Reference) error {
923932

924933
func (d *DotGit) readReferenceFile(path, name string) (ref *plumbing.Reference, err error) {
925934
path = d.fs.Join(path, d.fs.Join(strings.Split(name, "/")...))
926-
f, err := d.fs.Open(path)
935+
936+
f, err := d.fsFromRefPath(path).Open(path)
927937
if err != nil {
928938
return nil, err
929939
}
@@ -932,6 +942,15 @@ func (d *DotGit) readReferenceFile(path, name string) (ref *plumbing.Reference,
932942
return d.readReferenceFrom(f, name)
933943
}
934944

945+
func (d *DotGit) fsFromRefPath(path string) billy.Filesystem {
946+
// In general, all pseudo refs are per working tree and all refs starting
947+
// with "refs/" are shared.
948+
if strings.HasPrefix(path, "refs/") {
949+
return d.fs
950+
}
951+
return d.localfs
952+
}
953+
935954
func (d *DotGit) CountLooseRefs() (int, error) {
936955
var refs []*plumbing.Reference
937956
var seen = make(map[plumbing.ReferenceName]bool)

storage/filesystem/dotgit/dotgit_setref.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func (d *DotGit) setRefRwfs(fileName, content string, old *plumbing.Reference) (
2525
mode |= os.O_TRUNC
2626
}
2727

28-
f, err := d.fs.OpenFile(fileName, mode, 0666)
28+
f, err := d.fsFromRefPath(fileName).OpenFile(fileName, mode, 0666)
2929
if err != nil {
3030
return err
3131
}
@@ -59,9 +59,11 @@ func (d *DotGit) setRefRwfs(fileName, content string, old *plumbing.Reference) (
5959
// making it compatible with these simple filesystems. This is usually not
6060
// a problem as they should be accessed by only one process at a time.
6161
func (d *DotGit) setRefNorwfs(fileName, content string, old *plumbing.Reference) error {
62-
_, err := d.fs.Stat(fileName)
62+
fs := d.fsFromRefPath(fileName)
63+
64+
_, err := fs.Stat(fileName)
6365
if err == nil && old != nil {
64-
fRead, err := d.fs.Open(fileName)
66+
fRead, err := fs.Open(fileName)
6567
if err != nil {
6668
return err
6769
}
@@ -78,7 +80,7 @@ func (d *DotGit) setRefNorwfs(fileName, content string, old *plumbing.Reference)
7880
}
7981
}
8082

81-
f, err := d.fs.Create(fileName)
83+
f, err := fs.Create(fileName)
8284
if err != nil {
8385
return err
8486
}

storage/filesystem/storage.go

+20-4
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import (
1212
// standard git format (this is, the .git directory). Zero values of this type
1313
// are not safe to use, see the NewStorage function below.
1414
type Storage struct {
15-
fs billy.Filesystem
16-
dir *dotgit.DotGit
15+
fs billy.Filesystem
16+
commonfs billy.Filesystem
17+
dir *dotgit.DotGit
1718

1819
ObjectStorage
1920
ReferenceStorage
@@ -31,6 +32,9 @@ type Options struct {
3132
// KeepDescriptors makes the file descriptors to be reused but they will
3233
// need to be manually closed calling Close().
3334
KeepDescriptors bool
35+
// CommonDir sets the directory used for accessing non-worktree files that
36+
// would normally be taken from the root directory.
37+
CommonDir billy.Filesystem
3438
}
3539

3640
// NewStorage returns a new Storage backed by a given `fs.Filesystem` and cache.
@@ -44,12 +48,18 @@ func NewStorageWithOptions(fs billy.Filesystem, cache cache.Object, ops Options)
4448
dirOps := dotgit.Options{
4549
ExclusiveAccess: ops.ExclusiveAccess,
4650
KeepDescriptors: ops.KeepDescriptors,
51+
CommonDir: ops.CommonDir,
4752
}
53+
4854
dir := dotgit.NewWithOptions(fs, dirOps)
55+
if ops.CommonDir == nil {
56+
ops.CommonDir = fs
57+
}
4958

5059
return &Storage{
51-
fs: fs,
52-
dir: dir,
60+
fs: fs,
61+
commonfs: ops.CommonDir,
62+
dir: dir,
5363

5464
ObjectStorage: *NewObjectStorageWithOptions(dir, cache, ops),
5565
ReferenceStorage: ReferenceStorage{dir: dir},
@@ -65,6 +75,12 @@ func (s *Storage) Filesystem() billy.Filesystem {
6575
return s.fs
6676
}
6777

78+
// MainFilesystem returns the underlying filesystem for the main
79+
// working-tree/common git directory
80+
func (s *Storage) MainFilesystem() billy.Filesystem {
81+
return s.commonfs
82+
}
83+
6884
// Init initializes .git directory
6985
func (s *Storage) Init() error {
7086
return s.dir.Initialize()

worktree_test.go

+75
Original file line numberDiff line numberDiff line change
@@ -1965,3 +1965,78 @@ func (s *WorktreeSuite) TestAddAndCommit(c *C) {
19651965
})
19661966
c.Assert(err, IsNil)
19671967
}
1968+
1969+
func (s *WorktreeSuite) TestLinkedWorktree(c *C) {
1970+
fs := fixtures.ByTag("linked-worktree").One().Worktree()
1971+
1972+
// Open main repo.
1973+
{
1974+
fs, err := fs.Chroot("main")
1975+
c.Assert(err, IsNil)
1976+
repo, err := PlainOpen(fs.Root())
1977+
c.Assert(err, IsNil)
1978+
1979+
wt, err := repo.Worktree()
1980+
c.Assert(err, IsNil)
1981+
1982+
status, err := wt.Status()
1983+
c.Assert(err, IsNil)
1984+
c.Assert(len(status), Equals, 2) // 2 files
1985+
1986+
head, err := repo.Head()
1987+
c.Assert(err, IsNil)
1988+
c.Assert(string(head.Name()), Equals, "refs/heads/master")
1989+
}
1990+
1991+
// Open linked-worktree #1.
1992+
{
1993+
fs, err := fs.Chroot("linked-worktree-1")
1994+
c.Assert(err, IsNil)
1995+
repo, err := PlainOpen(fs.Root())
1996+
c.Assert(err, IsNil)
1997+
1998+
wt, err := repo.Worktree()
1999+
c.Assert(err, IsNil)
2000+
2001+
status, err := wt.Status()
2002+
c.Assert(err, IsNil)
2003+
c.Assert(len(status), Equals, 3) // 3 files
2004+
2005+
_, ok := status["linked-worktree-1-unique-file.txt"]
2006+
c.Assert(ok, Equals, true)
2007+
2008+
head, err := repo.Head()
2009+
c.Assert(err, IsNil)
2010+
c.Assert(string(head.Name()), Equals, "refs/heads/linked-worktree-1")
2011+
}
2012+
2013+
// Open linked-worktree #2.
2014+
{
2015+
fs, err := fs.Chroot("linked-worktree-2")
2016+
c.Assert(err, IsNil)
2017+
repo, err := PlainOpen(fs.Root())
2018+
c.Assert(err, IsNil)
2019+
2020+
wt, err := repo.Worktree()
2021+
c.Assert(err, IsNil)
2022+
2023+
status, err := wt.Status()
2024+
c.Assert(err, IsNil)
2025+
c.Assert(len(status), Equals, 3) // 3 files
2026+
2027+
_, ok := status["linked-worktree-2-unique-file.txt"]
2028+
c.Assert(ok, Equals, true)
2029+
2030+
head, err := repo.Head()
2031+
c.Assert(err, IsNil)
2032+
c.Assert(string(head.Name()), Equals, "refs/heads/branch-with-different-name")
2033+
}
2034+
2035+
// Open linked-worktree #2.
2036+
{
2037+
fs, err := fs.Chroot("linked-worktree-invalid-commondir")
2038+
c.Assert(err, IsNil)
2039+
_, err = PlainOpen(fs.Root())
2040+
c.Assert(err, Equals, ErrRepositoryIncomplete)
2041+
}
2042+
}

0 commit comments

Comments
 (0)