mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-05 21:46:49 -04:00
Keep copied commits when switching to another worktree or repo
If you copy commits with shift-C and then switch to another worktree, the copied commits are gone, so you can't paste them there with shift-V. This is a common workflow: copy commits from the branch that is checked out in one worktree, maybe drop them there right away, then switch to the worktree of another branch and paste them. The copied commits live in the cherry-picking mode, and each repo state creates its own. We keep a separate repo state per worktree, so every worktree starts with an empty clipboard. Create the cherry-picking mode once and share it between all repo states. This also lets you paste in a different clone of the same repo, as long as that clone has the copied commits (for example because you fetched them there). If the current repo doesn't have them, git cherry-pick fails with "bad object", and we show this as an error. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
fc85e9353c
commit
1f05a6b152
+7
-1
@@ -73,6 +73,11 @@ type Gui struct {
|
||||
|
||||
CustomCommandsClient *custom_commands.Client
|
||||
|
||||
// Unlike the other modes, the cherry-picking mode is shared by all repo
|
||||
// states, so that you can copy commits in one worktree or repo and paste
|
||||
// them in another.
|
||||
cherryPicking *cherrypicking.CherryPicking
|
||||
|
||||
// this is a mapping of repos to gui states, so that we can restore the original
|
||||
// gui state when returning from a subrepo.
|
||||
// In repos with multiple worktrees, we store a separate repo state per worktree.
|
||||
@@ -647,7 +652,7 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
|
||||
},
|
||||
Modes: &types.Modes{
|
||||
Filtering: filtering.New(startArgs.FilterPath, ""),
|
||||
CherryPicking: cherrypicking.New(),
|
||||
CherryPicking: gui.cherryPicking,
|
||||
Diffing: diffing.New(),
|
||||
MarkedBaseCommit: marked_base_commit.New(),
|
||||
},
|
||||
@@ -800,6 +805,7 @@ func NewGui(
|
||||
showRecentRepos: showRecentRepos,
|
||||
RepoPathStack: &utils.Stack[types.RepoLocation]{},
|
||||
RepoStateMap: map[Repo]*GuiRepoState{},
|
||||
cherryPicking: cherrypicking.New(),
|
||||
GuiLog: []string{},
|
||||
|
||||
// initializing this to true for the time being; it will be reset to the
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
type Modes struct {
|
||||
Filtering filtering.Filtering
|
||||
CherryPicking *cherrypicking.CherryPicking
|
||||
CherryPicking *cherrypicking.CherryPicking // shared by all repo states; see Gui.cherryPicking
|
||||
Diffing diffing.Diffing
|
||||
MarkedBaseCommit marked_base_commit.MarkedBaseCommit
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package cherry_pick
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var CherryPickAcrossWorktrees = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Copy commits in one worktree, drop them there, and paste them in another worktree",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
EmptyCommit("base").
|
||||
AddWorktree("master", "../linked-worktree", "newbranch").
|
||||
EmptyCommit("one").
|
||||
EmptyCommit("two")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("two").IsSelected(),
|
||||
Contains("one"),
|
||||
Contains("base"),
|
||||
).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
Press(keys.Commits.CherryPickCopy).
|
||||
Tap(func() {
|
||||
t.Views().Information().Content(Contains("2 commits copied"))
|
||||
}).
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Drop commit")).
|
||||
Content(Contains("Are you sure you want to drop the selected commit(s)?")).
|
||||
Confirm()
|
||||
}).
|
||||
Lines(
|
||||
Contains("base").IsSelected(),
|
||||
)
|
||||
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
NavigateToLine(Contains("newbranch")).
|
||||
Press(keys.Universal.Select).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Switch to worktree")).
|
||||
Content(Equals("This branch is checked out by worktree linked-worktree. Do you want to switch to that worktree?")).
|
||||
Confirm()
|
||||
}).
|
||||
Lines(
|
||||
Contains("newbranch").IsSelected(),
|
||||
Contains("master (worktree repo)"),
|
||||
)
|
||||
|
||||
t.Views().Information().Content(Contains("2 commits copied"))
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("base").IsSelected(),
|
||||
).
|
||||
Press(keys.Commits.PasteCommits).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Cherry-pick")).
|
||||
Content(Contains("Are you sure you want to cherry-pick the 2 copied commit(s) onto this branch?")).
|
||||
Confirm()
|
||||
}).
|
||||
Lines(
|
||||
Contains("two"),
|
||||
Contains("one"),
|
||||
Contains("base").IsSelected(),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -93,6 +93,7 @@ var tests = []*components.IntegrationTest{
|
||||
branch.Suggestions,
|
||||
branch.UnsetUpstream,
|
||||
cherry_pick.CherryPick,
|
||||
cherry_pick.CherryPickAcrossWorktrees,
|
||||
cherry_pick.CherryPickCommitThatBecomesEmpty,
|
||||
cherry_pick.CherryPickConflicts,
|
||||
cherry_pick.CherryPickConflictsEmptyCommitAfterResolving,
|
||||
|
||||
Reference in New Issue
Block a user