From a8fdd9a303526f2570da7969530e0dbd0d2ccb3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Wed, 17 Feb 2021 20:22:23 +0100 Subject: [PATCH 1/3] Further clarify what will happen on `repo create` In local git directory: 1. `This will add an "origin" git remote to your local repository. Continue?` 2. "origin" git remote is added in current directory. Outside of a local git directory: 1. This will create the "REPO" repository on GitHub. Continue? 2. `Create a local project directory for "REPO"?` 3. new directory called "REPO" now set up for the GitHub repository. --- pkg/cmd/repo/create/create.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/pkg/cmd/repo/create/create.go b/pkg/cmd/repo/create/create.go index 2aa7834ea..0b193b426 100644 --- a/pkg/cmd/repo/create/create.go +++ b/pkg/cmd/repo/create/create.go @@ -132,6 +132,7 @@ func createRun(opts *CreateOptions) error { isNameAnArg := false isDescEmpty := opts.Description == "" isVisibilityPassed := false + inLocalRepo := projectDirErr == nil if opts.Name != "" { isNameAnArg = true @@ -250,7 +251,7 @@ func createRun(opts *CreateOptions) error { createLocalDirectory := opts.ConfirmSubmit if !opts.ConfirmSubmit { - opts.ConfirmSubmit, err = confirmSubmission(input.Name, input.OwnerID, projectDirErr) + opts.ConfirmSubmit, err = confirmSubmission(input.Name, input.OwnerID, inLocalRepo) if err != nil { return err } @@ -284,7 +285,7 @@ func createRun(opts *CreateOptions) error { } remoteURL := ghrepo.FormatRemoteURL(repo, protocol) - if projectDirErr == nil { + if inLocalRepo { _, err = git.AddRemote("origin", remoteURL) if err != nil { return err @@ -295,7 +296,7 @@ func createRun(opts *CreateOptions) error { } else { if opts.IO.CanPrompt() { if !createLocalDirectory { - err := prompt.Confirm(fmt.Sprintf("Create a local project directory for %s?", ghrepo.FullName(repo)), &createLocalDirectory) + err := prompt.Confirm(fmt.Sprintf(`Create a local project directory for "%s"?`, ghrepo.FullName(repo)), &createLocalDirectory) if err != nil { return err } @@ -388,16 +389,18 @@ func interactiveRepoCreate(isDescEmpty bool, isVisibilityPassed bool, repoName s return answers.RepoName, answers.RepoDescription, strings.ToUpper(answers.RepoVisibility), nil } -func confirmSubmission(repoName string, repoOwner string, projectDirErr error) (bool, error) { +func confirmSubmission(repoName string, repoOwner string, inLocalRepo bool) (bool, error) { qs := []*survey.Question{} promptString := "" - if projectDirErr == nil { - promptString = "This will add remote origin to your current directory. Continue? " - } else if repoOwner != "" { - promptString = fmt.Sprintf("This will create '%s/%s' in your current directory. Continue? ", repoOwner, repoName) + if inLocalRepo { + promptString = `This will add an "origin" git remote to your local repository. Continue?` } else { - promptString = fmt.Sprintf("This will create '%s' in your current directory. Continue? ", repoName) + targetRepo := repoName + if repoOwner != "" { + targetRepo = fmt.Sprintf("%s/%s", repoOwner, repoName) + } + promptString = fmt.Sprintf(`This will create the "%s" repository on GitHub. Continue?`, targetRepo) } confirmSubmitQuestion := &survey.Question{ From e596f8732b0c3402026d07b4524ec4bdebd1f596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Wed, 17 Feb 2021 20:26:06 +0100 Subject: [PATCH 2/3] Fix creating a repository from template Fixes a problem where setting up a new local directory for the repository created from a template would not contain any files: gh repo create -p OWNER/some-template my-repo --private --confirm ls my-repo //=> [empty directory] Fixes #2290 --- pkg/cmd/repo/create/create.go | 59 +++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/pkg/cmd/repo/create/create.go b/pkg/cmd/repo/create/create.go index 0b193b426..30ddaaf90 100644 --- a/pkg/cmd/repo/create/create.go +++ b/pkg/cmd/repo/create/create.go @@ -304,32 +304,11 @@ func createRun(opts *CreateOptions) error { } if createLocalDirectory { path := repo.Name - - gitInit, err := git.GitCommand("init", path) - if err != nil { - return err - } - isTTY := opts.IO.IsStdoutTTY() - if isTTY { - gitInit.Stdout = stdout - } - gitInit.Stderr = stderr - err = run.PrepareCmd(gitInit).Run() - if err != nil { - return err - } - gitRemoteAdd, err := git.GitCommand("-C", path, "remote", "add", "origin", remoteURL) - if err != nil { - return err - } - gitRemoteAdd.Stdout = stdout - gitRemoteAdd.Stderr = stderr - err = run.PrepareCmd(gitRemoteAdd).Run() - if err != nil { + if err := localInit(opts.IO, remoteURL, path, opts.Template != ""); err != nil { return err } if isTTY { - fmt.Fprintf(stderr, "%s Initialized repository in './%s/'\n", cs.SuccessIcon(), path) + fmt.Fprintf(stderr, "%s Initialized repository in \"%s\"\n", cs.SuccessIcon(), path) } } } @@ -340,6 +319,40 @@ func createRun(opts *CreateOptions) error { return nil } +func localInit(io *iostreams.IOStreams, remoteURL, path string, isTemplate bool) error { + if isTemplate { + cloneCmd, err := git.GitCommand("clone", remoteURL, path) + if err != nil { + return err + } + cloneCmd.Stdout = io.Out + cloneCmd.Stderr = io.ErrOut + return run.PrepareCmd(cloneCmd).Run() + } + + gitInit, err := git.GitCommand("init", path) + if err != nil { + return err + } + isTTY := io.IsStdoutTTY() + if isTTY { + gitInit.Stdout = io.Out + } + gitInit.Stderr = io.ErrOut + err = run.PrepareCmd(gitInit).Run() + if err != nil { + return err + } + + gitRemoteAdd, err := git.GitCommand("-C", path, "remote", "add", "origin", remoteURL) + if err != nil { + return err + } + gitRemoteAdd.Stdout = io.Out + gitRemoteAdd.Stderr = io.ErrOut + return run.PrepareCmd(gitRemoteAdd).Run() +} + func interactiveRepoCreate(isDescEmpty bool, isVisibilityPassed bool, repoName string) (string, string, string, error) { qs := []*survey.Question{} From 9e63199a652a8e8791d03558b4f24be8c4ed42ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 1 Mar 2021 14:12:56 +0100 Subject: [PATCH 3/3] Add tests for checking out repository after creating from template --- pkg/cmd/repo/create/create.go | 51 ++++++++++++++++++++++-------- pkg/cmd/repo/create/create_test.go | 36 ++++++++------------- 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/pkg/cmd/repo/create/create.go b/pkg/cmd/repo/create/create.go index 30ddaaf90..4ab6025f9 100644 --- a/pkg/cmd/repo/create/create.go +++ b/pkg/cmd/repo/create/create.go @@ -202,6 +202,7 @@ func createRun(opts *CreateOptions) error { repoToCreate = ghrepo.New("", opts.Name) } + var templateRepoMainBranch string // Find template repo ID if opts.Template != "" { httpClient, err := opts.HttpClient() @@ -231,6 +232,7 @@ func createRun(opts *CreateOptions) error { } opts.Template = repo.ID + templateRepoMainBranch = repo.DefaultBranchRef.Name } input := repoCreateInput{ @@ -304,7 +306,14 @@ func createRun(opts *CreateOptions) error { } if createLocalDirectory { path := repo.Name - if err := localInit(opts.IO, remoteURL, path, opts.Template != ""); err != nil { + checkoutBranch := "" + if opts.Template != "" { + // NOTE: we cannot read `defaultBranchRef` from the newly created repository as it will + // be null at this time. Instead, we assume that the main branch name of the new + // repository will be the same as that of the template repository. + checkoutBranch = templateRepoMainBranch + } + if err := localInit(opts.IO, remoteURL, path, checkoutBranch); err != nil { return err } if isTTY { @@ -319,17 +328,7 @@ func createRun(opts *CreateOptions) error { return nil } -func localInit(io *iostreams.IOStreams, remoteURL, path string, isTemplate bool) error { - if isTemplate { - cloneCmd, err := git.GitCommand("clone", remoteURL, path) - if err != nil { - return err - } - cloneCmd.Stdout = io.Out - cloneCmd.Stderr = io.ErrOut - return run.PrepareCmd(cloneCmd).Run() - } - +func localInit(io *iostreams.IOStreams, remoteURL, path, checkoutBranch string) error { gitInit, err := git.GitCommand("init", path) if err != nil { return err @@ -350,7 +349,33 @@ func localInit(io *iostreams.IOStreams, remoteURL, path string, isTemplate bool) } gitRemoteAdd.Stdout = io.Out gitRemoteAdd.Stderr = io.ErrOut - return run.PrepareCmd(gitRemoteAdd).Run() + err = run.PrepareCmd(gitRemoteAdd).Run() + if err != nil { + return err + } + + if checkoutBranch == "" { + return nil + } + + gitFetch, err := git.GitCommand("-C", path, "fetch", "origin", fmt.Sprintf("+refs/heads/%[1]s:refs/remotes/origin/%[1]s", checkoutBranch)) + if err != nil { + return err + } + gitFetch.Stdout = io.Out + gitFetch.Stderr = io.ErrOut + err = run.PrepareCmd(gitFetch).Run() + if err != nil { + return err + } + + gitCheckout, err := git.GitCommand("-C", path, "checkout", checkoutBranch) + if err != nil { + return err + } + gitCheckout.Stdout = io.Out + gitCheckout.Stderr = io.ErrOut + return run.PrepareCmd(gitCheckout).Run() } func interactiveRepoCreate(isDescEmpty bool, isVisibilityPassed bool, repoName string) (string, string, string, error) { diff --git a/pkg/cmd/repo/create/create_test.go b/pkg/cmd/repo/create/create_test.go index 9914807ea..9aa975724 100644 --- a/pkg/cmd/repo/create/create_test.go +++ b/pkg/cmd/repo/create/create_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + "github.com/MakeNowJust/heredoc" "github.com/cli/cli/internal/config" "github.com/cli/cli/internal/run" "github.com/cli/cli/pkg/cmdutil" @@ -345,6 +346,7 @@ func TestRepoCreate_orgWithTeam(t *testing.T) { func TestRepoCreate_template(t *testing.T) { reg := &httpmock.Registry{} + defer reg.Verify(t) reg.Register( httpmock.GraphQL(`mutation CloneTemplateRepository\b`), httpmock.StringResponse(` @@ -370,32 +372,26 @@ func TestRepoCreate_template(t *testing.T) { cs, cmdTeardown := run.Stub() defer cmdTeardown(t) - cs.Register(`git remote add -f origin https://github\.com/OWNER/REPO\.git`, 0, "") - cs.Register(`git rev-parse --show-toplevel`, 0, "") + cs.Register(`git rev-parse --show-toplevel`, 1, "") + cs.Register(`git init REPO`, 0, "") + cs.Register(`git -C REPO remote add`, 0, "") + cs.Register(`git -C REPO fetch origin \+refs/heads/main:refs/remotes/origin/main`, 0, "") + cs.Register(`git -C REPO checkout main`, 0, "") - as, surveyTearDown := prompt.InitAskStubber() + _, surveyTearDown := prompt.InitAskStubber() defer surveyTearDown() - as.Stub([]*prompt.QuestionStub{ - { - Name: "repoVisibility", - Value: "PRIVATE", - }, - }) - as.Stub([]*prompt.QuestionStub{ - { - Name: "confirmSubmit", - Value: true, - }, - }) - - output, err := runCommand(httpClient, "REPO --template='OWNER/REPO'", true) + output, err := runCommand(httpClient, "REPO -y --private --template='OWNER/REPO'", true) if err != nil { t.Errorf("error running command `repo create`: %v", err) + return } assert.Equal(t, "", output.String()) - assert.Equal(t, "āœ“ Created repository OWNER/REPO on GitHub\nāœ“ Added remote https://github.com/OWNER/REPO.git\n", output.Stderr()) + assert.Equal(t, heredoc.Doc(` + āœ“ Created repository OWNER/REPO on GitHub + āœ“ Initialized repository in "REPO" + `), output.Stderr()) var reqBody struct { Query string @@ -404,10 +400,6 @@ func TestRepoCreate_template(t *testing.T) { } } - if len(reg.Requests) != 3 { - t.Fatalf("expected 3 HTTP requests, got %d", len(reg.Requests)) - } - bodyBytes, _ := ioutil.ReadAll(reg.Requests[2].Body) _ = json.Unmarshal(bodyBytes, &reqBody) if repoName := reqBody.Variables.Input["name"].(string); repoName != "REPO" {