From 280c67ff9866968821356e5be7286b5a268563d0 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 8 Nov 2026 12:48:03 +0100 Subject: [PATCH] Make "go to bottom" stop at the bad, current and good commits of a bisect During a bisect, the commits panel shows the commits known to be bad at the top, the known good ones at the bottom, and the ones that the bisect hasn't ruled out yet between them. Pressing '>' now stops at the bottommost bad commit, then at the commit to test, and then at the topmost good commit. It doesn't stop at each commit marked good. The commits between two of them are all good, and they don't form a section of their own. During a bisect, it also doesn't stop at the beginnings of the other sections, such as the beginning of a branch. The commits panel doesn't color the commits by whether they are merged while bisecting, so these would look like arbitrary commits. Co-Authored-By: Claude Opus 5.5 --- pkg/gui/context/commit_list_goto_bottom.go | 39 ++++++++++++++- .../context/commit_list_goto_bottom_test.go | 4 +- pkg/gui/context/local_commits_context.go | 2 +- pkg/gui/context/sub_commits_context.go | 3 +- pkg/integration/tests/bisect/goto_bottom.go | 47 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 6 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 pkg/integration/tests/bisect/goto_bottom.go diff --git a/pkg/gui/context/commit_list_goto_bottom.go b/pkg/gui/context/commit_list_goto_bottom.go index 3d955f628..288d276d6 100644 --- a/pkg/gui/context/commit_list_goto_bottom.go +++ b/pkg/gui/context/commit_list_goto_bottom.go @@ -2,6 +2,7 @@ package context import ( "github.com/jesseduffield/generics/set" + "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/samber/lo" "github.com/stefanhaller/git-todo-parser/todo" @@ -18,9 +19,21 @@ import ( // - each branch of a stack, given by branchesBelow. Among the todos of a // rebase, update-ref todos separate the branches. // - the commits that aren't merged into a main branch yet +// +// During a bisect, it stops at the bottommost commit marked bad, at the commit +// to test, and at the topmost commit marked good instead. The commits list +// doesn't color the commits by whether they are merged during a bisect, so the +// beginnings of the sections would look like arbitrary commits. func indexForGotoBottomInCommits( - commits []*models.Commit, selectedIdx int, branchesBelow []*models.Branch, + commits []*models.Commit, + selectedIdx int, + branchesBelow []*models.Branch, + bisectInfo *git_commands.BisectInfo, ) int { + if bisectInfo.Started() { + return closestStopBelow(bisectStops(commits, bisectInfo), selectedIdx, len(commits)-1) + } + stops := []int{} if headIdx := models.HeadCommitIdx(commits); headIdx > 0 { @@ -51,6 +64,30 @@ func indexForGotoBottomInCommits( return closestStopBelow(stops, selectedIdx, len(commits)-1) } +func bisectStops(commits []*models.Commit, bisectInfo *git_commands.BisectInfo) []int { + hasStatus := func(status git_commands.BisectStatus) func(*models.Commit) bool { + return func(commit *models.Commit) bool { + commitStatus, ok := bisectInfo.Status(commit.Hash()) + return ok && commitStatus == status + } + } + isCommitToTest := func(commit *models.Commit) bool { + return bisectInfo.HasCommitToTest() && commit.Hash() == bisectInfo.GetCurrentHash() + } + + stops := []int{} + if _, idx, found := lo.FindLastIndexOf(commits, hasStatus(git_commands.BisectStatusNew)); found { + stops = append(stops, idx) + } + if _, idx, found := lo.FindIndexOf(commits, isCommitToTest); found { + stops = append(stops, idx) + } + if _, idx, found := lo.FindIndexOf(commits, hasStatus(git_commands.BisectStatusOld)); found { + stops = append(stops, idx) + } + return stops +} + // Returns the smallest of the given stops that is greater than the selected // index, or the last index if there is none func closestStopBelow(stops []int, selectedIdx int, lastIdx int) int { diff --git a/pkg/gui/context/commit_list_goto_bottom_test.go b/pkg/gui/context/commit_list_goto_bottom_test.go index bb942e480..36956b1e2 100644 --- a/pkg/gui/context/commit_list_goto_bottom_test.go +++ b/pkg/gui/context/commit_list_goto_bottom_test.go @@ -3,6 +3,7 @@ package context import ( "testing" + "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/stefanhaller/git-todo-parser/todo" @@ -185,7 +186,8 @@ func TestIndexForGotoBottomInCommits(t *testing.T) { for _, s := range scenarios { t.Run(s.testName, func(t *testing.T) { - assert.Equal(t, s.expected, indexForGotoBottomInCommits(s.commits, s.selectedIdx, s.branchesBelow)) + assert.Equal(t, s.expected, indexForGotoBottomInCommits( + s.commits, s.selectedIdx, s.branchesBelow, git_commands.NewNullBisectInfo())) }) } } diff --git a/pkg/gui/context/local_commits_context.go b/pkg/gui/context/local_commits_context.go index 23abc0239..6d63c1efd 100644 --- a/pkg/gui/context/local_commits_context.go +++ b/pkg/gui/context/local_commits_context.go @@ -525,5 +525,5 @@ func (self *LocalCommitsContext) IndexForGotoBottom() int { commits := self.GetCommits() branchesBelow := branchstack.BranchesBelow( commits, c.Model().Branches, c.Model().CheckedOutBranch, c.UserConfig().Git.MainBranches) - return indexForGotoBottomInCommits(commits, self.GetSelectedLineIdx(), branchesBelow) + return indexForGotoBottomInCommits(commits, self.GetSelectedLineIdx(), branchesBelow, c.Model().BisectInfo) } diff --git a/pkg/gui/context/sub_commits_context.go b/pkg/gui/context/sub_commits_context.go index 73adc4b89..edada3e52 100644 --- a/pkg/gui/context/sub_commits_context.go +++ b/pkg/gui/context/sub_commits_context.go @@ -276,5 +276,6 @@ func (self *SubCommitsContext) IndexForGotoBottom() int { branchesBelow = branchstack.BranchesBelow( commits, self.c.Model().Branches, self.GetRef().RefName(), self.c.UserConfig().Git.MainBranches) } - return indexForGotoBottomInCommits(commits, self.GetSelectedLineIdx(), branchesBelow) + return indexForGotoBottomInCommits( + commits, self.GetSelectedLineIdx(), branchesBelow, git_commands.NewNullBisectInfo()) } diff --git a/pkg/integration/tests/bisect/goto_bottom.go b/pkg/integration/tests/bisect/goto_bottom.go new file mode 100644 index 000000000..10f31be1a --- /dev/null +++ b/pkg/integration/tests/bisect/goto_bottom.go @@ -0,0 +1,47 @@ +package bisect + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var GotoBottom = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Go to the bottom of the commits list in steps, stopping only at the bad commit, the current commit, and the first good commit", + ExtraCmdArgs: []string{}, + Skip: false, + SetupRepo: func(shell *Shell) { + shell. + CreateNCommits(3). + NewBranch("mybranch"). + CreateNCommitsStartingAt(7, 4). + StartBisect("HEAD~1", "HEAD~8") + }, + SetupConfig: func(cfg *config.AppConfig) { + cfg.GetUserConfig().Git.Log.ShowGraph = "never" + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("CI commit-10"), + Contains("CI commit-09").Contains("<-- bad"), + Contains("CI commit-08"), + Contains("CI commit-07"), + Contains("CI commit-06"), + Contains("CI commit-05").Contains("<-- current").IsSelected(), + Contains("CI commit-04"), + Contains("CI commit-03"), + Contains("CI commit-02").Contains("<-- good"), + Contains("CI commit-01"), + ). + Press(keys.Universal.GotoTop). + Press(keys.Universal.GotoBottom). + SelectedLine(Contains("CI commit-09")). + Press(keys.Universal.GotoBottom). + SelectedLine(Contains("CI commit-05")). + Press(keys.Universal.GotoBottom). + SelectedLine(Contains("CI commit-02")). + Press(keys.Universal.GotoBottom). + SelectedLine(Contains("CI commit-01")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 0219fb43e..09ba2a464 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -39,6 +39,7 @@ var tests = []*components.IntegrationTest{ bisect.CheckOutBranchBeforeBisecting, bisect.ChooseTerms, bisect.FromOtherBranch, + bisect.GotoBottom, bisect.MarkCandidatesWhileTestingMergeBase, bisect.OfferResetWhenMergeBaseIsBad, bisect.SelectCurrentCommit,