summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/commands/commit.go12
-rw-r--r--pkg/commands/git.go15
-rw-r--r--pkg/git/commit_list_builder.go46
-rw-r--r--pkg/gui/commits_panel.go68
-rw-r--r--pkg/gui/gui.go12
-rw-r--r--pkg/gui/keybindings.go19
-rw-r--r--pkg/i18n/english.go15
7 files changed, 169 insertions, 18 deletions
diff --git a/pkg/commands/commit.go b/pkg/commands/commit.go
index 1df6afbd0..4253a5495 100644
--- a/pkg/commands/commit.go
+++ b/pkg/commands/commit.go
@@ -12,6 +12,7 @@ type Commit struct {
Status string // one of "unpushed", "pushed", "merged", or "rebasing"
DisplayString string
Action string // one of "", "pick", "edit", "squash", "reword", "drop", "fixup"
+ Copied bool // to know if this commit is ready to be cherry-picked somewhere
}
// GetDisplayStrings is a function.
@@ -19,9 +20,14 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
- white := color.New(color.FgWhite)
blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan)
+ white := color.New(color.FgWhite)
+
+ // for some reason, setting the background to blue pads out the other commits
+ // horizontally. For the sake of accessibility I'm considering this a feature,
+ // not a bug
+ copied := color.New(color.FgCyan, color.BgBlue)
var shaColor *color.Color
switch c.Status {
@@ -37,6 +43,10 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
shaColor = white
}
+ if c.Copied {
+ shaColor = copied
+ }
+
actionString := ""
if c.Action != "" {
actionString = cyan.Sprint(utils.WithPadding(c.Action, 7)) + " "
diff --git a/pkg/commands/git.go b/pkg/commands/git.go
index 7b2c2f2fa..7127a427e 100644
--- a/pkg/commands/git.go
+++ b/pkg/commands/git.go
@@ -745,3 +745,18 @@ func (c *GitCommand) MoveTodoDown(index int) error {
func (c *GitCommand) Revert(sha string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git revert %s", sha))
}
+
+// CherryPickShas begins an interactive rebase with the given shas being cherry picked onto HEAD
+func (c *GitCommand) CherryPickShas(shas []string) error {
+ todo := ""
+ for _, sha := range shas {
+ todo = "pick " + sha + "\n" + todo
+ }
+
+ cmd, err := c.PrepareInteractiveRebaseCommand("HEAD", todo, false)
+ if err != nil {
+ return err
+ }
+
+ return c.OSCommand.RunPreparedCommand(cmd)
+}
diff --git a/pkg/git/commit_list_builder.go b/pkg/git/commit_list_builder.go
index ffb97c53b..3de255d02 100644
--- a/pkg/git/commit_list_builder.go
+++ b/pkg/git/commit_list_builder.go
@@ -23,19 +23,21 @@ import (
// CommitListBuilder returns a list of Branch objects for the current repo
type CommitListBuilder struct {
- Log *logrus.Entry
- GitCommand *commands.GitCommand
- OSCommand *commands.OSCommand
- Tr *i18n.Localizer
+ Log *logrus.Entry
+ GitCommand *commands.GitCommand
+ OSCommand *commands.OSCommand
+ Tr *i18n.Localizer
+ CherryPickedShas []string
}
// NewCommitListBuilder builds a new commit list builder
-func NewCommitListBuilder(log *logrus.Entry, gitCommand *commands.GitCommand, osCommand *commands.OSCommand, tr *i18n.Localizer) (*CommitListBuilder, error) {
+func NewCommitListBuilder(log *logrus.Entry, gitCommand *commands.GitCommand, osCommand *commands.OSCommand, tr *i18n.Localizer, cherryPickedShas []string) (*CommitListBuilder, error) {
return &CommitListBuilder{
- Log: log,
- GitCommand: gitCommand,
- OSCommand: osCommand,
- Tr: tr,
+ Log: log,
+ GitCommand: gitCommand,
+ OSCommand: osCommand,
+ Tr: tr,
+ CherryPickedShas: cherryPickedShas,
}, nil
}
@@ -80,7 +82,18 @@ func (c *CommitListBuilder) GetCommits() ([]*commands.Commit, error) {
youAreHere := blue.Sprintf("<-- %s ---", c.Tr.SLocalize("YouAreHere"))
currentCommit.Name = fmt.Sprintf("%s %s", youAreHere, currentCommit.Name)
}
- return c.setCommitMergedStatuses(commits)
+
+ commits, err = c.setCommitMergedStatuses(commits)
+ if err != nil {
+ return nil, err
+ }
+
+ commits, err = c.setCommitCherryPickStatuses(commits)
+ if err != nil {
+ return nil, err
+ }
+
+ return commits, nil
}
// git-rebase-todo example:
@@ -106,7 +119,7 @@ func (c *CommitListBuilder) getRebasingCommits() ([]*commands.Commit, error) {
commits := []*commands.Commit{}
lines := strings.Split(string(bytesContent), "\n")
for _, line := range lines {
- if line == "" {
+ if line == "" || line == "noop" {
return commits, nil
}
splitLine := strings.Split(line, " ")
@@ -144,6 +157,17 @@ func (c *CommitListBuilder) setCommitMergedStatuses(commits []*commands.Commit)
return commits, nil
}
+func (c *CommitListBuilder) setCommitCherryPickStatuses(commits []*commands.Commit) ([]*commands.Commit, error) {
+ for _, commit := range commits {
+ for _, sha := range c.CherryPickedShas {
+ if commit.Sha == sha {
+ commit.Copied = true
+ }
+ }
+ }
+ return commits, nil
+}
+
func (c *CommitListBuilder) getMergeBase() (string, error) {
currentBranch, err := c.GitCommand.CurrentBranchName()
if err != nil {
diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go
index 383f9b6ba..37b7538f7 100644
--- a/pkg/gui/commits_panel.go
+++ b/pkg/gui/commits_panel.go
@@ -2,6 +2,8 @@ package gui
import (
"fmt"
+ "strconv"
+ "strings"
"github.com/go-errors/errors"
@@ -40,7 +42,7 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
g.Update(func(*gocui.Gui) error {
- builder, err := git.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr)
+ builder, err := git.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr, gui.State.CherryPickedShas)
if err != nil {
return err
}
@@ -347,3 +349,67 @@ func (gui *Gui) handleCommitRevert(g *gocui.Gui, v *gocui.View) error {
gui.State.Panels.Commits.SelectedLine++
return gui.refreshCommits(gui.g)
}
+
+func (gui *Gui) handleCopyCommit(g *gocui.Gui, v *gocui.View) error {
+ // get currently selected commit, add the sha to state.
+ sha := gui.State.Commits[gui.State.Panels.Commits.SelectedLine].Sha
+
+ // we will un-copy it if it's already copied
+ for index, cherryPickedSha := range gui.State.CherryPickedShas {
+ if sha == cherryPickedSha {
+ gui.State.CherryPickedShas = append(gui.State.CherryPickedShas[0:index], gui.State.CherryPickedShas[index+1:]...)
+ gui.Log.Info("removed copied sha. New shas:\n" + strings.Join(gui.State.CherryPickedShas, "\n"))
+ return gui.refreshCommits(gui.g)
+ }
+ }
+
+ gui.addCommitToCherryPickedShas(gui.State.Panels.Commits.SelectedLine)
+ return gui.refreshCommits(gui.g)
+}
+
+func (gui *Gui) addCommitToCherryPickedShas(index int) {
+ defer func() { gui.Log.Info("new copied shas:\n" + strings.Join(gui.State.CherryPickedShas, "\n")) }()
+
+ // not super happy with modifying the state of the Commits array here
+ // but the alternative would be very tricky
+ gui.State.Commits[index].Copied = true
+
+ newShas := []string{}
+ for _, commit := range gui.State.Commits {
+ if commit.Copied {
+ newShas = append(newShas, commit.Sha)
+ }
+ }
+
+ gui.State.CherryPickedShas = newShas
+}
+
+func (gui *Gui) handleCopyCommitRange(g *gocui.Gui, v *gocui.View) error {
+ // whenever I add a commit, I need to make sure I retain its order
+
+ // find the last commit that is copied that's above our position
+ // if there are none, startIndex = 0
+ startIndex := 0
+ for index, commit := range gui.State.Commits[0:gui.State.Panels.Commits.SelectedLine] {
+ if commit.Copied {
+ startIndex = index
+ }
+ }
+
+ gui.Log.Info("commit copy start index: " + strconv.Itoa(startIndex))
+
+ for index := startIndex; index <= gui.State.Panels.Commits.SelectedLine; index++ {
+ gui.addCommitToCherryPickedShas(index)
+ }
+
+ return gui.refreshCommits(gui.g)
+}
+
+// HandlePasteCommits begins a cherry-pick rebase with the commits the user has copied
+func (gui *Gui) HandlePasteCommits(g *gocui.Gui, v *gocui.View) error {
+ return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("CherryPick"), gui.Tr.SLocalize("SureCherryPick"), func(g *gocui.Gui, v *gocui.View) error {
+ err := gui.GitCommand.CherryPickShas(gui.State.CherryPickedShas)
+ return gui.handleGenericMergeCommandResult(err)
+ }, nil)
+
+}
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 11daec652..2103856fd 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -133,17 +133,19 @@ type guiState struct {
Panels *panelStates
WorkingTreeState string // one of "merging", "rebasing", "normal"
Contexts map[string]string
+ CherryPickedShas []string
}
// NewGui builds a new gui handler
func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer, updater *updates.Updater) (*Gui, error) {
initialState := guiState{
- Files: make([]*commands.File, 0),
- PreviousView: "files",
- Commits: make([]*commands.Commit, 0),
- StashEntries: make([]*commands.StashEntry, 0),
- Platform: *oSCommand.Platform,
+ Files: make([]*commands.File, 0),
+ PreviousView: "files",
+ Commits: make([]*commands.Commit, 0),
+ CherryPickedShas: []string{},
+ StashEntries: make([]*commands.StashEntry, 0),
+ Platform: *oSCommand.Platform,
Panels: &panelStates{
Files: &filePanelState{SelectedLine: -1},
Branches: &branchPanelState{SelectedLine: 0},
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index ab70616ea..3992513ef 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -359,12 +359,31 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleCommitRevert,
Description: gui.Tr.SLocalize("revertCommit"),
}, {
+ ViewName: "commits",
+ Key: 'c',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleCopyCommit,
+ Description: gui.Tr.SLocalize("cherryPickCopy"),
+ }, {
+ ViewName: "commits",
+ Key: 'C',
+ Modifier: gocui.ModNone,
+ Handler: gui.handleCopyCommitRange,
+ Description: gui.Tr.SLocalize("cherryPickCopyRange"),
+ }, {
+ ViewName: "commits",
+ Key: 'v',
+ Modifier: gocui.ModNone,
+ Handler: gui.HandlePasteCommits,
+ Description: gui.Tr.SLocalize("pasteCommits"),
+ }, {
ViewName: "stash",
Key: gocui.KeySpace,
Modifier: gocui.ModNone,
Handler: gui.handleStashApply,
Description: gui.Tr.SLocalize("apply"),
}, {
+
ViewName: "stash",
Key: 'g',
Modifier: gocui.ModNone,
diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go
index f302cd650..626d6d4cc 100644
--- a/pkg/i18n/english.go
+++ b/pkg/i18n/english.go
@@ -571,6 +571,21 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "rewordNotSupported",
Other: "rewording commits while interactively rebasing is not currently supported",
+ }, &i18n.Message{
+ ID: "cherryPickCopy",
+ Other: "copy commit (cherry-pick)",
+ }, &i18n.Message{
+ ID: "cherryPickCopyRange",
+ Other: "copy commit range (cherry-pick)",
+ }, &i18n.Message{
+ ID: "pasteCommits",
+ Other: "paste commits (cherry-pick)",
+ }, &i18n.Message{
+ ID: "SureCherryPick",
+ Other: "Are you sure you want to cherry-pick the copied commits onto this branch?",
+ }, &i18n.Message{
+ ID: "CherryPick",
+ Other: "Cherry-Pick",
},
)
}