diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2023-05-02 19:05:42 +1000 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2023-05-02 19:05:42 +1000 |
commit | 5dacbb6293bcff48649fd9fcae6af8e1b76d23ad (patch) | |
tree | 58199784e1d135eedaf836ba4cbf8303d30fee38 | |
parent | 8d68ab41b6670c37802279bef7079991bea6888a (diff) |
merge master into refactor-better-encapsulation
25 files changed, 782 insertions, 277 deletions
@@ -22,7 +22,7 @@ A simple terminal UI for git commands, written in Go with the [gocui](https://gi </p> <p align="center"> -<!-- sponsors --><a href="https://github.com/intabulas"><img src="https://github.com/intabulas.png" width="60px" alt="" /></a><a href="https://github.com/piot"><img src="https://github.com/piot.png" width="60px" alt="" /></a><a href="https://github.com/rgwood"><img src="https://github.com/rgwood.png" width="60px" alt="" /></a><a href="https://github.com/oliverguenther"><img src="https://github.com/oliverguenther.png" width="60px" alt="" /></a><a href="https://github.com/pawanjay176"><img src="https://github.com/pawanjay176.png" width="60px" alt="" /></a><a href="https://github.com/bdach"><img src="https://github.com/bdach.png" width="60px" alt="" /></a><a href="https://github.com/davidklsn"><img src="https://github.com/davidklsn.png" width="60px" alt="" /></a><a href="https://github.com/naoey"><img src="https://github.com/naoey.png" width="60px" alt="" /></a><a href="https://github.com/jryom"><img src="https://github.com/jryom.png" width="60px" alt="" /></a><a href="https://github.com/carstengehling"><img src="https://github.com/carstengehling.png" width="60px" alt="" /></a><a href="https://github.com/ceuk"><img src="https://github.com/ceuk.png" width="60px" alt="" /></a><a href="https://github.com/akospwc"><img src="https://github.com/akospwc.png" width="60px" alt="" /></a><a href="https://github.com/Xetera"><img src="https://github.com/Xetera.png" width="60px" alt="" /></a><a href="https://github.com/HoldenLucas"><img src="https://github.com/HoldenLucas.png" width="60px" alt="" /></a><a href="https://github.com/barbados-clemens"><img src="https://github.com/barbados-clemens.png" width="60px" alt="" /></a><a href="https://github.com/nartc"><img src="https://github.com/nartc.png" width="60px" alt="" /></a><a href="https://github.com/"><img src="https://github.com/.png" width="60px" alt="" /></a><a href="https://github.com/matejcik"><img src="https://github.com/matejcik.png" width="60px" alt="" /></a><a href="https://github.com/lucatume"><img src="https://github.com/lucatume.png" width="60px" alt="" /></a><a href="https://github.com/zach-fuller"><img src="https://github.com/zach-fuller.png" width="60px" alt="" /></a><a href="https://github.com/davdroman"><img src="https://github.com/davdroman.png" width="60px" alt="" /></a><a href="https://github.com/KowalskiPiotr98"><img src="https://github.com/KowalskiPiotr98.png" width="60px" alt="" /></a><a href="https://github.com/nicholascloud"><img src="https://github.com/nicholascloud.png" width="60px" alt="" /></a><a href="https://github.com/topher200"><img src="https://github.com/topher200.png" width="60px" alt="" /></a><a href="https://github.com/PhotonQuantum"><img src="https://github.com/PhotonQuantum.png" width="60px" alt="" /></a><a href="https://github.com/GitSquared"><img src="https://github.com/GitSquared.png" width="60px" alt="" /></a><a href="https://github.com/ava1ar"><img src="https://github.com/ava1ar.png" width="60px" alt="" /></a><a href="https://github.com/pedropombeiro"><img src="https://github.com/pedropombeiro.png" width="60px" alt="" /></a><a href="https://github.com/minidfx"><img src="https://github.com/minidfx.png" width="60px" alt="" /></a><a href="https://github.com/JoeKlemmer"><img src="https://github.com/JoeKlemmer.png" width="60px" alt="" /></a><a href="https://github.com/ColonelBucket8"><img src="https://github.com/ColonelBucket8.png" width="60px" alt="" /></a><a href="https://github.com/mutewinter"><img src="https://github.com/mutewinter.png" width="60px" alt="" /></a><a href="https://github.com/tobi"><img src="https://github.com/tobi.png" width="60px" alt="" /></a><a href="https://github.com/benbfortis"><img src="https://github.com/benbfortis.png" width="60px" alt="" /></a><a href="https://github.com/jakewarren"><img src="https://github.com/jakewarren.png" width="60px" alt="" /></a><a href="https://github.com/tgpholly"><img src="https://github.com/tgpholly.png" width="60px" alt="" /></a><a href="https://github.com/jisantuc"><img src="https://github.com/jisantuc.png" width="60px" alt="" /></a><a href="https://github.com/zabil"><img src="https://github.com/zabil.png" width="60px" alt="" /></a><a href="https://github.com/bitprophet"><img src="https://github.com/bitprophet.png" width="60px" alt="" /></a><a href="https://github.com/tayleighr"><img src="https://github.com/tayleighr.png" width="60px" alt="" /></a><a href="https://github.com/Novakov"><img src="https://github.com/Novakov.png" width="60px" alt="" /></a><a href="https://github.com/mthuggett"><img src="https://github.com/mthuggett.png" width="60px" alt="" /></a><a href="https://github.com/portothree"><img src="https://github.com/portothree.png" width="60px" alt="" /></a><a href="https://github.com/farzadmf"><img src="https://github.com/farzadmf.png" width="60px" alt="" /></a><a href="https://github.com/nekhaevskiy"><img src="https://github.com/nekhaevskiy.png" width="60px" alt="" /></a><a href="https://github.com/reivilibre"><img src="https://github.com/reivilibre.png" width="60px" alt="" /></a><a href="https://github.com/andreaskurth"><img src="https://github.com/andreaskurth.png" width="60px" alt="" /></a><a href="https://github.com/froody"><img src="https://github.com/froody.png" width="60px" alt="" /></a><a href="https://github.com/RohanM"><img src="https://github.com/RohanM.png" width="60px" alt="" /></a><!-- sponsors --> +<!-- sponsors --><a href="https://github.com/intabulas"><img src="https://github.com/intabulas.png" width="60px" alt="" /></a><a href="https://github.com/piot"><img src="https://github.com/piot.png" width="60px" alt="" /></a><a href="https://github.com/rgwood"><img src="https://github.com/rgwood.png" width="60px" alt="" /></a><a href="https://github.com/oliverguenther"><img src="https://github.com/oliverguenther.png" width="60px" alt="" /></a><a href="https://github.com/pawanjay176"><img src="https://github.com/pawanjay176.png" width="60px" alt="" /></a><a href="https://github.com/bdach"><img src="https://github.com/bdach.png" width="60px" alt="" /></a><a href="https://github.com/davidklsn"><img src="https://github.com/davidklsn.png" width="60px" alt="" /></a><a href="https://github.com/naoey"><img src="https://github.com/naoey.png" width="60px" alt="" /></a><a href="https://github.com/jryom"><img src="https://github.com/jryom.png" width="60px" alt="" /></a><a href="https://github.com/carstengehling"><img src="https://github.com/carstengehling.png" width="60px" alt="" /></a><a href="https://github.com/ceuk"><img src="https://github.com/ceuk.png" width="60px" alt="" /></a><a href="https://github.com/akospwc"><img src="https://github.com/akospwc.png" width="60px" alt="" /></a><a href="https://github.com/Xetera"><img src="https://github.com/Xetera.png" width="60px" alt="" /></a><a href="https://github.com/HoldenLucas"><img src="https://github.com/HoldenLucas.png" width="60px" alt="" /></a><a href="https://github.com/barbados-clemens"><img src="https://github.com/barbados-clemens.png" width="60px" alt="" /></a><a href="https://github.com/nartc"><img src="https://github.com/nartc.png" width="60px" alt="" /></a><a href="https://github.com/"><img src="https://github.com/.png" width="60px" alt="" /></a><a href="https://github.com/matejcik"><img src="https://github.com/matejcik.png" width="60px" alt="" /></a><a href="https://github.com/lucatume"><img src="https://github.com/lucatume.png" width="60px" alt="" /></a><a href="https://github.com/zach-fuller"><img src="https://github.com/zach-fuller.png" width="60px" alt="" /></a><a href="https://github.com/davdroman"><img src="https://github.com/davdroman.png" width="60px" alt="" /></a><a href="https://github.com/KowalskiPiotr98"><img src="https://github.com/KowalskiPiotr98.png" width="60px" alt="" /></a><a href="https://github.com/nicholascloud"><img src="https://github.com/nicholascloud.png" width="60px" alt="" /></a><a href="https://github.com/topher200"><img src="https://github.com/topher200.png" width="60px" alt="" /></a><a href="https://github.com/PhotonQuantum"><img src="https://github.com/PhotonQuantum.png" width="60px" alt="" /></a><a href="https://github.com/GitSquared"><img src="https://github.com/GitSquared.png" width="60px" alt="" /></a><a href="https://github.com/ava1ar"><img src="https://github.com/ava1ar.png" width="60px" alt="" /></a><a href="https://github.com/pedropombeiro"><img src="https://github.com/pedropombeiro.png" width="60px" alt="" /></a><a href="https://github.com/minidfx"><img src="https://github.com/minidfx.png" width="60px" alt="" /></a><a href="https://github.com/JoeKlemmer"><img src="https://github.com/JoeKlemmer.png" width="60px" alt="" /></a><a href="https://github.com/ColonelBucket8"><img src="https://github.com/ColonelBucket8.png" width="60px" alt="" /></a><a href="https://github.com/mutewinter"><img src="https://github.com/mutewinter.png" width="60px" alt="" /></a><a href="https://github.com/tobi"><img src="https://github.com/tobi.png" width="60px" alt="" /></a><a href="https://github.com/benbfortis"><img src="https://github.com/benbfortis.png" width="60px" alt="" /></a><a href="https://github.com/jakewarren"><img src="https://github.com/jakewarren.png" width="60px" alt="" /></a><a href="https://github.com/tgpholly"><img src="https://github.com/tgpholly.png" width="60px" alt="" /></a><a href="https://github.com/jisantuc"><img src="https://github.com/jisantuc.png" width="60px" alt="" /></a><a href="https://github.com/zabil"><img src="https://github.com/zabil.png" width="60px" alt="" /></a><a href="https://github.com/bitprophet"><img src="https://github.com/bitprophet.png" width="60px" alt="" /></a><a href="https://github.com/tayleighr"><img src="https://github.com/tayleighr.png" width="60px" alt="" /></a><a href="https://github.com/Novakov"><img src="https://github.com/Novakov.png" width="60px" alt="" /></a><a href="https://github.com/mthuggett"><img src="https://github.com/mthuggett.png" width="60px" alt="" /></a><a href="https://github.com/portothree"><img src="https://github.com/portothree.png" width="60px" alt="" /></a><a href="https://github.com/farzadmf"><img src="https://github.com/farzadmf.png" width="60px" alt="" /></a><a href="https://github.com/nekhaevskiy"><img src="https://github.com/nekhaevskiy.png" width="60px" alt="" /></a><a href="https://github.com/reivilibre"><img src="https://github.com/reivilibre.png" width="60px" alt="" /></a><a href="https://github.com/andreaskurth"><img src="https://github.com/andreaskurth.png" width="60px" alt="" /></a><a href="https://github.com/froody"><img src="https://github.com/froody.png" width="60px" alt="" /></a><a href="https://github.com/RohanM"><img src="https://github.com/RohanM.png" width="60px" alt="" /></a><a href="https://github.com/BSteffaniak"><img src="https://github.com/BSteffaniak.png" width="60px" alt="" /></a><!-- sponsors --> </p> ## Elevator Pitch diff --git a/docs/Config.md b/docs/Config.md index 837230367..34e89c130 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -57,6 +57,7 @@ gui: showFileTree: true # for rendering changes files in a tree format showListFooter: true # for seeing the '5 of 20' message in list panels showRandomTip: true + experimentalShowBranchHeads: false # visualize branch heads with (*) in commits list showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you) showCommandLog: true showIcons: false @@ -9,7 +9,7 @@ require ( github.com/cli/safeexec v1.0.0 github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 github.com/creack/pty v1.1.11 - github.com/fsmiamoto/git-todo-parser v0.0.4-0.20230403011024-617a5a7ce980 + github.com/fsmiamoto/git-todo-parser v0.0.4 github.com/fsnotify/fsnotify v1.4.7 github.com/gdamore/tcell/v2 v2.6.0 github.com/go-errors/errors v1.4.2 @@ -30,8 +30,8 @@ github.com/fatih/color v1.7.1-0.20180516100307-2d684516a886/go.mod h1:Zm6kSWBoL9 github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fsmiamoto/git-todo-parser v0.0.4-0.20230403011024-617a5a7ce980 h1:ay9aM+Ay9I4LJttUVF4EFVmeNUkS9/snYVFK6lwieVQ= -github.com/fsmiamoto/git-todo-parser v0.0.4-0.20230403011024-617a5a7ce980/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLIaEWvwr2sxKYYb0Fas= +github.com/fsmiamoto/git-todo-parser v0.0.4 h1:fzcGaoAFDHWzJRKw//CSZFrXucsLKplIvOSab3FtWWM= +github.com/fsmiamoto/git-todo-parser v0.0.4/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLIaEWvwr2sxKYYb0Fas= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= diff --git a/pkg/app/daemon/daemon.go b/pkg/app/daemon/daemon.go index e040e79c2..adc287309 100644 --- a/pkg/app/daemon/daemon.go +++ b/pkg/app/daemon/daemon.go @@ -1,13 +1,17 @@ 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. @@ -15,38 +19,58 @@ 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 + DaemonKindMoveFixupCommitDown ) const ( DaemonKindEnvKey string = "LAZYGIT_DAEMON_KIND" - RebaseTODOEnvKey string = "LAZYGIT_REBASE_TODO" - // The `PrependLinesEnvKey` env variable is set to `true` to tell our daemon - // to prepend the content of `RebaseTODOEnvKey` to the default `git-rebase-todo` - // file instead of using it as a replacement. - PrependLinesEnvKey string = "LAZYGIT_PREPEND_LINES" + // Contains json-encoded arguments to the daemon + DaemonInstructionEnvKey string = "LAZYGIT_DAEMON_INSTRUCTION" ) -type Daemon interface { - Run() error +func getInstruction() Instruction { + jsonData := os.Getenv(DaemonInstructionEnvKey) + + mapping := map[DaemonKind]func(string) Instruction{ + DaemonKindExitImmediately: deserializeInstruction[*ExitImmediatelyInstruction], + DaemonKindCherryPick: deserializeInstruction[*CherryPickCommitsInstruction], + DaemonKindChangeTodoActions: deserializeInstruction[*ChangeTodoActionsInstruction], + DaemonKindMoveFixupCommitDown: deserializeInstruction[*MoveFixupCommitDownInstruction], + DaemonKindMoveTodoUp: deserializeInstruction[*MoveTodoUpInstruction], + DaemonKindMoveTodoDown: deserializeInstruction[*MoveTodoDownInstruction], + DaemonKindInsertBreak: deserializeInstruction[*InsertBreakInstruction], + } + + 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) } @@ -54,73 +78,229 @@ func Handle(common *common.Common) { } func InDaemonMode() bool { - return getDaemonKind() != "" + return getDaemonKind() != DaemonKindUnknown } -func getDaemon(common *common.Common) Daemon { - switch getDaemonKind() { - case InteractiveRebase: - return &rebaseDaemon{c: common} - case ExitImmediately: - return &exitImmediatelyDaemon{c: common} +func getDaemonKind() DaemonKind { + intValue, err := strconv.Atoi(os.Getenv(DaemonKindEnvKey)) + if err != nil { + return DaemonKindUnknown } - return nil + return DaemonKind(intValue) } -func getDaemonKind() DaemonKind { - return DaemonKind(os.Getenv(DaemonKindEnvKey)) +// 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 } -type rebaseDaemon struct { - c *common.Common +func serializeInstruction[T any](instruction T) string { + jsonData, err := json.Marshal(instruction) + if err != nil { + // this should never happen + panic(err) + } + + return string(jsonData) } -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] +func deserializeInstruction[T Instruction](jsonData string) Instruction { + var instruction T + err := json.Unmarshal([]byte(jsonData), &instruction) + if err != nil { + panic(err) + } - 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 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) writeTodoFile(path string) error { - todoContent := []byte(os.Getenv(RebaseTODOEnvKey)) +func NewExitImmediatelyInstruction() Instruction { + return &ExitImmediatelyInstruction{} +} + +type CherryPickCommitsInstruction struct { + Todo string +} - prependLines := os.Getenv(PrependLinesEnvKey) != "" - if prependLines { - existingContent, err := os.ReadFile(path) - if err != nil { - return err +func NewCherryPickCommitsInstruction(commits []*models.Commit) Instruction { + todoLines := lo.Map(commits, func(commit *models.Commit, _ int) TodoLine { + return TodoLine{ + Action: "pick", + Commit: commit, } + }) - todoContent = append(todoContent, existingContent...) + todo := TodoLinesToString(todoLines) + + return &CherryPickCommitsInstruction{ + Todo: todo, } +} - return os.WriteFile(path, todoContent, 0o644) +func (self *CherryPickCommitsInstruction) Kind() DaemonKind { + return DaemonKindCherryPick } -func gitDir() string { - dir := env.GetGitDirEnv() - if dir == "" { - return ".git" +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 + }) +} + +// Takes the sha of some commit, and the sha of a fixup commit that was created +// at the end of the branch, then moves the fixup commit down to right after the +// original commit, changing its type to "fixup" +type MoveFixupCommitDownInstruction struct { + OriginalSha string + FixupSha string +} + +func NewMoveFixupCommitDownInstruction(originalSha string, fixupSha string) Instruction { + return &MoveFixupCommitDownInstruction{ + OriginalSha: originalSha, + FixupSha: fixupSha, + } +} + +func (self *MoveFixupCommitDownInstruction) Kind() DaemonKind { + return DaemonKindMoveFixupCommitDown +} + +func (self *MoveFixupCommitDownInstruction) SerializedInstructions() string { + return serializeInstruction(self) +} + +func (self *MoveFixupCommitDownInstruction) run(common *common.Common) error { + return handleInteractiveRebase(common, func(path string) error { + return utils.MoveFixupCommitDown(path, self.OriginalSha, self.FixupSha) + }) +} + +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/deps_test.go b/pkg/commands/git_commands/deps_test.go index df2c96253..bcf36b168 100644 --- a/pkg/commands/git_commands/deps_test.go +++ b/pkg/commands/git_commands/deps_test.go @@ -15,6 +15,7 @@ import ( type commonDeps struct { runner *oscommands.FakeCmdObjRunner userConfig *config.UserConfig + gitVersion *GitVersion gitConfig *git_config.FakeGitConfig getenv func(string) string removeFile func(string) error @@ -48,6 +49,11 @@ func buildGitCommon(deps commonDeps) *GitCommon { gitCommon.Common.UserConfig = config.GetDefaultConfig() } + gitCommon.version = deps.gitVersion + if gitCommon.version == nil { + gitCommon.version = &GitVersion{2, 0, 0, ""} + } + gitConfig := deps.gitConfig if gitConfig == nil { gitConfig = git_config.NewFakeGitConfig(nil) diff --git a/pkg/commands/git_commands/patch.go b/pkg/commands/git_commands/patch.go index 8c956529c..06e5e0f67 100644 --- a/pkg/commands/git_commands/patch.go +++ b/pkg/commands/git_commands/patch.go @@ -3,7 +3,9 @@ package git_commands import ( "fmt" + "github.com/fsmiamoto/git-todo-parser/todo" "github.com/go-errors/errors" + "github.com/jesseduffield/lazygit/pkg/app/daemon" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/patch" "github.com/jesseduffield/lazygit/pkg/commands/types/enums" @@ -103,18 +105,16 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s baseIndex := sourceCommitIdx + 1 - todoLines := self.rebase.BuildTodoLines(commits[0:baseIndex], func(commit *models.Commit, i int) string { - if i == sourceCommitIdx || i == destinationCommitIdx { - return "edit" - } else { - return "pick" - } - }) |