summaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2020-08-07 17:52:17 +1000
committerJesse Duffield <jessedduffield@gmail.com>2020-08-07 18:01:26 +1000
commit469ac116efde8dd107bc227ef8fc0f1927a629cb (patch)
tree5fb8fe4c5d47bc62bf656dfc35f18df895f2dfb0 /pkg
parenta86103479b42b0877b124b7306cda177e86e47d0 (diff)
allow renames to be discarded
Diffstat (limited to 'pkg')
-rw-r--r--pkg/commands/file.go6
-rw-r--r--pkg/commands/git.go75
-rw-r--r--pkg/commands/git_test.go2
-rw-r--r--pkg/gui/files_panel.go2
4 files changed, 78 insertions, 7 deletions
diff --git a/pkg/commands/file.go b/pkg/commands/file.go
index 37a1dcccb..754b2871f 100644
--- a/pkg/commands/file.go
+++ b/pkg/commands/file.go
@@ -1,5 +1,7 @@
package commands
+import "strings"
+
// File : A file from git status
// duplicating this for now
type File struct {
@@ -14,3 +16,7 @@ type File struct {
Type string // one of 'file', 'directory', and 'other'
ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
}
+
+func (f *File) IsRename() bool {
+ return strings.Contains(f.Name, " -> ")
+}
diff --git a/pkg/commands/git.go b/pkg/commands/git.go
index 784dad087..20cbd9fb2 100644
--- a/pkg/commands/git.go
+++ b/pkg/commands/git.go
@@ -231,8 +231,12 @@ func (c *GitCommand) ShowStashEntryCmdStr(index int) string {
}
// GetStatusFiles git status files
-func (c *GitCommand) GetStatusFiles() []*File {
- statusOutput, _ := c.GitStatus()
+type GetStatusFileOptions struct {
+ NoRenames bool
+}
+
+func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*File {
+ statusOutput, _ := c.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames})
statusStrings := utils.SplitLines(statusOutput)
files := []*File{}
@@ -542,8 +546,16 @@ func (c *GitCommand) UnStageFile(fileName string, tracked bool) error {
}
// GitStatus returns the plaintext short status of the repo
-func (c *GitCommand) GitStatus() (string, error) {
- return c.OSCommand.RunCommandWithOutput("git status --untracked-files=all --porcelain")
+type GitStatusOptions struct {
+ NoRenames bool
+}
+
+func (c *GitCommand) GitStatus(opts GitStatusOptions) (string, error) {
+ noRenamesFlag := ""
+ if opts.NoRenames {
+ noRenamesFlag = "--no-renames"
+ }
+ return c.OSCommand.RunCommandWithOutput("git status --untracked-files=all --porcelain %s", noRenamesFlag)
}
// IsInMergeState states whether we are still mid-merge
@@ -569,8 +581,61 @@ func (c *GitCommand) RebaseMode() (string, error) {
}
}
+func (c *GitCommand) BeforeAndAfterFileForRename(file *File) (*File, *File, error) {
+
+ if !file.IsRename() {
+ return nil, nil, errors.New("Expected renamed file")
+ }
+
+ // we've got a file that represents a rename from one file to another. Unfortunately
+ // our File abstraction fails to consider this case, so here we will refetch
+ // all files, passing the --no-renames flag and then recursively call the function
+ // again for the before file and after file. At some point we should fix the abstraction itself
+
+ split := strings.Split(file.Name, " -> ")
+ filesWithoutRenames := c.GetStatusFiles(GetStatusFileOptions{NoRenames: true})
+ var beforeFile *File
+ var afterFile *File
+ for _, f := range filesWithoutRenames {
+ if f.Name == split[0] {
+ beforeFile = f
+ }
+ if f.Name == split[1] {
+ afterFile = f
+ }
+ }
+
+ if beforeFile == nil || afterFile == nil {
+ return nil, nil, errors.New("Could not find deleted file or new file for file rename")
+ }
+
+ if beforeFile.IsRename() || afterFile.IsRename() {
+ // probably won't happen but we want to ensure we don't get an infinite loop
+ return nil, nil, errors.New("Nested rename found")
+ }
+
+ return beforeFile, afterFile, nil
+}
+
// DiscardAllFileChanges directly
func (c *GitCommand) DiscardAllFileChanges(file *File) error {
+ if file.IsRename() {
+ beforeFile, afterFile, err := c.BeforeAndAfterFileForRename(file)
+ if err != nil {
+ return err
+ }
+
+ if err := c.DiscardAllFileChanges(beforeFile); err != nil {
+ return err
+ }
+
+ if err := c.DiscardAllFileChanges(afterFile); err != nil {
+ return err
+ }
+
+ return nil
+ }
+
// if the file isn't tracked, we assume you want to delete it
quotedFileName := c.OSCommand.Quote(file.Name)
if file.HasStagedChanges || file.HasMergeConflicts {
@@ -1095,7 +1160,7 @@ func (c *GitCommand) StashSaveStagedChanges(message string) error {
// if you had staged an untracked file, that will now appear as 'AD' in git status
// meaning it's deleted in your working tree but added in your index. Given that it's
// now safely stashed, we need to remove it.
- files := c.GetStatusFiles()
+ files := c.GetStatusFiles(GetStatusFileOptions{})
for _, file := range files {
if file.ShortStatus == "AD" {
if err := c.UnStageFile(file.Name, false); err != nil {
diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go
index 750e09dcd..43accd654 100644
--- a/pkg/commands/git_test.go
+++ b/pkg/commands/git_test.go
@@ -420,7 +420,7 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
gitCmd := NewDummyGitCommand()
gitCmd.OSCommand.command = s.command
- s.test(gitCmd.GetStatusFiles())
+ s.test(gitCmd.GetStatusFiles(GetStatusFileOptions{}))
})
}
}
diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go
index 034a21fb6..6cba05715 100644
--- a/pkg/gui/files_panel.go
+++ b/pkg/gui/files_panel.go
@@ -411,7 +411,7 @@ func (gui *Gui) handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) refreshStateFiles() error {
// get files to stage
- files := gui.GitCommand.GetStatusFiles()
+ files := gui.GitCommand.GetStatusFiles(commands.GetStatusFileOptions{})
gui.State.Files = gui.GitCommand.MergeStatusFiles(gui.State.Files, files)
if err := gui.fileWatcher.addFilesToFileWatcher(files); err != nil {