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  }