From 63c42e530e9ffdf070f74a2674aa1a1fc24703a8 Mon Sep 17 00:00:00 2001 From: Timofey Kirillov Date: Thu, 28 Mar 2019 20:32:01 +0000 Subject: Support `.git/commondir` repository layout Git creates `.git/commondir` when there are custom worktrees (see "git worktree add" related commands). `.git/commondir` in such case contains a link to another dot-git repository tree, which could contain some folders like: - objects; - config; - refs; - etc. In this PR a new dotgit.RepositoryFilesystem struct is defined, which is billy.Filesystem interface compatible object-wrapper, that can handle commondir and dispatch all operations to the correct file path. `git.PlainOpen` remain unchanged, but `git.PlainOpenWithOptions` has a new option: `PlainOpenOptions.EnableDotGitCommonDir=true|false` (which is false by default). When `EnableDotGitCommonDir=true` repository-open procedure will read `.git/commondir` (if it exists) and then create dotgit.RepositoryFilesystem object initialized with 2 filesystems. This object then passed into storage and then into dotgit.DotGit as `billy.Filesystem` interface. This object will catch all filesystem operations and dispatch to the correct repository-filesystem (dot-git or common-dot-git) according to the rules described in the doc: https://git-scm.com/docs/gitrepository-layout#Documentation/gitrepository-layout.txt. EnableDotGitCommonDir option will only work with the filesystem-backed storage. Also worktree_test.go has been adopted from an older, already existing existing PR: https://github.com/src-d/go-git/pull/1098. This PR needs new fixtures added in the following PR: https://github.com/go-git/go-git-fixtures/pull/1. --- .../dotgit/repository_filesystem_test.go | 124 +++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 storage/filesystem/dotgit/repository_filesystem_test.go (limited to 'storage/filesystem/dotgit/repository_filesystem_test.go') diff --git a/storage/filesystem/dotgit/repository_filesystem_test.go b/storage/filesystem/dotgit/repository_filesystem_test.go new file mode 100644 index 0000000..880ec0d --- /dev/null +++ b/storage/filesystem/dotgit/repository_filesystem_test.go @@ -0,0 +1,124 @@ +package dotgit + +import ( + "io/ioutil" + "log" + "os" + + "github.com/go-git/go-billy/v5/osfs" + + . "gopkg.in/check.v1" +) + +func (s *SuiteDotGit) TestRepositoryFilesystem(c *C) { + dir, err := ioutil.TempDir("", "repository_filesystem") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + + fs := osfs.New(dir) + + err = fs.MkdirAll("dotGit", 0777) + c.Assert(err, IsNil) + dotGitFs, err := fs.Chroot("dotGit") + c.Assert(err, IsNil) + + err = fs.MkdirAll("commonDotGit", 0777) + c.Assert(err, IsNil) + commonDotGitFs, err := fs.Chroot("commonDotGit") + c.Assert(err, IsNil) + + repositoryFs := NewRepositoryFilesystem(dotGitFs, commonDotGitFs) + c.Assert(repositoryFs.Root(), Equals, dotGitFs.Root()) + + somedir, err := repositoryFs.Chroot("somedir") + c.Assert(err, IsNil) + c.Assert(somedir.Root(), Equals, repositoryFs.Join(dotGitFs.Root(), "somedir")) + + _, err = repositoryFs.Create("somefile") + c.Assert(err, IsNil) + + _, err = repositoryFs.Stat("somefile") + c.Assert(err, IsNil) + + file, err := repositoryFs.Open("somefile") + c.Assert(err, IsNil) + err = file.Close() + c.Assert(err, IsNil) + + file, err = repositoryFs.OpenFile("somefile", os.O_RDONLY, 0666) + c.Assert(err, IsNil) + err = file.Close() + c.Assert(err, IsNil) + + file, err = repositoryFs.Create("somefile2") + c.Assert(err, IsNil) + err = file.Close() + c.Assert(err, IsNil) + _, err = repositoryFs.Stat("somefile2") + c.Assert(err, IsNil) + err = repositoryFs.Rename("somefile2", "newfile") + c.Assert(err, IsNil) + + tempDir, err := repositoryFs.TempFile("tmp", "myprefix") + c.Assert(err, IsNil) + c.Assert(repositoryFs.Join(repositoryFs.Root(), "tmp", tempDir.Name()), Equals, repositoryFs.Join(dotGitFs.Root(), "tmp", tempDir.Name())) + + err = repositoryFs.Symlink("newfile", "somelink") + c.Assert(err, IsNil) + + _, err = repositoryFs.Lstat("somelink") + c.Assert(err, IsNil) + + link, err := repositoryFs.Readlink("somelink") + c.Assert(err, IsNil) + c.Assert(link, Equals, "newfile") + + err = repositoryFs.Remove("somelink") + c.Assert(err, IsNil) + + _, err = repositoryFs.Stat("somelink") + c.Assert(os.IsNotExist(err), Equals, true) + + dirs := []string{objectsPath, refsPath, packedRefsPath, configPath, branchesPath, hooksPath, infoPath, remotesPath, logsPath, shallowPath, worktreesPath} + for _, dir := range dirs { + err := repositoryFs.MkdirAll(dir, 0777) + c.Assert(err, IsNil) + _, err = commonDotGitFs.Stat(dir) + c.Assert(err, IsNil) + _, err = dotGitFs.Stat(dir) + c.Assert(os.IsNotExist(err), Equals, true) + } + + exceptionsPaths := []string{repositoryFs.Join(logsPath, "HEAD"), repositoryFs.Join(refsPath, "bisect"), repositoryFs.Join(refsPath, "rewritten"), repositoryFs.Join(refsPath, "worktree")} + for _, path := range exceptionsPaths { + _, err := repositoryFs.Create(path) + c.Assert(err, IsNil) + _, err = commonDotGitFs.Stat(path) + c.Assert(os.IsNotExist(err), Equals, true) + _, err = dotGitFs.Stat(path) + c.Assert(err, IsNil) + } + + err = repositoryFs.MkdirAll("refs/heads", 0777) + c.Assert(err, IsNil) + _, err = commonDotGitFs.Stat("refs/heads") + c.Assert(err, IsNil) + _, err = dotGitFs.Stat("refs/heads") + c.Assert(os.IsNotExist(err), Equals, true) + + err = repositoryFs.MkdirAll("objects/pack", 0777) + c.Assert(err, IsNil) + _, err = commonDotGitFs.Stat("objects/pack") + c.Assert(err, IsNil) + _, err = dotGitFs.Stat("objects/pack") + c.Assert(os.IsNotExist(err), Equals, true) + + err = repositoryFs.MkdirAll("a/b/c", 0777) + c.Assert(err, IsNil) + _, err = commonDotGitFs.Stat("a/b/c") + c.Assert(os.IsNotExist(err), Equals, true) + _, err = dotGitFs.Stat("a/b/c") + c.Assert(err, IsNil) +} -- cgit