From 0f3639790292f843a2577d97df1f0c3665e06501 Mon Sep 17 00:00:00 2001 From: Ben Talbot Date: Thu, 17 Mar 2022 22:12:56 -0700 Subject: git: worktree, add RestoreStaged which works like the "git restore --staged ..." command Small formatting and style fixes before rebasing against master Setup args for restore in TestExamples Fix typo in error message and remove dependency on fmt in worktree_test --- worktree_test.go | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) (limited to 'worktree_test.go') diff --git a/worktree_test.go b/worktree_test.go index 668c30a..e84f3b1 100644 --- a/worktree_test.go +++ b/worktree_test.go @@ -26,6 +26,7 @@ import ( "github.com/go-git/go-git/v5/storage/memory" "github.com/stretchr/testify/assert" + "github.com/go-git/go-billy/v5" "github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-billy/v5/util" @@ -3019,3 +3020,173 @@ func TestWindowsValidPath(t *testing.T) { }) } } + +var statusCodeNames = map[StatusCode]string{ + Unmodified: "Unmodified", + Untracked: "Untracked", + Modified: "Modified", + Added: "Added", + Deleted: "Deleted", + Renamed: "Renamed", + Copied: "Copied", + UpdatedButUnmerged: "UpdatedButUnmerged", +} + +func setupForRestore(c *C, s *WorktreeSuite) (fs billy.Filesystem, w *Worktree, names []string) { + fs = memfs.New() + w = &Worktree{ + r: s.Repository, + Filesystem: fs, + } + + err := w.Checkout(&CheckoutOptions{}) + c.Assert(err, IsNil) + + names = []string{"foo", "CHANGELOG", "LICENSE", "binary.jpg"} + verifyStatus(c, "Checkout", w, names, []FileStatus{ + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Untracked, Staging: Untracked}, + }) + + // Touch of bunch of files including create a new file and delete an exsiting file + for _, name := range names { + err = util.WriteFile(fs, name, []byte("Foo Bar"), 0755) + c.Assert(err, IsNil) + } + err = util.RemoveAll(fs, names[3]) + c.Assert(err, IsNil) + + // Confirm the status after doing the edits without staging anything + verifyStatus(c, "Edits", w, names, []FileStatus{ + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Modified, Staging: Unmodified}, + {Worktree: Modified, Staging: Unmodified}, + {Worktree: Deleted, Staging: Unmodified}, + }) + + // Stage all files and verify the updated status + for _, name := range names { + _, err = w.Add(name) + c.Assert(err, IsNil) + } + verifyStatus(c, "Staged", w, names, []FileStatus{ + {Worktree: Unmodified, Staging: Added}, + {Worktree: Unmodified, Staging: Modified}, + {Worktree: Unmodified, Staging: Modified}, + {Worktree: Unmodified, Staging: Deleted}, + }) + + // Add secondary changes to a file to make sure we only restore the staged file + err = util.WriteFile(fs, names[1], []byte("Foo Bar:11"), 0755) + c.Assert(err, IsNil) + err = util.WriteFile(fs, names[2], []byte("Foo Bar:22"), 0755) + c.Assert(err, IsNil) + + verifyStatus(c, "Secondary Edits", w, names, []FileStatus{ + {Worktree: Unmodified, Staging: Added}, + {Worktree: Modified, Staging: Modified}, + {Worktree: Modified, Staging: Modified}, + {Worktree: Unmodified, Staging: Deleted}, + }) + + return +} + +func verifyStatus(c *C, marker string, w *Worktree, files []string, statuses []FileStatus) { + c.Assert(len(files), Equals, len(statuses)) + + status, err := w.Status() + c.Assert(err, IsNil) + + for i, file := range files { + current := status.File(file) + expected := statuses[i] + c.Assert(current.Worktree, Equals, expected.Worktree, Commentf("%s - [%d] : %s Worktree %s != %s", marker, i, file, statusCodeNames[current.Worktree], statusCodeNames[expected.Worktree])) + c.Assert(current.Staging, Equals, expected.Staging, Commentf("%s - [%d] : %s Staging %s != %s", marker, i, file, statusCodeNames[current.Staging], statusCodeNames[expected.Staging])) + } +} + +func (s *WorktreeSuite) TestRestoreStaged(c *C) { + fs, w, names := setupForRestore(c, s) + + // Attempt without files should throw an error like the git restore --staged + opts := RestoreOptions{Staged: true} + err := w.Restore(&opts) + c.Assert(err, Equals, ErrNoRestorePaths) + + // Restore Staged files in 2 groups and confirm status + opts.Files = []string{names[0], names[1]} + err = w.Restore(&opts) + c.Assert(err, IsNil) + verifyStatus(c, "Restored First", w, names, []FileStatus{ + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Modified, Staging: Unmodified}, + {Worktree: Modified, Staging: Modified}, + {Worktree: Unmodified, Staging: Deleted}, + }) + + // Make sure the restore didn't overwrite our secondary changes + contents, err := util.ReadFile(fs, names[1]) + c.Assert(err, IsNil) + c.Assert(string(contents), Equals, "Foo Bar:11") + + opts.Files = []string{names[2], names[3]} + err = w.Restore(&opts) + c.Assert(err, IsNil) + verifyStatus(c, "Restored Second", w, names, []FileStatus{ + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Modified, Staging: Unmodified}, + {Worktree: Modified, Staging: Unmodified}, + {Worktree: Deleted, Staging: Unmodified}, + }) + + // Make sure the restore didn't overwrite our secondary changes + contents, err = util.ReadFile(fs, names[2]) + c.Assert(err, IsNil) + c.Assert(string(contents), Equals, "Foo Bar:22") +} + +func (s *WorktreeSuite) TestRestoreWorktree(c *C) { + _, w, names := setupForRestore(c, s) + + // Attempt without files should throw an error like the git restore + opts := RestoreOptions{} + err := w.Restore(&opts) + c.Assert(err, Equals, ErrNoRestorePaths) + + opts.Files = []string{names[0], names[1]} + err = w.Restore(&opts) + c.Assert(err, Equals, ErrRestoreWorktreeOnlyNotSupported) +} + +func (s *WorktreeSuite) TestRestoreBoth(c *C) { + _, w, names := setupForRestore(c, s) + + // Attempt without files should throw an error like the git restore --staged --worktree + opts := RestoreOptions{Staged: true, Worktree: true} + err := w.Restore(&opts) + c.Assert(err, Equals, ErrNoRestorePaths) + + // Restore Staged files in 2 groups and confirm status + opts.Files = []string{names[0], names[1]} + err = w.Restore(&opts) + c.Assert(err, IsNil) + verifyStatus(c, "Restored First", w, names, []FileStatus{ + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Modified, Staging: Modified}, + {Worktree: Unmodified, Staging: Deleted}, + }) + + opts.Files = []string{names[2], names[3]} + err = w.Restore(&opts) + c.Assert(err, IsNil) + verifyStatus(c, "Restored Second", w, names, []FileStatus{ + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Untracked, Staging: Untracked}, + {Worktree: Untracked, Staging: Untracked}, + }) +} -- cgit