diff --git a/pkg/app/daemon/daemon.go b/pkg/app/daemon/daemon.go index 7053a8032..d8f32e608 100644 --- a/pkg/app/daemon/daemon.go +++ b/pkg/app/daemon/daemon.go @@ -2,15 +2,16 @@ package daemon import ( "encoding/json" + "fmt" "log" "os" - "path/filepath" - "strings" + "strconv" "github.com/fsmiamoto/git-todo-parser/todo" + "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/common" - "github.com/jesseduffield/lazygit/pkg/env" "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/samber/lo" ) // Sometimes lazygit will be invoked in daemon mode from a parent lazygit process. @@ -18,58 +19,56 @@ import ( // For example, if we want to ensure that a git command doesn't hang due to // waiting for an editor to save a commit message, we can tell git to invoke lazygit // as the editor via 'GIT_EDITOR=lazygit', and use the env var -// 'LAZYGIT_DAEMON_KIND=EXIT_IMMEDIATELY' to specify that we want to run lazygit -// as a daemon which simply exits immediately. Any additional arguments we want -// to pass to a daemon can be done via other env vars. +// 'LAZYGIT_DAEMON_KIND=1' (exit immediately) to specify that we want to run lazygit +// as a daemon which simply exits immediately. +// +// 'Daemon' is not the best name for this, because it's not a persistent background +// process, but it's close enough. -type DaemonKind string +type DaemonKind int const ( - InteractiveRebase DaemonKind = "INTERACTIVE_REBASE" - ExitImmediately DaemonKind = "EXIT_IMMEDIATELY" + // for when we fail to parse the daemon kind + DaemonKindUnknown DaemonKind = iota + + DaemonKindExitImmediately + DaemonKindCherryPick + DaemonKindMoveTodoUp + DaemonKindMoveTodoDown + DaemonKindInsertBreak + DaemonKindChangeTodoActions ) const ( DaemonKindEnvKey string = "LAZYGIT_DAEMON_KIND" - // Contains a json-encoded instance of the InteractiveRebaseInstructions struct - InteractiveRebaseInstructionsEnvKey string = "LAZYGIT_DAEMON_INSTRUCTIONS" + // Contains json-encoded arguments to the daemon + DaemonInstructionEnvKey string = "LAZYGIT_DAEMON_INSTRUCTION" ) -// Exactly one of the fields in this struct is expected to be non-empty -type InteractiveRebaseInstructions struct { - // If this is non-empty, this string is prepended to the git-rebase-todo - // file. The string is expected to have newlines at the end of each line. - LinesToPrependToRebaseTODO string +func getInstruction() Instruction { + jsonData := os.Getenv(DaemonInstructionEnvKey) - // If this is non-empty, it tells lazygit to read the original todo file, and - // change the action for one or more entries in it. - // The existing action of the todo to be changed is expected to be "pick". - ChangeTodoActions []ChangeTodoAction + mapping := map[DaemonKind]func(string) Instruction{ + DaemonKindExitImmediately: deserializeInstruction[*ExitImmediatelyInstruction], + DaemonKindCherryPick: deserializeInstruction[*CherryPickCommitsInstruction], + DaemonKindChangeTodoActions: deserializeInstruction[*ChangeTodoActionsInstruction], + DaemonKindMoveTodoUp: deserializeInstruction[*MoveTodoUpInstruction], + DaemonKindMoveTodoDown: deserializeInstruction[*MoveTodoDownInstruction], + DaemonKindInsertBreak: deserializeInstruction[*InsertBreakInstruction], + } - // Can be set to the sha of a "pick" todo that will be moved down by one. - ShaToMoveDown string - - // Can be set to the sha of a "pick" todo that will be moved up by one. - ShaToMoveUp string -} - -type ChangeTodoAction struct { - Sha string - NewAction todo.TodoCommand -} - -type Daemon interface { - Run() error + return mapping[getDaemonKind()](jsonData) } func Handle(common *common.Common) { - d := getDaemon(common) - if d == nil { + if !InDaemonMode() { return } - if err := d.Run(); err != nil { + instruction := getInstruction() + + if err := instruction.run(common); err != nil { log.Fatal(err) } @@ -77,89 +76,200 @@ func Handle(common *common.Common) { } func InDaemonMode() bool { - return getDaemonKind() != "" -} - -func getDaemon(common *common.Common) Daemon { - switch getDaemonKind() { - case InteractiveRebase: - return &rebaseDaemon{c: common} - case ExitImmediately: - return &exitImmediatelyDaemon{c: common} - } - - return nil + return getDaemonKind() != DaemonKindUnknown } func getDaemonKind() DaemonKind { - return DaemonKind(os.Getenv(DaemonKindEnvKey)) -} - -type rebaseDaemon struct { - c *common.Common -} - -func (self *rebaseDaemon) Run() error { - self.c.Log.Info("Lazygit invoked as interactive rebase demon") - self.c.Log.Info("args: ", os.Args) - path := os.Args[1] - - if strings.HasSuffix(path, "git-rebase-todo") { - return self.writeTodoFile(path) - } else if strings.HasSuffix(path, filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test - // if we are rebasing and squashing, we'll see a COMMIT_EDITMSG - // but in this case we don't need to edit it, so we'll just return - } else { - self.c.Log.Info("Lazygit demon did not match on any use cases") - } - - return nil -} - -func (self *rebaseDaemon) writeTodoFile(path string) error { - jsonData := os.Getenv(InteractiveRebaseInstructionsEnvKey) - instructions := InteractiveRebaseInstructions{} - err := json.Unmarshal([]byte(jsonData), &instructions) + intValue, err := strconv.Atoi(os.Getenv(DaemonKindEnvKey)) if err != nil { - return err + return DaemonKindUnknown } - if instructions.LinesToPrependToRebaseTODO != "" { - return utils.PrependStrToTodoFile(path, []byte(instructions.LinesToPrependToRebaseTODO)) - } else if len(instructions.ChangeTodoActions) != 0 { - return self.changeTodoAction(path, instructions.ChangeTodoActions) - } else if instructions.ShaToMoveDown != "" { - return utils.MoveTodoDown(path, instructions.ShaToMoveDown, todo.Pick) - } else if instructions.ShaToMoveUp != "" { - return utils.MoveTodoUp(path, instructions.ShaToMoveUp, todo.Pick) + return DaemonKind(intValue) +} + +// An Instruction is a command to be run by lazygit in daemon mode. +// It is serialized to json and passed to lazygit via environment variables +type Instruction interface { + Kind() DaemonKind + SerializedInstructions() string + + // runs the instruction + run(common *common.Common) error +} + +func serializeInstruction[T any](instruction T) string { + jsonData, err := json.Marshal(instruction) + if err != nil { + // this should never happen + panic(err) } - self.c.Log.Error("No instructions were given to daemon") + return string(jsonData) +} + +func deserializeInstruction[T Instruction](jsonData string) Instruction { + var instruction T + err := json.Unmarshal([]byte(jsonData), &instruction) + if err != nil { + panic(err) + } + + return instruction +} + +func ToEnvVars(instruction Instruction) []string { + return []string{ + fmt.Sprintf("%s=%d", DaemonKindEnvKey, instruction.Kind()), + fmt.Sprintf("%s=%s", DaemonInstructionEnvKey, instruction.SerializedInstructions()), + } +} + +type ExitImmediatelyInstruction struct{} + +func (self *ExitImmediatelyInstruction) Kind() DaemonKind { + return DaemonKindExitImmediately +} + +func (self *ExitImmediatelyInstruction) SerializedInstructions() string { + return serializeInstruction(self) +} + +func (self *ExitImmediatelyInstruction) run(common *common.Common) error { return nil } -func (self *rebaseDaemon) changeTodoAction(path string, changeTodoActions []ChangeTodoAction) error { - for _, c := range changeTodoActions { - if err := utils.EditRebaseTodo(path, c.Sha, todo.Pick, c.NewAction); err != nil { - return err +func NewExitImmediatelyInstruction() Instruction { + return &ExitImmediatelyInstruction{} +} + +type CherryPickCommitsInstruction struct { + Todo string +} + +func NewCherryPickCommitsInstruction(commits []*models.Commit) Instruction { + todoLines := lo.Map(commits, func(commit *models.Commit, _ int) TodoLine { + return TodoLine{ + Action: "pick", + Commit: commit, } + }) + + todo := TodoLinesToString(todoLines) + + return &CherryPickCommitsInstruction{ + Todo: todo, } - - return nil } -func gitDir() string { - dir := env.GetGitDirEnv() - if dir == "" { - return ".git" +func (self *CherryPickCommitsInstruction) Kind() DaemonKind { + return DaemonKindCherryPick +} + +func (self *CherryPickCommitsInstruction) SerializedInstructions() string { + return serializeInstruction(self) +} + +func (self *CherryPickCommitsInstruction) run(common *common.Common) error { + return handleInteractiveRebase(common, func(path string) error { + return utils.PrependStrToTodoFile(path, []byte(self.Todo)) + }) +} + +type ChangeTodoActionsInstruction struct { + Changes []ChangeTodoAction +} + +func NewChangeTodoActionsInstruction(changes []ChangeTodoAction) Instruction { + return &ChangeTodoActionsInstruction{ + Changes: changes, } - return dir } -type exitImmediatelyDaemon struct { - c *common.Common +func (self *ChangeTodoActionsInstruction) Kind() DaemonKind { + return DaemonKindChangeTodoActions } -func (self *exitImmediatelyDaemon) Run() error { - return nil +func (self *ChangeTodoActionsInstruction) SerializedInstructions() string { + return serializeInstruction(self) +} + +func (self *ChangeTodoActionsInstruction) run(common *common.Common) error { + return handleInteractiveRebase(common, func(path string) error { + for _, c := range self.Changes { + if err := utils.EditRebaseTodo(path, c.Sha, todo.Pick, c.NewAction); err != nil { + return err + } + } + + return nil + }) +} + +type MoveTodoUpInstruction struct { + Sha string +} + +func NewMoveTodoUpInstruction(sha string) Instruction { + return &MoveTodoUpInstruction{ + Sha: sha, + } +} + +func (self *MoveTodoUpInstruction) Kind() DaemonKind { + return DaemonKindMoveTodoUp +} + +func (self *MoveTodoUpInstruction) SerializedInstructions() string { + return serializeInstruction(self) +} + +func (self *MoveTodoUpInstruction) run(common *common.Common) error { + return handleInteractiveRebase(common, func(path string) error { + return utils.MoveTodoUp(path, self.Sha, todo.Pick) + }) +} + +type MoveTodoDownInstruction struct { + Sha string +} + +func NewMoveTodoDownInstruction(sha string) Instruction { + return &MoveTodoDownInstruction{ + Sha: sha, + } +} + +func (self *MoveTodoDownInstruction) Kind() DaemonKind { + return DaemonKindMoveTodoDown +} + +func (self *MoveTodoDownInstruction) SerializedInstructions() string { + return serializeInstruction(self) +} + +func (self *MoveTodoDownInstruction) run(common *common.Common) error { + return handleInteractiveRebase(common, func(path string) error { + return utils.MoveTodoDown(path, self.Sha, todo.Pick) + }) +} + +type InsertBreakInstruction struct{} + +func NewInsertBreakInstruction() Instruction { + return &InsertBreakInstruction{} +} + +func (self *InsertBreakInstruction) Kind() DaemonKind { + return DaemonKindInsertBreak +} + +func (self *InsertBreakInstruction) SerializedInstructions() string { + return serializeInstruction(self) +} + +func (self *InsertBreakInstruction) run(common *common.Common) error { + return handleInteractiveRebase(common, func(path string) error { + return utils.PrependStrToTodoFile(path, []byte("break\n")) + }) } diff --git a/pkg/app/daemon/rebase.go b/pkg/app/daemon/rebase.go new file mode 100644 index 000000000..8702f0f69 --- /dev/null +++ b/pkg/app/daemon/rebase.go @@ -0,0 +1,64 @@ +package daemon + +import ( + "os" + "path/filepath" + "strings" + + "github.com/fsmiamoto/git-todo-parser/todo" + "github.com/jesseduffield/generics/slices" + "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/common" + "github.com/jesseduffield/lazygit/pkg/env" +) + +type TodoLine struct { + Action string + Commit *models.Commit +} + +func (self *TodoLine) ToString() string { + if self.Action == "break" { + return self.Action + "\n" + } else { + return self.Action + " " + self.Commit.Sha + " " + self.Commit.Name + "\n" + } +} + +func TodoLinesToString(todoLines []TodoLine) string { + lines := slices.Map(todoLines, func(todoLine TodoLine) string { + return todoLine.ToString() + }) + + return strings.Join(slices.Reverse(lines), "") +} + +type ChangeTodoAction struct { + Sha string + NewAction todo.TodoCommand +} + +func handleInteractiveRebase(common *common.Common, f func(path string) error) error { + common.Log.Info("Lazygit invoked as interactive rebase demon") + common.Log.Info("args: ", os.Args) + path := os.Args[1] + + if strings.HasSuffix(path, "git-rebase-todo") { + return f(path) + } else if strings.HasSuffix(path, filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test + // if we are rebasing and squashing, we'll see a COMMIT_EDITMSG + // but in this case we don't need to edit it, so we'll just return + } else { + common.Log.Info("Lazygit demon did not match on any use cases") + } + + return nil +} + +func gitDir() string { + dir := env.GetGitDirEnv() + if dir == "" { + return ".git" + } + return dir +} diff --git a/pkg/commands/git_commands/patch.go b/pkg/commands/git_commands/patch.go index 0f8bfed9d..06e5e0f67 100644 --- a/pkg/commands/git_commands/patch.go +++ b/pkg/commands/git_commands/patch.go @@ -105,15 +105,16 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s baseIndex := sourceCommitIdx + 1 + changes := []daemon.ChangeTodoAction{ + {Sha: commits[sourceCommitIdx].Sha, NewAction: todo.Edit}, + {Sha: commits[destinationCommitIdx].Sha, NewAction: todo.Edit}, + } + self.os.LogCommand(logTodoChanges(changes), false) + err := self.rebase.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ baseShaOrRoot: commits[baseIndex].Sha, overrideEditor: true, - instruction: ChangeTodoActionsInstruction{ - actions: []daemon.ChangeTodoAction{ - {Sha: commits[sourceCommitIdx].Sha, NewAction: todo.Edit}, - {Sha: commits[destinationCommitIdx].Sha, NewAction: todo.Edit}, - }, - }, + instruction: daemon.NewChangeTodoActionsInstruction(changes), }).Run() if err != nil { return err diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go index ead8da27a..be5384773 100644 --- a/pkg/commands/git_commands/rebase.go +++ b/pkg/commands/git_commands/rebase.go @@ -1,7 +1,6 @@ package git_commands import ( - "encoding/json" "fmt" "path/filepath" "strings" @@ -13,6 +12,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/samber/lo" ) type RebaseCommands struct { @@ -56,14 +56,15 @@ func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, me } func (self *RebaseCommands) RewordCommitInEditor(commits []*models.Commit, index int) (oscommands.ICmdObj, error) { + changes := []daemon.ChangeTodoAction{{ + Sha: commits[index].Sha, + NewAction: todo.Reword, + }} + self.os.LogCommand(logTodoChanges(changes), false) + return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ baseShaOrRoot: getBaseShaOrRoot(commits, index+1), - instruction: ChangeTodoActionsInstruction{ - actions: []daemon.ChangeTodoAction{{ - Sha: commits[index].Sha, - NewAction: todo.Reword, - }}, - }, + instruction: daemon.NewChangeTodoActionsInstruction(changes), }), nil } @@ -102,9 +103,13 @@ func (self *RebaseCommands) GenericAmend(commits []*models.Commit, index int, f func (self *RebaseCommands) MoveCommitDown(commits []*models.Commit, index int) error { baseShaOrRoot := getBaseShaOrRoot(commits, index+2) + sha := commits[index].Sha + + self.os.LogCommand(fmt.Sprintf("Moving TODO down: %s", utils.ShortSha(sha)), false) + return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ baseShaOrRoot: baseShaOrRoot, - instruction: MoveDownInstruction{sha: commits[index].Sha}, + instruction: daemon.NewMoveTodoDownInstruction(sha), overrideEditor: true, }).Run() } @@ -112,9 +117,13 @@ func (self *RebaseCommands) MoveCommitDown(commits []*models.Commit, index int) func (self *RebaseCommands) MoveCommitUp(commits []*models.Commit, index int) error { baseShaOrRoot := getBaseShaOrRoot(commits, index+1) + sha := commits[index].Sha + + self.os.LogCommand(fmt.Sprintf("Moving TODO up: %s", utils.ShortSha(sha)), false) + return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ baseShaOrRoot: baseShaOrRoot, - instruction: MoveUpInstruction{sha: commits[index].Sha}, + instruction: daemon.NewMoveTodoUpInstruction(sha), overrideEditor: true, }).Run() } @@ -127,74 +136,37 @@ func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, index in baseShaOrRoot := getBaseShaOrRoot(commits, baseIndex) + changes := []daemon.ChangeTodoAction{{ + Sha: commits[index].Sha, + NewAction: action, + }} + self.os.LogCommand(logTodoChanges(changes), false) + return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ - baseShaOrRoot: baseShaOrRoot, - instruction: ChangeTodoActionsInstruction{ - actions: []daemon.ChangeTodoAction{{ - Sha: commits[index].Sha, - NewAction: action, - }}, - }, + baseShaOrRoot: baseShaOrRoot, overrideEditor: true, + instruction: daemon.NewChangeTodoActionsInstruction(changes), }).Run() } func (self *RebaseCommands) EditRebase(branchRef string) error { - commands := []TodoLine{{Action: "break"}} + self.os.LogCommand(fmt.Sprintf("Beginning interactive rebase at '%s'", branchRef), false) return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ baseShaOrRoot: branchRef, - instruction: PrependLinesInstruction{todoLines: commands}, + instruction: daemon.NewInsertBreakInstruction(), }).Run() } -type InteractiveRebaseInstruction interface { - // Add our data to the instructions struct, and return a log string - serialize(instructions *daemon.InteractiveRebaseInstructions) string -} - -type PrependLinesInstruction struct { - todoLines []TodoLine -} - -func (self PrependLinesInstruction) serialize(instructions *daemon.InteractiveRebaseInstructions) string { - todoStr := TodoLinesToString(self.todoLines) - instructions.LinesToPrependToRebaseTODO = todoStr - return fmt.Sprintf("Creating TODO file for interactive rebase: \n\n%s", todoStr) -} - -type ChangeTodoActionsInstruction struct { - actions []daemon.ChangeTodoAction -} - -func (self ChangeTodoActionsInstruction) serialize(instructions *daemon.InteractiveRebaseInstructions) string { - instructions.ChangeTodoActions = self.actions - changeTodoStr := strings.Join(slices.Map(self.actions, func(c daemon.ChangeTodoAction) string { +func logTodoChanges(changes []daemon.ChangeTodoAction) string { + changeTodoStr := strings.Join(slices.Map(changes, func(c daemon.ChangeTodoAction) string { return fmt.Sprintf("%s:%s", c.Sha, c.NewAction) }), "\n") return fmt.Sprintf("Changing TODO actions: %s", changeTodoStr) } -type MoveDownInstruction struct { - sha string -} - -func (self MoveDownInstruction) serialize(instructions *daemon.InteractiveRebaseInstructions) string { - instructions.ShaToMoveDown = self.sha - return fmt.Sprintf("Moving TODO down: %s", self.sha) -} - -type MoveUpInstruction struct { - sha string -} - -func (self MoveUpInstruction) serialize(instructions *daemon.InteractiveRebaseInstructions) string { - instructions.ShaToMoveUp = self.sha - return fmt.Sprintf("Moving TODO up: %s", self.sha) -} - type PrepareInteractiveRebaseCommandOpts struct { baseShaOrRoot string - instruction InteractiveRebaseInstruction + instruction daemon.Instruction overrideEditor bool } @@ -222,22 +194,12 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract gitSequenceEditor := ex if opts.instruction != nil { - instructions := daemon.InteractiveRebaseInstructions{} - logStr := opts.instruction.serialize(&instructions) - jsonData, err := json.Marshal(instructions) - if err == nil { - envVar := fmt.Sprintf("%s=%s", daemon.InteractiveRebaseInstructionsEnvKey, jsonData) - cmdObj.AddEnvVars(envVar) - self.os.LogCommand(logStr, false) - } else { - self.Log.Error(err) - } + cmdObj.AddEnvVars(daemon.ToEnvVars(opts.instruction)...) } else { gitSequenceEditor = "true" } cmdObj.AddEnvVars( - daemon.DaemonKindEnvKey+"="+string(daemon.InteractiveRebase), "DEBUG="+debug, "LANG=en_US.UTF-8", // Force using EN as language "LC_ALL=en_US.UTF-8", // Force using EN as language @@ -309,15 +271,16 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommit(commits []*models.Co return errors.New(self.Tr.DisabledForGPG) } + changes := []daemon.ChangeTodoAction{{ + Sha: commits[commitIndex].Sha, + NewAction: todo.Edit, + }} + self.os.LogCommand(logTodoChanges(changes), false) + return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ baseShaOrRoot: getBaseShaOrRoot(commits, commitIndex+1), overrideEditor: true, - instruction: ChangeTodoActionsInstruction{ - actions: []daemon.ChangeTodoAction{{ - Sha: commits[commitIndex].Sha, - NewAction: todo.Edit, - }}, - }, + instruction: daemon.NewChangeTodoActionsInstruction(changes), }).Run() } @@ -364,15 +327,16 @@ func (self *RebaseCommands) GenericMergeOrRebaseAction(commandType string, comma } func (self *RebaseCommands) runSkipEditorCommand(cmdObj oscommands.ICmdObj) error { + instruction := daemon.NewExitImmediatelyInstruction() lazyGitPath := oscommands.GetLazygitPath() return cmdObj. AddEnvVars( - daemon.DaemonKindEnvKey+"="+string(daemon.ExitImmediately), "GIT_EDITOR="+lazyGitPath, "GIT_SEQUENCE_EDITOR="+lazyGitPath, "EDITOR="+lazyGitPath, "VISUAL="+lazyGitPath, ). + AddEnvVars(daemon.ToEnvVars(instruction)...). Run() } @@ -406,49 +370,17 @@ func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, comm // CherryPickCommits begins an interactive rebase with the given shas being cherry picked onto HEAD func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error { - todoLines := self.BuildTodoLinesSingleAction(commits, "pick") + commitLines := lo.Map(commits, func(commit *models.Commit, _ int) string { + return fmt.Sprintf("%s %s", utils.ShortSha(commit.Sha), commit.Name) + }) + self.os.LogCommand(fmt.Sprintf("Cherry-picking commits:\n%s", strings.Join(commitLines, "\n")), false) return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ baseShaOrRoot: "HEAD", - instruction: PrependLinesInstruction{ - todoLines: todoLines, - }, + instruction: daemon.NewCherryPickCommitsInstruction(commits), }).Run() } -func TodoLinesToString(todoLines []TodoLine) string { - lines := slices.Map(todoLines, func(todoLine TodoLine) string { - return todoLine.ToString() - }) - - return strings.Join(slices.Reverse(lines), "") -} - -func (self *RebaseCommands) BuildTodoLines(commits []*models.Commit, f func(*models.Commit, int) string) []TodoLine { - return slices.MapWithIndex(commits, func(commit *models.Commit, i int) TodoLine { - return TodoLine{Action: f(commit, i), Commit: commit} - }) -} - -func (self *RebaseCommands) BuildTodoLinesSingleAction(commits []*models.Commit, action string) []TodoLine { - return self.BuildTodoLines(commits, func(commit *models.Commit, i int) string { - return action - }) -} - -type TodoLine struct { - Action string - Commit *models.Commit -} - -func (self *TodoLine) ToString() string { - if self.Action == "break" { - return self.Action + "\n" - } else { - return self.Action + " " + self.Commit.Sha + " " + self.Commit.Name + "\n" - } -} - // we can't start an interactive rebase from the first commit without passing the // '--root' arg func getBaseShaOrRoot(commits []*models.Commit, index int) string { diff --git a/pkg/commands/git_commands/rebase_test.go b/pkg/commands/git_commands/rebase_test.go index b2cd88323..1ef22ff5d 100644 --- a/pkg/commands/git_commands/rebase_test.go +++ b/pkg/commands/git_commands/rebase_test.go @@ -2,6 +2,7 @@ package git_commands import ( "regexp" + "strconv" "testing" "github.com/go-errors/errors" @@ -63,7 +64,7 @@ func TestRebaseSkipEditorCommand(t *testing.T) { `^EDITOR=.*$`, `^GIT_EDITOR=.*$`, `^GIT_SEQUENCE_EDITOR=.*$`, - "^" + daemon.DaemonKindEnvKey + "=" + string(daemon.ExitImmediately) + "$", + "^" + daemon.DaemonKindEnvKey + "=" + strconv.Itoa(int(daemon.DaemonKindExitImmediately)) + "$", } { regexStr := regexStr foundMatch := lo.ContainsBy(envVars, func(envVar string) bool {