Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to switch branches in Commit View (#4115) #4117

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions pkg/gui/controllers/basic_commits_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
48 changes: 48 additions & 0 deletions pkg/gui/controllers/helpers/refs_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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},
stefanhaller marked this conversation as resolved.
Show resolved Hide resolved
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,
Expand Down
38 changes: 23 additions & 15 deletions pkg/i18n/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ type TranslationSet struct {
FetchingRemoteStatus string
CheckoutCommit string
CheckoutCommitTooltip string
NoBranchesFoundAtCommitTooltip string
SureCheckoutThisCommit string
GitFlowOptions string
NotAGitFlowBranch string
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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?",
Expand Down Expand Up @@ -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",
Expand Down
69 changes: 69 additions & 0 deletions pkg/integration/tests/commit/checkout.go
Original file line number Diff line number Diff line change
@@ -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"),
)
},
})
6 changes: 3 additions & 3 deletions pkg/integration/tests/reflog/checkout.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions pkg/integration/tests/test_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ var tests = []*components.IntegrationTest{
commit.AddCoAuthorWhileCommitting,
commit.Amend,
commit.AutoWrapMessage,
commit.Checkout,
commit.Commit,
commit.CommitMultiline,
commit.CommitSwitchToEditor,
Expand Down
Loading