X Tutup
Skip to content

Commit 3122696

Browse files
author
Corey Johnson
authored
Merge pull request cli#982 from cli/remote-delete
Remote delete
2 parents be5fc07 + 6c5c7cf commit 3122696

File tree

4 files changed

+92
-29
lines changed

4 files changed

+92
-29
lines changed

api/queries_pr.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,6 @@ func PullRequests(client *Client, repo ghrepo.Interface, currentPRNumber int, cu
265265
state
266266
url
267267
headRefName
268-
mergeable
269268
headRepositoryOwner {
270269
login
271270
}
@@ -1014,6 +1013,14 @@ func PullRequestReady(client *Client, repo ghrepo.Interface, pr *PullRequest) er
10141013
return err
10151014
}
10161015

1016+
func BranchDeleteRemote(client *Client, repo ghrepo.Interface, branch string) error {
1017+
var response struct {
1018+
NodeID string `json:"node_id"`
1019+
}
1020+
path := fmt.Sprintf("repos/%s/%s/git/refs/heads/%s", repo.RepoOwner(), repo.RepoName(), branch)
1021+
return client.REST("DELETE", path, nil, &response)
1022+
}
1023+
10171024
func min(a, b int) int {
10181025
if a < b {
10191026
return a

command/pr.go

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func init() {
2828
prCmd.AddCommand(prCloseCmd)
2929
prCmd.AddCommand(prReopenCmd)
3030
prCmd.AddCommand(prMergeCmd)
31-
prMergeCmd.Flags().BoolP("delete-branch", "d", true, "Delete the local branch after merge")
31+
prMergeCmd.Flags().BoolP("delete-branch", "d", true, "Delete the local and remote branch after merge")
3232
prMergeCmd.Flags().BoolP("merge", "m", false, "Merge the commits with the base branch")
3333
prMergeCmd.Flags().BoolP("rebase", "r", false, "Rebase the commits onto the base branch")
3434
prMergeCmd.Flags().BoolP("squash", "s", false, "Squash the commits into one commit and merge it into the base branch")
@@ -499,6 +499,9 @@ func prMerge(cmd *cobra.Command, args []string) error {
499499
return err
500500
}
501501

502+
deleteLocalBranch := !cmd.Flags().Changed("repo")
503+
crossRepoPR := pr.HeadRepositoryOwner.Login != baseRepo.RepoOwner()
504+
502505
// Ensure only one merge method is specified
503506
enabledFlagCount := 0
504507
isInteractive := false
@@ -522,7 +525,7 @@ func prMerge(cmd *cobra.Command, args []string) error {
522525
}
523526

524527
if isInteractive {
525-
mergeMethod, deleteBranch, err = prInteractiveMerge()
528+
mergeMethod, deleteBranch, err = prInteractiveMerge(deleteLocalBranch, crossRepoPR)
526529
if err != nil {
527530
return nil
528531
}
@@ -549,7 +552,7 @@ func prMerge(cmd *cobra.Command, args []string) error {
549552

550553
fmt.Fprintf(colorableOut(cmd), "%s %s pull request #%d\n", utils.Magenta("✔"), action, pr.Number)
551554

552-
if deleteBranch && !cmd.Flags().Changed("repo") {
555+
if deleteBranch {
553556
repo, err := api.GitHubRepo(apiClient, baseRepo)
554557
if err != nil {
555558
return err
@@ -560,25 +563,47 @@ func prMerge(cmd *cobra.Command, args []string) error {
560563
return err
561564
}
562565

563-
if currentBranch == pr.HeadRefName {
564-
err = git.CheckoutBranch(repo.DefaultBranchRef.Name)
566+
branchSwitchString := ""
567+
568+
if deleteLocalBranch && !crossRepoPR {
569+
var branchToSwitchTo string
570+
if currentBranch == pr.HeadRefName {
571+
branchToSwitchTo = repo.DefaultBranchRef.Name
572+
err = git.CheckoutBranch(repo.DefaultBranchRef.Name)
573+
if err != nil {
574+
return err
575+
}
576+
}
577+
578+
localBranchExists := git.HasLocalBranch(pr.HeadRefName)
579+
if localBranchExists {
580+
err = git.DeleteLocalBranch(pr.HeadRefName)
581+
if err != nil {
582+
err = fmt.Errorf("failed to delete local branch %s: %w", utils.Cyan(pr.HeadRefName), err)
583+
return err
584+
}
585+
}
586+
587+
if branchToSwitchTo != "" {
588+
branchSwitchString = fmt.Sprintf(" and switched to branch %s", utils.Cyan(branchToSwitchTo))
589+
}
590+
}
591+
592+
if !crossRepoPR {
593+
err = api.BranchDeleteRemote(apiClient, baseRepo, pr.HeadRefName)
565594
if err != nil {
595+
err = fmt.Errorf("failed to delete remote branch %s: %w", utils.Cyan(pr.HeadRefName), err)
566596
return err
567597
}
568598
}
569599

570-
err = git.DeleteLocalBranch(pr.HeadRefName)
571-
if err != nil {
572-
fmt.Fprintf(colorableErr(cmd), "%s Could not delete local branch %s: %s\n", utils.Red("!"), utils.Cyan(pr.HeadRefName), err)
573-
return err
574-
}
575-
fmt.Fprintf(colorableOut(cmd), "%s Deleted local branch %s\n", utils.Red("✔"), utils.Cyan(pr.HeadRefName))
600+
fmt.Fprintf(colorableOut(cmd), "%s Deleted branch %s%s\n", utils.Red("✔"), utils.Cyan(pr.HeadRefName), branchSwitchString)
576601
}
577602

578603
return nil
579604
}
580605

581-
func prInteractiveMerge() (api.PullRequestMergeMethod, bool, error) {
606+
func prInteractiveMerge(deleteLocalBranch bool, crossRepoPR bool) (api.PullRequestMergeMethod, bool, error) {
582607
mergeMethodQuestion := &survey.Question{
583608
Name: "mergeMethod",
584609
Prompt: &survey.Select{
@@ -588,15 +613,25 @@ func prInteractiveMerge() (api.PullRequestMergeMethod, bool, error) {
588613
},
589614
}
590615

591-
deleteBranchQuestion := &survey.Question{
592-
Name: "deleteBranch",
593-
Prompt: &survey.Confirm{
594-
Message: "Delete the branch locally?",
595-
Default: true,
596-
},
597-
}
616+
qs := []*survey.Question{mergeMethodQuestion}
617+
618+
if !crossRepoPR {
619+
var message string
620+
if deleteLocalBranch {
621+
message = "Delete the branch locally and on GitHub?"
622+
} else {
623+
message = "Delete the branch on GitHub?"
624+
}
598625

599-
qs := []*survey.Question{mergeMethodQuestion, deleteBranchQuestion}
626+
deleteBranchQuestion := &survey.Question{
627+
Name: "deleteBranch",
628+
Prompt: &survey.Confirm{
629+
Message: message,
630+
Default: true,
631+
},
632+
}
633+
qs = append(qs, deleteBranchQuestion)
634+
}
600635

601636
answers := struct {
602637
MergeMethod int

command/pr_test.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,7 @@ func TestPrMerge(t *testing.T) {
994994
"pullRequest": { "number": 1, "closed": false, "state": "OPEN"}
995995
} } }`)},
996996
stubResponse{200, bytes.NewBufferString(`{"id": "THE-ID"}`)},
997+
stubResponse{200, bytes.NewBufferString(`{"node_id": "THE-ID"}`)},
997998
)
998999

9991000
cs, cmdTeardown := test.InitCmdStubber()
@@ -1021,9 +1022,11 @@ func TestPrMerge_withRepoFlag(t *testing.T) {
10211022
initBlankContext("", "OWNER/REPO", "master")
10221023
http := initFakeHTTP()
10231024
http.StubResponse(200, bytes.NewBufferString(`{ "data": { "repository": {
1024-
"pullRequest": { "number": 1, "closed": false, "state": "OPEN"}
1025+
"pullRequest": { "number": 1, "closed": false, "state": "OPEN"}
10251026
} } }`))
1026-
http.StubResponse(200, bytes.NewBufferString(`{"id": "THE-ID"}`))
1027+
http.StubResponse(200, bytes.NewBufferString(`{ "data": {} }`))
1028+
http.StubRepoResponse("OWNER", "REPO")
1029+
http.StubResponse(200, bytes.NewBufferString(`{"node_id": "THE-ID"}`))
10271030

10281031
cs, cmdTeardown := test.InitCmdStubber()
10291032
defer cmdTeardown()
@@ -1048,22 +1051,25 @@ func TestPrMerge_deleteBranch(t *testing.T) {
10481051
{ "data": { "repository": { "pullRequests": { "nodes": [
10491052
{ "headRefName": "blueberries", "id": "THE-ID", "number": 3}
10501053
] } } } }`)},
1051-
stubResponse{200, bytes.NewBufferString(`{ "data": {} }`)})
1054+
stubResponse{200, bytes.NewBufferString(`{ "data": {} }`)},
1055+
stubResponse{200, bytes.NewBufferString(`{"node_id": "THE-ID"}`)},
1056+
)
10521057

10531058
cs, cmdTeardown := test.InitCmdStubber()
10541059
defer cmdTeardown()
10551060

10561061
cs.Stub("") // git config --get-regexp ^branch\.blueberries\.(remote|merge)$
1057-
cs.Stub("") // git symbolic-ref --quiet --short HEAD
10581062
cs.Stub("") // git checkout master
1063+
cs.Stub("") // git rev-parse --verify blueberries`
10591064
cs.Stub("") // git branch -d
1065+
cs.Stub("") // git push origin --delete blueberries
10601066

10611067
output, err := RunCommand(`pr merge --merge --delete-branch`)
10621068
if err != nil {
10631069
t.Fatalf("Got unexpected error running `pr merge` %s", err)
10641070
}
10651071

1066-
test.ExpectLines(t, output.String(), "Merged pull request #3", "Deleted local branch")
1072+
test.ExpectLines(t, output.String(), "Merged pull request #3", "Deleted branch blueberries")
10671073
}
10681074

10691075
func TestPrMerge_deleteNonCurrentBranch(t *testing.T) {
@@ -1075,19 +1081,22 @@ func TestPrMerge_deleteNonCurrentBranch(t *testing.T) {
10751081
{ "headRefName": "blueberries", "id": "THE-ID", "number": 3}
10761082
] } } } }`))
10771083
http.StubResponse(200, bytes.NewBufferString(`{ "data": {} }`))
1084+
http.StubResponse(200, bytes.NewBufferString(`{"node_id": "THE-ID"}`))
10781085
http.StubRepoResponse("OWNER", "REPO")
10791086

10801087
cs, cmdTeardown := test.InitCmdStubber()
10811088
defer cmdTeardown()
10821089
// We don't expect the default branch to be checked out, just that blueberries is deleted
1090+
cs.Stub("") // git rev-parse --verify blueberries
10831091
cs.Stub("") // git branch -d blueberries
1092+
cs.Stub("") // git push origin --delete blueberries
10841093

10851094
output, err := RunCommand(`pr merge --merge --delete-branch blueberries`)
10861095
if err != nil {
10871096
t.Fatalf("Got unexpected error running `pr merge` %s", err)
10881097
}
10891098

1090-
test.ExpectLines(t, output.String(), "Merged pull request #3", "Deleted local branch")
1099+
test.ExpectLines(t, output.String(), "Merged pull request #3", "Deleted branch blueberries")
10911100
}
10921101

10931102
func TestPrMerge_noPrNumberGiven(t *testing.T) {
@@ -1106,6 +1115,7 @@ func TestPrMerge_noPrNumberGiven(t *testing.T) {
11061115
initWithStubs("blueberries",
11071116
stubResponse{200, jsonFile},
11081117
stubResponse{200, bytes.NewBufferString(`{"id": "THE-ID"}`)},
1118+
stubResponse{200, bytes.NewBufferString(`{"node_id": "THE-ID"}`)},
11091119
)
11101120

11111121
output, err := RunCommand("pr merge --merge")
@@ -1126,6 +1136,7 @@ func TestPrMerge_rebase(t *testing.T) {
11261136
"pullRequest": { "number": 2, "closed": false, "state": "OPEN"}
11271137
} } }`)},
11281138
stubResponse{200, bytes.NewBufferString(`{"id": "THE-ID"}`)},
1139+
stubResponse{200, bytes.NewBufferString(`{"node_id": "THE-ID"}`)},
11291140
)
11301141

