Merge pull request #5916 from cli/fork-without-rename

repo fork: directly fork under the desired name
This commit is contained in:
Mislav Marohnić 2022-07-12 11:37:59 +02:00 committed by GitHub
commit 1037917d67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 54 deletions

View file

@ -500,13 +500,16 @@ type repositoryV3 struct {
}
// ForkRepo forks the repository on GitHub and returns the new repository
func ForkRepo(client *Client, repo ghrepo.Interface, org string) (*Repository, error) {
func ForkRepo(client *Client, repo ghrepo.Interface, org, newName string) (*Repository, error) {
path := fmt.Sprintf("repos/%s/forks", ghrepo.FullName(repo))
params := map[string]interface{}{}
if org != "" {
params["organization"] = org
}
if newName != "" {
params["name"] = newName
}
body := &bytes.Buffer{}
enc := json.NewEncoder(body)

View file

@ -680,7 +680,7 @@ func handlePush(opts CreateOptions, ctx CreateContext) error {
// one by forking the base repository
if headRepo == nil && ctx.IsPushEnabled {
opts.IO.StartProgressIndicator()
headRepo, err = api.ForkRepo(client, ctx.BaseRepo, "")
headRepo, err = api.ForkRepo(client, ctx.BaseRepo, "", "")
opts.IO.StopProgressIndicator()
if err != nil {
return fmt.Errorf("error forking repo: %w", err)

View file

@ -6,7 +6,6 @@ import (
"net/http"
"os/exec"
"path/filepath"
"regexp"
"strings"
"github.com/AlecAivazis/survey/v2"
@ -16,6 +15,7 @@ import (
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/run"
"github.com/cli/cli/v2/pkg/cmd/repo/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/pkg/prompt"
@ -816,9 +816,9 @@ func interactiveSource() (string, error) {
}
func confirmSubmission(repoWithOwner, visibility string) error {
targetRepo := normalizeRepoName(repoWithOwner)
targetRepo := shared.NormalizeRepoName(repoWithOwner)
if idx := strings.IndexRune(repoWithOwner, '/'); idx > 0 {
targetRepo = repoWithOwner[0:idx+1] + normalizeRepoName(repoWithOwner[idx+1:])
targetRepo = repoWithOwner[0:idx+1] + shared.NormalizeRepoName(repoWithOwner[idx+1:])
}
var answer struct {
ConfirmSubmit bool
@ -838,8 +838,3 @@ func confirmSubmission(repoWithOwner, visibility string) error {
}
return nil
}
// normalizeRepoName takes in the repo name the user inputted and normalizes it using the same logic as GitHub (GitHub.com/new)
func normalizeRepoName(repoName string) string {
return strings.TrimSuffix(regexp.MustCompile(`[^\w._-]+`).ReplaceAllString(repoName, "-"), ".git")
}

View file

@ -495,44 +495,3 @@ func Test_createRun(t *testing.T) {
})
}
}
func Test_getModifiedNormalizedName(t *testing.T) {
// confirmed using GitHub.com/new
tests := []struct {
LocalName string
NormalizedName string
}{
{
LocalName: "cli",
NormalizedName: "cli",
},
{
LocalName: "cli.git",
NormalizedName: "cli",
},
{
LocalName: "@-#$^",
NormalizedName: "---",
},
{
LocalName: "[cli]",
NormalizedName: "-cli-",
},
{
LocalName: "Hello World, I'm a new repo!",
NormalizedName: "Hello-World-I-m-a-new-repo-",
},
{
LocalName: " @E3H*(#$#_$-ZVp,n.7lGq*_eMa-(-zAZSJYg!",
NormalizedName: "-E3H-_--ZVp-n.7lGq-_eMa---zAZSJYg-",
},
{
LocalName: "I'm a crazy .git repo name .git.git .git",
NormalizedName: "I-m-a-crazy-.git-repo-name-.git.git-",
},
}
for _, tt := range tests {
output := normalizeRepoName(tt.LocalName)
assert.Equal(t, tt.NormalizedName, output)
}
}

View file

@ -14,6 +14,7 @@ import (
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/run"
"github.com/cli/cli/v2/pkg/cmd/repo/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/pkg/prompt"
@ -179,7 +180,7 @@ func forkRun(opts *ForkOptions) error {
apiClient := api.NewClientFromHTTP(httpClient)
opts.IO.StartProgressIndicator()
forkedRepo, err := api.ForkRepo(apiClient, repoToFork, opts.Organization)
forkedRepo, err := api.ForkRepo(apiClient, repoToFork, opts.Organization, opts.ForkName)
opts.IO.StopProgressIndicator()
if err != nil {
return fmt.Errorf("failed to fork: %w", err)
@ -206,8 +207,8 @@ func forkRun(opts *ForkOptions) error {
}
}
// Rename the forked repo if ForkName is specified in opts.
if opts.ForkName != "" {
// Rename the new repo if necessary
if opts.ForkName != "" && !strings.EqualFold(forkedRepo.RepoName(), shared.NormalizeRepoName(opts.ForkName)) {
forkedRepo, err = api.RenameRepo(apiClient, forkedRepo, opts.ForkName)
if err != nil {
return fmt.Errorf("could not rename fork: %w", err)

View file

@ -0,0 +1,14 @@
package shared
import (
"regexp"
"strings"
)
var invalidCharactersRE = regexp.MustCompile(`[^\w._-]+`)
// NormalizeRepoName takes in the repo name the user inputted and normalizes it using the same logic as GitHub (GitHub.com/new)
func NormalizeRepoName(repoName string) string {
newName := invalidCharactersRE.ReplaceAllString(repoName, "-")
return strings.TrimSuffix(newName, ".git")
}

View file

@ -0,0 +1,48 @@
package shared
import (
"testing"
)
func TestNormalizeRepoName(t *testing.T) {
// confirmed using GitHub.com/new
tests := []struct {
LocalName string
NormalizedName string
}{
{
LocalName: "cli",
NormalizedName: "cli",
},
{
LocalName: "cli.git",
NormalizedName: "cli",
},
{
LocalName: "@-#$^",
NormalizedName: "---",
},
{
LocalName: "[cli]",
NormalizedName: "-cli-",
},
{
LocalName: "Hello World, I'm a new repo!",
NormalizedName: "Hello-World-I-m-a-new-repo-",
},
{
LocalName: " @E3H*(#$#_$-ZVp,n.7lGq*_eMa-(-zAZSJYg!",
NormalizedName: "-E3H-_--ZVp-n.7lGq-_eMa---zAZSJYg-",
},
{
LocalName: "I'm a crazy .git repo name .git.git .git",
NormalizedName: "I-m-a-crazy-.git-repo-name-.git.git-",
},
}
for _, tt := range tests {
output := NormalizeRepoName(tt.LocalName)
if output != tt.NormalizedName {
t.Errorf("Expected %q, got %q", tt.NormalizedName, output)
}
}
}