Repository: cli/cli
Pull request #6527 (2022-10-28 17:13:09)

Enable `gh pr create` from outside of a local git repo
Fixes https://github.com/cli/cli/issues/5906
Fixes https://github.com/cli/cli/issues/5075


Patch:
diff --git a/pkg/cmd/pr/create/create.go b/pkg/cmd/pr/create/create.go
index 75d4b58f1..1882488a8 100644
--- a/pkg/cmd/pr/create/create.go
+++ b/pkg/cmd/pr/create/create.go
@@ -480,9 +480,14 @@ func NewCreateContext(opts *CreateOptions) (*CreateContext, error) {
 	}
 	client := api.NewClientFromHTTP(httpClient)
 
+	// TODO: consider obtaining remotes from GitClient instead
 	remotes, err := opts.Remotes()
 	if err != nil {
-		return nil, err
+		// When a repo override value is given, ignore errors when fetching git remotes
+		// to support using this command outside of git repos.
+		if opts.RepoOverride == "" {
+			return nil, err
+		}
 	}
 
 	repoContext, err := ghContext.ResolveRemotesToRepos(remotes, client, opts.RepoOverride)


Test patch:
diff --git a/pkg/cmd/pr/create/create_test.go b/pkg/cmd/pr/create/create_test.go
index 0764ffe3b..1a709ee35 100644
--- a/pkg/cmd/pr/create/create_test.go
+++ b/pkg/cmd/pr/create/create_test.go
@@ -2,6 +2,7 @@ package create
 
 import (
 	"encoding/json"
+	"errors"
 	"fmt"
 	"net/http"
 	"os"
@@ -17,7 +18,6 @@ import (
 	"github.com/cli/cli/v2/internal/ghrepo"
 	"github.com/cli/cli/v2/internal/run"
 	"github.com/cli/cli/v2/pkg/cmd/pr/shared"
-	prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
 	"github.com/cli/cli/v2/pkg/cmdutil"
 	"github.com/cli/cli/v2/pkg/httpmock"
 	"github.com/cli/cli/v2/pkg/iostreams"
@@ -779,7 +779,7 @@ func Test_createRun(t *testing.T) {
 			setup: func(opts *CreateOptions, t *testing.T) func() {
 				tmpfile, err := os.CreateTemp(t.TempDir(), "testrecover*")
 				assert.NoError(t, err)
-				state := prShared.IssueMetadataState{
+				state := shared.IssueMetadataState{
 					Title:     "recovered title",
 					Body:      "recovered body",
 					Reviewers: []string{"jillValentine"},
@@ -811,6 +811,31 @@ func Test_createRun(t *testing.T) {
 			},
 			wantErr: "cannot open in browser: maximum URL length exceeded",
 		},
+		{
+			name: "no local git repo",
+			setup: func(opts *CreateOptions, t *testing.T) func() {
+				opts.Title = "My PR"
+				opts.TitleProvided = true
+				opts.Body = ""
+				opts.BodyProvided = true
+				opts.HeadBranch = "feature"
+				opts.RepoOverride = "OWNER/REPO"
+				opts.Remotes = func() (context.Remotes, error) {
+					return nil, errors.New("not a git repository")
+				}
+				return func() {}
+			},
+			httpStubs: func(reg *httpmock.Registry, t *testing.T) {
+				reg.Register(
+					httpmock.GraphQL(`mutation PullRequestCreate\b`),
+					httpmock.StringResponse(`
+						{ "data": { "createPullRequest": { "pullRequest": {
+							"URL": "https://github.com/OWNER/REPO/pull/12"
+						} } } }
+					`))
+			},
+			expectedOut: "https://github.com/OWNER/REPO/pull/12\n",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -997,7 +1022,7 @@ func Test_generateCompareURL(t *testing.T) {
 	tests := []struct {
 		name    string
 		ctx     CreateContext
-		state   prShared.IssueMetadataState
+		state   shared.IssueMetadataState
 		want    string
 		wantErr bool
 	}{
@@ -1018,7 +1043,7 @@ func Test_generateCompareURL(t *testing.T) {
 				BaseBranch:      "a",
 				HeadBranchLabel: "b",
 			},
-			state: prShared.IssueMetadataState{
+			state: shared.IssueMetadataState{
 				Labels: []string{"one", "two three"},
 			},
 			want:    "https://github.com/OWNER/REPO/compare/a...b?body=&expand=1&labels=one%2Ctwo+three",