1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-10 04:07:18 +02:00

Allow cherry-picking commits during a rebase (#3013)

This commit is contained in:
Stefan Haller 2023-09-19 07:15:10 +02:00 committed by GitHub
commit 1aae1772ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 129 additions and 9 deletions

View File

@ -457,6 +457,20 @@ func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error {
}).Run()
}
// CherryPickCommitsDuringRebase simply prepends the given commits to the existing git-rebase-todo file
func (self *RebaseCommands) CherryPickCommitsDuringRebase(commits []*models.Commit) error {
todoLines := lo.Map(commits, func(commit *models.Commit, _ int) daemon.TodoLine {
return daemon.TodoLine{
Action: "pick",
Commit: commit,
}
})
todo := daemon.TodoLinesToString(todoLines)
filePath := filepath.Join(self.repoPaths.worktreeGitDirPath, "rebase-merge/git-rebase-todo")
return utils.PrependStrToTodoFile(filePath, []byte(todo))
}
// we can't start an interactive rebase from the first commit without passing the
// '--root' arg
func getBaseShaOrRoot(commits []*models.Commit, index int) string {

View File

@ -25,19 +25,16 @@ func NewStatusCommands(
// RebaseMode returns "" for non-rebase mode, "normal" for normal rebase
// and "interactive" for interactive rebase
func (self *StatusCommands) RebaseMode() (enums.RebaseMode, error) {
exists, err := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply"))
if err != nil {
return enums.REBASE_MODE_NONE, err
}
if exists {
ok, err := self.IsInNormalRebase()
if err == nil && ok {
return enums.REBASE_MODE_NORMAL, nil
}
exists, err = self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge"))
if exists {
ok, err = self.IsInInteractiveRebase()
if err == nil && ok {
return enums.REBASE_MODE_INTERACTIVE, err
} else {
return enums.REBASE_MODE_NONE, err
}
return enums.REBASE_MODE_NONE, err
}
func (self *StatusCommands) WorkingTreeState() enums.RebaseMode {
@ -68,6 +65,14 @@ func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
return strconv.ParseBool(strings.TrimSpace(res))
}
func (self *StatusCommands) IsInNormalRebase() (bool, error) {
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply"))
}
func (self *StatusCommands) IsInInteractiveRebase() (bool, error) {
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge"))
}
// IsInMergeState states whether we are still mid-merge
func (self *StatusCommands) IsInMergeState() (bool, error) {
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "MERGE_HEAD"))

View File

@ -76,6 +76,19 @@ func (self *CherryPickHelper) Paste() error {
Title: self.c.Tr.CherryPick,
Prompt: self.c.Tr.SureCherryPick,
HandleConfirm: func() error {
isInRebase, err := self.c.Git().Status.IsInInteractiveRebase()
if err != nil {
return err
}
if isInRebase {
if err := self.c.Git().Rebase.CherryPickCommitsDuringRebase(self.getData().CherryPickedCommits); err != nil {
return err
}
return self.c.Refresh(types.RefreshOptions{
Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS},
})
}
return self.c.WithWaitingStatus(self.c.Tr.CherryPickingStatus, func(gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.CherryPick)
err := self.c.Git().Rebase.CherryPickCommits(self.getData().CherryPickedCommits)

View File

@ -0,0 +1,87 @@
package cherry_pick
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var CherryPickDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Cherry pick commits from the subcommits view during a rebase",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.
EmptyCommit("base").
NewBranch("first-branch").
NewBranch("second-branch").
Checkout("first-branch").
EmptyCommit("one").
EmptyCommit("two").
Checkout("second-branch").
EmptyCommit("three").
EmptyCommit("four").
Checkout("first-branch")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Branches().
Focus().
Lines(
Contains("first-branch"),
Contains("second-branch"),
Contains("master"),
).
SelectNextItem().
PressEnter()
t.Views().SubCommits().
IsFocused().
Lines(
Contains("four").IsSelected(),
Contains("three"),
Contains("base"),
).
// copy commit 'three'
SelectNextItem().
Press(keys.Commits.CherryPickCopy)
t.Views().Information().Content(Contains("1 commit copied"))
t.Views().Commits().
Focus().
Lines(
Contains("CI two").IsSelected(),
Contains("CI one"),
Contains("CI base"),
).
SelectNextItem().
Press(keys.Universal.Edit).
Lines(
Contains("pick CI two"),
Contains(" CI <-- YOU ARE HERE --- one").IsSelected(),
Contains(" CI base"),
).
Press(keys.Commits.PasteCommits).
Tap(func() {
t.ExpectPopup().Alert().
Title(Equals("Cherry-pick")).
Content(Contains("Are you sure you want to cherry-pick the copied commits onto this branch?")).
Confirm()
}).
Lines(
Contains("pick CI two"),
Contains("pick CI three"),
Contains(" CI <-- YOU ARE HERE --- one"),
Contains(" CI base"),
).
Tap(func() {
t.Common().ContinueRebase()
}).
Lines(
Contains("CI two"),
Contains("CI three"),
Contains("CI one"),
Contains("CI base"),
)
},
})

View File

@ -55,6 +55,7 @@ var tests = []*components.IntegrationTest{
branch.Suggestions,
cherry_pick.CherryPick,
cherry_pick.CherryPickConflicts,
cherry_pick.CherryPickDuringRebase,
commit.AddCoAuthor,
commit.Amend,
commit.Commit,