gitlab.com/prarit/lab@v0.14.0/cmd/mr_checkout.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/spf13/cobra"
     8  	"github.com/tcnksm/go-gitconfig"
     9  	"github.com/xanzy/go-gitlab"
    10  	"github.com/zaquestion/lab/internal/git"
    11  	lab "github.com/zaquestion/lab/internal/gitlab"
    12  )
    13  
    14  // mrCheckoutConfig holds configuration values for calls to lab mr checkout
    15  type mrCheckoutConfig struct {
    16  	branch string
    17  	track  bool
    18  }
    19  
    20  var (
    21  	mrCheckoutCfg mrCheckoutConfig
    22  )
    23  
    24  // listCmd represents the list command
    25  var checkoutCmd = &cobra.Command{
    26  	Use:   "checkout",
    27  	Short: "Checkout an open merge request",
    28  	Long:  ``,
    29  	Args:  cobra.ExactArgs(1),
    30  	Run: func(cmd *cobra.Command, args []string) {
    31  		rn, mrID, err := parseArgs(args)
    32  		if err != nil {
    33  			log.Fatal(err)
    34  		}
    35  
    36  		mrs, err := lab.MRList(rn, gitlab.ListProjectMergeRequestsOptions{
    37  			IIDs: []int{int(mrID)},
    38  		}, 1)
    39  		if err != nil {
    40  			log.Fatal(err)
    41  		}
    42  		if len(mrs) < 1 {
    43  			fmt.Printf("MR #%d not found\n", mrID)
    44  			return
    45  		}
    46  
    47  		mr := mrs[0]
    48  		// If the config does not specify a branch, use the mr source branch name
    49  		if mrCheckoutCfg.branch == "" {
    50  			mrCheckoutCfg.branch = mr.SourceBranch
    51  		}
    52  		// By default, fetch to configured branch
    53  		fetchToRef := mrCheckoutCfg.branch
    54  
    55  		// If track, make sure we have a remote for the mr author and then set
    56  		// the fetchToRef to the mr author/sourceBranch
    57  		if mrCheckoutCfg.track {
    58  			// Check if remote already exists
    59  			if _, err := gitconfig.Local("remote." + mr.Author.Username + ".url"); err != nil {
    60  				// Find and create remote
    61  				mrProject, err := lab.GetProject(mr.SourceProjectID)
    62  				if err != nil {
    63  					log.Fatal(err)
    64  				}
    65  				if err := git.RemoteAdd(mr.Author.Username, mrProject.SSHURLToRepo, "."); err != nil {
    66  					log.Fatal(err)
    67  				}
    68  			}
    69  			fetchToRef = fmt.Sprintf("refs/remotes/%s/%s", mr.Author.Username, mr.SourceBranch)
    70  		}
    71  
    72  		// https://docs.gitlab.com/ce/user/project/merge_requests/#checkout-merge-requests-locally
    73  		mrRef := fmt.Sprintf("refs/merge-requests/%d/head", mrID)
    74  		fetchRefSpec := fmt.Sprintf("%s:%s", mrRef, fetchToRef)
    75  		if err := git.New("fetch", forkedFromRemote, fetchRefSpec).Run(); err != nil {
    76  			log.Fatal(err)
    77  		}
    78  
    79  		if mrCheckoutCfg.track {
    80  			// Create configured branch with tracking from fetchToRef
    81  			// git branch --flags <branchname> [<start-point>]
    82  			if err := git.New("branch", "--track", mrCheckoutCfg.branch, fetchToRef).Run(); err != nil {
    83  				log.Fatal(err)
    84  			}
    85  		}
    86  
    87  		// Check out branch
    88  		if err := git.New("checkout", mrCheckoutCfg.branch).Run(); err != nil {
    89  			log.Fatal(err)
    90  		}
    91  	},
    92  }
    93  
    94  func init() {
    95  	checkoutCmd.Flags().StringVarP(&mrCheckoutCfg.branch, "branch", "b", "", "checkout merge request with <branch> name")
    96  	checkoutCmd.Flags().BoolVarP(&mrCheckoutCfg.track, "track", "t", false, "set checked out branch to track mr author remote branch, adds remote if needed")
    97  	mrCmd.AddCommand(checkoutCmd)
    98  }