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

add support for .git as file, fixes #348 #363

Merged
merged 1 commit into from
Apr 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 55 additions & 9 deletions repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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
Expand Down
87 changes: 87 additions & 0 deletions repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down