From 4486e8d149827ecafd3a60a25d16fe32d69b422e Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 7 Nov 2026 09:30:39 +0100 Subject: [PATCH] Extract the check for a rescheduled rebase command into a helper We are going to need it for recovering from lock errors during a rebase. Co-Authored-By: Claude Opus 5.5 --- pkg/commands/git_commands/commit_loader.go | 33 +-------------------- pkg/utils/rebase_todo.go | 34 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/pkg/commands/git_commands/commit_loader.go b/pkg/commands/git_commands/commit_loader.go index 987f2394e..b4a938880 100644 --- a/pkg/commands/git_commands/commit_loader.go +++ b/pkg/commands/git_commands/commit_loader.go @@ -430,38 +430,7 @@ func (self *CommitLoader) getConflictedCommitImpl(hashPool *utils.StringPool, to return nil } - // In certain cases, git reschedules commands that failed. One example is if - // a patch would overwrite an untracked file (another one is an "exec" that - // failed, but we don't care about that here because we dealt with exec - // already above). To detect this, compare the last command of the "done" - // file against the first command of "git-rebase-todo"; if they are the - // same, the command was rescheduled. - if len(doneTodos) > 0 && len(todos) > 0 && doneTodos[len(doneTodos)-1] == todos[0] { - // Command was rescheduled, no need to display it - return nil - } - - // Older versions of git have a bug whereby, if a command is rescheduled, - // the last successful command is appended to the "done" file again. To - // detect this, we need to compare the second-to-last done entry against the - // first todo entry, and also compare the last done entry against the - // last-but-two done entry; this latter check is needed for the following - // case: - // pick A - // exec make test - // pick B - // exec make test - // If pick B fails with conflicts, then the "done" file contains - // pick A - // exec make test - // pick B - // and git-rebase-todo contains - // exec make test - // Without the last condition we would erroneously treat this as the exec - // command being rescheduled, so we wouldn't display our fake entry for - // "pick B". - if len(doneTodos) >= 3 && len(todos) > 0 && doneTodos[len(doneTodos)-2] == todos[0] && - doneTodos[len(doneTodos)-1] == doneTodos[len(doneTodos)-3] { + if utils.LastDoneTodoWasRescheduled(doneTodos, todos) { // Command was rescheduled, no need to display it return nil } diff --git a/pkg/utils/rebase_todo.go b/pkg/utils/rebase_todo.go index 577b3da07..f6fe5cb32 100644 --- a/pkg/utils/rebase_todo.go +++ b/pkg/utils/rebase_todo.go @@ -68,6 +68,40 @@ func findTodo(todos []todo.Todo, todoToFind Todo) (int, bool) { return idx, ok } +// LastDoneTodoWasRescheduled tells whether git put the last command of +// rebase-merge/done back into git-rebase-todo, given the parsed contents of +// these two files. In certain cases, git reschedules commands that failed. One +// example is if a patch would overwrite an untracked file; another one is an +// "exec" that failed. +func LastDoneTodoWasRescheduled(doneTodos []todo.Todo, todos []todo.Todo) bool { + // If the last command of the "done" file is the same as the first command + // of "git-rebase-todo", the command was rescheduled. + if len(doneTodos) > 0 && len(todos) > 0 && doneTodos[len(doneTodos)-1] == todos[0] { + return true + } + + // Older versions of git have a bug whereby, if a command is rescheduled, + // the last successful command is appended to the "done" file again. To + // detect this, we need to compare the second-to-last done entry against the + // first todo entry, and also compare the last done entry against the + // last-but-two done entry; this latter check is needed for the following + // case: + // pick A + // exec make test + // pick B + // exec make test + // If pick B fails with conflicts, then the "done" file contains + // pick A + // exec make test + // pick B + // and git-rebase-todo contains + // exec make test + // Without the last condition we would erroneously treat this as the exec + // command being rescheduled. + return len(doneTodos) >= 3 && len(todos) > 0 && doneTodos[len(doneTodos)-2] == todos[0] && + doneTodos[len(doneTodos)-1] == doneTodos[len(doneTodos)-3] +} + func ReadRebaseTodoFile(fileName string, commentChar byte) ([]todo.Todo, error) { f, err := os.Open(fileName) if err != nil {