diff options
author | Daniel Martí <mvdan@mvdan.cc> | 2018-03-14 16:32:48 +0000 |
---|---|---|
committer | Daniel Martí <mvdan@mvdan.cc> | 2018-04-03 16:14:07 +0100 |
commit | 5e8e011f6537e6822350a16a243db5802d4151e6 (patch) | |
tree | 2c27503469c0e5fb5ae6028819d20df673a2ab76 | |
parent | 247cf690745dfd67ccd9f0c07878e6dd85e6c9ed (diff) | |
download | go-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.go | 11 | ||||
-rw-r--r-- | repository.go | 38 | ||||
-rw-r--r-- | repository_test.go | 30 |
3 files changed, 73 insertions, 6 deletions
@@ -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(), |