cli/commands/mr/checkout/mr_checkout.go

129 lines
3.8 KiB
Go

package checkout
import (
"fmt"
"strings"
"github.com/MakeNowJust/heredoc"
"github.com/spf13/cobra"
"github.com/xanzy/go-gitlab"
"gitlab.com/gitlab-org/cli/api"
"gitlab.com/gitlab-org/cli/commands/cmdutils"
"gitlab.com/gitlab-org/cli/commands/mr/mrutils"
"gitlab.com/gitlab-org/cli/pkg/git"
)
type mrCheckoutConfig struct {
branch string
track bool
upstream string
}
var mrCheckoutCfg mrCheckoutConfig
func NewCmdCheckout(f *cmdutils.Factory) *cobra.Command {
mrCheckoutCmd := &cobra.Command{
Use: "checkout [<id> | <branch>]",
Short: "Checkout to an open merge request",
Long: ``,
Example: heredoc.Doc(`
glab mr checkout 1
glab mr checkout branch
glab mr checkout 12 --branch todo-fix
glab mr checkout new-feature --set-upstream-to=upstream/main
glab mr checkout # use checked out branch
`),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
var err error
var upstream string
if mrCheckoutCfg.upstream != "" {
upstream = mrCheckoutCfg.upstream
if val := strings.Split(mrCheckoutCfg.upstream, "/"); len(val) > 1 {
// Verify that we have the remote set
repo, err := f.Remotes()
if err != nil {
return err
}
_, err = repo.FindByName(val[0])
if err != nil {
return err
}
}
}
apiClient, err := f.HttpClient()
if err != nil {
return err
}
mr, _, err := mrutils.MRFromArgs(f, args, "any")
if err != nil {
return err
}
if mrCheckoutCfg.branch == "" {
mrCheckoutCfg.branch = mr.SourceBranch
}
var mrRef string
var mrProject *gitlab.Project
mrProject, err = api.GetProject(apiClient, mr.SourceProjectID)
if err != nil {
// If we don't have access to the source project, let's try the target project
mrProject, err = api.GetProject(apiClient, mr.TargetProjectID)
if err != nil {
return err
} else {
// We found the target project, let's find the ref another way
mrRef = fmt.Sprintf("refs/merge-requests/%d/head", mr.IID)
}
} else {
mrRef = fmt.Sprintf("refs/heads/%s", mr.SourceBranch)
}
fetchRefSpec := fmt.Sprintf("%s:%s", mrRef, mrCheckoutCfg.branch)
if err := git.RunCmd([]string{"fetch", mrProject.SSHURLToRepo, fetchRefSpec}); err != nil {
return err
}
// .remote is needed for `git pull` to work
// .pushRemote is needed for `git push` to work, if user has set `remote.pushDefault`.
// see https://git-scm.com/docs/git-config#Documentation/git-config.txt-branchltnamegtremote
if err := git.RunCmd([]string{"config", fmt.Sprintf("branch.%s.remote", mrCheckoutCfg.branch), mrProject.SSHURLToRepo}); err != nil {
return err
}
if mr.AllowCollaboration {
if err := git.RunCmd([]string{"config", fmt.Sprintf("branch.%s.pushRemote", mrCheckoutCfg.branch), mrProject.SSHURLToRepo}); err != nil {
return err
}
}
if err := git.RunCmd([]string{"config", fmt.Sprintf("branch.%s.merge", mrCheckoutCfg.branch), mrRef}); err != nil {
return err
}
// Check out branch
if err := git.CheckoutBranch(mrCheckoutCfg.branch); err != nil {
return err
}
// Check out the branch
if upstream != "" {
if err := git.RunCmd([]string{"branch", "--set-upstream-to", upstream}); err != nil {
return err
}
}
return nil
},
}
mrCheckoutCmd.Flags().StringVarP(&mrCheckoutCfg.branch, "branch", "b", "", "checkout merge request with <branch> name")
mrCheckoutCmd.Flags().BoolVarP(&mrCheckoutCfg.track, "track", "t", true, "set checked out branch to track remote branch")
_ = mrCheckoutCmd.Flags().MarkDeprecated("track", "Now enabled by default")
mrCheckoutCmd.Flags().StringVarP(&mrCheckoutCfg.upstream, "set-upstream-to", "u", "", "set tracking of checked out branch to [REMOTE/]BRANCH")
return mrCheckoutCmd
}