aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSantiago M. Mola <santi@mola.io>2017-04-21 15:00:22 +0200
committerSantiago M. Mola <santi@mola.io>2017-04-27 11:00:51 +0200
commit4060f60af044ce085a097e12bf19cb3fdd123076 (patch)
treeaf10cdadc9adad7a5cb31e05e958d3b9078aec03
parent64cd72debb2a94a49de5ffd3c3a6bfd626df7340 (diff)
downloadgo-git-4060f60af044ce085a097e12bf19cb3fdd123076.tar.gz
add support for .git as file, fixes #348
-rw-r--r--repository.go64
-rw-r--r--repository_test.go87
2 files changed, 142 insertions, 9 deletions
diff --git a/repository.go b/repository.go
index bb59afe..8a7b348 100644
--- a/repository.go
+++ b/repository.go
@@ -3,8 +3,10 @@ package git
import (
"errors"
"fmt"
+ stdioutil "io/ioutil"
"os"
"path/filepath"
+ "strings"
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/internal/revision"
@@ -13,6 +15,7 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/storage"
"gopkg.in/src-d/go-git.v4/storage/filesystem"
+ "gopkg.in/src-d/go-git.v4/utils/ioutil"
"gopkg.in/src-d/go-billy.v2"
"gopkg.in/src-d/go-billy.v2/osfs"
@@ -193,26 +196,69 @@ func PlainInit(path string, isBare bool) (*Repository, error) {
// repository is bare or a normal one. If the path doesn't contain a valid
// repository ErrRepositoryNotExists is returned
func PlainOpen(path string) (*Repository, error) {
- var wt, dot billy.Filesystem
+ dot, wt, err := dotGitToFilesystems(path)
+ if err != nil {
+ return nil, err
+ }
+
+ s, err := filesystem.NewStorage(dot)
+ if err != nil {
+ return nil, err
+ }
+ return Open(s, wt)
+}
+
+func dotGitToFilesystems(path string) (dot, wt billy.Filesystem, err error) {
fs := osfs.New(path)
- if _, err := fs.Stat(".git"); err != nil {
+ fi, err := fs.Stat(".git")
+ if err != nil {
if !os.IsNotExist(err) {
- return nil, err
+ return nil, nil, err
}
- dot = fs
- } else {
- wt = fs
- dot = fs.Dir(".git")
+ return fs, nil, nil
}
- s, err := filesystem.NewStorage(dot)
+ if fi.IsDir() {
+ return fs.Dir(".git"), fs, nil
+ }
+
+ dot, err = dotGitFileToFilesystem(fs)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return dot, fs, nil
+}
+
+func dotGitFileToFilesystem(fs billy.Filesystem) (billy.Filesystem, error) {
+ var err error
+
+ f, err := fs.Open(".git")
if err != nil {
return nil, err
}
+ defer ioutil.CheckClose(f, &err)
- return Open(s, wt)
+ b, err := stdioutil.ReadAll(f)
+ if err != nil {
+ return nil, err
+ }
+
+ line := string(b)
+ const prefix = "gitdir: "
+ if !strings.HasPrefix(line, prefix) {
+ return nil, fmt.Errorf(".git file has no %s prefix", prefix)
+ }
+
+ gitdir := line[len(prefix):]
+ gitdir = strings.TrimSpace(gitdir)
+ if filepath.IsAbs(gitdir) {
+ return osfs.New(gitdir), nil
+ }
+
+ return fs.Dir(gitdir), err
}
// PlainClone a repository into the path with the given options, isBare defines
diff --git a/repository_test.go b/repository_test.go
index 77bfde2..fd8d405 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -272,6 +272,93 @@ func (s *RepositorySuite) TestPlainOpenNotBare(c *C) {
c.Assert(r, IsNil)
}
+func (s *RepositorySuite) testPlainOpenGitFile(c *C, f func(string, string) string) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ altDir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(altDir)
+
+ err = ioutil.WriteFile(filepath.Join(altDir, ".git"), []byte(f(dir, altDir)), 0644)
+ c.Assert(err, IsNil)
+
+ r, err = PlainOpen(altDir)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenBareAbsoluteGitDirFile(c *C) {
+ s.testPlainOpenGitFile(c, func(dir, altDir string) string {
+ return fmt.Sprintf("gitdir: %s\n", dir)
+ })
+}
+
+func (s *RepositorySuite) TestPlainOpenBareAbsoluteGitDirFileNoEOL(c *C) {
+ s.testPlainOpenGitFile(c, func(dir, altDir string) string {
+ return fmt.Sprintf("gitdir: %s", dir)
+ })
+}
+
+func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFile(c *C) {
+ s.testPlainOpenGitFile(c, func(dir, altDir string) string {
+ dir, err := filepath.Rel(altDir, dir)
+ c.Assert(err, IsNil)
+ return fmt.Sprintf("gitdir: %s\n", dir)
+ })
+}
+
+func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFileNoEOL(c *C) {
+ s.testPlainOpenGitFile(c, func(dir, altDir string) string {
+ dir, err := filepath.Rel(altDir, dir)
+ c.Assert(err, IsNil)
+ return fmt.Sprintf("gitdir: %s\n", dir)
+ })
+}
+
+func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFileTrailingGarbage(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ altDir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(altDir, ".git"), []byte(fmt.Sprintf("gitdir: %s\nTRAILING", dir)), 0644)
+ c.Assert(err, IsNil)
+
+ r, err = PlainOpen(altDir)
+ c.Assert(err, Equals, ErrRepositoryNotExists)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFileBadPrefix(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ altDir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(altDir, ".git"), []byte(fmt.Sprintf("xgitdir: %s\n", dir)), 0644)
+ c.Assert(err, IsNil)
+
+ r, err = PlainOpen(altDir)
+ c.Assert(err, ErrorMatches, ".*gitdir.*")
+ c.Assert(r, IsNil)
+}
+
func (s *RepositorySuite) TestPlainOpenNotExists(c *C) {
r, err := PlainOpen("/not-exists/")
c.Assert(err, Equals, ErrRepositoryNotExists)