/commands/checkout.go

https://gitlab.com/jslee1/hub · Go · 127 lines · 102 code · 23 blank · 2 comment · 23 complexity · 35e8eebe9d9dcdece2122478721cd53c MD5 · raw file

  1. package commands
  2. import (
  3. "fmt"
  4. "regexp"
  5. "github.com/github/hub/github"
  6. "github.com/github/hub/utils"
  7. )
  8. var cmdCheckout = &Command{
  9. Run: checkout,
  10. GitExtension: true,
  11. Usage: "checkout <PULLREQ-URL> [<BRANCH>]",
  12. Long: `Check out the head of a pull request as a local branch.
  13. ## Examples:
  14. $ hub checkout https://github.com/jingweno/gh/pull/73
  15. > git remote add -f --no-tags -t feature git://github:com/jingweno/gh.git
  16. > git checkout --track -B jingweno-feature jingweno/feature
  17. ## See also:
  18. hub-merge(1), hub-am(1), hub(1), git-checkout(1)
  19. `,
  20. }
  21. func init() {
  22. CmdRunner.Use(cmdCheckout)
  23. }
  24. func checkout(command *Command, args *Args) {
  25. if !args.IsParamsEmpty() {
  26. err := transformCheckoutArgs(args)
  27. utils.Check(err)
  28. }
  29. }
  30. func transformCheckoutArgs(args *Args) error {
  31. words := args.Words()
  32. if len(words) == 0 {
  33. return nil
  34. }
  35. checkoutURL := words[0]
  36. var newBranchName string
  37. if len(words) > 1 {
  38. newBranchName = words[1]
  39. }
  40. url, err := github.ParseURL(checkoutURL)
  41. if err != nil {
  42. // not a valid GitHub URL
  43. return nil
  44. }
  45. pullURLRegex := regexp.MustCompile("^pull/(\\d+)")
  46. projectPath := url.ProjectPath()
  47. if !pullURLRegex.MatchString(projectPath) {
  48. // not a valid PR URL
  49. return nil
  50. }
  51. err = sanitizeCheckoutFlags(args)
  52. if err != nil {
  53. return err
  54. }
  55. id := pullURLRegex.FindStringSubmatch(projectPath)[1]
  56. gh := github.NewClient(url.Project.Host)
  57. pullRequest, err := gh.PullRequest(url.Project, id)
  58. if err != nil {
  59. return err
  60. }
  61. if idx := args.IndexOfParam(newBranchName); idx >= 0 {
  62. args.RemoveParam(idx)
  63. }
  64. branch := pullRequest.Head.Ref
  65. headRepo := pullRequest.Head.Repo
  66. if headRepo == nil {
  67. return fmt.Errorf("Error: that fork is not available anymore")
  68. }
  69. user := headRepo.Owner.Login
  70. if newBranchName == "" {
  71. newBranchName = fmt.Sprintf("%s-%s", user, branch)
  72. }
  73. repo, err := github.LocalRepo()
  74. utils.Check(err)
  75. _, err = repo.RemoteByName(user)
  76. if err == nil {
  77. args.Before("git", "remote", "set-branches", "--add", user, branch)
  78. remoteURL := fmt.Sprintf("+refs/heads/%s:refs/remotes/%s/%s", branch, user, branch)
  79. args.Before("git", "fetch", user, remoteURL)
  80. } else {
  81. u := url.Project.GitURL(pullRequest.Head.Repo.Name, user, pullRequest.Head.Repo.Private)
  82. args.Before("git", "remote", "add", "-f", "--no-tags", "-t", branch, user, u)
  83. }
  84. remoteName := fmt.Sprintf("%s/%s", user, branch)
  85. replaceCheckoutParam(args, checkoutURL, newBranchName, remoteName)
  86. return nil
  87. }
  88. func sanitizeCheckoutFlags(args *Args) error {
  89. if i := args.IndexOfParam("-b"); i != -1 {
  90. return fmt.Errorf("Unsupported flag -b when checking out pull request")
  91. }
  92. if i := args.IndexOfParam("--orphan"); i != -1 {
  93. return fmt.Errorf("Unsupported flag --orphan when checking out pull request")
  94. }
  95. return nil
  96. }
  97. func replaceCheckoutParam(args *Args, checkoutURL, branchName, remoteName string) {
  98. idx := args.IndexOfParam(checkoutURL)
  99. args.RemoveParam(idx)
  100. args.InsertParam(idx, "--track", "-B", branchName, remoteName)
  101. }