diff options
author | Máximo Cuadros <mcuadros@gmail.com> | 2017-01-27 17:46:46 +0100 |
---|---|---|
committer | Máximo Cuadros <mcuadros@gmail.com> | 2017-01-28 00:42:22 +0100 |
commit | 24c1878260351d9f9f6c575cbeeb5878104d6a0e (patch) | |
tree | c6a9575aa84197f7f33515ca4d55816134d1569b | |
parent | 1c2602a791371e76d52f89b2c8193cb200c66ad6 (diff) | |
download | go-git-24c1878260351d9f9f6c575cbeeb5878104d6a0e.tar.gz |
new repository constructors and worktree
-rw-r--r-- | common_test.go | 27 | ||||
-rw-r--r-- | fixtures/fixtures.go | 6 | ||||
-rw-r--r-- | plumbing/reference.go | 3 | ||||
-rw-r--r-- | remote_test.go | 2 | ||||
-rw-r--r-- | repository.go | 218 | ||||
-rw-r--r-- | repository_test.go | 304 | ||||
-rw-r--r-- | worktree.go | 49 | ||||
-rw-r--r-- | worktree_test.go | 39 |
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") +} |