11311142
cs, cmdTeardown := test.InitCmdStubber()
@@ -1154,6 +1165,7 @@ func TestPrMerge_squash(t *testing.T) {
11541165
"pullRequest": { "number": 3, "closed": false, "state": "OPEN"}
11551166
} } }`)},
11561167
stubResponse{200, bytes.NewBufferString(`{"id": "THE-ID"}`)},
1168+
stubResponse{200, bytes.NewBufferString(`{"node_id": "THE-ID"}`)},
11571169
)
11581170

11591171
cs, cmdTeardown := test.InitCmdStubber()
@@ -1182,6 +1194,7 @@ func TestPrMerge_alreadyMerged(t *testing.T) {
11821194
"pullRequest": { "number": 4, "closed": true, "state": "MERGED"}
11831195
} } }`)},
11841196
stubResponse{200, bytes.NewBufferString(`{"id": "THE-ID"}`)},
1197+
stubResponse{200, bytes.NewBufferString(`{"node_id": "THE-ID"}`)},
11851198
)
11861199

11871200
cs, cmdTeardown := test.InitCmdStubber()
@@ -1208,8 +1221,9 @@ func TestPRMerge_interactive(t *testing.T) {
12081221
initWithStubs("blueberries",
12091222
stubResponse{200, bytes.NewBufferString(`
12101223
{ "data": { "repository": { "pullRequests": { "nodes": [
1211-
{ "headRefName": "blueberries", "id": "THE-ID", "number": 3}
1224+
{ "headRefName": "blueberries", "headRepositoryOwner": {"login": "OWNER"}, "id": "THE-ID", "number": 3}
12121225
] } } } }`)},
1226+
stubResponse{200, bytes.NewBufferString(`{"node_id": "THE-ID"}`)},
12131227
stubResponse{200, bytes.NewBufferString(`{ "data": {} }`)})
12141228

12151229
cs, cmdTeardown := test.InitCmdStubber()
@@ -1218,6 +1232,7 @@ func TestPRMerge_interactive(t *testing.T) {
12181232
cs.Stub("") // git config --get-regexp ^branch\.blueberries\.(remote|merge)$
12191233
cs.Stub("") // git symbolic-ref --quiet --short HEAD
12201234
cs.Stub("") // git checkout master
1235+
cs.Stub("") // git push origin --delete blueberries
12211236
cs.Stub("") // git branch -d
12221237

12231238
as, surveyTeardown := initAskStubber()
@@ -1239,7 +1254,7 @@ func TestPRMerge_interactive(t *testing.T) {
12391254
t.Fatalf("Got unexpected error running `pr merge` %s", err)
12401255
}
12411256

1242-
test.ExpectLines(t, output.String(), "Merged pull request #3", "Deleted local branch")
1257+
test.ExpectLines(t, output.String(), "Merged pull request #3", "Deleted branch blueberries")
12431258
}
12441259

12451260
func TestPrMerge_multipleMergeMethods(t *testing.T) {

git/git.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,12 @@ func DeleteLocalBranch(branch string) error {
209209
return err
210210
}
211211

212+
func HasLocalBranch(branch string) bool {
213+
configCmd := GitCommand("rev-parse", "--verify", "refs/heads/"+branch)
214+
_, err := run.PrepareCmd(configCmd).Output()
215+
return err == nil
216+
}
217+
212218
func CheckoutBranch(branch string) error {
213219
configCmd := GitCommand("checkout", branch)
214220
err := run.PrepareCmd(configCmd).Run()

0 commit comments

Comments
 (0)
X Tutup