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 <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-11-08 14:08:51 +01:00
co-authored by Claude Opus 5.5
parent 462a168d87
commit 280c67ff98
6 changed files with 92 additions and 4 deletions
+38 -1
View File
@@ -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 {
@@ -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()))
})
}
}
+1 -1
View File
@@ -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)
}
+2 -1
View File
@@ -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())
}
@@ -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"))
},
})
+1
View File
@@ -39,6 +39,7 @@ var tests = []*components.IntegrationTest{
bisect.CheckOutBranchBeforeBisecting,
bisect.ChooseTerms,
bisect.FromOtherBranch,
bisect.GotoBottom,
bisect.MarkCandidatesWhileTestingMergeBase,
bisect.OfferResetWhenMergeBaseIsBad,
bisect.SelectCurrentCommit,