Allow range drop stashes (#4451)

- **PR Description**

This pr implement dropping multiple stashes feature.

In #3196, dropping multiple stashes feature is listed as possible but
less needed. In practice, I tend to use `apply` instead of `drop` in
order to retrieve some temporary changes in the near future, but the
consequence of this is that at the end of the work week I often have to
go back and manually clean up stashes that I no longer need or that I
simply forgot to drop.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
This commit is contained in:
Stefan Haller 2025-04-02 11:37:39 +02:00 committed by GitHub
commit facc73a88b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 65 additions and 9 deletions

View file

@ -50,8 +50,8 @@ func (self *StashController) GetKeybindings(opts types.KeybindingsOpts) []*types
},
{
Key: opts.GetKey(opts.Config.Universal.Remove),
Handler: self.withItem(self.handleStashDrop),
GetDisabledReason: self.require(self.singleItemSelected()),
Handler: self.withItems(self.handleStashDrop),
GetDisabledReason: self.require(self.itemRangeSelected()),
Description: self.c.Tr.Drop,
Tooltip: self.c.Tr.StashDropTooltip,
DisplayOnScreen: true,
@ -161,16 +161,19 @@ func (self *StashController) handleStashPop(stashEntry *models.StashEntry) error
return nil
}
func (self *StashController) handleStashDrop(stashEntry *models.StashEntry) error {
func (self *StashController) handleStashDrop(stashEntries []*models.StashEntry) error {
self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.StashDrop,
Prompt: self.c.Tr.SureDropStashEntry,
HandleConfirm: func() error {
self.c.LogAction(self.c.Tr.Actions.Stash)
err := self.c.Git().Stash.Drop(stashEntry.Index)
_ = self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.STASH}})
if err != nil {
return err
startIndex := stashEntries[0].Index
for range stashEntries {
err := self.c.Git().Stash.Drop(startIndex)
_ = self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.STASH}})
if err != nil {
return err
}
}
return nil
},

View file

@ -1247,7 +1247,7 @@ func EnglishTranslationSet() *TranslationSet {
StashApplyTooltip: "Apply the stash entry to your working directory.",
NoStashEntries: "No stash entries",
StashDrop: "Stash drop",
SureDropStashEntry: "Are you sure you want to drop this stash entry?",
SureDropStashEntry: "Are you sure you want to drop the selected stash entry(ies)?",
StashPop: "Stash pop",
SurePopStashEntry: "Are you sure you want to pop this stash entry?",
StashApply: "Stash apply",

View file

@ -28,7 +28,7 @@ var Drop = NewIntegrationTest(NewIntegrationTestArgs{
Tap(func() {
t.ExpectPopup().Confirmation().
Title(Equals("Stash drop")).
Content(Contains("Are you sure you want to drop this stash entry?")).
Content(Contains("Are you sure you want to drop the selected stash entry(ies)?")).
Confirm()
}).
IsEmpty()

View file

@ -0,0 +1,52 @@
package stash
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var DropMultiple = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Drop multiple stash entries",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("initial commit")
shell.CreateFileAndAdd("file1", "content1")
shell.Stash("stash one")
shell.CreateFileAndAdd("file2", "content2")
shell.Stash("stash two")
shell.CreateFileAndAdd("file3", "content3")
shell.Stash("stash three")
shell.CreateFileAndAdd("file4", "content4")
shell.Stash("stash four")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Files().IsEmpty()
t.Views().Stash().
Focus().
SelectNextItem().
Lines(
Contains("stash four"),
Contains("stash three").IsSelected(),
Contains("stash two"),
Contains("stash one"),
).
Press(keys.Universal.ToggleRangeSelect).
Press(keys.Universal.RangeSelectDown).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().Confirmation().
Title(Equals("Stash drop")).
Content(Contains("Are you sure you want to drop the selected stash entry(ies)?")).
Confirm()
}).
Lines(
Contains("stash four"),
Contains("stash one"),
)
t.Views().Files().IsEmpty()
},
})

View file

@ -323,6 +323,7 @@ var tests = []*components.IntegrationTest{
stash.ApplyPatch,
stash.CreateBranch,
stash.Drop,
stash.DropMultiple,
stash.Pop,
stash.PreventDiscardingFileChanges,
stash.Rename,