From a0f4614fdc06f6df269b7c10c894f7cc9d7eb691 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Mon, 25 Aug 2025 14:58:48 +0200 Subject: [PATCH 1/3] Cleanup: remove unnecessary ToggleRangeSelect The next RangeSelectDown actually cancels the sticky range select and turns it into a non-sticky one. --- pkg/integration/tests/stash/drop_multiple.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/integration/tests/stash/drop_multiple.go b/pkg/integration/tests/stash/drop_multiple.go index 7d6ab2a63..4bb40a573 100644 --- a/pkg/integration/tests/stash/drop_multiple.go +++ b/pkg/integration/tests/stash/drop_multiple.go @@ -33,7 +33,6 @@ var DropMultiple = NewIntegrationTest(NewIntegrationTestArgs{ Contains("stash two"), Contains("stash one"), ). - Press(keys.Universal.ToggleRangeSelect). Press(keys.Universal.RangeSelectDown). Press(keys.Universal.Remove). Tap(func() { From ef3e899f5ba5733814056b7c0c523f618283b509 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Mon, 25 Aug 2025 15:56:39 +0200 Subject: [PATCH 2/3] Add test demonstrating problem with dropping stashes in filtering mode As can be seen from the test, it deletes the wrong stashes in this case, because it assumes the selection is contiguous. --- .../stash/drop_multiple_in_filtered_mode.go | 81 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 2 files changed, 82 insertions(+) create mode 100644 pkg/integration/tests/stash/drop_multiple_in_filtered_mode.go diff --git a/pkg/integration/tests/stash/drop_multiple_in_filtered_mode.go b/pkg/integration/tests/stash/drop_multiple_in_filtered_mode.go new file mode 100644 index 000000000..c7dc9ee32 --- /dev/null +++ b/pkg/integration/tests/stash/drop_multiple_in_filtered_mode.go @@ -0,0 +1,81 @@ +package stash + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var DropMultipleInFilteredMode = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Drop multiple stash entries when filtering by path", + 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", "content2a") + shell.Stash("stash two-a") + shell.CreateFileAndAdd("file3", "content3") + shell.Stash("stash three") + shell.CreateFileAndAdd("file2", "content2b") + shell.Stash("stash two-b") + shell.CreateFileAndAdd("file4", "content4") + shell.Stash("stash four") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Stash(). + Lines( + Contains("stash four"), + Contains("stash two-b"), + Contains("stash three"), + Contains("stash two-a"), + Contains("stash one"), + ) + + t.GlobalPress(keys.Universal.FilteringMenu) + t.ExpectPopup().Menu(). + Title(Equals("Filtering")). + Select(Contains("Enter path to filter by")). + Confirm() + + t.ExpectPopup().Prompt(). + Title(Equals("Enter path:")). + Type("file2"). + Confirm() + + t.Views().Stash(). + Focus(). + Lines( + Contains("stash two-b").IsSelected(), + Contains("stash two-a"), + ). + 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() + }). + /* EXPECTED: + IsEmpty() + ACTUAL: */ + Lines( + Contains("stash two-a"), + ) + + t.GlobalPress(keys.Universal.Return) // cancel filtering mode + t.Views().Stash(). + Lines( + /* EXPECTED: + Contains("stash four"), + Contains("stash three"), + Contains("stash one"), + ACTUAL: */ + Contains("stash four"), + Contains("stash two-a"), + Contains("stash one"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index bd352d38e..a292227b3 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -358,6 +358,7 @@ var tests = []*components.IntegrationTest{ stash.CreateBranch, stash.Drop, stash.DropMultiple, + stash.DropMultipleInFilteredMode, stash.FilterByPath, stash.Pop, stash.PreventDiscardingFileChanges, From bf419abb8efa55bf3a4455cd5342f811e0e62384 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Mon, 25 Aug 2025 18:41:06 +0200 Subject: [PATCH 3/3] Fix dropping a range of stashes in filtered mode To fix the problem described in the previous commit, iterate backwards over the stashes that we want to delete. This allows us to use their Index field. --- pkg/gui/controllers/stash_controller.go | 5 ++--- .../tests/stash/drop_multiple_in_filtered_mode.go | 10 ---------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/pkg/gui/controllers/stash_controller.go b/pkg/gui/controllers/stash_controller.go index c9a938bb5..6af074386 100644 --- a/pkg/gui/controllers/stash_controller.go +++ b/pkg/gui/controllers/stash_controller.go @@ -161,9 +161,8 @@ func (self *StashController) handleStashDrop(stashEntries []*models.StashEntry) Prompt: self.c.Tr.SureDropStashEntry, HandleConfirm: func() error { self.c.LogAction(self.c.Tr.Actions.Stash) - startIndex := stashEntries[0].Index - for range stashEntries { - err := self.c.Git().Stash.Drop(startIndex) + for i := len(stashEntries) - 1; i >= 0; i-- { + err := self.c.Git().Stash.Drop(stashEntries[i].Index) self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.STASH}}) if err != nil { return err diff --git a/pkg/integration/tests/stash/drop_multiple_in_filtered_mode.go b/pkg/integration/tests/stash/drop_multiple_in_filtered_mode.go index c7dc9ee32..b4a6e1c5d 100644 --- a/pkg/integration/tests/stash/drop_multiple_in_filtered_mode.go +++ b/pkg/integration/tests/stash/drop_multiple_in_filtered_mode.go @@ -58,24 +58,14 @@ var DropMultipleInFilteredMode = NewIntegrationTest(NewIntegrationTestArgs{ Content(Contains("Are you sure you want to drop the selected stash entry(ies)?")). Confirm() }). - /* EXPECTED: IsEmpty() - ACTUAL: */ - Lines( - Contains("stash two-a"), - ) t.GlobalPress(keys.Universal.Return) // cancel filtering mode t.Views().Stash(). Lines( - /* EXPECTED: Contains("stash four"), Contains("stash three"), Contains("stash one"), - ACTUAL: */ - Contains("stash four"), - Contains("stash two-a"), - Contains("stash one"), ) }, })