aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2017-01-27 17:46:46 +0100
committerMáximo Cuadros <mcuadros@gmail.com>2017-01-28 00:42:22 +0100
commit24c1878260351d9f9f6c575cbeeb5878104d6a0e (patch)
treec6a9575aa84197f7f33515ca4d55816134d1569b
parent1c2602a791371e76d52f89b2c8193cb200c66ad6 (diff)
downloadgo-git-24c1878260351d9f9f6c575cbeeb5878104d6a0e.tar.gz
new repository constructors and worktree
-rw-r--r--common_test.go27
-rw-r--r--fixtures/fixtures.go6
-rw-r--r--plumbing/reference.go3
-rw-r--r--remote_test.go2
-rw-r--r--repository.go218
-rw-r--r--repository_test.go304
-rw-r--r--worktree.go49
-rw-r--r--worktree_test.go39
8 files changed, 535 insertions, 113 deletions
diff --git a/common_test.go b/common_test.go
index 032fdfa..8a6cb3d 100644
--- a/common_test.go
+++ b/common_test.go
@@ -4,13 +4,19 @@ import (
"fmt"
"testing"
+ "srcd.works/go-billy.v1/os"
+
"gopkg.in/src-d/go-git.v4/fixtures"
+ "gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
"gopkg.in/src-d/go-git.v4/plumbing/transport/client"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
+ "gopkg.in/src-d/go-git.v4/storage/memory"
. "gopkg.in/check.v1"
+ memoryfs "srcd.works/go-billy.v1/memory"
)
func Test(t *testing.T) { TestingT(t) }
@@ -44,7 +50,14 @@ func (s *BaseSuite) buildBasicRepository(c *C) {
}
func (s *BaseSuite) NewRepository(f *fixtures.Fixture) *Repository {
- r, err := NewFilesystemRepository(f.DotGit().Base())
+
+ fs := os.New(f.DotGit().Base())
+ st, err := filesystem.NewStorage(fs)
+ if err != nil {
+ panic(err)
+ }
+
+ r, err := Open(st, fs)
if err != nil {
panic(err)
}
@@ -58,13 +71,12 @@ func (s *BaseSuite) NewRepositoryFromPackfile(f *fixtures.Fixture) *Repository {
return r
}
- r := NewMemoryRepository()
-
+ storer := memory.NewStorage()
p := f.Packfile()
defer p.Close()
n := packfile.NewScanner(p)
- d, err := packfile.NewDecoder(n, r.s)
+ d, err := packfile.NewDecoder(n, storer)
if err != nil {
panic(err)
}
@@ -74,6 +86,13 @@ func (s *BaseSuite) NewRepositoryFromPackfile(f *fixtures.Fixture) *Repository {
panic(err)
}
+ storer.SetReference(plumbing.NewHashReference(plumbing.HEAD, f.Head))
+
+ r, err := Open(storer, memoryfs.New())
+ if err != nil {
+ panic(err)
+ }
+
s.cache[h] = r
return r
}
diff --git a/fixtures/fixtures.go b/fixtures/fixtures.go
index 6a86bdd..f79e107 100644
--- a/fixtures/fixtures.go
+++ b/fixtures/fixtures.go
@@ -67,7 +67,10 @@ var fixtures = Fixtures{{
URL: "https://github.com/git-fixtures/basic.git",
DotGitHash: plumbing.NewHash("935e5ac17c41c309c356639816ea0694a568c484"),
}, {
-
+ Tags: []string{".git", "worktree"},
+ URL: "https://github.com/git-fixtures/basic.git",
+ WorktreeHash: plumbing.NewHash("e4413db6700d0e72e7680b17c3d5ebbc2d1861bc"),
+}, {
Tags: []string{"packfile", ".git", "unpacked", "multi-packfile"},
URL: "https://github.com/src-d/go-git.git",
Head: plumbing.NewHash("e8788ad9165781196e917292d6055cba1d78664e"),
@@ -155,6 +158,7 @@ type Fixture struct {
Head plumbing.Hash
PackfileHash plumbing.Hash
DotGitHash plumbing.Hash
+ WorktreeHash plumbing.Hash
ObjectsCount int32
}
diff --git a/plumbing/reference.go b/plumbing/reference.go
index 98516c7..b9f4a95 100644
--- a/plumbing/reference.go
+++ b/plumbing/reference.go
@@ -42,7 +42,8 @@ func (r ReferenceName) Short() string {
}
const (
- HEAD ReferenceName = "HEAD"
+ HEAD ReferenceName = "HEAD"
+ Master ReferenceName = "refs/heads/master"
)
// Reference is a representation of git reference
diff --git a/remote_test.go b/remote_test.go
index 7f40979..626aece 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -229,7 +229,7 @@ func (s *RemoteSuite) TestPushToEmptyRepository(c *C) {
dstSto, err := filesystem.NewStorage(dstFs)
c.Assert(err, IsNil)
- dstRepo, err := NewRepository(dstSto)
+ dstRepo, err := Open(dstSto, nil)
c.Assert(err, IsNil)
iter, err := sto.IterReferences()
diff --git a/repository.go b/repository.go
index b2320b9..dd6663a 100644
--- a/repository.go
+++ b/repository.go
@@ -3,6 +3,7 @@ package git
import (
"errors"
"fmt"
+ "os"
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -10,23 +11,27 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/storage/filesystem"
- "gopkg.in/src-d/go-git.v4/storage/memory"
+ billy "srcd.works/go-billy.v1"
osfs "srcd.works/go-billy.v1/os"
)
var (
- ErrObjectNotFound = errors.New("object not found")
- ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch")
- ErrRepositoryNonEmpty = errors.New("repository non empty")
- ErrRemoteNotFound = errors.New("remote not found")
- ErrRemoteExists = errors.New("remote already exists")
+ ErrObjectNotFound = errors.New("object not found")
+ ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch")
+ ErrRepositoryNotExists = errors.New("repository not exists")
+ ErrRepositoryAlreadyExists = errors.New("repository already exists")
+ ErrRemoteNotFound = errors.New("remote not found")
+ ErrRemoteExists = errors.New("remote already exists")
+ ErrWorktreeNotProvided = errors.New("worktree should be provided")
+ ErrIsBareRepository = errors.New("worktree not available in a bare repository")
)
// Repository giturl string, auth common.AuthMethod repository struct
type Repository struct {
- r map[string]*Remote
- s Storer
+ r map[string]*Remote
+ s Storer
+ wt billy.Filesystem
// Progress is where the human readable information sent by the server is
// stored, if nil nothing is stored and the capability (if supported)
@@ -34,30 +39,136 @@ type Repository struct {
Progress sideband.Progress
}
-// NewMemoryRepository creates a new repository, backed by a memory.Storage
-func NewMemoryRepository() *Repository {
- r, _ := NewRepository(memory.NewStorage())
- return r
+// Init create an empty git repository, based on the given Storer and worktree.
+// The worktree Filesystem is optional, if nil a bare repository is created. If
+// the given storer is not empty ErrRepositoryAlreadyExists is returned
+func Init(s Storer, worktree billy.Filesystem) (*Repository, error) {
+ r := newRepository(s, worktree)
+ _, err := r.Reference(plumbing.HEAD, false)
+ switch err {
+ case plumbing.ErrReferenceNotFound:
+ case nil:
+ return nil, ErrRepositoryAlreadyExists
+ default:
+ return nil, err
+ }
+
+ h := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.Master)
+ if err := s.SetReference(h); err != nil {
+ return nil, err
+ }
+
+ if worktree == nil {
+ r.setIsBare(true)
+ }
+
+ return r, nil
+}
+
+// Open opens a git repository using the given Storer and worktree filesystem,
+// if the given storer is complete empty ErrRepositoryNotExists is returned.
+// The worktree can be nil when the repository being open is bare, if the
+// a non-bare repository is not provied and worktree is nil, the err
+// ErrWorktreeNotProvided is returned
+func Open(s Storer, worktree billy.Filesystem) (*Repository, error) {
+ _, err := s.Reference(plumbing.HEAD)
+ if err == plumbing.ErrReferenceNotFound {
+ return nil, ErrRepositoryNotExists
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ cfg, err := s.Config()
+ if err != nil {
+ return nil, err
+ }
+
+ if !cfg.Core.IsBare && worktree == nil {
+ return nil, ErrWorktreeNotProvided
+ }
+
+ return newRepository(s, worktree), nil
}
-// NewFilesystemRepository creates a new repository, backed by a filesystem.Storage
-// based on a fs.OS, if you want to use a custom one you need to use the function
-// NewRepository and build you filesystem.Storage
-func NewFilesystemRepository(path string) (*Repository, error) {
- s, err := filesystem.NewStorage(osfs.New(path))
+// Clone a repository into the given Storer and worktree Filesystem with the
+// given options, if worktree is nil a bare repository is created. If the given
+// storer is not empty ErrRepositoryAlreadyExists is returned
+func Clone(s Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) {
+ r, err := Init(s, worktree)
+ if err != nil {
+ return nil, err
+ }
+
+ return r, r.Clone(o)
+}
+
+// PlainInit create an empty git repository at the given path. isBare defines
+// if the repository will have worktree (non-bare) or not (bare), if the path
+// is not empty ErrRepositoryAlreadyExists is returned
+func PlainInit(path string, isBare bool) (*Repository, error) {
+ var wt, dot billy.Filesystem
+
+ if isBare {
+ dot = osfs.New(path)
+ } else {
+ wt = osfs.New(path)
+ dot = wt.Dir(".git")
+ }
+
+ s, err := filesystem.NewStorage(dot)
+ if err != nil {
+ return nil, err
+ }
+
+ return Init(s, wt)
+}
+
+// PlainOpen opens a git repository from the given path. It detects is the
+// 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
+
+ fs := osfs.New(path)
+ if _, err := fs.Stat(".git"); err != nil {
+ if !os.IsNotExist(err) {
+ return nil, err
+ }
+
+ dot = fs
+ } else {
+ wt = fs
+ dot = fs.Dir(".git")
+ }
+
+ s, err := filesystem.NewStorage(dot)
if err != nil {
return nil, err
}
- return NewRepository(s)
+ return Open(s, wt)
}
-// NewRepository creates a new repository with the given Storage
-func NewRepository(s Storer) (*Repository, error) {
+// PlainClone a repository into the path with the given options, isBare defines
+// if the new repository will be bare or normal. If the path is not empty
+// ErrRepositoryAlreadyExists is returned
+func PlainClone(path string, isBare bool, o *CloneOptions) (*Repository, error) {
+ r, err := PlainInit(path, isBare)
+ if err != nil {
+ return nil, err
+ }
+
+ return r, r.Clone(o)
+}
+
+func newRepository(s Storer, worktree billy.Filesystem) *Repository {
return &Repository{
- s: s,
- r: make(map[string]*Remote, 0),
- }, nil
+ s: s,
+ wt: worktree,
+ r: make(map[string]*Remote, 0),
+ }
}
// Config return the repository config
@@ -136,15 +247,6 @@ func (r *Repository) DeleteRemote(name string) error {
// Clone clones a remote repository
func (r *Repository) Clone(o *CloneOptions) error {
- empty, err := r.IsEmpty()
- if err != nil {
- return err
- }
-
- if !empty {
- return ErrRepositoryNonEmpty
- }
-
if err := o.Validate(); err != nil {
return err
}
@@ -183,6 +285,10 @@ func (r *Repository) Clone(o *CloneOptions) error {
return err
}
+ if err := r.updateWorktree(); err != nil {
+ return err
+ }
+
return r.updateRemoteConfig(remote, o, c, head)
}
@@ -315,20 +421,6 @@ func updateReferenceStorerIfNeeded(
return false, nil
}
-// IsEmpty returns true if the repository is empty
-func (r *Repository) IsEmpty() (bool, error) {
- iter, err := r.References()
- if err != nil {
- return false, err
- }
-
- var count int
- return count == 0, iter.ForEach(func(r *plumbing.Reference) error {
- count++
- return nil
- })
-}
-
// Pull incorporates changes from a remote repository into the current branch.
// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
// no changes to be fetched, or an error.
@@ -372,7 +464,25 @@ func (r *Repository) Pull(o *PullOptions) error {
return NoErrAlreadyUpToDate
}
- return nil
+ return r.updateWorktree()
+}
+
+func (r *Repository) updateWorktree() error {
+ if r.wt == nil {
+ return nil
+ }
+
+ w, err := r.Worktree(nil)
+ if err != nil {
+ return err
+ }
+
+ h, err := r.Head()
+ if err != nil {
+ return err
+ }
+
+ return w.Checkout(h.Hash())
}
// Fetch fetches changes from a remote repository.
@@ -512,3 +622,17 @@ func (r *Repository) Reference(name plumbing.ReferenceName, resolved bool) (
func (r *Repository) References() (storer.ReferenceIter, error) {
return r.s.IterReferences()
}
+
+// Worktree returns a worktree based on the given fs, if nil the default
+// worktree will be used.
+func (r *Repository) Worktree(fs billy.Filesystem) (*Worktree, error) {
+ if r.wt == nil && fs == nil {
+ return nil, ErrIsBareRepository
+ }
+
+ if fs == nil {
+ fs = r.wt
+ }
+
+ return &Worktree{r: r, fs: fs}, nil
+}
diff --git a/repository_test.go b/repository_test.go
index 5cbc8dd..1c0419a 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -3,6 +3,7 @@ package git
import (
"bytes"
"fmt"
+ "io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -12,10 +13,11 @@ import (
"gopkg.in/src-d/go-git.v4/fixtures"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
"gopkg.in/src-d/go-git.v4/storage/memory"
. "gopkg.in/check.v1"
- "gopkg.in/src-d/go-git.v4/storage/filesystem"
+ memoryfs "srcd.works/go-billy.v1/memory"
)
type RepositorySuite struct {
@@ -24,13 +26,95 @@ type RepositorySuite struct {
var _ = Suite(&RepositorySuite{})
-func (s *RepositorySuite) TestNewRepository(c *C) {
- r := NewMemoryRepository()
+func (s *RepositorySuite) TestInit(c *C) {
+ r, err := Init(memory.NewStorage(), memoryfs.New())
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ cfg, err := r.Config()
+ c.Assert(err, IsNil)
+ c.Assert(cfg.Core.IsBare, Equals, false)
+}
+
+func (s *RepositorySuite) TestInitBare(c *C) {
+ r, err := Init(memory.NewStorage(), nil)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ cfg, err := r.Config()
+ c.Assert(err, IsNil)
+ c.Assert(cfg.Core.IsBare, Equals, true)
+
+}
+
+func (s *RepositorySuite) TestInitAlreadyExists(c *C) {
+ st := memory.NewStorage()
+
+ r, err := Init(st, nil)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = Init(st, nil)
+ c.Assert(err, Equals, ErrRepositoryAlreadyExists)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestOpen(c *C) {
+ st := memory.NewStorage()
+
+ r, err := Init(st, memoryfs.New())
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = Open(st, memoryfs.New())
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+}
+
+func (s *RepositorySuite) TestOpenBare(c *C) {
+ st := memory.NewStorage()
+
+ r, err := Init(st, nil)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = Open(st, nil)
+ c.Assert(err, IsNil)
c.Assert(r, NotNil)
}
+func (s *RepositorySuite) TestOpenMissingWorktree(c *C) {
+ st := memory.NewStorage()
+
+ r, err := Init(st, memoryfs.New())
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = Open(st, nil)
+ c.Assert(err, Equals, ErrWorktreeNotProvided)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestOpenNotExists(c *C) {
+ r, err := Open(memory.NewStorage(), nil)
+ c.Assert(err, Equals, ErrRepositoryNotExists)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestClone(c *C) {
+ r, err := Clone(memory.NewStorage(), nil, &CloneOptions{
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ c.Assert(err, IsNil)
+
+ remotes, err := r.Remotes()
+ c.Assert(err, IsNil)
+ c.Assert(remotes, HasLen, 1)
+}
+
func (s *RepositorySuite) TestCreateRemoteAndRemote(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
remote, err := r.CreateRemote(&config.RemoteConfig{
Name: "foo",
URL: "http://foo/foo.git",
@@ -48,7 +132,7 @@ func (s *RepositorySuite) TestCreateRemoteAndRemote(c *C) {
func (s *RepositorySuite) TestRemoteWithProgress(c *C) {
buf := bytes.NewBuffer(nil)
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
r.Progress = buf
remote, err := r.CreateRemote(&config.RemoteConfig{
@@ -61,7 +145,7 @@ func (s *RepositorySuite) TestRemoteWithProgress(c *C) {
}
func (s *RepositorySuite) TestCreateRemoteInvalid(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
remote, err := r.CreateRemote(&config.RemoteConfig{})
c.Assert(err, Equals, config.ErrRemoteConfigEmptyName)
@@ -69,7 +153,7 @@ func (s *RepositorySuite) TestCreateRemoteInvalid(c *C) {
}
func (s *RepositorySuite) TestDeleteRemote(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
_, err := r.CreateRemote(&config.RemoteConfig{
Name: "foo",
URL: "http://foo/foo.git",
@@ -85,8 +169,94 @@ func (s *RepositorySuite) TestDeleteRemote(c *C) {
c.Assert(alt, IsNil)
}
+func (s *RepositorySuite) TestPlainInit(c *C) {
+ dir, err := ioutil.TempDir("", "plain-init")
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ cfg, err := r.Config()
+ c.Assert(err, IsNil)
+ c.Assert(cfg.Core.IsBare, Equals, true)
+}
+
+func (s *RepositorySuite) TestPlainInitAlreadyExists(c *C) {
+ dir, err := ioutil.TempDir("", "plain-init")
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = PlainInit(dir, true)
+ c.Assert(err, Equals, ErrRepositoryAlreadyExists)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestPlainOpen(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, false)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = PlainOpen(dir)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenBare(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = PlainOpen(dir)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenNotBare(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, false)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = PlainOpen(filepath.Join(dir, ".git"))
+ c.Assert(err, Equals, ErrWorktreeNotProvided)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenNotExists(c *C) {
+ r, err := PlainOpen("/not-exists/")
+ c.Assert(err, Equals, ErrRepositoryNotExists)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestPlainClone(c *C) {
+ dir, err := ioutil.TempDir("", "plain-clone")
+ defer os.RemoveAll(dir)
+
+ r, err := PlainClone(dir, false, &CloneOptions{
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ c.Assert(err, IsNil)
+
+ remotes, err := r.Remotes()
+ c.Assert(err, IsNil)
+ c.Assert(remotes, HasLen, 1)
+}
+
func (s *RepositorySuite) TestFetch(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
_, err := r.CreateRemote(&config.RemoteConfig{
Name: DefaultRemoteName,
URL: s.GetBasicLocalRepositoryURL(),
@@ -98,7 +268,7 @@ func (s *RepositorySuite) TestFetch(c *C) {
c.Assert(err, IsNil)
c.Assert(remotes, HasLen, 1)
- _, err = r.Reference(plumbing.HEAD, false)
+ _, err = r.Head()
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
branch, err := r.Reference("refs/remotes/origin/master", false)
@@ -108,8 +278,9 @@ func (s *RepositorySuite) TestFetch(c *C) {
c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
}
-func (s *RepositorySuite) TestClone(c *C) {
- r := NewMemoryRepository()
+func (s *RepositorySuite) TestCloneDeep(c *C) {
+ fs := memoryfs.New()
+ r, _ := Init(memory.NewStorage(), fs)
head, err := r.Head()
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
@@ -141,10 +312,14 @@ func (s *RepositorySuite) TestClone(c *C) {
c.Assert(branch, NotNil)
c.Assert(branch.Type(), Equals, plumbing.HashReference)
c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
+
+ fi, err := fs.ReadDir("")
+ c.Assert(err, IsNil)
+ c.Assert(fi, HasLen, 8)
}
func (s *RepositorySuite) TestCloneConfig(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
head, err := r.Head()
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
@@ -164,23 +339,9 @@ func (s *RepositorySuite) TestCloneConfig(c *C) {
c.Assert(cfg.Remotes["origin"].Name, Equals, "origin")
c.Assert(cfg.Remotes["origin"].URL, Not(Equals), "")
}
-func (s *RepositorySuite) TestCloneNonEmpty(c *C) {
- r := NewMemoryRepository()
-
- head, err := r.Head()
- c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
- c.Assert(head, IsNil)
-
- o := &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}
- err = r.Clone(o)
- c.Assert(err, IsNil)
-
- err = r.Clone(o)
- c.Assert(err, Equals, ErrRepositoryNonEmpty)
-}
func (s *RepositorySuite) TestCloneSingleBranchAndNonHEAD(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
head, err := r.Head()
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
@@ -217,7 +378,7 @@ func (s *RepositorySuite) TestCloneSingleBranchAndNonHEAD(c *C) {
}
func (s *RepositorySuite) TestCloneSingleBranch(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
head, err := r.Head()
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
@@ -253,7 +414,7 @@ func (s *RepositorySuite) TestCloneSingleBranch(c *C) {
}
func (s *RepositorySuite) TestCloneDetachedHEAD(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
ReferenceName: plumbing.ReferenceName("refs/tags/v1.0.0"),
@@ -266,8 +427,24 @@ func (s *RepositorySuite) TestCloneDetachedHEAD(c *C) {
c.Assert(head.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
}
+func (s *RepositorySuite) TestPullCheckout(c *C) {
+ fs := memoryfs.New()
+ r, _ := Init(memory.NewStorage(), fs)
+ r.CreateRemote(&config.RemoteConfig{
+ Name: DefaultRemoteName,
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ err := r.Pull(&PullOptions{})
+ c.Assert(err, IsNil)
+
+ fi, err := fs.ReadDir("")
+ c.Assert(err, IsNil)
+ c.Assert(fi, HasLen, 8)
+}
+
func (s *RepositorySuite) TestPullUpdateReferencesIfNeeded(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
r.CreateRemote(&config.RemoteConfig{
Name: DefaultRemoteName,
URL: s.GetBasicLocalRepositoryURL(),
@@ -295,7 +472,7 @@ func (s *RepositorySuite) TestPullUpdateReferencesIfNeeded(c *C) {
}
func (s *RepositorySuite) TestPullSingleBranch(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
SingleBranch: true,
@@ -320,7 +497,7 @@ func (s *RepositorySuite) TestPullSingleBranch(c *C) {
func (s *RepositorySuite) TestPullAdd(c *C) {
path := fixtures.Basic().One().Worktree().Base()
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{
URL: fmt.Sprintf("file://%s", filepath.Join(path, ".git")),
})
@@ -368,7 +545,7 @@ func (s *RepositorySuite) TestPushToEmptyRepository(c *C) {
dstFs := fixtures.ByTag("empty").One().DotGit()
url := fmt.Sprintf("file://%s", dstFs.Base())
- r, err := NewRepository(sto)
+ r, err := Open(sto, srcFs)
c.Assert(err, IsNil)
_, err = r.CreateRemote(&config.RemoteConfig{
@@ -382,7 +559,7 @@ func (s *RepositorySuite) TestPushToEmptyRepository(c *C) {
sto, err = filesystem.NewStorage(dstFs)
c.Assert(err, IsNil)
- dstRepo, err := NewRepository(sto)
+ dstRepo, err := Open(sto, nil)
c.Assert(err, IsNil)
iter, err := sto.IterReferences()
@@ -406,30 +583,15 @@ func (s *RepositorySuite) TestPushNonExistentRemote(c *C) {
sto, err := filesystem.NewStorage(srcFs)
c.Assert(err, IsNil)
- r, err := NewRepository(sto)
+ r, err := Open(sto, srcFs)
c.Assert(err, IsNil)
err = r.Push(&PushOptions{RemoteName: "myremote"})
c.Assert(err, ErrorMatches, ".*remote not found.*")
}
-func (s *RepositorySuite) TestIsEmpty(c *C) {
- r := NewMemoryRepository()
-
- empty, err := r.IsEmpty()
- c.Assert(err, IsNil)
- c.Assert(empty, Equals, true)
-
- err = r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
- c.Assert(err, IsNil)
-
- empty, err = r.IsEmpty()
- c.Assert(err, IsNil)
- c.Assert(empty, Equals, false)
-}
-
func (s *RepositorySuite) TestCommit(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
})
@@ -453,7 +615,7 @@ func (s *RepositorySuite) TestCommit(c *C) {
}
func (s *RepositorySuite) TestCommits(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
@@ -480,7 +642,7 @@ func (s *RepositorySuite) TestTag(c *C) {
fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
)
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{URL: url})
c.Assert(err, IsNil)
@@ -498,7 +660,7 @@ func (s *RepositorySuite) TestTags(c *C) {
fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
)
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{URL: url})
c.Assert(err, IsNil)
@@ -523,7 +685,7 @@ func (s *RepositorySuite) TestTags(c *C) {
}
func (s *RepositorySuite) TestCommitIterClosePanic(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
@@ -533,7 +695,7 @@ func (s *RepositorySuite) TestCommitIterClosePanic(c *C) {
}
func (s *RepositorySuite) TestRef(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
@@ -547,7 +709,7 @@ func (s *RepositorySuite) TestRef(c *C) {
}
func (s *RepositorySuite) TestRefs(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
@@ -559,7 +721,7 @@ func (s *RepositorySuite) TestRefs(c *C) {
}
func (s *RepositorySuite) TestObject(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
@@ -572,7 +734,7 @@ func (s *RepositorySuite) TestObject(c *C) {
}
func (s *RepositorySuite) TestObjectNotFound(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
@@ -582,6 +744,30 @@ func (s *RepositorySuite) TestObjectNotFound(c *C) {
c.Assert(tag, IsNil)
}
+func (s *RepositorySuite) TestWorktree(c *C) {
+ def := memoryfs.New()
+ r, _ := Init(memory.NewStorage(), def)
+ w, err := r.Worktree(nil)
+ c.Assert(err, IsNil)
+ c.Assert(w.fs, Equals, def)
+}
+
+func (s *RepositorySuite) TestWorktreeAlternative(c *C) {
+ r, _ := Init(memory.NewStorage(), memoryfs.New())
+
+ alt := memoryfs.New()
+ w, err := r.Worktree(alt)
+ c.Assert(err, IsNil)
+ c.Assert(w.fs, Equals, alt)
+}
+
+func (s *RepositorySuite) TestWorktreeBare(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+ w, err := r.Worktree(nil)
+ c.Assert(err, Equals, ErrIsBareRepository)
+ c.Assert(w, IsNil)
+}
+
func ExecuteOnPath(c *C, path string, cmds ...string) error {
for _, cmd := range cmds {
err := executeOnPath(path, cmd)
diff --git a/worktree.go b/worktree.go
new file mode 100644
index 0000000..2aefa76
--- /dev/null
+++ b/worktree.go
@@ -0,0 +1,49 @@
+package git
+
+import (
+ "io"
+ "os"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/object"
+
+ "srcd.works/go-billy.v1"
+)
+
+type Worktree struct {
+ r *Repository
+ fs billy.Filesystem
+}
+
+func (w *Worktree) Checkout(commit plumbing.Hash) error {
+ c, err := w.r.Commit(commit)
+ if err != nil {
+ return err
+ }
+
+ files, err := c.Files()
+ if err != nil {
+ return err
+ }
+
+ return files.ForEach(w.checkoutFile)
+}
+
+func (w *Worktree) checkoutFile(f *object.File) error {
+ from, err := f.Reader()
+ if err != nil {
+ return err
+ }
+
+ defer from.Close()
+ to, err := w.fs.OpenFile(f.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode)
+ if err != nil {
+ return err
+ }
+
+ if _, err := io.Copy(to, from); err != nil {
+ return err
+ }
+
+ return to.Close()
+}
diff --git a/worktree_test.go b/worktree_test.go
new file mode 100644
index 0000000..9c5d8e3
--- /dev/null
+++ b/worktree_test.go
@@ -0,0 +1,39 @@
+package git
+
+import (
+ "io/ioutil"
+
+ . "gopkg.in/check.v1"
+ "srcd.works/go-billy.v1/memory"
+)
+
+type WorktreeSuite struct {
+ BaseSuite
+}
+
+var _ = Suite(&WorktreeSuite{})
+
+func (s *WorktreeSuite) TestCheckout(c *C) {
+ h, err := s.Repository.Head()
+ c.Assert(err, IsNil)
+
+ fs := memory.New()
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err = w.Checkout(h.Hash())
+ c.Assert(err, IsNil)
+
+ entries, err := fs.ReadDir("/")
+ c.Assert(err, IsNil)
+
+ c.Assert(entries, HasLen, 8)
+ ch, err := fs.Open("CHANGELOG")
+ c.Assert(err, IsNil)
+
+ content, err := ioutil.ReadAll(ch)
+ c.Assert(err, IsNil)
+ c.Assert(string(content), Equals, "Initial changelog\n")
+}