summaryrefslogtreecommitdiffstats
path: root/pkg/commands
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2019-02-19 23:36:29 +1100
committerJesse Duffield <jessedduffield@gmail.com>2019-02-19 23:36:36 +1100
commit0228e250847d042730038281f435b0b21d992c42 (patch)
tree9f15ef6444c6215852a301a3286dcddf8a2b0cb3 /pkg/commands
parent935f77483443a12ab159e19a958dfdf61a947b36 (diff)
work towards more interactive rebase options
Diffstat (limited to 'pkg/commands')
-rw-r--r--pkg/commands/commit.go10
-rw-r--r--pkg/commands/git.go99
-rw-r--r--pkg/commands/git_test.go64
3 files changed, 55 insertions, 118 deletions
diff --git a/pkg/commands/commit.go b/pkg/commands/commit.go
index 6ae6acd02..1df6afbd0 100644
--- a/pkg/commands/commit.go
+++ b/pkg/commands/commit.go
@@ -2,6 +2,7 @@ package commands
import (
"github.com/fatih/color"
+ "github.com/jesseduffield/lazygit/pkg/utils"
)
// Commit : A git commit
@@ -10,6 +11,7 @@ type Commit struct {
Name string
Status string // one of "unpushed", "pushed", "merged", or "rebasing"
DisplayString string
+ Action string // one of "", "pick", "edit", "squash", "reword", "drop", "fixup"
}
// GetDisplayStrings is a function.
@@ -19,6 +21,7 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
green := color.New(color.FgGreen)
white := color.New(color.FgWhite)
blue := color.New(color.FgBlue)
+ cyan := color.New(color.FgCyan)
var shaColor *color.Color
switch c.Status {
@@ -34,5 +37,10 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
shaColor = white
}
- return []string{shaColor.Sprint(c.Sha), white.Sprint(c.Name)}
+ actionString := ""
+ if c.Action != "" {
+ actionString = cyan.Sprint(utils.WithPadding(c.Action, 7)) + " "
+ }
+
+ return []string{shaColor.Sprint(c.Sha), actionString + white.Sprint(c.Name)}
}
diff --git a/pkg/commands/git.go b/pkg/commands/git.go
index 845b81ff2..fa6e4c5fe 100644
--- a/pkg/commands/git.go
+++ b/pkg/commands/git.go
@@ -2,6 +2,7 @@ package commands
import (
"fmt"
+ "io/ioutil"
"os"
"os/exec"
"strings"
@@ -360,46 +361,6 @@ func (c *GitCommand) Push(branchName string, force bool, ask func(string) string
return c.OSCommand.DetectUnamePass(cmd, ask)
}
-// SquashPreviousTwoCommits squashes a commit down to the one below it
-// retaining the message of the higher commit
-func (c *GitCommand) SquashPreviousTwoCommits(message string) error {
- // TODO: test this
- if err := c.OSCommand.RunCommand("git reset --soft HEAD^"); err != nil {
- return err
- }
- // TODO: if password is required, we need to return a subprocess
- return c.OSCommand.RunCommand(fmt.Sprintf("git commit --amend -m %s", c.OSCommand.Quote(message)))
-}
-
-// SquashFixupCommit squashes a 'FIXUP' commit into the commit beneath it,
-// retaining the commit message of the lower commit
-func (c *GitCommand) SquashFixupCommit(branchName string, shaValue string) error {
- commands := []string{
- fmt.Sprintf("git checkout -q %s", shaValue),
- fmt.Sprintf("git reset --soft %s^", shaValue),
- fmt.Sprintf("git commit --amend -C %s^", shaValue),
- fmt.Sprintf("git rebase --onto HEAD %s %s", shaValue, branchName),
- }
- for _, command := range commands {
- c.Log.Info(command)
-
- if output, err := c.OSCommand.RunCommandWithOutput(command); err != nil {
- ret := output
- // We are already in an error state here so we're just going to append
- // the output of these commands
- output, _ := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git branch -d %s", shaValue))
- ret += output
- output, _ = c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git checkout %s", branchName))
- ret += output
-
- c.Log.Info(ret)
- return errors.New(ret)
- }
- }
-
- return nil
-}
-
// CatFile obtains the content of a file
func (c *GitCommand) CatFile(fileName string) (string, error) {
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("cat %s", c.OSCommand.Quote(fileName)))
@@ -606,7 +567,7 @@ func (c *GitCommand) FastForward(branchName string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git fetch %s %s:%s", upstream, branchName, branchName))
}
-// GenericMerge takes a commandType of "merging" or "rebasing" and a command of "abort", "skip" or "continue"
+// GenericMerge takes a commandType of "merge" or "rebase" and a command of "abort", "skip" or "continue"
// By default we skip the editor in the case where a commit will be made
func (c *GitCommand) GenericMerge(commandType string, command string) error {
gitCommand := fmt.Sprintf("git %s %s --%s", c.OSCommand.Platform.skipEditorArg, commandType, command)
@@ -619,7 +580,7 @@ func (c *GitCommand) RewordCommit(commits []*Commit, index int) (*exec.Cmd, erro
return nil, err
}
- return c.PrepareInteractiveRebaseCommand(commits[index+1].Sha, todo, true)
+ return c.PrepareInteractiveRebaseCommand(commits[index+1].Sha, todo, false)
}
func (c *GitCommand) MoveCommitDown(commits []*Commit, index int) error {
@@ -633,7 +594,7 @@ func (c *GitCommand) MoveCommitDown(commits []*Commit, index int) error {
todo := ""
orderedCommits := append(commits[0:index], commits[index+1], commits[index])
for _, commit := range orderedCommits {
- todo = "pick " + commit.Sha + "\n" + todo
+ todo = "pick " + commit.Sha + " " + commit.Name + "\n" + todo
}
cmd, err := c.PrepareInteractiveRebaseCommand(commits[index+2].Sha, todo, true)
@@ -650,7 +611,6 @@ func (c *GitCommand) InteractiveRebase(commits []*Commit, index int, action stri
return err
}
- // TODO: decide whether to autostash when action == editing
cmd, err := c.PrepareInteractiveRebaseCommand(commits[index+1].Sha, todo, true)
if err != nil {
return err
@@ -659,7 +619,7 @@ func (c *GitCommand) InteractiveRebase(commits []*Commit, index int, action stri
return c.OSCommand.RunPreparedCommand(cmd)
}
-func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string, autoStash bool) (*exec.Cmd, error) {
+func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string, overrideEditor bool) (*exec.Cmd, error) {
ex, err := os.Executable() // get the executable path for git to use
if err != nil {
ex = os.Args[0] // fallback to the first call argument if needed
@@ -670,12 +630,7 @@ func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string
debug = "TRUE"
}
- autoStashFlag := ""
- if autoStash {
- autoStashFlag = "--autostash"
- }
-
- splitCmd := str.ToArgv(fmt.Sprintf("git rebase --interactive %s %s", autoStashFlag, baseSha))
+ splitCmd := str.ToArgv(fmt.Sprintf("git rebase --interactive --autostash %s", baseSha))
cmd := exec.Command(splitCmd[0], splitCmd[1:]...)
@@ -690,6 +645,10 @@ func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string
"GIT_SEQUENCE_EDITOR="+ex,
)
+ if overrideEditor {
+ cmd.Env = append(cmd.Env, "EDITOR="+ex)
+ }
+
return cmd, nil
}
@@ -697,7 +656,11 @@ func (c *GitCommand) HardReset(baseSha string) error {
return c.OSCommand.RunCommand("git reset --hard " + baseSha)
}
-func (v *GitCommand) GenerateGenericRebaseTodo(commits []*Commit, index int, action string) (string, error) {
+func (c *GitCommand) SoftReset(baseSha string) error {
+ return c.OSCommand.RunCommand("git reset --soft " + baseSha)
+}
+
+func (c *GitCommand) GenerateGenericRebaseTodo(commits []*Commit, index int, action string) (string, error) {
if len(commits) <= index+1 {
// assuming they aren't picking the bottom commit
// TODO: support more than say 30 commits and ensure this logic is correct, and i18n
@@ -710,7 +673,37 @@ func (v *GitCommand) GenerateGenericRebaseTodo(commits []*Commit, index int, act
if i == index {
a = action
}
- todo = a + " " + commit.Sha + "\n" + todo
+ todo = a + " " + commit.Sha + " " + commit.Name + "\n" + todo
}
return todo, nil
}
+
+// AmendTo amends the given commit with whatever files are staged
+func (c *GitCommand) AmendTo(sha string) error {
+ if err := c.OSCommand.RunCommand(fmt.Sprintf("git commit --fixup=%s", sha)); err != nil {
+ return err
+ }
+ return c.OSCommand.RunCommand(fmt.Sprintf("git %s rebase --interactive --autostash --autosquash %s^", c.OSCommand.Platform.skipEditorArg, sha))
+}
+
+func (c *GitCommand) EditRebaseTodo(index int, action string) error {
+ fileName := ".git/rebase-merge/git-rebase-todo"
+ bytes, err := ioutil.ReadFile(fileName)
+ if err != nil {
+ return err
+ }
+
+ content := strings.Split(string(bytes), "\n")
+
+ contentIndex := len(content) - 2 - index
+ splitLine := strings.Split(content[contentIndex], " ")
+ content[contentIndex] = action + " " + strings.Join(splitLine[1:], " ")
+ result := strings.Join(content, "\n")
+
+ return ioutil.WriteFile(fileName, []byte(result), 0644)
+}
+
+// Revert reverts the selected commit by sha
+func (c *GitCommand) Revert(sha string) error {
+ return c.OSCommand.RunCommand(fmt.Sprintf("git revert %s", sha))
+}
diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go
index 4159683ca..5af2bfe51 100644
--- a/pkg/commands/git_test.go
+++ b/pkg/commands/git_test.go
@@ -1152,70 +1152,6 @@ func TestGitCommandSquashPreviousTwoCommits(t *testing.T) {
}
}
-// TestGitCommandSquashFixupCommit is a function.
-func TestGitCommandSquashFixupCommit(t *testing.T) {
- type scenario struct {
- testName string
- command func() (func(string, ...string) *exec.Cmd, *[][]string)
- test func(*[][]string, error)
- }
-
- scenarios := []scenario{
- {
- "An error occurred with one of the sub git command",
- func() (func(string, ...string) *exec.Cmd, *[][]string) {
- cmdsCalled := [][]string{}
- return func(cmd string, args ...string) *exec.Cmd {
- cmdsCalled = append(cmdsCalled, args)
- if len(args) > 0 && args[0] == "checkout" {
- return exec.Command("test")
- }
-
- return exec.Command("echo")
- }, &cmdsCalled
- },
- func(cmdsCalled *[][]string, err error) {
- assert.NotNil(t, err)
- assert.Len(t, *cmdsCalled, 3)
- assert.EqualValues(t, *cmdsCalled, [][]string{
- {"checkout", "-q", "6789abcd"},
- {"branch", "-d", "6789abcd"},
- {"checkout", "test"},
- })
- },
- },
- {
- "Squash fixup succeeded",
- func() (func(string, ...string) *exec.Cmd, *[][]string) {
- cmdsCalled := [][]string{}
- return func(cmd string, args ...string) *exec.Cmd {
- cmdsCalled = append(cmdsCalled, args)
- return exec.Command("echo")
- }, &cmdsCalled
- },
- func(cmdsCalled *[][]string, err error) {
- assert.Nil(t, err)
- assert.Len(t, *cmdsCalled, 4)
- assert.EqualValues(t, *cmdsCalled, [][]string{
- {"checkout", "-q", "6789abcd"},
- {"reset", "--soft", "6789abcd^"},
- {"commit", "--amend", "-C", "6789abcd^"},
- {"rebase", "--onto", "HEAD", "6789abcd", "test"},
- })
- },
- },
- }
-
- for _, s := range scenarios {
- t.Run(s.testName, func(t *testing.T) {
- var cmdsCalled *[][]string
- gitCmd := newDummyGitCommand()
- gitCmd.OSCommand.command, cmdsCalled = s.command()
- s.test(cmdsCalled, gitCmd.SquashFixupCommit("test", "6789abcd"))
- })
- }
-}
-
// TestGitCommandCatFile is a function.
func TestGitCommandCatFile(t *testing.T) {
gitCmd := newDummyGitCommand()