diff --git a/pkg/commands/git.go b/pkg/commands/git.go index afd18dee7..9024e1479 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -721,14 +721,11 @@ func (c *GitCommand) GenerateGenericRebaseTodo(commits []*Commit, index int, act // 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 { + if err := c.CreateFixupCommit(sha); err != nil { return err } - return c.RunSkipEditorCommand( - fmt.Sprintf( - "git rebase --interactive --autostash --autosquash %s^", sha, - ), - ) + + return c.SquashAllAboveFixupCommits(sha) } // EditRebaseTodo sets the action at a given index in the git-rebase-todo file @@ -916,3 +913,19 @@ func (c *GitCommand) DiffCommits(sha1, sha2 string) (string, error) { cmd := fmt.Sprintf("git diff --color %s %s", sha1, sha2) return c.OSCommand.RunCommandWithOutput(cmd) } + +// CreateFixupCommit creates a commit that fixes up a previous commit +func (c *GitCommand) CreateFixupCommit(sha string) error { + cmd := fmt.Sprintf("git commit --fixup=%s", sha) + return c.OSCommand.RunCommand(cmd) +} + +// SquashAllAboveFixupCommits squashes all fixup! commits above the given one +func (c *GitCommand) SquashAllAboveFixupCommits(sha string) error { + return c.RunSkipEditorCommand( + fmt.Sprintf( + "git rebase --interactive --autostash --autosquash %s^", + sha, + ), + ) +} diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index aa1785af8..941fe6646 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -2066,3 +2066,38 @@ func TestGitCommandResetHardHead(t *testing.T) { }) } } + +// TestGitCommandCreateFixupCommit is a function. +func TestGitCommandCreateFixupCommit(t *testing.T) { + type scenario struct { + testName string + sha string + command func(string, ...string) *exec.Cmd + test func(error) + } + + scenarios := []scenario{ + { + "valid case", + "12345", + test.CreateMockCommand(t, []*test.CommandSwapper{ + { + Expect: `git commit --fixup=12345`, + Replace: "echo", + }, + }), + func(err error) { + assert.NoError(t, err) + }, + }, + } + + gitCmd := NewDummyGitCommand() + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd.OSCommand.command = s.command + s.test(gitCmd.CreateFixupCommit(s.sha)) + }) + } +} diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go index cd7aa9530..98817bf59 100644 --- a/pkg/gui/commits_panel.go +++ b/pkg/gui/commits_panel.go @@ -515,3 +515,42 @@ func (gui *Gui) hasCommit(commits []*commands.Commit, target string) (int, bool) func (gui *Gui) unchooseCommit(commits []*commands.Commit, i int) []*commands.Commit { return append(commits[:i], commits[i+1:]...) } + +func (gui *Gui) handleCreateFixupCommit(g *gocui.Gui, v *gocui.View) error { + commit := gui.getSelectedCommit(g) + if commit == nil { + return nil + } + + return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("CreateFixupCommit"), gui.Tr.TemplateLocalize( + "SureCreateFixupCommit", + Teml{ + "commit": commit.Sha, + }, + ), func(g *gocui.Gui, v *gocui.View) error { + if err := gui.GitCommand.CreateFixupCommit(commit.Sha); err != nil { + return gui.createErrorPanel(g, err.Error()) + } + + return gui.refreshSidePanels(gui.g) + }, nil) +} + +func (gui *Gui) handleSquashAllAboveFixupCommits(g *gocui.Gui, v *gocui.View) error { + commit := gui.getSelectedCommit(g) + if commit == nil { + return nil + } + + return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("SquashAboveCommits"), gui.Tr.TemplateLocalize( + "SureSquashAboveCommits", + Teml{ + "commit": commit.Sha, + }, + ), func(g *gocui.Gui, v *gocui.View) error { + return gui.WithWaitingStatus(gui.Tr.SLocalize("SquashingStatus"), func() error { + err := gui.GitCommand.SquashAllAboveFixupCommits(commit.Sha) + return gui.handleGenericMergeCommandResult(err) + }) + }, nil) +} diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 28a60d473..7397aa8d2 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -322,6 +322,18 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Modifier: gocui.ModNone, Handler: gui.handleCommitFixup, Description: gui.Tr.SLocalize("fixupCommit"), + }, { + ViewName: "commits", + Key: 'F', + Modifier: gocui.ModNone, + Handler: gui.handleCreateFixupCommit, + Description: gui.Tr.SLocalize("createFixupCommit"), + }, { + ViewName: "commits", + Key: 'S', + Modifier: gocui.ModNone, + Handler: gui.handleSquashAllAboveFixupCommits, + Description: gui.Tr.SLocalize("squashAboveCommits"), }, { ViewName: "commits", Key: 'd', diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index d5c3423d5..2a5cd5551 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -700,6 +700,24 @@ func addDutch(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "hardReset", Other: "hard reset", + }, &i18n.Message{ + ID: "createFixupCommit", + Other: `create fixup commit for this commit`, + }, &i18n.Message{ + ID: "squashAboveCommits", + Other: `squash above commits`, + }, &i18n.Message{ + ID: "SquashAboveCommits", + Other: `Squash above commits`, + }, &i18n.Message{ + ID: "SureSquashAboveCommits", + Other: `Are you sure you want to squash all fixup! commits above {{.commit}}?`, + }, &i18n.Message{ + ID: "CreateFixupCommit", + Other: `Create fixup commit`, + }, &i18n.Message{ + ID: "SureCreateFixupCommit", + Other: `Are you sure you want to create a fixup! commit for commit {{.commit}}?`, }, ) } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 5d838cb89..7cebb04d0 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -723,6 +723,24 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "viewResetOptions", Other: `view reset options`, + }, &i18n.Message{ + ID: "createFixupCommit", + Other: `create fixup commit for this commit`, + }, &i18n.Message{ + ID: "squashAboveCommits", + Other: `squash above commits`, + }, &i18n.Message{ + ID: "SquashAboveCommits", + Other: `Squash above commits`, + }, &i18n.Message{ + ID: "SureSquashAboveCommits", + Other: `Are you sure you want to squash all fixup! commits above {{.commit}}?`, + }, &i18n.Message{ + ID: "CreateFixupCommit", + Other: `Create fixup commit`, + }, &i18n.Message{ + ID: "SureCreateFixupCommit", + Other: `Are you sure you want to create a fixup! commit for commit {{.commit}}?`, }, ) } diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index 38ef84489..1eb2486dc 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -683,6 +683,24 @@ func addPolish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "viewResetOptions", Other: `view reset options`, + }, &i18n.Message{ + ID: "createFixupCommit", + Other: `create fixup commit for this commit`, + }, &i18n.Message{ + ID: "squashAboveCommits", + Other: `squash above commits`, + }, &i18n.Message{ + ID: "SquashAboveCommits", + Other: `Squash above commits`, + }, &i18n.Message{ + ID: "SureSquashAboveCommits", + Other: `Are you sure you want to squash all fixup! commits above {{.commit}}?`, + }, &i18n.Message{ + ID: "CreateFixupCommit", + Other: `Create fixup commit`, + }, &i18n.Message{ + ID: "SureCreateFixupCommit", + Other: `Are you sure you want to create a fixup! commit for commit {{.commit}}?`, }, ) }