X Tutup
Skip to content

Commit d43cdfe

Browse files
author
Nate Smith
authored
Merge pull request cli#302 from cli/co-unique
Prefix branch names when checking out a PR locally
2 parents 4177413 + 5b8d7c1 commit d43cdfe

File tree

3 files changed

+143
-132
lines changed

3 files changed

+143
-132
lines changed

command/pr.go

Lines changed: 0 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package command
22

33
import (
4-
"errors"
54
"fmt"
65
"io"
7-
"os"
8-
"os/exec"
96
"regexp"
107
"strconv"
118
"strings"
@@ -46,17 +43,6 @@ A pull request can be supplied as argument in any of the following formats:
4643
- by URL, e.g. "https://github.com/OWNER/REPO/pull/123"; or
4744
- by the name of its head branch, e.g. "patch-1" or "OWNER:patch-1".`,
4845
}
49-
var prCheckoutCmd = &cobra.Command{
50-
Use: "checkout {<number> | <url> | <branch>}",
51-
Short: "Check out a pull request in Git",
52-
Args: func(cmd *cobra.Command, args []string) error {
53-
if len(args) < 1 {
54-
return errors.New("requires a PR number as an argument")
55-
}
56-
return nil
57-
},
58-
RunE: prCheckout,
59-
}
6046
var prListCmd = &cobra.Command{
6147
Use: "list",
6248
Short: "List and filter pull requests in this repository",
@@ -386,95 +372,6 @@ func prSelectorForCurrentBranch(ctx context.Context) (prNumber int, prHeadRef st
386372
return
387373
}
388374

389-
func prCheckout(cmd *cobra.Command, args []string) error {
390-
ctx := contextForCommand(cmd)
391-
currentBranch, _ := ctx.Branch()
392-
remotes, err := ctx.Remotes()
393-
if err != nil {
394-
return err
395-
}
396-
// FIXME: duplicates logic from fsContext.BaseRepo
397-
baseRemote, err := remotes.FindByName("upstream", "github", "origin", "*")
398-
if err != nil {
399-
return err
400-
}
401-
apiClient, err := apiClientForContext(ctx)
402-
if err != nil {
403-
return err
404-
}
405-
406-
pr, err := prFromArg(apiClient, baseRemote, args[0])
407-
if err != nil {
408-
return err
409-
}
410-
411-
headRemote := baseRemote
412-
if pr.IsCrossRepository {
413-
headRemote, _ = remotes.FindByRepo(pr.HeadRepositoryOwner.Login, pr.HeadRepository.Name)
414-
}
415-
416-
cmdQueue := [][]string{}
417-
418-
newBranchName := pr.HeadRefName
419-
if headRemote != nil {
420-
// there is an existing git remote for PR head
421-
remoteBranch := fmt.Sprintf("%s/%s", headRemote.Name, pr.HeadRefName)
422-
refSpec := fmt.Sprintf("+refs/heads/%s:refs/remotes/%s", pr.HeadRefName, remoteBranch)
423-
424-
cmdQueue = append(cmdQueue, []string{"git", "fetch", headRemote.Name, refSpec})
425-
426-
// local branch already exists
427-
if git.VerifyRef("refs/heads/" + newBranchName) {
428-
cmdQueue = append(cmdQueue, []string{"git", "checkout", newBranchName})
429-
cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", fmt.Sprintf("refs/remotes/%s", remoteBranch)})
430-
} else {
431-
cmdQueue = append(cmdQueue, []string{"git", "checkout", "-b", newBranchName, "--no-track", remoteBranch})
432-
cmdQueue = append(cmdQueue, []string{"git", "config", fmt.Sprintf("branch.%s.remote", newBranchName), headRemote.Name})
433-
cmdQueue = append(cmdQueue, []string{"git", "config", fmt.Sprintf("branch.%s.merge", newBranchName), "refs/heads/" + pr.HeadRefName})
434-
}
435-
} else {
436-
// no git remote for PR head
437-
438-
// avoid naming the new branch the same as the default branch
439-
if newBranchName == pr.HeadRepository.DefaultBranchRef.Name {
440-
newBranchName = fmt.Sprintf("%s/%s", pr.HeadRepositoryOwner.Login, newBranchName)
441-
}
442-
443-
ref := fmt.Sprintf("refs/pull/%d/head", pr.Number)
444-
if newBranchName == currentBranch {
445-
// PR head matches currently checked out branch
446-
cmdQueue = append(cmdQueue, []string{"git", "fetch", baseRemote.Name, ref})
447-
cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", "FETCH_HEAD"})
448-
} else {
449-
// create a new branch
450-
cmdQueue = append(cmdQueue, []string{"git", "fetch", baseRemote.Name, fmt.Sprintf("%s:%s", ref, newBranchName)})
451-
cmdQueue = append(cmdQueue, []string{"git", "checkout", newBranchName})
452-
}
453-
454-
remote := baseRemote.Name
455-
mergeRef := ref
456-
if pr.MaintainerCanModify {
457-
remote = fmt.Sprintf("https://github.com/%s/%s.git", pr.HeadRepositoryOwner.Login, pr.HeadRepository.Name)
458-
mergeRef = fmt.Sprintf("refs/heads/%s", pr.HeadRefName)
459-
}
460-
if mc, err := git.Config(fmt.Sprintf("branch.%s.merge", newBranchName)); err != nil || mc == "" {
461-
cmdQueue = append(cmdQueue, []string{"git", "config", fmt.Sprintf("branch.%s.remote", newBranchName), remote})
462-
cmdQueue = append(cmdQueue, []string{"git", "config", fmt.Sprintf("branch.%s.merge", newBranchName), mergeRef})
463-
}
464-
}
465-
466-
for _, args := range cmdQueue {
467-
cmd := exec.Command(args[0], args[1:]...)
468-
cmd.Stdout = os.Stdout
469-
cmd.Stderr = os.Stderr
470-
if err := utils.PrepareCmd(cmd).Run(); err != nil {
471-
return err
472-
}
473-
}
474-
475-
return nil
476-
}
477-
478375
func printPrs(w io.Writer, totalCount int, prs ...api.PullRequest) {
479376
for _, pr := range prs {
480377
prNumber := fmt.Sprintf("#%d", pr.Number)

command/pr_checkout.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package command
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"os/exec"
8+
9+
"github.com/cli/cli/git"
10+
"github.com/cli/cli/utils"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
func prCheckout(cmd *cobra.Command, args []string) error {
15+
ctx := contextForCommand(cmd)
16+
currentBranch, _ := ctx.Branch()
17+
remotes, err := ctx.Remotes()
18+
if err != nil {
19+
return err
20+
}
21+
// FIXME: duplicates logic from fsContext.BaseRepo
22+
baseRemote, err := remotes.FindByName("upstream", "github", "origin", "*")
23+
if err != nil {
24+
return err
25+
}
26+
apiClient, err := apiClientForContext(ctx)
27+
if err != nil {
28+
return err
29+
}
30+
31+
pr, err := prFromArg(apiClient, baseRemote, args[0])
32+
if err != nil {
33+
return err
34+
}
35+
36+
headRemote := baseRemote
37+
if pr.IsCrossRepository {
38+
headRemote, _ = remotes.FindByRepo(pr.HeadRepositoryOwner.Login, pr.HeadRepository.Name)
39+
}
40+
41+
cmdQueue := [][]string{}
42+
43+
// namespace PR checkout branches to avoid local branch name collisions
44+
newBranchName := fmt.Sprintf("pr/%d/%s", pr.Number, pr.HeadRefName)
45+
if headRemote != nil {
46+
// there is an existing git remote for PR head
47+
remoteBranch := fmt.Sprintf("%s/%s", headRemote.Name, pr.HeadRefName)
48+
refSpec := fmt.Sprintf("+refs/heads/%s:refs/remotes/%s", pr.HeadRefName, remoteBranch)
49+
50+
cmdQueue = append(cmdQueue, []string{"git", "fetch", headRemote.Name, refSpec})
51+
52+
// local branch already exists
53+
if git.VerifyRef("refs/heads/" + newBranchName) {
54+
cmdQueue = append(cmdQueue, []string{"git", "checkout", newBranchName})
55+
cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", fmt.Sprintf("refs/remotes/%s", remoteBranch)})
56+
} else {
57+
cmdQueue = append(cmdQueue, []string{"git", "checkout", "-b", newBranchName, "--no-track", remoteBranch})
58+
cmdQueue = append(cmdQueue, []string{"git", "config", fmt.Sprintf("branch.%s.remote", newBranchName), headRemote.Name})
59+
cmdQueue = append(cmdQueue, []string{"git", "config", fmt.Sprintf("branch.%s.merge", newBranchName), "refs/heads/" + pr.HeadRefName})
60+
}
61+
} else {
62+
// no git remote for PR head
63+
64+
// avoid naming the new branch the same as the default branch
65+
if newBranchName == pr.HeadRepository.DefaultBranchRef.Name {
66+
newBranchName = fmt.Sprintf("%s/%s", pr.HeadRepositoryOwner.Login, newBranchName)
67+
}
68+
69+
ref := fmt.Sprintf("refs/pull/%d/head", pr.Number)
70+
if newBranchName == currentBranch {
71+
// PR head matches currently checked out branch
72+
cmdQueue = append(cmdQueue, []string{"git", "fetch", baseRemote.Name, ref})
73+
cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", "FETCH_HEAD"})
74+
} else {
75+
// create a new branch
76+
cmdQueue = append(cmdQueue, []string{"git", "fetch", baseRemote.Name, fmt.Sprintf("%s:%s", ref, newBranchName)})
77+
cmdQueue = append(cmdQueue, []string{"git", "checkout", newBranchName})
78+
}
79+
80+
remote := baseRemote.Name
81+
mergeRef := ref
82+
if pr.MaintainerCanModify {
83+
remote = fmt.Sprintf("https://github.com/%s/%s.git", pr.HeadRepositoryOwner.Login, pr.HeadRepository.Name)
84+
mergeRef = fmt.Sprintf("refs/heads/%s", pr.HeadRefName)
85+
}
86+
if mc, err := git.Config(fmt.Sprintf("branch.%s.merge", newBranchName)); err != nil || mc == "" {
87+
cmdQueue = append(cmdQueue, []string{"git", "config", fmt.Sprintf("branch.%s.remote", newBranchName), remote})
88+
cmdQueue = append(cmdQueue, []string{"git", "config", fmt.Sprintf("branch.%s.merge", newBranchName), mergeRef})
89+
}
90+
}
91+
92+
for _, args := range cmdQueue {
93+
cmd := exec.Command(args[0], args[1:]...)
94+
cmd.Stdout = os.Stdout
95+
cmd.Stderr = os.Stderr
96+
if err := utils.PrepareCmd(cmd).Run(); err != nil {
97+
return err
98+
}
99+
}
100+
101+
return nil
102+
}
103+
104+
var prCheckoutCmd = &cobra.Command{
105+
Use: "checkout {<number> | <url> | <branch>}",
106+
Short: "Check out a pull request in Git",
107+
Args: func(cmd *cobra.Command, args []string) error {
108+
if len(args) < 1 {
109+
return errors.New("requires a PR number as an argument")
110+
}
111+
return nil
112+
},
113+
RunE: prCheckout,
114+
}

0 commit comments

Comments
 (0)
X Tutup