feat: allow git remote names in gh repo set-default

When specifying a repository for `gh repo set-default`, users can now
use a git remote name (e.g., "origin", "upstream") instead of the
full OWNER/REPO format.

The command first checks if the argument is a git remote name. If
found, it uses the corresponding repository. Otherwise, it falls
back to parsing the argument as OWNER/REPO format.

Example:
  gh repo set-default origin

Fixes #9149

Signed-off-by: majiayu000 <1835304752@qq.com>
This commit is contained in:
majiayu000 2025-12-26 19:21:11 +08:00
parent 6acf74e04e
commit 73c7fd59ef
2 changed files with 49 additions and 15 deletions

View file

@ -70,6 +70,9 @@ func NewCmdSetDefault(f *cmdutil.Factory, runF func(*SetDefaultOptions) error) *
# Set a repository explicitly
$ gh repo set-default owner/repo
# Set a repository using a git remote name
$ gh repo set-default origin
# View the current default repository
$ gh repo set-default --view
@ -79,11 +82,28 @@ func NewCmdSetDefault(f *cmdutil.Factory, runF func(*SetDefaultOptions) error) *
`),
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if isLocal, err := opts.GitClient.IsLocalGitRepo(cmd.Context()); err != nil {
return err
} else if !isLocal {
return errors.New("must be run from inside a git repository")
}
if len(args) > 0 {
var err error
opts.Repo, err = ghrepo.FromFullName(args[0])
if err != nil {
return err
// First, try to find argument as a git remote name
if !strings.Contains(args[0], "/") && opts.Remotes != nil {
if remotes, err := opts.Remotes(); err == nil {
if remote, err := remotes.FindByName(args[0]); err == nil {
opts.Repo = remote.Repo
}
}
}
// If not found as remote name, try parsing as OWNER/REPO
if opts.Repo == nil {
var err error
opts.Repo, err = ghrepo.FromFullName(args[0])
if err != nil {
return err
}
}
}
@ -91,12 +111,6 @@ func NewCmdSetDefault(f *cmdutil.Factory, runF func(*SetDefaultOptions) error) *
return cmdutil.FlagErrorf("repository required when not running interactively")
}
if isLocal, err := opts.GitClient.IsLocalGitRepo(cmd.Context()); err != nil {
return err
} else if !isLocal {
return errors.New("must be run from inside a git repository")
}
if runF != nil {
return runF(opts)
}

View file

@ -21,6 +21,7 @@ func TestNewCmdSetDefault(t *testing.T) {
tests := []struct {
name string
gitStubs func(*run.CommandStubber)
remotes func() (context.Remotes, error)
input string
output SetDefaultOptions
wantErr bool
@ -43,11 +44,13 @@ func TestNewCmdSetDefault(t *testing.T) {
output: SetDefaultOptions{Repo: ghrepo.New("cli", "cli")},
},
{
name: "invalid repo argument",
gitStubs: func(cs *run.CommandStubber) {},
input: "some_invalid_format",
wantErr: true,
errMsg: `expected the "[HOST/]OWNER/REPO" format, got "some_invalid_format"`,
name: "invalid repo argument",
gitStubs: func(cs *run.CommandStubber) {
cs.Register(`git rev-parse --git-dir`, 0, ".git")
},
input: "some_invalid_format",
wantErr: true,
errMsg: `expected the "[HOST/]OWNER/REPO" format, got "some_invalid_format"`,
},
{
name: "view flag",
@ -74,6 +77,22 @@ func TestNewCmdSetDefault(t *testing.T) {
wantErr: true,
errMsg: "must be run from inside a git repository",
},
{
name: "remote name argument",
gitStubs: func(cs *run.CommandStubber) {
cs.Register(`git rev-parse --git-dir`, 0, ".git")
},
remotes: func() (context.Remotes, error) {
return context.Remotes{
{
Remote: &git.Remote{Name: "origin"},
Repo: ghrepo.New("OWNER", "REPO"),
},
}, nil
},
input: "origin",
output: SetDefaultOptions{Repo: ghrepo.New("OWNER", "REPO")},
},
}
for _, tt := range tests {
@ -84,6 +103,7 @@ func TestNewCmdSetDefault(t *testing.T) {
f := &cmdutil.Factory{
IOStreams: io,
GitClient: &git.Client{GitPath: "/fake/path/to/git"},
Remotes: tt.remotes,
}
var gotOpts *SetDefaultOptions