Cover the cases where the next change isn't the obvious one

Three cases where landing on the change that took the place of the one acted
on is not the same as landing on the next line, or on the same line number:
staging an inserted line moves every later line of the file, so the hunk
below it is somewhere else afterwards; consecutive deletions all sit at the
same place in the new file, so nothing but their order tells them apart; and
unstaging half of a modification carries on in the pane the staged side has,
which is where the work was already.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-10-05 11:20:31 +02:00
co-authored by Claude Opus 5
parent b967d02616
commit 308e696fc7
4 changed files with 136 additions and 0 deletions
@@ -0,0 +1,45 @@
package main_view
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var AdvanceAfterStagingShiftsLineNumbers = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Staging a hunk that adds a line still leaves the selection on the next hunk, whose line numbers it moved",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(cfg *config.AppConfig) {
cfg.GetUserConfig().Gui.UseHunkModeInStagingView = true
},
SetupRepo: func(shell *Shell) {
shell.CreateFileAndAdd("file1", "1\n2\n3\n4\n5\n6\n7\n8\n")
shell.Commit("one")
// Three change blocks: a modification, an added line, and another modification
// below it. Staging the middle one changes how many lines the file has, and so
// where the last one sits.
shell.UpdateFile("file1", "1\nX\n3\n4\nNEW\n5\n6\nY\n8\n")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Files().
IsFocused().
Press(keys.Universal.FocusMainView)
t.Views().Main().
IsFocused().
SelectedLines(
Contains("-2"),
Contains("+X"),
).
Press(keys.Main.NextHunk).
SelectedLines(
Contains("+NEW"),
).
PressPrimaryAction().
SelectedLines(
Contains("-7"),
Contains("+Y"),
)
},
})
@@ -0,0 +1,45 @@
package main_view
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var SelectNextChangeAfterUnstaging = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Taking one line of a staged modification back out of the index moves on to the line that replaced it, in the pane the staged side ended up in",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(cfg *config.AppConfig) {
cfg.GetUserConfig().Gui.UseHunkModeInStagingView = false
},
SetupRepo: func(shell *Shell) {
shell.CreateFileAndAdd("file1", "c0\na1\na2\na3\nc1\nc2\noldB\nc3\n")
shell.Commit("one")
// Staged: a block of deletions, and a modification below it.
shell.UpdateFileAndAdd("file1", "c0\nc1\nc2\nnewB\nc3\n")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Files().
IsFocused().
Press(keys.Universal.FocusMainView)
// The file has nothing but staged changes, so the pane holding them is the only
// one shown.
t.Views().Secondary().
IsFocused().
SelectedLines(
Contains("-a1"),
).
NavigateToLine(Contains("-oldB")).
PressPrimaryAction()
// The unstaged pane has appeared above, and the work carries on where it was:
// on the line that takes the place of the one taken out.
t.Views().Secondary().
IsFocused().
SelectedLines(
Contains("+newB"),
)
},
})
@@ -0,0 +1,43 @@
package main_view
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var SelectNextDeletionAfterStagingOne = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Staging one deletion in the middle of a block of them moves on to the next, not back to the block's first line",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(cfg *config.AppConfig) {
cfg.GetUserConfig().Gui.UseHunkModeInStagingView = false
},
SetupRepo: func(shell *Shell) {
shell.CreateFileAndAdd("file1", "keep1\nd1\nd2\nd3\nd4\nkeep2\n")
shell.Commit("one")
// Four deletions in a row. They all sit at the same place in the new file, so
// that position alone cannot tell them apart.
shell.UpdateFile("file1", "keep1\nkeep2\n")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Files().
IsFocused().
Press(keys.Universal.FocusMainView)
t.Views().Main().
IsFocused().
SelectedLines(
Contains("-d1"),
).
Press(keys.Universal.NextItem).
Press(keys.Universal.NextItem).
SelectedLines(
Contains("-d3"),
).
PressPrimaryAction().
SelectedLines(
Contains("-d4"),
)
},
})
+3
View File
@@ -367,6 +367,7 @@ var tests = []*components.IntegrationTest{
interactive_rebase.SwapInRebaseWithConflictAndEdit,
interactive_rebase.SwapWithConflict,
interactive_rebase.ViewFilesOfTodoEntries,
main_view.AdvanceAfterStagingShiftsLineNumbers,
main_view.ClickSelectsDiffLine,
main_view.CommitFromMainView,
main_view.CopyRowsThatAreNoDiffLine,
@@ -416,6 +417,8 @@ var tests = []*components.IntegrationTest{
main_view.SelectInADiffReadInPart,
main_view.SelectLineWhenWholeFileIsOneHunk,
main_view.SelectNextChangeAfterStaging,
main_view.SelectNextChangeAfterUnstaging,
main_view.SelectNextDeletionAfterStagingOne,
main_view.SelectVisibleChangeOnFocusingMainView,
main_view.SelectVisibleHunkOnFocusingMainView,
main_view.SelectionCommandsOnlyWhereTheyApply,