11package command
22
33import (
4+ "errors"
45 "fmt"
56 "io"
67 "regexp"
78 "sort"
89 "strconv"
910 "strings"
1011
12+ "github.com/AlecAivazis/survey/v2"
1113 "github.com/cli/cli/api"
1214 "github.com/cli/cli/context"
1315 "github.com/cli/cli/git"
@@ -26,7 +28,8 @@ func init() {
2628 prCmd .AddCommand (prCloseCmd )
2729 prCmd .AddCommand (prReopenCmd )
2830 prCmd .AddCommand (prMergeCmd )
29- prMergeCmd .Flags ().BoolP ("merge" , "m" , true , "Merge the commits with the base branch" )
31+ prMergeCmd .Flags ().BoolP ("delete-branch" , "d" , true , "Delete the local branch after merge" )
32+ prMergeCmd .Flags ().BoolP ("merge" , "m" , false , "Merge the commits with the base branch" )
3033 prMergeCmd .Flags ().BoolP ("rebase" , "r" , false , "Rebase the commits onto the base branch" )
3134 prMergeCmd .Flags ().BoolP ("squash" , "s" , false , "Squash the commits into one commit and merge it into the base branch" )
3235 prCmd .AddCommand (prReadyCmd )
@@ -451,7 +454,15 @@ func prMerge(cmd *cobra.Command, args []string) error {
451454
452455 var pr * api.PullRequest
453456 if len (args ) > 0 {
454- pr , err = prFromArg (apiClient , baseRepo , args [0 ])
457+ var prNumber string
458+ n , _ := prFromURL (args [0 ])
459+ if n != "" {
460+ prNumber = n
461+ } else {
462+ prNumber = args [0 ]
463+ }
464+
465+ pr , err = prFromArg (apiClient , baseRepo , prNumber )
455466 if err != nil {
456467 return err
457468 }
@@ -471,41 +482,146 @@ func prMerge(cmd *cobra.Command, args []string) error {
471482 }
472483 }
473484
474- if pr .State == "MERGED" {
485+ if pr .Mergeable == "CONFLICTING" {
486+ err := fmt .Errorf ("%s Pull request #%d has conflicts and isn't mergeable " , utils .Red ("!" ), pr .Number )
487+ return err
488+ } else if pr .Mergeable == "UNKNOWN" {
489+ err := fmt .Errorf ("%s Pull request #%d can't be merged right now; try again in a few seconds" , utils .Red ("!" ), pr .Number )
490+ return err
491+ } else if pr .State == "MERGED" {
475492 err := fmt .Errorf ("%s Pull request #%d was already merged" , utils .Red ("!" ), pr .Number )
476493 return err
477494 }
478495
479- rebase , err := cmd .Flags ().GetBool ("rebase" )
496+ var mergeMethod api.PullRequestMergeMethod
497+ deleteBranch , err := cmd .Flags ().GetBool ("delete-branch" )
480498 if err != nil {
481499 return err
482500 }
483- squash , err := cmd .Flags ().GetBool ("squash" )
484- if err != nil {
485- return err
501+
502+ // Ensure only one merge method is specified
503+ enabledFlagCount := 0
504+ isInteractive := false
505+ if b , _ := cmd .Flags ().GetBool ("merge" ); b {
506+ enabledFlagCount ++
507+ mergeMethod = api .PullRequestMergeMethodMerge
508+ }
509+ if b , _ := cmd .Flags ().GetBool ("rebase" ); b {
510+ enabledFlagCount ++
511+ mergeMethod = api .PullRequestMergeMethodRebase
512+ }
513+ if b , _ := cmd .Flags ().GetBool ("squash" ); b {
514+ enabledFlagCount ++
515+ mergeMethod = api .PullRequestMergeMethodSquash
516+ }
517+
518+ if enabledFlagCount == 0 {
519+ isInteractive = true
520+ } else if enabledFlagCount > 1 {
521+ return errors .New ("expected exactly one of --merge, --rebase, or --squash to be true" )
522+ }
523+
524+ if isInteractive {
525+ mergeMethod , deleteBranch , err = prInteractiveMerge ()
526+ if err != nil {
527+ return nil
528+ }
486529 }
487530
488- var output string
489- if rebase {
490- output = fmt . Sprintf ( "%s Rebased and merged pull request #%d \n " , utils . Green ( "✔" ), pr . Number )
531+ var action string
532+ if mergeMethod == api . PullRequestMergeMethodRebase {
533+ action = " Rebased and merged"
491534 err = api .PullRequestMerge (apiClient , baseRepo , pr , api .PullRequestMergeMethodRebase )
492- } else if squash {
493- output = fmt . Sprintf ( "%s Squashed and merged pull request #%d \n " , utils . Green ( "✔" ), pr . Number )
535+ } else if mergeMethod == api . PullRequestMergeMethodSquash {
536+ action = " Squashed and merged"
494537 err = api .PullRequestMerge (apiClient , baseRepo , pr , api .PullRequestMergeMethodSquash )
495- } else {
496- output = fmt . Sprintf ( "%s Merged pull request #%d \n " , utils . Green ( "✔" ), pr . Number )
538+ } else if mergeMethod == api . PullRequestMergeMethodMerge {
539+ action = " Merged"
497540 err = api .PullRequestMerge (apiClient , baseRepo , pr , api .PullRequestMergeMethodMerge )
541+ } else {
542+ err = fmt .Errorf ("unknown merge method (%d) used" , mergeMethod )
543+ return err
498544 }
499545
500546 if err != nil {
501547 return fmt .Errorf ("API call failed: %w" , err )
502548 }
503549
504- fmt .Fprint (colorableOut (cmd ), output )
550+ fmt .Fprintf (colorableOut (cmd ), "%s %s pull request #%d\n " , utils .Magenta ("✔" ), action , pr .Number )
551+
552+ if deleteBranch && ! cmd .Flags ().Changed ("repo" ) {
553+ repo , err := api .GitHubRepo (apiClient , baseRepo )
554+ if err != nil {
555+ return err
556+ }
557+
558+ currentBranch , err := ctx .Branch ()
559+ if err != nil {
560+ return err
561+ }
562+
563+ if currentBranch == pr .HeadRefName {
564+ err = git .CheckoutBranch (repo .DefaultBranchRef .Name )
565+ if err != nil {
566+ return err
567+ }
568+ }
569+
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 ))
576+ }
505577
506578 return nil
507579}
508580
581+ func prInteractiveMerge () (api.PullRequestMergeMethod , bool , error ) {
582+ mergeMethodQuestion := & survey.Question {
583+ Name : "mergeMethod" ,
584+ Prompt : & survey.Select {
585+ Message : "What merge method would you like to use?" ,
586+ Options : []string {"Create a merge commit" , "Rebase and merge" , "Squash and merge" },
587+ Default : "Create a merge commit" ,
588+ },
589+ }
590+
591+ deleteBranchQuestion := & survey.Question {
592+ Name : "deleteBranch" ,
593+ Prompt : & survey.Confirm {
594+ Message : "Delete the branch locally?" ,
595+ Default : true ,
596+ },
597+ }
598+
599+ qs := []* survey.Question {mergeMethodQuestion , deleteBranchQuestion }
600+
601+ answers := struct {
602+ MergeMethod int
603+ DeleteBranch bool
604+ }{}
605+
606+ err := SurveyAsk (qs , & answers )
607+ if err != nil {
608+ return 0 , false , fmt .Errorf ("could not prompt: %w" , err )
609+ }
610+
611+ var mergeMethod api.PullRequestMergeMethod
612+ switch answers .MergeMethod {
613+ case 0 :
614+ mergeMethod = api .PullRequestMergeMethodMerge
615+ case 1 :
616+ mergeMethod = api .PullRequestMergeMethodRebase
617+ case 2 :
618+ mergeMethod = api .PullRequestMergeMethodSquash
619+ }
620+
621+ deleteBranch := answers .DeleteBranch
622+ return mergeMethod , deleteBranch , nil
623+ }
624+
509625func printPrPreview (out io.Writer , pr * api.PullRequest ) error {
510626 // Header (Title and State)
511627 fmt .Fprintln (out , utils .Bold (pr .Title ))
0 commit comments