diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2019-05-30 22:45:56 +1000 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2019-06-06 20:50:19 +1000 |
commit | 0f0fda16605059ebae73d29f0e4b9b5d1455ce73 (patch) | |
tree | 6d14bff955e00ee16f3170a0e0f80b7edc89d980 /pkg | |
parent | bd2170a99cb996f2e00467a3416645a85eaf0549 (diff) |
allow stashing staged changes
reinstate old stash functionality with the 's' keybinding
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/commands/file.go | 1 | ||||
-rw-r--r-- | pkg/commands/git.go | 42 | ||||
-rw-r--r-- | pkg/commands/git_test.go | 4 | ||||
-rw-r--r-- | pkg/commands/os.go | 61 | ||||
-rw-r--r-- | pkg/gui/files_panel.go | 43 | ||||
-rw-r--r-- | pkg/gui/keybindings.go | 10 | ||||
-rw-r--r-- | pkg/gui/menu_panel.go | 2 | ||||
-rw-r--r-- | pkg/gui/stash_panel.go | 9 | ||||
-rw-r--r-- | pkg/i18n/dutch.go | 15 | ||||
-rw-r--r-- | pkg/i18n/english.go | 15 | ||||
-rw-r--r-- | pkg/i18n/polish.go | 15 |
11 files changed, 199 insertions, 18 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 +} diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 3d2001ac1..043c6ceab 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -627,3 +627,46 @@ func (gui *Gui) handleCustomCommand(g *gocui.Gui, v *gocui.View) error { return gui.Errors.ErrSubProcess }) } + +type stashOption struct { + description string + handler func() error +} + +// GetDisplayStrings is a function. +func (o *stashOption) GetDisplayStrings(isFocused bool) []string { + return []string{o.description} +} + +func (gui *Gui) handleCreateStashMenu(g *gocui.Gui, v *gocui.View) error { + options := []*stashOption{ + { + description: gui.Tr.SLocalize("stashAllChanges"), + handler: func() error { + return gui.handleStashSave(gui.GitCommand.StashSave) + }, + }, + { + description: gui.Tr.SLocalize("stashStagedChanges"), + handler: func() error { + return gui.handleStashSave(gui.GitCommand.StashSaveStagedChanges) + }, + }, + { + description: gui.Tr.SLocalize("cancel"), + handler: func() error { + return nil + }, + }, + } + + handleMenuPress := func(index int) error { + return options[index].handler() + } + + return gui.createMenu(gui.Tr.SLocalize("stashOptions"), options, len(options), handleMenuPress) +} + +func (gui *Gui) handleStashChanges(g *gocui.Gui, v *gocui.View) error { + return gui.handleStashSave(gui.GitCommand.StashSave) +} diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 5cbd7b6eb..ce12cf66f 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -230,10 +230,16 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Description: gui.Tr.SLocalize("refreshFiles"), }, { ViewName: "files", + Key: 's', + Modifier: gocui.ModNone, + Handler: gui.handleStashChanges, + Description: gui.Tr.SLocalize("stashAllChanges"), + }, { + ViewName: "files", Key: 'S', Modifier: gocui.ModNone, - Handler: gui.handleStashSave, - Description: gui.Tr.SLocalize("stashFiles"), + Handler: gui.handleCreateStashMenu, + Description: gui.Tr.SLocalize("viewStashOptions"), }, { ViewName: "files", Key: 'a', diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 66dabd03e..370c7fa5a 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -70,7 +70,7 @@ func (gui *Gui) createMenu(title string, items interface{}, itemCount int, handl wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error { selectedLine := gui.State.Panels.Menu.SelectedLine if err := handlePress(selectedLine); err != nil { - return err + return gui.createErrorPanel(gui.g, err.Error()) } if _, err := gui.g.View("menu"); err == nil { if _, err := gui.g.SetViewOnBottom("menu"); err != nil { diff --git a/pkg/gui/stash_panel.go b/pkg/gui/stash_panel.go index 9c1851971..4a2b463ce 100644 --- a/pkg/gui/stash_panel.go +++ b/pkg/gui/stash_panel.go @@ -130,16 +130,15 @@ func (gui *Gui) stashDo(g *gocui.Gui, v *gocui.View, method string) error { return gui.refreshFiles() } -func (gui *Gui) handleStashSave(g *gocui.Gui, filesView *gocui.View) error { +func (gui *Gui) handleStashSave(stashFunc func(message string) error) error { if len(gui.trackedFiles()) == 0 && len(gui.stagedFiles()) == 0 { - return gui.createErrorPanel(g, gui.Tr.SLocalize("NoTrackedStagedFilesStash")) + return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("NoTrackedStagedFilesStash")) } - gui.createPromptPanel(g, filesView, gui.Tr.SLocalize("StashChanges"), func(g *gocui.Gui, v *gocui.View) error { - if err := gui.GitCommand.StashSave(gui.trimmedContent(v)); err != nil { + return gui.createPromptPanel(gui.g, gui.getFilesView(), gui.Tr.SLocalize("StashChanges"), func(g *gocui.Gui, v *gocui.View) error { + if err := stashFunc(gui.trimmedContent(v)); err != nil { gui.createErrorPanel(g, err.Error()) } gui.refreshStashEntries(g) return gui.refreshFiles() }) - return nil } diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index 6774928fd..d736e2771 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -86,9 +86,6 @@ func addDutch(i18nObject *i18n.Bundle) error { ID: "execute", Other: "uitvoeren", }, &i18n.Message{ - ID: "stashFiles", - Other: "stash-bestanden", - }, &i18n.Message{ ID: "open", Other: "open", }, &i18n.Message{ @@ -739,6 +736,18 @@ func addDutch(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "pressEnterToReturn", Other: "Press enter to return to lazygit", + }, &i18n.Message{ + ID: "viewStashOptions", + Other: "view stash options", + }, &i18n.Message{ + ID: "stashAllChanges", + Other: "stash-bestanden", + }, &i18n.Message{ + ID: "stashStagedChanges", + Other: "stash staged changes", + }, &i18n.Message{ + ID: "stashOptions", + Other: "Stash options", }, ) } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 36fb3e021..82ba1b505 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -106,9 +106,6 @@ func addEnglish(i18nObject *i18n.Bundle) error { ID: "execute", Other: "execute", }, &i18n.Message{ - ID: "stashFiles", - Other: "stash files", - }, &i18n.Message{ ID: "open", Other: "open", }, &i18n.Message{ @@ -762,6 +759,18 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "pressEnterToReturn", Other: "Press enter to return to lazygit", + }, &i18n.Message{ + ID: "viewStashOptions", + Other: "view stash options", + }, &i18n.Message{ + ID: "stashAllChanges", + Other: "stash changes", + }, &i18n.Message{ + ID: "stashStagedChanges", + Other: "stash staged changes", + }, &i18n.Message{ + ID: "stashOptions", + Other: "Stash options", }, ) } diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index c165ea2f0..bd5fed9d5 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -84,9 +84,6 @@ func addPolish(i18nObject *i18n.Bundle) error { ID: "execute", Other: "wykonaj", }, &i18n.Message{ - ID: "stashFiles", - Other: "przechowaj pliki", - }, &i18n.Message{ ID: "open", Other: "otwórz", }, &i18n.Message{ @@ -722,6 +719,18 @@ func addPolish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "pressEnterToReturn", Other: "Press enter to return to lazygit", + }, &i18n.Message{ + ID: "viewStashOptions", + Other: "view stash options", + }, &i18n.Message{ + ID: "stashAllChanges", + Other: "przechowaj pliki", + }, &i18n.Message{ + ID: "stashStagedChanges", + Other: "stash staged changes", + }, &i18n.Message{ + ID: "stashOptions", + Other: "Stash options", }, ) } |