Repository: jesseduffield/lazygit
File: pkg/commands/git_commands/stash.go (Go)

Code before:
func (self *StashCommands) Drop(index int) error {
	return self.cmd.New(fmt.Sprintf("git stash drop stash@{%d}", index)).Run()
}

func (self *StashCommands) Pop(index int) error {
	return self.cmd.New(fmt.Sprintf("git stash pop stash@{%d}", index)).Run()
}

func (self *StashCommands) Apply(index int) error {
	return self.cmd.New(fmt.Sprintf("git stash apply stash@{%d}", index)).Run()
}

// Save save stash
// TODO: before calling this, check if there is anything to save
func (self *StashCommands) Save(message string) error {
	return self.cmd.New("git stash save " + self.cmd.Quote(message)).Run()
}

func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
	cmdStr := fmt.Sprintf("git stash show -p --stat --color=%s --unified=%d stash@{%d}", self.UserConfig.Git.Paging.ColorArg, self.UserConfig.Git.DiffContextSize, index)

	return self.cmd.New(cmdStr).DontLog()
}

func (self *StashCommands) SaveUnstagedChanges(message string) error {
	return self.cmd.New("git stash --keep-index").Run()
}

// SaveStagedChanges 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 (self *StashCommands) SaveStagedChanges(message string) error {
	// wrap in 'writing', which uses a mutex
	if err := self.cmd.New("git stash --keep-index").Run(); err != nil {
		return err
	}

	if err := self.Save(message); err != nil {
		return err
	}

	if err := self.cmd.New("git stash apply stash@{1}").Run(); err != nil {
		return err
	}

	if err := self.os.PipeCommands("git stash show -p", "git apply -R"); err != nil {
		return err
	}

	if err := self.cmd.New("git stash drop stash@{1}").Run(); err != nil {
		return err
	}

Review comment (bug):
Although this does exactly want the linked issue wanted, I've just tested this and it actually doesn't only stash the unstaged changes. Instead, it stashes both staged and unstaged, but leaves the staged changes in the index. This stack overflow goes into some detail: https://stackoverflow.com/questions/20028507/git-stash-uncached-how-to-put-away-all-unstaged-changes

So I'm thinking we just change the label on the menu item to 'stash all changes and keep index'. I'd also give it a separate keybinding like 'i' for 'index' (can't use 'k' due to vim keybindings). That means later on we can add a keybinding for actually only stashing unstaged changes. What do you think?

Code after:
func (self *StashCommands) Drop(index int) error {
	return self.cmd.New(fmt.Sprintf("git stash drop stash@{%d}", index)).Run()
}

func (self *StashCommands) Pop(index int) error {
	return self.cmd.New(fmt.Sprintf("git stash pop stash@{%d}", index)).Run()
}

func (self *StashCommands) Apply(index int) error {
	return self.cmd.New(fmt.Sprintf("git stash apply stash@{%d}", index)).Run()
}

// Save save stash
func (self *StashCommands) Save(message string) error {
	return self.cmd.New("git stash save " + self.cmd.Quote(message)).Run()
}

func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
	cmdStr := fmt.Sprintf("git stash show -p --stat --color=%s --unified=%d stash@{%d}", self.UserConfig.Git.Paging.ColorArg, self.UserConfig.Git.DiffContextSize, index)

	return self.cmd.New(cmdStr).DontLog()
}

func (self *StashCommands) StashAndKeepIndex(message string) error {
	return self.cmd.New(fmt.Sprintf("git stash save %s --keep-index", self.cmd.Quote(message))).Run()
}

func (self *StashCommands) StashUnstagedChanges(message string) error {
	if err := self.cmd.New("git commit --no-verify -m \"[lazygit] stashing unstaged changes\"").Run(); err != nil {
		return err
	}
	if err := self.Save(message); err != nil {
		return err
	}
	if err := self.cmd.New("git reset --soft HEAD^").Run(); err != nil {
		return err
	}
	return nil
}

// SaveStagedChanges 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 (self *StashCommands) SaveStagedChanges(message string) error {
	// wrap in 'writing', which uses a mutex
	if err := self.cmd.New("git stash --keep-index").Run(); err != nil {
		return err
	}

	if err := self.Save(message); err != nil {
		return err
	}