Skip to content

Commit 1fc9c8f

Browse files
committed
Test the scenario where the target branch already exists
1 parent 2616da0 commit 1fc9c8f

File tree

4 files changed

+81
-35
lines changed

4 files changed

+81
-35
lines changed

command/pr.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ func prCheckout(cmd *cobra.Command, args []string) error {
302302
cmdQueue = append(cmdQueue, []string{"git", "fetch", headRemote.Name, refSpec})
303303

304304
// local branch already exists
305-
if git.HasFile("refs", "heads", newBranchName) {
305+
if git.VerifyRef("refs/heads/" + newBranchName) {
306306
cmdQueue = append(cmdQueue, []string{"git", "checkout", newBranchName})
307307
cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", fmt.Sprintf("refs/remotes/%s", remoteBranch)})
308308
} else {

command/pr_checkout_test.go

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,75 @@ func TestPRCheckout_sameRepo(t *testing.T) {
4040

4141
ranCommands := [][]string{}
4242
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
43-
ranCommands = append(ranCommands, cmd.Args)
44-
return &outputStub{}
43+
switch strings.Join(cmd.Args, " ") {
44+
case "git show-ref --verify --quiet refs/heads/feature":
45+
return &errorStub{"exit status: 1"}
46+
default:
47+
ranCommands = append(ranCommands, cmd.Args)
48+
return &outputStub{}
49+
}
50+
})
51+
defer restoreCmd()
52+
53+
RootCmd.SetArgs([]string{"pr", "checkout", "123"})
54+
_, err := prCheckoutCmd.ExecuteC()
55+
eq(t, err, nil)
56+
57+
eq(t, len(ranCommands), 4)
58+
eq(t, strings.Join(ranCommands[0], " "), "git fetch origin +refs/heads/feature:refs/remotes/origin/feature")
59+
eq(t, strings.Join(ranCommands[1], " "), "git checkout -b feature --no-track origin/feature")
60+
eq(t, strings.Join(ranCommands[2], " "), "git config branch.feature.remote origin")
61+
eq(t, strings.Join(ranCommands[3], " "), "git config branch.feature.merge refs/heads/feature")
62+
}
63+
64+
func TestPRCheckout_existingBranch(t *testing.T) {
65+
ctx := context.NewBlank()
66+
ctx.SetBranch("master")
67+
ctx.SetRemotes(map[string]string{
68+
"origin": "OWNER/REPO",
69+
})
70+
initContext = func() context.Context {
71+
return ctx
72+
}
73+
http := initFakeHTTP()
74+
75+
http.StubResponse(200, bytes.NewBufferString(`
76+
{ "data": { "repository": { "pullRequest": {
77+
"headRefName": "feature",
78+
"headRepositoryOwner": {
79+
"login": "hubot"
80+
},
81+
"headRepository": {
82+
"name": "REPO",
83+
"defaultBranchRef": {
84+
"name": "master"
85+
}
86+
},
87+
"isCrossRepository": false,
88+
"maintainerCanModify": false
89+
} } } }
90+
`))
91+
92+
ranCommands := [][]string{}
93+
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
94+
switch strings.Join(cmd.Args, " ") {
95+
case "git show-ref --verify --quiet refs/heads/feature":
96+
return &outputStub{}
97+
default:
98+
ranCommands = append(ranCommands, cmd.Args)
99+
return &outputStub{}
100+
}
45101
})
46102
defer restoreCmd()
47103

48104
RootCmd.SetArgs([]string{"pr", "checkout", "123"})
49105
_, err := prCheckoutCmd.ExecuteC()
50106
eq(t, err, nil)
51107

52-
eq(t, len(ranCommands), 6)
53-
eq(t, strings.Join(ranCommands[0], " "), "git rev-parse -q --git-path refs/heads/feature")
54-
eq(t, strings.Join(ranCommands[1], " "), "git rev-parse -q --git-dir")
55-
eq(t, strings.Join(ranCommands[2], " "), "git fetch origin +refs/heads/feature:refs/remotes/origin/feature")
56-
eq(t, strings.Join(ranCommands[3], " "), "git checkout -b feature --no-track origin/feature")
57-
eq(t, strings.Join(ranCommands[4], " "), "git config branch.feature.remote origin")
58-
eq(t, strings.Join(ranCommands[5], " "), "git config branch.feature.merge refs/heads/feature")
108+
eq(t, len(ranCommands), 3)
109+
eq(t, strings.Join(ranCommands[0], " "), "git fetch origin +refs/heads/feature:refs/remotes/origin/feature")
110+
eq(t, strings.Join(ranCommands[1], " "), "git checkout feature")
111+
eq(t, strings.Join(ranCommands[2], " "), "git merge --ff-only refs/remotes/origin/feature")
59112
}
60113

61114
func TestPRCheckout_differentRepo(t *testing.T) {

command/testing.go

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

33
import (
4+
"errors"
5+
46
"github.com/github/gh-cli/api"
57
"github.com/github/gh-cli/context"
68
)
@@ -34,3 +36,15 @@ func (s outputStub) Output() ([]byte, error) {
3436
func (s outputStub) Run() error {
3537
return nil
3638
}
39+
40+
type errorStub struct {
41+
message string
42+
}
43+
44+
func (s errorStub) Output() ([]byte, error) {
45+
return nil, errors.New(s.message)
46+
}
47+
48+
func (s errorStub) Run() error {
49+
return errors.New(s.message)
50+
}

git/git.go

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,31 +41,10 @@ func WorkdirName() (string, error) {
4141
return dir, err
4242
}
4343

44-
func HasFile(segments ...string) bool {
45-
// The blessed way to resolve paths within git dir since Git 2.5.0
46-
pathCmd := exec.Command("git", "rev-parse", "-q", "--git-path", filepath.Join(segments...))
47-
if output, err := utils.PrepareCmd(pathCmd).Output(); err == nil {
48-
if lines := outputLines(output); len(lines) == 1 {
49-
if _, err := os.Stat(lines[0]); err == nil {
50-
return true
51-
}
52-
}
53-
}
54-
55-
// Fallback for older git versions
56-
dir, err := Dir()
57-
if err != nil {
58-
return false
59-
}
60-
61-
s := []string{dir}
62-
s = append(s, segments...)
63-
path := filepath.Join(s...)
64-
if _, err := os.Stat(path); err == nil {
65-
return true
66-
}
67-
68-
return false
44+
func VerifyRef(ref string) bool {
45+
showRef := exec.Command("git", "show-ref", "--verify", "--quiet", ref)
46+
err := utils.PrepareCmd(showRef).Run()
47+
return err == nil
6948
}
7049

7150
func BranchAtRef(paths ...string) (name string, err error) {

0 commit comments

Comments
 (0)