aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COMPATIBILITY.md4
-rw-r--r--plumbing/format/index/index.go12
-rw-r--r--plumbing/format/index/index_test.go17
-rw-r--r--worktree_status.go65
-rw-r--r--worktree_test.go110
5 files changed, 206 insertions, 2 deletions
diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md
index 81ff6b6..1c17483 100644
--- a/COMPATIBILITY.md
+++ b/COMPATIBILITY.md
@@ -16,8 +16,8 @@ is supported by go-git.
| status | ✔ |
| commit | ✔ |
| reset | ✔ |
-| rm | ✖ |
-| mv | ✖ |
+| rm | ✔ |
+| mv | ✔ |
| **branching and merging** |
| branch | ✔ |
| checkout | ✔ | Basic usages of checkout are supported. |
diff --git a/plumbing/format/index/index.go b/plumbing/format/index/index.go
index 782e3d1..9de4230 100644
--- a/plumbing/format/index/index.go
+++ b/plumbing/format/index/index.go
@@ -62,6 +62,18 @@ func (i *Index) Entry(path string) (*Entry, error) {
return nil, ErrEntryNotFound
}
+// Remove remove the entry that match the give path and returns deleted entry.
+func (i *Index) Remove(path string) (*Entry, error) {
+ for index, e := range i.Entries {
+ if e.Name == path {
+ i.Entries = append(i.Entries[:index], i.Entries[index+1:]...)
+ return e, nil
+ }
+ }
+
+ return nil, ErrEntryNotFound
+}
+
// String is equivalent to `git ls-files --stage --debug`
func (i *Index) String() string {
buf := bytes.NewBuffer(nil)
diff --git a/plumbing/format/index/index_test.go b/plumbing/format/index/index_test.go
index 67286b3..cad5f9c 100644
--- a/plumbing/format/index/index_test.go
+++ b/plumbing/format/index/index_test.go
@@ -20,3 +20,20 @@ func (s *IndexSuite) TestIndexEntry(c *C) {
c.Assert(e, IsNil)
c.Assert(err, Equals, ErrEntryNotFound)
}
+
+func (s *IndexSuite) TestIndexRemove(c *C) {
+ idx := &Index{
+ Entries: []*Entry{
+ {Name: "foo", Size: 42},
+ {Name: "bar", Size: 82},
+ },
+ }
+
+ e, err := idx.Remove("foo")
+ c.Assert(err, IsNil)
+ c.Assert(e.Name, Equals, "foo")
+
+ e, err = idx.Remove("foo")
+ c.Assert(e, IsNil)
+ c.Assert(err, Equals, ErrEntryNotFound)
+}
diff --git a/worktree_status.go b/worktree_status.go
index 632f102..eb4a83a 100644
--- a/worktree_status.go
+++ b/worktree_status.go
@@ -2,7 +2,9 @@ package git
import (
"bytes"
+ "errors"
"io"
+ "os"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/filemode"
@@ -15,6 +17,10 @@ import (
"gopkg.in/src-d/go-git.v4/utils/merkletrie/noder"
)
+// ErrDestinationExists in an Move operation means that the target exists on
+// the worktree.
+var ErrDestinationExists = errors.New("destination exists")
+
// Status returns the working tree status.
func (w *Worktree) Status() (Status, error) {
ref, err := w.r.Head()
@@ -247,6 +253,7 @@ func (w *Worktree) addOrUpdateFileToIndex(filename string, h plumbing.Hash) erro
return err
}
}
+
return w.r.Storer.SetIndex(idx)
}
@@ -266,6 +273,8 @@ func (w *Worktree) doUpdateFileToIndex(e *index.Entry, filename string, h plumbi
e.Hash = h
e.ModifiedAt = info.ModTime()
e.Mode, err = filemode.NewFromOSFileMode(info.Mode())
+ e.Size = uint32(info.Size())
+
if err != nil {
return err
}
@@ -273,3 +282,59 @@ func (w *Worktree) doUpdateFileToIndex(e *index.Entry, filename string, h plumbi
fillSystemInfo(e, info.Sys())
return nil
}
+
+// Remove removes files from the working tree and from the index.
+func (w *Worktree) Remove(path string) (plumbing.Hash, error) {
+ hash, err := w.deleteFromIndex(path)
+ if err != nil {
+ return plumbing.ZeroHash, err
+ }
+
+ return hash, w.deleteFromFilesystem(path)
+}
+
+func (w *Worktree) deleteFromIndex(path string) (plumbing.Hash, error) {
+ idx, err := w.r.Storer.Index()
+ if err != nil {
+ return plumbing.ZeroHash, err
+ }
+
+ e, err := idx.Remove(path)
+ if err != nil {
+ return plumbing.ZeroHash, err
+ }
+
+ return e.Hash, w.r.Storer.SetIndex(idx)
+}
+
+func (w *Worktree) deleteFromFilesystem(path string) error {
+ err := w.fs.Remove(path)
+ if os.IsNotExist(err) {
+ return nil
+ }
+
+ return err
+}
+
+// Move moves or rename a file in the worktree and the index, directories are
+// not supported.
+func (w *Worktree) Move(from, to string) (plumbing.Hash, error) {
+ if _, err := w.fs.Stat(from); err != nil {
+ return plumbing.ZeroHash, err
+ }
+
+ if _, err := w.fs.Stat(to); err == nil {
+ return plumbing.ZeroHash, ErrDestinationExists
+ }
+
+ hash, err := w.deleteFromIndex(from)
+ if err != nil {
+ return plumbing.ZeroHash, err
+ }
+
+ if err := w.fs.Rename(from, to); err != nil {
+ return hash, err
+ }
+
+ return hash, w.addOrUpdateFileToIndex(to, hash)
+}
diff --git a/worktree_test.go b/worktree_test.go
index 68760f2..6ca2ed0 100644
--- a/worktree_test.go
+++ b/worktree_test.go
@@ -590,3 +590,113 @@ func (s *WorktreeSuite) TestAddUnmodified(c *C) {
c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f")
c.Assert(err, IsNil)
}
+
+func (s *WorktreeSuite) TestRemove(c *C) {
+ fs := memfs.New()
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err := w.Checkout(&CheckoutOptions{Force: true})
+ c.Assert(err, IsNil)
+
+ hash, err := w.Remove("LICENSE")
+ c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f")
+ c.Assert(err, IsNil)
+
+ status, err := w.Status()
+ c.Assert(err, IsNil)
+ c.Assert(status, HasLen, 1)
+ c.Assert(status.File("LICENSE").Staging, Equals, Deleted)
+}
+
+func (s *WorktreeSuite) TestRemoveNotExistentEntry(c *C) {
+ fs := memfs.New()
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err := w.Checkout(&CheckoutOptions{Force: true})
+ c.Assert(err, IsNil)
+
+ hash, err := w.Remove("not-exists")
+ c.Assert(hash.IsZero(), Equals, true)
+ c.Assert(err, NotNil)
+}
+
+func (s *WorktreeSuite) TestRemoveDeletedFromWorktree(c *C) {
+ fs := memfs.New()
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err := w.Checkout(&CheckoutOptions{Force: true})
+ c.Assert(err, IsNil)
+
+ err = fs.Remove("LICENSE")
+ c.Assert(err, IsNil)
+
+ hash, err := w.Remove("LICENSE")
+ c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f")
+ c.Assert(err, IsNil)
+
+ status, err := w.Status()
+ c.Assert(err, IsNil)
+ c.Assert(status, HasLen, 1)
+ c.Assert(status.File("LICENSE").Staging, Equals, Deleted)
+}
+
+func (s *WorktreeSuite) TestMove(c *C) {
+ fs := memfs.New()
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err := w.Checkout(&CheckoutOptions{Force: true})
+ c.Assert(err, IsNil)
+
+ hash, err := w.Move("LICENSE", "foo")
+ c.Check(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f")
+ c.Assert(err, IsNil)
+
+ status, err := w.Status()
+ c.Assert(err, IsNil)
+ c.Assert(status, HasLen, 2)
+ c.Assert(status.File("LICENSE").Staging, Equals, Deleted)
+ c.Assert(status.File("foo").Staging, Equals, Added)
+
+}
+
+func (s *WorktreeSuite) TestMoveNotExistentEntry(c *C) {
+ fs := memfs.New()
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err := w.Checkout(&CheckoutOptions{Force: true})
+ c.Assert(err, IsNil)
+
+ hash, err := w.Move("not-exists", "foo")
+ c.Assert(hash.IsZero(), Equals, true)
+ c.Assert(err, NotNil)
+}
+
+func (s *WorktreeSuite) TestMoveToExistent(c *C) {
+ fs := memfs.New()
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err := w.Checkout(&CheckoutOptions{Force: true})
+ c.Assert(err, IsNil)
+
+ hash, err := w.Move(".gitignore", "LICENSE")
+ c.Assert(hash.IsZero(), Equals, true)
+ c.Assert(err, Equals, ErrDestinationExists)
+}