Skip to content

Commit 7cc74c5

Browse files
committed
Isolate pr merge command
1 parent 74b191d commit 7cc74c5

5 files changed

Lines changed: 782 additions & 678 deletions

File tree

command/pr.go

Lines changed: 0 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,13 @@ import (
88
"strconv"
99
"strings"
1010

11-
"github.com/AlecAivazis/survey/v2"
1211
"github.com/MakeNowJust/heredoc"
1312
"github.com/cli/cli/api"
1413
"github.com/cli/cli/context"
1514
"github.com/cli/cli/git"
1615
"github.com/cli/cli/internal/ghrepo"
1716
"github.com/cli/cli/pkg/cmd/pr/shared"
1817
"github.com/cli/cli/pkg/cmdutil"
19-
"github.com/cli/cli/pkg/prompt"
2018
"github.com/cli/cli/pkg/text"
2119
"github.com/cli/cli/utils"
2220
"github.com/spf13/cobra"
@@ -31,11 +29,6 @@ func init() {
3129
prCmd.AddCommand(prStatusCmd)
3230
prCmd.AddCommand(prCloseCmd)
3331
prCmd.AddCommand(prReopenCmd)
34-
prCmd.AddCommand(prMergeCmd)
35-
prMergeCmd.Flags().BoolP("delete-branch", "d", true, "Delete the local and remote branch after merge")
36-
prMergeCmd.Flags().BoolP("merge", "m", false, "Merge the commits with the base branch")
37-
prMergeCmd.Flags().BoolP("rebase", "r", false, "Rebase the commits onto the base branch")
38-
prMergeCmd.Flags().BoolP("squash", "s", false, "Squash the commits into one commit and merge it into the base branch")
3932
prCmd.AddCommand(prReadyCmd)
4033

4134
prCmd.AddCommand(prListCmd)
@@ -93,18 +86,6 @@ var prReopenCmd = &cobra.Command{
9386
Args: cobra.ExactArgs(1),
9487
RunE: prReopen,
9588
}
96-
var prMergeCmd = &cobra.Command{
97-
Use: "merge [<number> | <url> | <branch>]",
98-
Short: "Merge a pull request",
99-
Long: heredoc.Doc(`
100-
Merge a pull request on GitHub.
101-
102-
By default, the head branch of the pull request will get deleted on both remote and local repositories.
103-
To retain the branch, use '--delete-branch=false'.
104-
`),
105-
Args: cobra.MaximumNArgs(1),
106-
RunE: prMerge,
107-
}
10889
var prReadyCmd = &cobra.Command{
10990
Use: "ready [<number> | <url> | <branch>]",
11091
Short: "Mark a pull request as ready for review",
@@ -367,200 +348,6 @@ func prReopen(cmd *cobra.Command, args []string) error {
367348
return nil
368349
}
369350

370-
func prMerge(cmd *cobra.Command, args []string) error {
371-
ctx := contextForCommand(cmd)
372-
apiClient, err := apiClientForContext(ctx)
373-
if err != nil {
374-
return err
375-
}
376-
377-
pr, baseRepo, err := prFromArgs(ctx, apiClient, cmd, args)
378-
if err != nil {
379-
return err
380-
}
381-
382-
if pr.Mergeable == "CONFLICTING" {
383-
err := fmt.Errorf("%s Pull request #%d (%s) has conflicts and isn't mergeable ", utils.Red("!"), pr.Number, pr.Title)
384-
return err
385-
} else if pr.Mergeable == "UNKNOWN" {
386-
err := fmt.Errorf("%s Pull request #%d (%s) can't be merged right now; try again in a few seconds", utils.Red("!"), pr.Number, pr.Title)
387-
return err
388-
} else if pr.State == "MERGED" {
389-
err := fmt.Errorf("%s Pull request #%d (%s) was already merged", utils.Red("!"), pr.Number, pr.Title)
390-
return err
391-
}
392-
393-
var mergeMethod api.PullRequestMergeMethod
394-
deleteBranch, err := cmd.Flags().GetBool("delete-branch")
395-
if err != nil {
396-
return err
397-
}
398-
399-
deleteLocalBranch := !cmd.Flags().Changed("repo")
400-
crossRepoPR := pr.HeadRepositoryOwner.Login != baseRepo.RepoOwner()
401-
402-
// Ensure only one merge method is specified
403-
enabledFlagCount := 0
404-
isInteractive := false
405-
if b, _ := cmd.Flags().GetBool("merge"); b {
406-
enabledFlagCount++
407-
mergeMethod = api.PullRequestMergeMethodMerge
408-
}
409-
if b, _ := cmd.Flags().GetBool("rebase"); b {
410-
enabledFlagCount++
411-
mergeMethod = api.PullRequestMergeMethodRebase
412-
}
413-
if b, _ := cmd.Flags().GetBool("squash"); b {
414-
enabledFlagCount++
415-
mergeMethod = api.PullRequestMergeMethodSquash
416-
}
417-
418-
if enabledFlagCount == 0 {
419-
if !connectedToTerminal(cmd) {
420-
return errors.New("--merge, --rebase, or --squash required when not attached to a tty")
421-
}
422-
isInteractive = true
423-
} else if enabledFlagCount > 1 {
424-
return errors.New("expected exactly one of --merge, --rebase, or --squash to be true")
425-
}
426-
427-
if isInteractive {
428-
mergeMethod, deleteBranch, err = prInteractiveMerge(deleteLocalBranch, crossRepoPR)
429-
if err != nil {
430-
return nil
431-
}
432-
}
433-
434-
var action string
435-
if mergeMethod == api.PullRequestMergeMethodRebase {
436-
action = "Rebased and merged"
437-
err = api.PullRequestMerge(apiClient, baseRepo, pr, api.PullRequestMergeMethodRebase)
438-
} else if mergeMethod == api.PullRequestMergeMethodSquash {
439-
action = "Squashed and merged"
440-
err = api.PullRequestMerge(apiClient, baseRepo, pr, api.PullRequestMergeMethodSquash)
441-
} else if mergeMethod == api.PullRequestMergeMethodMerge {
442-
action = "Merged"
443-
err = api.PullRequestMerge(apiClient, baseRepo, pr, api.PullRequestMergeMethodMerge)
444-
} else {
445-
err = fmt.Errorf("unknown merge method (%d) used", mergeMethod)
446-
return err
447-
}
448-
449-
if err != nil {
450-
return fmt.Errorf("API call failed: %w", err)
451-
}
452-
453-
if connectedToTerminal(cmd) {
454-
fmt.Fprintf(colorableErr(cmd), "%s %s pull request #%d (%s)\n", utils.Magenta("✔"), action, pr.Number, pr.Title)
455-
}
456-
457-
if deleteBranch {
458-
branchSwitchString := ""
459-
460-
if deleteLocalBranch && !crossRepoPR {
461-
currentBranch, err := ctx.Branch()
462-
if err != nil {
463-
return err
464-
}
465-
466-
var branchToSwitchTo string
467-
if currentBranch == pr.HeadRefName {
468-
branchToSwitchTo, err = api.RepoDefaultBranch(apiClient, baseRepo)
469-
if err != nil {
470-
return err
471-
}
472-
err = git.CheckoutBranch(branchToSwitchTo)
473-
if err != nil {
474-
return err
475-
}
476-
}
477-
478-
localBranchExists := git.HasLocalBranch(pr.HeadRefName)
479-
if localBranchExists {
480-
err = git.DeleteLocalBranch(pr.HeadRefName)
481-
if err != nil {
482-
err = fmt.Errorf("failed to delete local branch %s: %w", utils.Cyan(pr.HeadRefName), err)
483-
return err
484-
}
485-
}
486-
487-
if branchToSwitchTo != "" {
488-
branchSwitchString = fmt.Sprintf(" and switched to branch %s", utils.Cyan(branchToSwitchTo))
489-
}
490-
}
491-
492-
if !crossRepoPR {
493-
err = api.BranchDeleteRemote(apiClient, baseRepo, pr.HeadRefName)
494-
var httpErr api.HTTPError
495-
// The ref might have already been deleted by GitHub
496-
if err != nil && (!errors.As(err, &httpErr) || httpErr.StatusCode != 422) {
497-
err = fmt.Errorf("failed to delete remote branch %s: %w", utils.Cyan(pr.HeadRefName), err)
498-
return err
499-
}
500-
}
501-
502-
if connectedToTerminal(cmd) {
503-
fmt.Fprintf(colorableErr(cmd), "%s Deleted branch %s%s\n", utils.Red("✔"), utils.Cyan(pr.HeadRefName), branchSwitchString)
504-
}
505-
}
506-
507-
return nil
508-
}
509-
510-
func prInteractiveMerge(deleteLocalBranch bool, crossRepoPR bool) (api.PullRequestMergeMethod, bool, error) {
511-
mergeMethodQuestion := &survey.Question{
512-
Name: "mergeMethod",
513-
Prompt: &survey.Select{
514-
Message: "What merge method would you like to use?",
515-
Options: []string{"Create a merge commit", "Rebase and merge", "Squash and merge"},
516-
Default: "Create a merge commit",
517-
},
518-
}
519-
520-
qs := []*survey.Question{mergeMethodQuestion}
521-
522-
if !crossRepoPR {
523-
var message string
524-
if deleteLocalBranch {
525-
message = "Delete the branch locally and on GitHub?"
526-
} else {
527-
message = "Delete the branch on GitHub?"
528-
}
529-
530-
deleteBranchQuestion := &survey.Question{
531-
Name: "deleteBranch",
532-
Prompt: &survey.Confirm{
533-
Message: message,
534-
Default: true,
535-
},
536-
}
537-
qs = append(qs, deleteBranchQuestion)
538-
}
539-
540-
answers := struct {
541-
MergeMethod int
542-
DeleteBranch bool
543-
}{}
544-
545-
err := prompt.SurveyAsk(qs, &answers)
546-
if err != nil {
547-
return 0, false, fmt.Errorf("could not prompt: %w", err)
548-
}
549-
550-
var mergeMethod api.PullRequestMergeMethod
551-
switch answers.MergeMethod {
552-
case 0:
553-
mergeMethod = api.PullRequestMergeMethodMerge
554-
case 1:
555-
mergeMethod = api.PullRequestMergeMethodRebase
556-
case 2:
557-
mergeMethod = api.PullRequestMergeMethodSquash
558-
}
559-
560-
deleteBranch := answers.DeleteBranch
561-
return mergeMethod, deleteBranch, nil
562-
}
563-
564351
func prStateWithDraft(pr *api.PullRequest) string {
565352
if pr.IsDraft && pr.State == "OPEN" {
566353
return "DRAFT"

0 commit comments

Comments
 (0)