1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-15 11:56:37 +02:00

When checking out a remote branch by name, ask the user how

The choices are to create a new local branch that tracks the remote, or a
detached head.
This commit is contained in:
Stefan Haller 2024-03-15 20:56:44 +01:00
parent 0360b82aab
commit e42cbf95ae
4 changed files with 94 additions and 0 deletions

View File

@ -28,6 +28,17 @@ func (self *BranchCommands) New(name string, base string) error {
return self.cmd.New(cmdArgs).Run()
}
// CreateWithUpstream creates a new branch with a given upstream, but without
// checking it out
func (self *BranchCommands) CreateWithUpstream(name string, upstream string) error {
cmdArgs := NewGitCmd("branch").
Arg("--track").
Arg(name, upstream).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// CurrentBranchInfo get the current branch information.
func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
branchName, err := self.cmd.New(

View File

@ -436,6 +436,10 @@ func (self *BranchesController) checkoutByName() error {
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetRefsSuggestionsFunc(),
HandleConfirm: func(response string) error {
self.c.LogAction("Checkout branch")
_, branchName, found := self.c.Helpers().Refs.ParseRemoteBranchName(response)
if found {
return self.c.Helpers().Refs.CheckoutRemoteBranch(response, branchName)
}
return self.c.Helpers().Refs.CheckoutRef(response, types.CheckoutRefOptions{
OnRefNotFound: func(ref string) error {
return self.c.Confirm(types.ConfirmOpts{

View File

@ -96,6 +96,57 @@ func (self *RefsHelper) CheckoutRef(ref string, options types.CheckoutRefOptions
})
}
// Shows a prompt to choose between creating a new branch or checking out a detached head
func (self *RefsHelper) CheckoutRemoteBranch(fullBranchName string, localBranchName string) error {
checkout := func(branchName string) error {
if err := self.CheckoutRef(branchName, types.CheckoutRefOptions{}); err != nil {
return err
}
if self.c.CurrentContext() != self.c.Contexts().Branches {
return self.c.PushContext(self.c.Contexts().Branches)
}
return nil
}
// If a branch with this name already exists locally, just check it out. We
// don't bother checking whether it actually tracks this remote branch, since
// it's very unlikely that it doesn't.
if lo.ContainsBy(self.c.Model().Branches, func(branch *models.Branch) bool {
return branch.Name == localBranchName
}) {
return checkout(localBranchName)
}
return self.c.Menu(types.CreateMenuOptions{
Title: utils.ResolvePlaceholderString(self.c.Tr.RemoteBranchCheckoutTitle, map[string]string{
"branchName": fullBranchName,
}),
Items: []*types.MenuItem{
{
Label: self.c.Tr.CheckoutTypeNewBranch,
Tooltip: self.c.Tr.CheckoutTypeNewBranchTooltip,
OnPress: func() error {
// First create the local branch with the upstream set, and
// then check it out. We could do that in one step using
// "git checkout -b", but we want to benefit from all the
// nice features of the CheckoutRef function.
if err := self.c.Git().Branch.CreateWithUpstream(localBranchName, fullBranchName); err != nil {
return self.c.Error(err)
}
return checkout(localBranchName)
},
},
{
Label: self.c.Tr.CheckoutTypeDetachedHead,
Tooltip: self.c.Tr.CheckoutTypeDetachedHeadTooltip,
OnPress: func() error {
return checkout(fullBranchName)
},
},
},
})
}
func (self *RefsHelper) GetCheckedOutRef() *models.Branch {
if len(self.c.Model().Branches) == 0 {
return nil
@ -232,3 +283,21 @@ func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggest
func SanitizedBranchName(input string) string {
return strings.Replace(input, " ", "-", -1)
}
// Checks if the given branch name is a remote branch, and returns the name of
// the remote and the bare branch name if it is.
func (self *RefsHelper) ParseRemoteBranchName(fullBranchName string) (string, string, bool) {
remoteName, branchName, found := strings.Cut(fullBranchName, "/")
if !found {
return "", "", false
}
// See if the part before the first slash is actually one of our remotes
if !lo.ContainsBy(self.c.Model().Remotes, func(remote *models.Remote) bool {
return remote.Name == remoteName
}) {
return "", "", false
}
return remoteName, branchName, true
}

View File

@ -113,6 +113,11 @@ type TranslationSet struct {
ForceCheckoutTooltip string
CheckoutByName string
CheckoutByNameTooltip string
RemoteBranchCheckoutTitle string
CheckoutTypeNewBranch string
CheckoutTypeNewBranchTooltip string
CheckoutTypeDetachedHead string
CheckoutTypeDetachedHeadTooltip string
NewBranch string
NewBranchFromStashTooltip string
NoBranchesThisRepo string
@ -1065,6 +1070,11 @@ func EnglishTranslationSet() TranslationSet {
ForceCheckoutTooltip: "Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch.",
CheckoutByName: "Checkout by name",
CheckoutByNameTooltip: "Checkout by name. In the input box you can enter '-' to switch to the last branch.",
RemoteBranchCheckoutTitle: "Checkout {{.branchName}}",
CheckoutTypeNewBranch: "New local branch",
CheckoutTypeNewBranchTooltip: "Checkout the remote branch as a local branch, tracking the remote branch.",
CheckoutTypeDetachedHead: "Detached head",
CheckoutTypeDetachedHeadTooltip: "Checkout the remote branch as a detached head, which can be useful if you just want to test the branch but not work on it yourself. You can still create a local branch from it later.",
NewBranch: "New branch",
NewBranchFromStashTooltip: "Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit.",
NoBranchesThisRepo: "No branches for this repo",