mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-02 23:27:32 +02:00
Allow to switch branches in Commit View
When the user checks out a commit which has a local branch ref attached to it, they can select between checking out the branch or checking out the commit as detached head.
This commit is contained in:
parent
03d7bc854e
commit
f4c8287143
@ -280,15 +280,7 @@ func (self *BasicCommitsController) createResetMenu(commit *models.Commit) error
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) checkout(commit *models.Commit) error {
|
||||
self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.CheckoutCommit,
|
||||
Prompt: self.c.Tr.SureCheckoutThisCommit,
|
||||
HandleConfirm: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.CheckoutCommit)
|
||||
return self.c.Helpers().Refs.CheckoutRef(commit.Hash, types.CheckoutRefOptions{})
|
||||
},
|
||||
})
|
||||
return nil
|
||||
return self.c.Helpers().Refs.CreateCheckoutMenu(commit)
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copyRange(*models.Commit) error {
|
||||
|
@ -18,6 +18,7 @@ type IRefsHelper interface {
|
||||
CheckoutRef(ref string, options types.CheckoutRefOptions) error
|
||||
GetCheckedOutRef() *models.Branch
|
||||
CreateGitResetMenu(ref string) error
|
||||
CreateCheckoutMenu(commit *models.Commit) error
|
||||
ResetToRef(ref string, strength string, envVars []string) error
|
||||
NewBranch(from string, fromDescription string, suggestedBranchname string) error
|
||||
}
|
||||
@ -271,6 +272,53 @@ func (self *RefsHelper) CreateGitResetMenu(ref string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RefsHelper) CreateCheckoutMenu(commit *models.Commit) error {
|
||||
branches := lo.Filter(self.c.Model().Branches, func(branch *models.Branch, _ int) bool {
|
||||
return commit.Hash == branch.CommitHash && branch.Name != self.c.Model().CheckedOutBranch
|
||||
})
|
||||
|
||||
hash := commit.Hash
|
||||
var menuItems []*types.MenuItem
|
||||
|
||||
if len(branches) > 0 {
|
||||
menuItems = append(menuItems, lo.Map(branches, func(branch *models.Branch, index int) *types.MenuItem {
|
||||
var key types.Key
|
||||
if index < 9 {
|
||||
key = rune(index + 1 + '0') // Convert 1-based index to key
|
||||
}
|
||||
return &types.MenuItem{
|
||||
LabelColumns: []string{fmt.Sprintf(self.c.Tr.Actions.CheckoutBranchAtCommit, branch.Name)},
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.CheckoutBranch)
|
||||
return self.CheckoutRef(branch.RefName(), types.CheckoutRefOptions{})
|
||||
},
|
||||
Key: key,
|
||||
}
|
||||
})...)
|
||||
} else {
|
||||
menuItems = append(menuItems, &types.MenuItem{
|
||||
LabelColumns: []string{self.c.Tr.Actions.CheckoutBranch},
|
||||
OnPress: func() error { return nil },
|
||||
DisabledReason: &types.DisabledReason{Text: self.c.Tr.NoBranchesFoundAtCommitTooltip},
|
||||
Key: '1',
|
||||
})
|
||||
}
|
||||
|
||||
menuItems = append(menuItems, &types.MenuItem{
|
||||
LabelColumns: []string{fmt.Sprintf(self.c.Tr.Actions.CheckoutCommitAsDetachedHead, utils.ShortHash(hash))},
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.CheckoutCommit)
|
||||
return self.CheckoutRef(hash, types.CheckoutRefOptions{})
|
||||
},
|
||||
Key: 'd',
|
||||
})
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
Title: self.c.Tr.Actions.CheckoutBranchOrCommit,
|
||||
Items: menuItems,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggestedBranchName string) error {
|
||||
message := utils.ResolvePlaceholderString(
|
||||
self.c.Tr.NewBranchNameBranchOff,
|
||||
|
@ -527,6 +527,7 @@ type TranslationSet struct {
|
||||
FetchingRemoteStatus string
|
||||
CheckoutCommit string
|
||||
CheckoutCommitTooltip string
|
||||
NoBranchesFoundAtCommitTooltip string
|
||||
SureCheckoutThisCommit string
|
||||
GitFlowOptions string
|
||||
NotAGitFlowBranch string
|
||||
@ -860,8 +861,11 @@ type Log struct {
|
||||
|
||||
type Actions struct {
|
||||
CheckoutCommit string
|
||||
CheckoutBranchAtCommit string
|
||||
CheckoutCommitAsDetachedHead string
|
||||
CheckoutTag string
|
||||
CheckoutBranch string
|
||||
CheckoutBranchOrCommit string
|
||||
ForceCheckoutBranch string
|
||||
DeleteLocalBranch string
|
||||
Merge string
|
||||
@ -1522,21 +1526,22 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
DeleteRemoteTagPrompt: "Are you sure you want to delete the remote tag '{{.tagName}}' from '{{.upstream}}'?",
|
||||
PushTagTitle: "Remote to push tag '{{.tagName}}' to:",
|
||||
// Using 'push tag' rather than just 'push' to disambiguate from a global push
|
||||
PushTag: "Push tag",
|
||||
PushTagTooltip: "Push the selected tag to a remote. You'll be prompted to select a remote.",
|
||||
NewTag: "New tag",
|
||||
NewTagTooltip: "Create new tag from current commit. You'll be prompted to enter a tag name and optional description.",
|
||||
CreatingTag: "Creating tag",
|
||||
ForceTag: "Force Tag",
|
||||
ForceTagPrompt: "The tag '{{.tagName}}' exists already. Press {{.cancelKey}} to cancel, or {{.confirmKey}} to overwrite.",
|
||||
FetchRemoteTooltip: "Fetch updates from the remote repository. This retrieves new commits and branches without merging them into your local branches.",
|
||||
FetchingRemoteStatus: "Fetching remote",
|
||||
CheckoutCommit: "Checkout commit",
|
||||
CheckoutCommitTooltip: "Checkout the selected commit as a detached HEAD.",
|
||||
SureCheckoutThisCommit: "Are you sure you want to checkout this commit?",
|
||||
GitFlowOptions: "Show git-flow options",
|
||||
NotAGitFlowBranch: "This does not seem to be a git flow branch",
|
||||
NewGitFlowBranchPrompt: "New {{.branchType}} name:",
|
||||
PushTag: "Push tag",
|
||||
PushTagTooltip: "Push the selected tag to a remote. You'll be prompted to select a remote.",
|
||||
NewTag: "New tag",
|
||||
NewTagTooltip: "Create new tag from current commit. You'll be prompted to enter a tag name and optional description.",
|
||||
CreatingTag: "Creating tag",
|
||||
ForceTag: "Force Tag",
|
||||
ForceTagPrompt: "The tag '{{.tagName}}' exists already. Press {{.cancelKey}} to cancel, or {{.confirmKey}} to overwrite.",
|
||||
FetchRemoteTooltip: "Fetch updates from the remote repository. This retrieves new commits and branches without merging them into your local branches.",
|
||||
FetchingRemoteStatus: "Fetching remote",
|
||||
CheckoutCommit: "Checkout commit",
|
||||
CheckoutCommitTooltip: "Checkout the selected commit as a detached HEAD.",
|
||||
NoBranchesFoundAtCommitTooltip: "No branches found at selected commit.",
|
||||
SureCheckoutThisCommit: "Are you sure you want to checkout this commit?",
|
||||
GitFlowOptions: "Show git-flow options",
|
||||
NotAGitFlowBranch: "This does not seem to be a git flow branch",
|
||||
NewGitFlowBranchPrompt: "New {{.branchType}} name:",
|
||||
|
||||
IgnoreTracked: "Ignore tracked file",
|
||||
IgnoreTrackedPrompt: "Are you sure you want to ignore a tracked file?",
|
||||
@ -1822,9 +1827,12 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
Actions: Actions{
|
||||
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
|
||||
CheckoutCommit: "Checkout commit",
|
||||
CheckoutBranchAtCommit: "Checkout branch '%s'",
|
||||
CheckoutCommitAsDetachedHead: "Checkout commit %s as detached head",
|
||||
CheckoutTag: "Checkout tag",
|
||||
CheckoutBranch: "Checkout branch",
|
||||
ForceCheckoutBranch: "Force checkout branch",
|
||||
CheckoutBranchOrCommit: "Checkout branch or commit",
|
||||
DeleteLocalBranch: "Delete local branch",
|
||||
Merge: "Merge",
|
||||
SquashMerge: "Squash merge",
|
||||
|
69
pkg/integration/tests/commit/checkout.go
Normal file
69
pkg/integration/tests/commit/checkout.go
Normal file
@ -0,0 +1,69 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var Checkout = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Checkout a commit as a detached head, or checkout an existing branch at a commit",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("one")
|
||||
shell.EmptyCommit("two")
|
||||
shell.NewBranch("branch1")
|
||||
shell.NewBranch("branch2")
|
||||
shell.EmptyCommit("three")
|
||||
shell.EmptyCommit("four")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("four").IsSelected(),
|
||||
Contains("three"),
|
||||
Contains("two"),
|
||||
Contains("one"),
|
||||
).
|
||||
PressPrimaryAction()
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Contains("Checkout branch or commit")).
|
||||
Lines(
|
||||
Contains("Checkout branch").IsSelected(),
|
||||
MatchesRegexp("Checkout commit [a-f0-9]+ as detached head"),
|
||||
Contains("Cancel"),
|
||||
).
|
||||
Tooltip(Contains("Disabled: No branches found at selected commit.")).
|
||||
Select(MatchesRegexp("Checkout commit [a-f0-9]+ as detached head")).
|
||||
Confirm()
|
||||
t.Views().Branches().Lines(
|
||||
Contains("* (HEAD detached at"),
|
||||
Contains("branch2"),
|
||||
Contains("branch1"),
|
||||
Contains("master"),
|
||||
)
|
||||
|
||||
t.Views().Commits().
|
||||
NavigateToLine(Contains("two")).
|
||||
PressPrimaryAction()
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Contains("Checkout branch or commit")).
|
||||
Lines(
|
||||
Contains("Checkout branch 'branch1'").IsSelected(),
|
||||
Contains("Checkout branch 'master'"),
|
||||
MatchesRegexp("Checkout commit [a-f0-9]+ as detached head"),
|
||||
Contains("Cancel"),
|
||||
).
|
||||
Select(Contains("Checkout branch 'master'")).
|
||||
Confirm()
|
||||
t.Views().Branches().Lines(
|
||||
Contains("master"),
|
||||
Contains("branch2"),
|
||||
Contains("branch1"),
|
||||
)
|
||||
},
|
||||
})
|
@ -28,9 +28,9 @@ var Checkout = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
SelectNextItem().
|
||||
PressPrimaryAction().
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Contains("Checkout commit")).
|
||||
Content(Contains("Are you sure you want to checkout this commit?")).
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Contains("Checkout branch or commit")).
|
||||
Select(MatchesRegexp("Checkout commit [a-f0-9]+ as detached head")).
|
||||
Confirm()
|
||||
}).
|
||||
TopLines(
|
||||
|
@ -84,6 +84,7 @@ var tests = []*components.IntegrationTest{
|
||||
commit.AddCoAuthorWhileCommitting,
|
||||
commit.Amend,
|
||||
commit.AutoWrapMessage,
|
||||
commit.Checkout,
|
||||
commit.Commit,
|
||||
commit.CommitMultiline,
|
||||
commit.CommitSwitchToEditor,
|
||||
|
Loading…
x
Reference in New Issue
Block a user