diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 3ea742c8e..605309f9f 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -533,12 +533,10 @@ func findLocalCommitSelectionRange( commits []*models.Commit, selectionRange *localCommitSelectionRange, ) (int, int, bool, bool) { - _, selectedIdx, foundSelected := lo.FindIndexOf(commits, func(commit *models.Commit) bool { - return commit.Hash() == selectionRange.selectedHash && commit.IsTODO() == selectionRange.selectedIsTODO - }) - _, rangeStartIdx, foundRangeStart := lo.FindIndexOf(commits, func(commit *models.Commit) bool { - return commit.Hash() == selectionRange.rangeStartHash && commit.IsTODO() == selectionRange.rangeStartIsTODO - }) + selectedIdx, foundSelected := findCommitByHashPreferringTODOStatus( + commits, selectionRange.selectedHash, selectionRange.selectedIsTODO) + rangeStartIdx, foundRangeStart := findCommitByHashPreferringTODOStatus( + commits, selectionRange.rangeStartHash, selectionRange.rangeStartIsTODO) if !foundSelected || !foundRangeStart { return 0, 0, false, false } @@ -547,6 +545,30 @@ func findLocalCommitSelectionRange( return selectedIdx, rangeStartIdx, didMove, true } +// findCommitByHashPreferringTODOStatus finds the commit with the given hash. +// When both a TODO and a non-TODO commit share that hash - which happens while +// reverting or cherry-picking, where the rebase TODO entry has the same hash as +// the real commit - it returns the one whose TODO status matches isTODO. When +// only one commit has the hash, it is returned regardless of its TODO status, +// so that a selected commit which turned into a TODO entry across the refresh is +// still found (e.g. when starting an interactive rebase that stops to edit it). +func findCommitByHashPreferringTODOStatus(commits []*models.Commit, hash string, isTODO bool) (int, bool) { + fallbackIdx := -1 + for idx, commit := range commits { + if commit.Hash() != hash { + continue + } + if commit.IsTODO() == isTODO { + return idx, true + } + if fallbackIdx == -1 { + fallbackIdx = idx + } + } + + return fallbackIdx, fallbackIdx != -1 +} + func hasRestorableCommitHash(commits []*models.Commit, idx int) bool { return idx >= 0 && idx < len(commits) && commits[idx].Hash() != "" } diff --git a/pkg/gui/controllers/helpers/refresh_helper_test.go b/pkg/gui/controllers/helpers/refresh_helper_test.go index e8be06d61..3a5f6ea82 100644 --- a/pkg/gui/controllers/helpers/refresh_helper_test.go +++ b/pkg/gui/controllers/helpers/refresh_helper_test.go @@ -130,6 +130,19 @@ func TestFindLocalCommitSelectionRange(t *testing.T) { found: true, }, }, + { + name: "falls back to a todo entry when the selected commit became one", + commits: []*models.Commit{ + makeTodoCommitWithHash("b", todo.Pick), + makeCommits("c")[0], + }, + expected: expectation{ + selectedIdx: 0, + rangeStartIdx: 1, + moved: true, + found: true, + }, + }, } for _, testCase := range testCases {