diff --git a/pkg/commands/git_commands/commit_loader.go b/pkg/commands/git_commands/commit_loader.go index f116ded1f..f9cfff141 100644 --- a/pkg/commands/git_commands/commit_loader.go +++ b/pkg/commands/git_commands/commit_loader.go @@ -344,6 +344,8 @@ func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) []*mod for _, t := range todos { if t.Command == todo.UpdateRef { t.Msg = t.Ref + } else if t.Command == todo.Exec { + t.Msg = t.ExecCommand } else if t.Commit == "" { // Command does not have a commit associated, skip continue diff --git a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go index c2aa1418a..c5ad78c47 100644 --- a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go +++ b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go @@ -9,10 +9,12 @@ import ( "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands/git_commands" + "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/types/enums" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/samber/lo" + "github.com/stefanhaller/git-todo-parser/todo" ) type MergeAndRebaseHelper struct { @@ -110,7 +112,12 @@ func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error { // we should end up with a command like 'git merge --continue' // it's impossible for a rebase to require a commit so we'll use a subprocess only if it's a merge - if status == enums.REBASE_MODE_MERGING && command != REBASE_OPTION_ABORT && self.c.UserConfig.Git.Merging.ManualCommit { + needsSubprocess := (status == enums.REBASE_MODE_MERGING && command != REBASE_OPTION_ABORT && self.c.UserConfig.Git.Merging.ManualCommit) || + // but we'll also use a subprocess if we have exec todos; those are likely to be lengthy build + // tasks whose output the user will want to see in the terminal + (status == enums.REBASE_MODE_REBASING && command != REBASE_OPTION_ABORT && self.hasExecTodos()) + + if needsSubprocess { // TODO: see if we should be calling more of the code from self.Git.Rebase.GenericMergeOrRebaseAction return self.c.RunSubprocessAndRefresh( self.c.Git().Rebase.GenericMergeOrRebaseActionCmdObj(commandType, command), @@ -123,6 +130,18 @@ func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error { return nil } +func (self *MergeAndRebaseHelper) hasExecTodos() bool { + for _, commit := range self.c.Model().Commits { + if commit.Status != models.StatusRebasing { + break + } + if commit.Action == todo.Exec { + return true + } + } + return false +} + var conflictStrings = []string{ "Failed to merge in the changes", "When you have resolved this problem", diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index f8717f413..de5116b74 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -284,6 +284,9 @@ func (self *LocalCommitsController) GetOnRenderToMain() func() error { map[string]string{ "ref": strings.TrimPrefix(commit.Name, "refs/heads/"), })) + } else if commit.Action == todo.Exec { + task = types.NewRenderStringTask( + self.c.Tr.ExecCommandHere + "\n\n" + commit.Name) } else { cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash, self.c.Modes().Filtering.GetPath()) task = types.NewRunPtyTask(cmdObj.GetCmd()) diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index d32aafbf2..7aabbbc2b 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -164,6 +164,7 @@ type TranslationSet struct { RewordCommitEditor string NoCommitsThisBranch string UpdateRefHere string + ExecCommandHere string Error string Undo string UndoReflog string @@ -1093,6 +1094,7 @@ func EnglishTranslationSet() TranslationSet { SquashTooltip: "Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it.", NoCommitsThisBranch: "No commits for this branch", UpdateRefHere: "Update branch '{{.ref}}' here", + ExecCommandHere: "Execute the following command here:", CannotSquashOrFixupFirstCommit: "There's no commit below to squash into", Fixup: "Fixup", SureFixupThisCommit: "Are you sure you want to 'fixup' the selected commit(s) into the commit below?", diff --git a/pkg/integration/tests/interactive_rebase/show_exec_todos.go b/pkg/integration/tests/interactive_rebase/show_exec_todos.go new file mode 100644 index 000000000..b66036d02 --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/show_exec_todos.go @@ -0,0 +1,57 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var ShowExecTodos = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Show exec todos in the rebase todo list", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(cfg *config.AppConfig) { + cfg.UserConfig.CustomCommands = []config.CustomCommand{ + { + Key: "X", + Context: "commits", + Command: "git -c core.editor=: rebase -i -x false HEAD^^", + }, + } + }, + SetupRepo: func(shell *Shell) { + shell. + NewBranch("branch1"). + CreateNCommits(3) + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Press("X"). + Tap(func() { + t.ExpectPopup().Alert().Title(Equals("Error")).Content(Contains("Rebasing (2/4)Executing: false")).Confirm() + }). + Lines( + Contains("exec").Contains("false"), + Contains("pick").Contains("CI commit 03"), + Contains("CI ◯ <-- YOU ARE HERE --- commit 02"), + Contains("CI ◯ commit 01"), + ). + Tap(func() { + t.Common().ContinueRebase() + t.ExpectPopup().Alert().Title(Equals("Error")).Content(Contains("exit status 1")).Confirm() + }). + Lines( + Contains("CI ◯ <-- YOU ARE HERE --- commit 03"), + Contains("CI ◯ commit 02"), + Contains("CI ◯ commit 01"), + ). + Tap(func() { + t.Common().ContinueRebase() + }). + Lines( + Contains("CI ◯ commit 03"), + Contains("CI ◯ commit 02"), + Contains("CI ◯ commit 01"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index ea2b93f45..5fbbfb4e2 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -209,6 +209,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.RewordLastCommit, interactive_rebase.RewordYouAreHereCommit, interactive_rebase.RewordYouAreHereCommitWithEditor, + interactive_rebase.ShowExecTodos, interactive_rebase.SquashDownFirstCommit, interactive_rebase.SquashDownSecondCommit, interactive_rebase.SquashFixupsAbove,