Merge pull request #4886 from g14a/feature/target-repo-fork-name
Feature/target repo fork name
This commit is contained in:
commit
a564909f03
7 changed files with 140 additions and 68 deletions
|
|
@ -326,6 +326,7 @@ func (c Client) REST(hostname string, method string, p string, body io.Reader, d
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cli/cli/v2/internal/ghinstance"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
|
@ -524,6 +525,37 @@ func ForkRepo(client *Client, repo ghrepo.Interface, org string) (*Repository, e
|
|||
}, nil
|
||||
}
|
||||
|
||||
// RenameRepo renames the repository on GitHub and returns the renamed repository
|
||||
func RenameRepo(client *Client, repo ghrepo.Interface, newRepoName string) (*Repository, error) {
|
||||
input := map[string]string{"name": newRepoName}
|
||||
body := &bytes.Buffer{}
|
||||
enc := json.NewEncoder(body)
|
||||
if err := enc.Encode(input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%srepos/%s",
|
||||
ghinstance.RESTPrefix(repo.RepoHost()),
|
||||
ghrepo.FullName(repo))
|
||||
|
||||
result := repositoryV3{}
|
||||
err := client.REST(repo.RepoHost(), "PATCH", path, body, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Repository{
|
||||
ID: result.NodeID,
|
||||
Name: result.Name,
|
||||
CreatedAt: result.CreatedAt,
|
||||
Owner: RepositoryOwner{
|
||||
Login: result.Owner.Login,
|
||||
},
|
||||
ViewerPermission: "WRITE",
|
||||
hostname: repo.RepoHost(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func LastCommit(client *Client, repo ghrepo.Interface) (*Commit, error) {
|
||||
var responseData struct {
|
||||
Repository struct {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ type ForkOptions struct {
|
|||
PromptRemote bool
|
||||
RemoteName string
|
||||
Organization string
|
||||
ForkName string
|
||||
Rename bool
|
||||
}
|
||||
|
||||
|
|
@ -115,6 +116,7 @@ Additional 'git clone' flags can be passed in by listing them after '--'.`,
|
|||
cmd.Flags().BoolVar(&opts.Remote, "remote", false, "Add remote for fork {true|false}")
|
||||
cmd.Flags().StringVar(&opts.RemoteName, "remote-name", defaultRemoteName, "Specify a name for a fork's new remote.")
|
||||
cmd.Flags().StringVar(&opts.Organization, "org", "", "Create the fork in an organization")
|
||||
cmd.Flags().StringVar(&opts.ForkName, "fork-name", "", "Specify a name for the forked repo")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
@ -201,6 +203,17 @@ func forkRun(opts *ForkOptions) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Rename the forked repo if ForkName is specified in opts.
|
||||
if opts.ForkName != "" {
|
||||
forkedRepo, err = api.RenameRepo(apiClient, forkedRepo, opts.ForkName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not rename fork: %w", err)
|
||||
}
|
||||
if connectedToTerminal {
|
||||
fmt.Fprintf(stderr, "%s Renamed fork to %s\n", cs.SuccessIconWithColor(cs.Green), cs.Bold(ghrepo.FullName(forkedRepo)))
|
||||
}
|
||||
}
|
||||
|
||||
if (inParent && (!opts.Remote && !opts.PromptRemote)) || (!inParent && (!opts.Clone && !opts.PromptClone)) {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,16 @@ func TestNewCmdFork(t *testing.T) {
|
|||
wantErr: true,
|
||||
errMsg: "unknown flag: --depth\nSeparate git clone flags with '--'.",
|
||||
},
|
||||
{
|
||||
name: "with fork name",
|
||||
cli: "--fork-name new-fork",
|
||||
wants: ForkOptions{
|
||||
Remote: false,
|
||||
RemoteName: "origin",
|
||||
ForkName: "new-fork",
|
||||
Rename: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -534,6 +544,78 @@ func TestRepoFork(t *testing.T) {
|
|||
cs.Register(`git -C REPO remote add -f upstream https://github\.com/OWNER/REPO\.git`, 0, "")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non tty repo arg with fork-name",
|
||||
opts: &ForkOptions{
|
||||
Repository: "someone/REPO",
|
||||
Clone: false,
|
||||
ForkName: "NEW_REPO",
|
||||
},
|
||||
tty: false,
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
forkResult := `{
|
||||
"node_id": "123",
|
||||
"name": "REPO",
|
||||
"clone_url": "https://github.com/OWNER/REPO.git",
|
||||
"created_at": "2011-01-26T19:01:12Z",
|
||||
"owner": {
|
||||
"login": "OWNER"
|
||||
}
|
||||
}`
|
||||
renameResult := `{
|
||||
"node_id": "1234",
|
||||
"name": "NEW_REPO",
|
||||
"clone_url": "https://github.com/OWNER/NEW_REPO.git",
|
||||
"created_at": "2012-01-26T19:01:12Z",
|
||||
"owner": {
|
||||
"login": "OWNER"
|
||||
}
|
||||
}`
|
||||
reg.Register(
|
||||
httpmock.REST("POST", "repos/someone/REPO/forks"),
|
||||
httpmock.StringResponse(forkResult))
|
||||
reg.Register(
|
||||
httpmock.REST("PATCH", "repos/OWNER/REPO"),
|
||||
httpmock.StringResponse(renameResult))
|
||||
},
|
||||
wantErrOut: "",
|
||||
},
|
||||
{
|
||||
name: "tty repo arg with fork-name",
|
||||
opts: &ForkOptions{
|
||||
Repository: "someone/REPO",
|
||||
Clone: false,
|
||||
ForkName: "NEW_REPO",
|
||||
},
|
||||
tty: true,
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
forkResult := `{
|
||||
"node_id": "123",
|
||||
"name": "REPO",
|
||||
"clone_url": "https://github.com/OWNER/REPO.git",
|
||||
"created_at": "2011-01-26T19:01:12Z",
|
||||
"owner": {
|
||||
"login": "OWNER"
|
||||
}
|
||||
}`
|
||||
renameResult := `{
|
||||
"node_id": "1234",
|
||||
"name": "NEW_REPO",
|
||||
"clone_url": "https://github.com/OWNER/NEW_REPO.git",
|
||||
"created_at": "2012-01-26T19:01:12Z",
|
||||
"owner": {
|
||||
"login": "OWNER"
|
||||
}
|
||||
}`
|
||||
reg.Register(
|
||||
httpmock.REST("POST", "repos/someone/REPO/forks"),
|
||||
httpmock.StringResponse(forkResult))
|
||||
reg.Register(
|
||||
httpmock.REST("PATCH", "repos/OWNER/REPO"),
|
||||
httpmock.StringResponse(renameResult))
|
||||
},
|
||||
wantErrOut: "✓ Created fork OWNER/REPO\n✓ Renamed fork to OWNER/NEW_REPO\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
package rename
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/cli/cli/v2/api"
|
||||
"github.com/cli/cli/v2/internal/ghinstance"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
)
|
||||
|
||||
func apiRename(client *http.Client, repo ghrepo.Interface, newRepoName string) (ghrepo.Interface, error) {
|
||||
input := map[string]string{"name": newRepoName}
|
||||
body, err := json.Marshal(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%srepos/%s",
|
||||
ghinstance.RESTPrefix(repo.RepoHost()),
|
||||
ghrepo.FullName(repo))
|
||||
|
||||
request, err := http.NewRequest("PATCH", path, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode > 299 {
|
||||
return nil, api.HandleHTTPError(resp)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := struct {
|
||||
Name string
|
||||
Owner struct {
|
||||
Login string
|
||||
}
|
||||
}{}
|
||||
if err := json.Unmarshal(b, &result); err != nil {
|
||||
return nil, fmt.Errorf("error unmarshaling response: %w", err)
|
||||
}
|
||||
|
||||
newRepo := ghrepo.NewWithHost(result.Owner.Login, result.Name, repo.RepoHost())
|
||||
|
||||
return newRepo, nil
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package rename
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cli/cli/v2/api"
|
||||
"net/http"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
|
|
@ -114,11 +115,15 @@ func renameRun(opts *RenameOptions) error {
|
|||
}
|
||||
}
|
||||
|
||||
newRepo, err := apiRename(httpClient, currRepo, newRepoName)
|
||||
apiClient := api.NewClientFromHTTP(httpClient)
|
||||
|
||||
newRepo, err := api.RenameRepo(apiClient, currRepo, newRepoName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
renamedRepo := ghrepo.New(newRepo.Owner.Login, newRepo.Name)
|
||||
|
||||
cs := opts.IO.ColorScheme()
|
||||
if opts.IO.IsStdoutTTY() {
|
||||
fmt.Fprintf(opts.IO.Out, "%s Renamed repository %s\n", cs.SuccessIcon(), ghrepo.FullName(newRepo))
|
||||
|
|
@ -128,7 +133,7 @@ func renameRun(opts *RenameOptions) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
remote, err := updateRemote(currRepo, newRepo, opts)
|
||||
remote, err := updateRemote(currRepo, renamedRepo, opts)
|
||||
if err != nil {
|
||||
fmt.Fprintf(opts.IO.ErrOut, "%s Warning: unable to update remote %q: %v\n", cs.WarningIcon(), remote.Name, err)
|
||||
} else if opts.IO.IsStdoutTTY() {
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ func TestRenameRun(t *testing.T) {
|
|||
httpStubs: func(reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
httpmock.REST("PATCH", "repos/OWNER/REPO"),
|
||||
httpmock.StatusStringResponse(204, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
|
||||
httpmock.StatusStringResponse(200, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
|
||||
},
|
||||
execStubs: func(cs *run.CommandStubber) {
|
||||
cs.Register(`git remote set-url origin https://github.com/OWNER/NEW_REPO.git`, 0, "")
|
||||
|
|
@ -143,7 +143,7 @@ func TestRenameRun(t *testing.T) {
|
|||
httpStubs: func(reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
httpmock.REST("PATCH", "repos/OWNER/REPO"),
|
||||
httpmock.StatusStringResponse(204, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
|
||||
httpmock.StatusStringResponse(200, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
|
||||
},
|
||||
tty: true,
|
||||
},
|
||||
|
|
@ -156,7 +156,7 @@ func TestRenameRun(t *testing.T) {
|
|||
httpStubs: func(reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
httpmock.REST("PATCH", "repos/OWNER/REPO"),
|
||||
httpmock.StatusStringResponse(204, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
|
||||
httpmock.StatusStringResponse(200, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
|
||||
},
|
||||
execStubs: func(cs *run.CommandStubber) {
|
||||
cs.Register(`git remote set-url origin https://github.com/OWNER/NEW_REPO.git`, 0, "")
|
||||
|
|
@ -171,7 +171,7 @@ func TestRenameRun(t *testing.T) {
|
|||
httpStubs: func(reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
httpmock.REST("PATCH", "repos/OWNER/REPO"),
|
||||
httpmock.StatusStringResponse(204, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
|
||||
httpmock.StatusStringResponse(200, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
|
||||
},
|
||||
execStubs: func(cs *run.CommandStubber) {
|
||||
cs.Register(`git remote set-url origin https://github.com/OWNER/NEW_REPO.git`, 0, "")
|
||||
|
|
@ -192,7 +192,7 @@ func TestRenameRun(t *testing.T) {
|
|||
httpStubs: func(reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
httpmock.REST("PATCH", "repos/OWNER/REPO"),
|
||||
httpmock.StatusStringResponse(204, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
|
||||
httpmock.StatusStringResponse(200, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
|
||||
},
|
||||
execStubs: func(cs *run.CommandStubber) {
|
||||
cs.Register(`git remote set-url origin https://github.com/OWNER/NEW_REPO.git`, 0, "")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue