summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2023-05-02 19:05:42 +1000
committerJesse Duffield <jessedduffield@gmail.com>2023-05-02 19:05:42 +1000
commit5dacbb6293bcff48649fd9fcae6af8e1b76d23ad (patch)
tree58199784e1d135eedaf836ba4cbf8303d30fee38
parent8d68ab41b6670c37802279bef7079991bea6888a (diff)
merge master into refactor-better-encapsulation
-rw-r--r--README.md2
-rw-r--r--docs/Config.md1
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--pkg/app/daemon/daemon.go300
-rw-r--r--pkg/app/daemon/rebase.go64
-rw-r--r--pkg/commands/git_commands/deps_test.go6
-rw-r--r--pkg/commands/git_commands/patch.go16
-rw-r--r--pkg/commands/git_commands/rebase.go215
-rw-r--r--pkg/commands/git_commands/rebase_test.go3
-rw-r--r--pkg/config/user_config.go78
-rw-r--r--pkg/gui/context.go10
-rw-r--r--pkg/gui/controllers/local_commits_controller.go14
-rw-r--r--pkg/gui/gui_common.go4
-rw-r--r--pkg/gui/layout.go2
-rw-r--r--pkg/gui/presentation/commits.go2
-rw-r--r--pkg/gui/types/common.go1
-rw-r--r--pkg/integration/tests/interactive_rebase/amend_fixup_commit.go50
-rw-r--r--pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go25
-rw-r--r--pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref_show_branch_heads.go62
-rw-r--r--pkg/integration/tests/test_list.go2
-rw-r--r--pkg/utils/rebase_todo.go76
-rw-r--r--pkg/utils/rebase_todo_test.go108
-rw-r--r--vendor/github.com/fsmiamoto/git-todo-parser/todo/parse.go10
-rw-r--r--vendor/modules.txt2
25 files changed, 782 insertions, 277 deletions
diff --git a/README.md b/README.md
index bb30e8aef..2e9483a54 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/go.mod b/go.mod
index f86062a69..098d19c67 100644
--- a/go.mod
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
index e6a83d627..dd4a8b81e 100644
--- a/go.sum
+++ b/go.sum
@@ -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"
- }
- })