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

worktree: Remove and Move methods #395

Merged
merged 1 commit into from
May 21, 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
4 changes: 2 additions & 2 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down
12 changes: 12 additions & 0 deletions plumbing/format/index/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 17 additions & 0 deletions plumbing/format/index/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestIndexRemoveNonexistentEntry?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already tested

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I didn't know, because the codecov comment is not appearing...

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)
}
65 changes: 65 additions & 0 deletions worktree_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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()
Expand Down Expand Up @@ -247,6 +253,7 @@ func (w *Worktree) addOrUpdateFileToIndex(filename string, h plumbing.Hash) erro
return err
}
}

return w.r.Storer.SetIndex(idx)
}

Expand All @@ -266,10 +273,68 @@ 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
}

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)
}
110 changes: 110 additions & 0 deletions worktree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestRemoveNonexistentElement?

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)
}