mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-30 03:23:08 +03:00
allow stashing staged changes
reinstate old stash functionality with the 's' keybinding
This commit is contained in:
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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: "??",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user