aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Martí <mvdan@mvdan.cc>2018-03-14 16:32:48 +0000
committerDaniel Martí <mvdan@mvdan.cc>2018-04-03 16:14:07 +0100
commit5e8e011f6537e6822350a16a243db5802d4151e6 (patch)
tree2c27503469c0e5fb5ae6028819d20df673a2ab76
parent247cf690745dfd67ccd9f0c07878e6dd85e6c9ed (diff)
downloadgo-git-5e8e011f6537e6822350a16a243db5802d4151e6.tar.gz
add PlainOpen variant to find .git in parent dirs
This is the git tool's behavior that people are used to; if one runs a git command in a repository's subdirectory, git still works. Fixes #765. Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
-rw-r--r--options.go11
-rw-r--r--repository.go38
-rw-r--r--repository_test.go30
3 files changed, 73 insertions, 6 deletions
diff --git a/options.go b/options.go
index f0c2fd8..885980e 100644
--- a/options.go
+++ b/options.go
@@ -420,3 +420,14 @@ func (o *GrepOptions) Validate(w *Worktree) error {
return nil
}
+
+// PlainOpenOptions describes how opening a plain repository should be
+// performed.
+type PlainOpenOptions struct {
+ // DetectDotGit defines whether parent directories should be
+ // walked until a .git directory or file is found.
+ DetectDotGit bool
+}
+
+// Validate validates the fields and sets the default values.
+func (o *PlainOpenOptions) Validate() error { return nil }
diff --git a/repository.go b/repository.go
index 4ea51f5..53c545c 100644
--- a/repository.go
+++ b/repository.go
@@ -225,7 +225,14 @@ 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) {
- dot, wt, err := dotGitToOSFilesystems(path)
+ return PlainOpenWithOptions(path, &PlainOpenOptions{})
+}
+
+// PlainOpen opens a git repository from the given path. It detects if the
+// repository is bare or a normal one. If the path doesn't contain a valid
+// repository ErrRepositoryNotExists is returned
+func PlainOpenWithOptions(path string, o *PlainOpenOptions) (*Repository, error) {
+ dot, wt, err := dotGitToOSFilesystems(path, o.DetectDotGit)
if err != nil {
return nil, err
}
@@ -246,14 +253,33 @@ func PlainOpen(path string) (*Repository, error) {
return Open(s, wt)
}
-func dotGitToOSFilesystems(path string) (dot, wt billy.Filesystem, err error) {
- fs := osfs.New(path)
- fi, err := fs.Stat(".git")
- if err != nil {
+func dotGitToOSFilesystems(path string, detect bool) (dot, wt billy.Filesystem, err error) {
+ if path, err = filepath.Abs(path); err != nil {
+ return nil, nil, err
+ }
+ var fs billy.Filesystem
+ var fi os.FileInfo
+ for {
+ fs = osfs.New(path)
+ fi, err = fs.Stat(".git")
+ if err == nil {
+ // no error; stop
+ break
+ }
if !os.IsNotExist(err) {
+ // unknown error; stop
return nil, nil, err
}
-
+ if detect {
+ // try its parent as long as we haven't reached
+ // the root dir
+ if dir := filepath.Dir(path); dir != path {
+ path = dir
+ continue
+ }
+ }
+ // not detecting via parent dirs and the dir does not exist;
+ // stop
return fs, nil, nil
}
diff --git a/repository_test.go b/repository_test.go
index f39f358..4765a53 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -407,6 +407,36 @@ func (s *RepositorySuite) TestPlainOpenNotExists(c *C) {
c.Assert(r, IsNil)
}
+func (s *RepositorySuite) TestPlainOpenDetectDotGit(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ subdir := filepath.Join(dir, "a", "b")
+ err = os.MkdirAll(subdir, 0755)
+ c.Assert(err, IsNil)
+
+ r, err := PlainInit(dir, false)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ opt := &PlainOpenOptions{DetectDotGit: true}
+ r, err = PlainOpenWithOptions(subdir, opt)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenNotExistsDetectDotGit(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ opt := &PlainOpenOptions{DetectDotGit: true}
+ r, err := PlainOpenWithOptions(dir, opt)
+ c.Assert(err, Equals, ErrRepositoryNotExists)
+ c.Assert(r, IsNil)
+}
+
func (s *RepositorySuite) TestPlainClone(c *C) {
r, err := PlainClone(c.MkDir(), false, &CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),