github.com/scorpionis/hub@v2.2.1+incompatible/commands/checkout.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "regexp" 6 7 "github.com/github/hub/github" 8 "github.com/github/hub/utils" 9 ) 10 11 var cmdCheckout = &Command{ 12 Run: checkout, 13 GitExtension: true, 14 Usage: "checkout PULLREQ-URL [BRANCH]", 15 Short: "Switch the active branch to another branch", 16 Long: `Checks out the head of the pull request as a local branch, to allow for 17 reviewing, rebasing and otherwise cleaning up the commits in the pull 18 request before merging. The name of the local branch can explicitly be 19 set with BRANCH. 20 `, 21 } 22 23 func init() { 24 CmdRunner.Use(cmdCheckout) 25 } 26 27 /** 28 $ gh checkout https://github.com/jingweno/gh/pull/73 29 > git remote add -f -t feature git://github:com/foo/gh.git 30 > git checkout --track -B foo-feature foo/feature 31 32 $ gh checkout https://github.com/jingweno/gh/pull/73 custom-branch-name 33 **/ 34 func checkout(command *Command, args *Args) { 35 if !args.IsParamsEmpty() { 36 err := transformCheckoutArgs(args) 37 utils.Check(err) 38 } 39 } 40 41 func transformCheckoutArgs(args *Args) error { 42 words := args.Words() 43 44 if len(words) == 0 { 45 return nil 46 } 47 48 checkoutURL := words[0] 49 var newBranchName string 50 if len(words) > 1 { 51 newBranchName = words[1] 52 } 53 54 url, err := github.ParseURL(checkoutURL) 55 if err != nil { 56 // not a valid GitHub URL 57 return nil 58 } 59 60 pullURLRegex := regexp.MustCompile("^pull/(\\d+)") 61 projectPath := url.ProjectPath() 62 if !pullURLRegex.MatchString(projectPath) { 63 // not a valid PR URL 64 return nil 65 } 66 67 err = sanitizeCheckoutFlags(args) 68 if err != nil { 69 return err 70 } 71 72 id := pullURLRegex.FindStringSubmatch(projectPath)[1] 73 gh := github.NewClient(url.Project.Host) 74 pullRequest, err := gh.PullRequest(url.Project, id) 75 if err != nil { 76 return err 77 } 78 79 if idx := args.IndexOfParam(newBranchName); idx >= 0 { 80 args.RemoveParam(idx) 81 } 82 83 branch := pullRequest.Head.Ref 84 headRepo := pullRequest.Head.Repo 85 if headRepo == nil { 86 return fmt.Errorf("Error: that fork is not available anymore") 87 } 88 user := headRepo.Owner.Login 89 90 if newBranchName == "" { 91 newBranchName = fmt.Sprintf("%s-%s", user, branch) 92 } 93 94 repo, err := github.LocalRepo() 95 utils.Check(err) 96 97 _, err = repo.RemoteByName(user) 98 if err == nil { 99 args.Before("git", "remote", "set-branches", "--add", user, branch) 100 remoteURL := fmt.Sprintf("+refs/heads/%s:refs/remotes/%s/%s", branch, user, branch) 101 args.Before("git", "fetch", user, remoteURL) 102 } else { 103 u := url.Project.GitURL(pullRequest.Head.Repo.Name, user, pullRequest.Head.Repo.Private) 104 args.Before("git", "remote", "add", "-f", "-t", branch, user, u) 105 } 106 107 remoteName := fmt.Sprintf("%s/%s", user, branch) 108 replaceCheckoutParam(args, checkoutURL, newBranchName, remoteName) 109 110 return nil 111 } 112 113 func sanitizeCheckoutFlags(args *Args) error { 114 if i := args.IndexOfParam("-b"); i != -1 { 115 return fmt.Errorf("Unsupported flag -b when checking out pull request") 116 } 117 118 if i := args.IndexOfParam("--orphan"); i != -1 { 119 return fmt.Errorf("Unsupported flag --orphan when checking out pull request") 120 } 121 122 return nil 123 } 124 125 func replaceCheckoutParam(args *Args, checkoutURL, branchName, remoteName string) { 126 idx := args.IndexOfParam(checkoutURL) 127 args.RemoveParam(idx) 128 args.InsertParam(idx, "--track", "-B", branchName, remoteName) 129 }