summaryrefslogtreecommitdiffstats
path: root/pkg/commands
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2019-05-30 22:45:56 +1000
committerJesse Duffield <jessedduffield@gmail.com>2019-06-06 20:50:19 +1000
commit0f0fda16605059ebae73d29f0e4b9b5d1455ce73 (patch)
tree6d14bff955e00ee16f3170a0e0f80b7edc89d980 /pkg/commands
parentbd2170a99cb996f2e00467a3416645a85eaf0549 (diff)
allow stashing staged changes
reinstate old stash functionality with the 's' keybinding
Diffstat (limited to 'pkg/commands')
-rw-r--r--pkg/commands/file.go1
-rw-r--r--pkg/commands/git.go42
-rw-r--r--pkg/commands/git_test.go4
-rw-r--r--pkg/commands/os.go61
4 files changed, 107 insertions, 1 deletions
diff --git a/pkg/commands/file.go b/pkg/commands/file.go
index 39d989634..3c4e13f06 100644
--- a/pkg/commands/file.go
+++ b/pkg/commands/file.go
@@ -14,6 +14,7 @@ type File struct {
HasInlineMergeConflicts bool
DisplayString string
Type string // one of 'file', 'directory', and 'other'
+ ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
}
// GetDisplayStrings returns the display string of a file
diff --git a/pkg/commands/git.go b/pkg/commands/git.go
index 2a6f57659..8ef3a1903 100644
--- a/pkg/commands/git.go
+++ b/pkg/commands/git.go
@@ -187,6 +187,7 @@ func (c *GitCommand) GetStatusFiles() []*File {
HasMergeConflicts: change == "UU" || change == "AA" || change == "DU",
HasInlineMergeConflicts: change == "UU" || change == "AA",
Type: c.OSCommand.FileType(filename),
+ ShortStatus: change,
}
files = append(files, file)
}
@@ -436,7 +437,7 @@ 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 --short")
+ return c.OSCommand.RunCommandWithOutput("git status --untracked-files=all --porcelain")
}
// IsInMergeState states whether we are still mid-merge
@@ -965,3 +966,42 @@ func (c *GitCommand) SquashAllAboveFixupCommits(sha string) error {
),
)
}
+
+// StashSaveStagedChanges stashes only the currently staged changes. This takes a few steps
+// shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
+func (c *GitCommand) StashSaveStagedChanges(message string) error {
+
+ if err := c.OSCommand.RunCommand("git stash --keep-index"); err != nil {
+ return err
+ }
+
+ if err := c.StashSave(message); err != nil {
+ return err
+ }
+
+ if err := c.OSCommand.RunCommand("git stash apply stash@{1}"); err != nil {
+ return err
+ }
+
+ if err := c.OSCommand.PipeCommands("git stash show -p", "git apply -R"); err != nil {
+ return err
+ }
+
+ if err := c.OSCommand.RunCommand("git stash drop stash@{1}"); err != nil {
+ return err
+ }
+
+ // 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()
+ for _, file := range files {
+ if file.ShortStatus == "AD" {
+ if err := c.UnStageFile(file.Name, false); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go
index be9f8c883..35e97b181 100644
--- a/pkg/commands/git_test.go
+++ b/pkg/commands/git_test.go
@@ -372,6 +372,7 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
DisplayString: "MM file1.txt",
Type: "other",
+ ShortStatus: "MM",
},
{
Name: "file3.txt",
@@ -382,6 +383,7 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
DisplayString: "A file3.txt",
Type: "other",
+ ShortStatus: "A ",
},
{
Name: "file2.txt",
@@ -392,6 +394,7 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
DisplayString: "AM file2.txt",
Type: "other",
+ ShortStatus: "AM",
},
{
Name: "file4.txt",
@@ -402,6 +405,7 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
HasMergeConflicts: false,
DisplayString: "?? file4.txt",
Type: "other",
+ ShortStatus: "??",
},
}
diff --git a/pkg/commands/os.go b/pkg/commands/os.go
index 02d79a339..32d22ea5a 100644
--- a/pkg/commands/os.go
+++ b/pkg/commands/os.go
@@ -7,6 +7,7 @@ import (
"path/filepath"
"regexp"
"strings"
+ "sync"
"github.com/go-errors/errors"
@@ -301,3 +302,63 @@ func (c *OSCommand) GetLazygitPath() string {
func (c *OSCommand) RunCustomCommand(command string) *exec.Cmd {
return c.PrepareSubProcess(c.Platform.shell, c.Platform.shellArg, command)
}
+
+// PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C
+func (c *OSCommand) PipeCommands(commandStrings ...string) error {
+
+ cmds := make([]*exec.Cmd, len(commandStrings))
+
+ for i, str := range commandStrings {
+ cmds[i] = c.ExecutableFromString(str)
+ }
+
+ for i := 0; i < len(cmds)-1; i++ {
+ stdout, err := cmds[i].StdoutPipe()
+ if err != nil {
+ return err
+ }
+
+ cmds[i+1].Stdin = stdout
+ }
+
+ // keeping this here in case I adapt this code for some other purpose in the future
+ // cmds[len(cmds)-1].Stdout = os.Stdout
+
+ finalErrors := []string{}
+
+ wg := sync.WaitGroup{}
+ wg.Add(len(cmds))
+
+ for _, cmd := range cmds {
+ currentCmd := cmd
+ go func() {
+ stderr, err := currentCmd.StderrPipe()
+ if err != nil {
+ c.Log.Error(err)
+ }
+
+ if err := currentCmd.Start(); err != nil {
+ c.Log.Error(err)
+ }
+
+ if b, err := ioutil.ReadAll(stderr); err == nil {
+ if len(b) > 0 {
+ finalErrors = append(finalErrors, string(b))
+ }
+ }
+
+ if err := currentCmd.Wait(); err != nil {
+ c.Log.Error(err)
+ }
+
+ wg.Done()
+ }()
+ }
+
+ wg.Wait()
+
+ if len(finalErrors) > 0 {
+ return errors.New(strings.Join(finalErrors, "\n"))
+ }
+ return nil
+}