From 200042a57cc9637d2ae756e9aa0a07673faf52a7 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Tue, 21 Jul 2026 19:29:47 +0200 Subject: [PATCH] Add test showing that rapidly moving a rebase todo twice moves the wrong todo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moving a todo up or down rewrites the todo file and advances the selection synchronously, but the commits model is only rebuilt by the refresh, which finishes in the background. A second keypress arriving before that reads the pre-move model at the advanced selection index — that's the todo the first move swapped with, so the second press moves that one back instead of moving the selected todo further. Two rapid presses (e.g. from holding the key down) thus amount to a net no-op. The two presses also spawn two racing refreshes whose model updates can land in either order, so the todo list can even end up disagreeing with the todo file. That's why the test continues the rebase and asserts the resulting commit order instead of the displayed list: the rebase replays the file, which is deterministic. The test documents this currently broken behavior; the fix comes next. Co-Authored-By: Claude Fable 5 --- .../move_todo_down_with_rapid_keypresses.go | 68 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 2 files changed, 69 insertions(+) create mode 100644 pkg/integration/tests/interactive_rebase/move_todo_down_with_rapid_keypresses.go diff --git a/pkg/integration/tests/interactive_rebase/move_todo_down_with_rapid_keypresses.go b/pkg/integration/tests/interactive_rebase/move_todo_down_with_rapid_keypresses.go new file mode 100644 index 000000000..548d924f4 --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/move_todo_down_with_rapid_keypresses.go @@ -0,0 +1,68 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +// The second keypress arrives before the refresh triggered by the first one +// has rebuilt the commits model. The handler reads the selected todo from the +// model at the already-advanced selection index, so with the stale, pre-move +// model it grabs the todo the first move swapped with and moves that one back +// down — turning the two presses into a net no-op instead of moving the +// selected todo down two slots. This is what happens when holding down the +// move-down key to move a todo several slots. +// +// We continue the rebase and assert the resulting commit order rather than +// asserting the todo list, because the two presses also spawn two racing +// refreshes whose updates can land in either order, so what the todo list +// shows in the broken state is not deterministic (it can even disagree with +// the todo file). The rebase replays what's in the file. +var MoveTodoDownWithRapidKeypresses = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Move a todo down two slots with two keypresses in rapid succession", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateNCommits(4) + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("commit-04").IsSelected(), + Contains("commit-03"), + Contains("commit-02"), + Contains("commit-01"), + ). + NavigateToLine(Contains("commit-01")). + Press(keys.Universal.Edit). + Lines( + Contains("--- Pending rebase todos ---"), + Contains("commit-04"), + Contains("commit-03"), + Contains("commit-02"), + Contains("--- Commits ---"), + Contains("commit-01").IsSelected(), + ). + NavigateToLine(Contains("commit-04")). + PressRapidly(keys.Commits.MoveDownCommit, keys.Commits.MoveDownCommit). + Tap(func() { + t.Common().ContinueRebase() + }). + /* EXPECTED: + Lines( + Contains("commit-03"), + Contains("commit-02"), + Contains("commit-04"), + Contains("commit-01"), + ) + ACTUAL: */ + Lines( + Contains("commit-04"), + Contains("commit-03"), + Contains("commit-02"), + Contains("commit-01"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 2189d3506..1bb06741f 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -310,6 +310,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.Move, interactive_rebase.MoveAcrossBranchBoundaryOutsideRebase, interactive_rebase.MoveInRebase, + interactive_rebase.MoveTodoDownWithRapidKeypresses, interactive_rebase.MoveUpdateRefTodo, interactive_rebase.MoveWithCustomCommentChar, interactive_rebase.OutsideRebaseRangeSelect,