From 73c7fd59ef75a5248d71866dee76212a2b37b549 Mon Sep 17 00:00:00 2001 From: majiayu000 <1835304752@qq.com> Date: Fri, 26 Dec 2025 19:21:11 +0800 Subject: [PATCH 1/3] 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> --- pkg/cmd/repo/setdefault/setdefault.go | 34 +++++++++++++++------- pkg/cmd/repo/setdefault/setdefault_test.go | 30 +++++++++++++++---- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/pkg/cmd/repo/setdefault/setdefault.go b/pkg/cmd/repo/setdefault/setdefault.go index f2b4b52670f..63596d259d3 100644 --- a/pkg/cmd/repo/setdefault/setdefault.go +++ b/pkg/cmd/repo/setdefault/setdefault.go @@ -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) } diff --git a/pkg/cmd/repo/setdefault/setdefault_test.go b/pkg/cmd/repo/setdefault/setdefault_test.go index 0d2e2ddaacc..e910676eef8 100644 --- a/pkg/cmd/repo/setdefault/setdefault_test.go +++ b/pkg/cmd/repo/setdefault/setdefault_test.go @@ -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 From 5f305e81c28b2758abff976272bf232ce75cf933 Mon Sep 17 00:00:00 2001 From: majiayu000 <1835304752@qq.com> Date: Sat, 10 Jan 2026 00:54:51 +0800 Subject: [PATCH 2/3] Handle repo argument before remote name --- pkg/cmd/repo/setdefault/setdefault.go | 28 ++++++++++++---------- pkg/cmd/repo/setdefault/setdefault_test.go | 18 +++++++++++++- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/pkg/cmd/repo/setdefault/setdefault.go b/pkg/cmd/repo/setdefault/setdefault.go index 63596d259d3..71c993b9d1f 100644 --- a/pkg/cmd/repo/setdefault/setdefault.go +++ b/pkg/cmd/repo/setdefault/setdefault.go @@ -89,21 +89,23 @@ func NewCmdSetDefault(f *cmdutil.Factory, runF func(*SetDefaultOptions) error) * } if len(args) > 0 { - // 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 - } + var err error + opts.Repo, err = ghrepo.FromFullName(args[0]) + if err != nil { + if opts.Remotes == nil { + return fmt.Errorf("given arg is not a valid repo or git remote: %w", err) } - } - // 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 + + remotes, remoteErr := opts.Remotes() + if remoteErr != nil { + return remoteErr + } + + remote, findErr := remotes.FindByName(args[0]) + if findErr != nil { + return fmt.Errorf("given arg is not a valid repo or git remote: %w", err) } + opts.Repo = remote.Repo } } diff --git a/pkg/cmd/repo/setdefault/setdefault_test.go b/pkg/cmd/repo/setdefault/setdefault_test.go index e910676eef8..eb8d6282c23 100644 --- a/pkg/cmd/repo/setdefault/setdefault_test.go +++ b/pkg/cmd/repo/setdefault/setdefault_test.go @@ -50,7 +50,7 @@ func TestNewCmdSetDefault(t *testing.T) { }, input: "some_invalid_format", wantErr: true, - errMsg: `expected the "[HOST/]OWNER/REPO" format, got "some_invalid_format"`, + errMsg: `given arg is not a valid repo or git remote: expected the "[HOST/]OWNER/REPO" format, got "some_invalid_format"`, }, { name: "view flag", @@ -93,6 +93,22 @@ func TestNewCmdSetDefault(t *testing.T) { input: "origin", output: SetDefaultOptions{Repo: ghrepo.New("OWNER", "REPO")}, }, + { + name: "repo argument despite remote name matching owner/repo", + 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: "OWNER/REPO"}, + Repo: ghrepo.New("OTHER", "REPO"), + }, + }, nil + }, + input: "OWNER/REPO", + output: SetDefaultOptions{Repo: ghrepo.New("OWNER", "REPO")}, + }, } for _, tt := range tests { From 1d506f533111dfeebd14b086474d0e8cacfac84d Mon Sep 17 00:00:00 2001 From: lif <1835304752@qq.com> Date: Wed, 14 Jan 2026 18:35:04 +0800 Subject: [PATCH 3/3] fix: simplify set-default remote parsing --- pkg/cmd/repo/setdefault/setdefault.go | 4 ---- pkg/cmd/repo/setdefault/setdefault_test.go | 9 ++++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/repo/setdefault/setdefault.go b/pkg/cmd/repo/setdefault/setdefault.go index 71c993b9d1f..d4abac30925 100644 --- a/pkg/cmd/repo/setdefault/setdefault.go +++ b/pkg/cmd/repo/setdefault/setdefault.go @@ -92,10 +92,6 @@ func NewCmdSetDefault(f *cmdutil.Factory, runF func(*SetDefaultOptions) error) * var err error opts.Repo, err = ghrepo.FromFullName(args[0]) if err != nil { - if opts.Remotes == nil { - return fmt.Errorf("given arg is not a valid repo or git remote: %w", err) - } - remotes, remoteErr := opts.Remotes() if remoteErr != nil { return remoteErr diff --git a/pkg/cmd/repo/setdefault/setdefault_test.go b/pkg/cmd/repo/setdefault/setdefault_test.go index eb8d6282c23..5b2c4d6a30b 100644 --- a/pkg/cmd/repo/setdefault/setdefault_test.go +++ b/pkg/cmd/repo/setdefault/setdefault_test.go @@ -116,10 +116,17 @@ func TestNewCmdSetDefault(t *testing.T) { io.SetStdoutTTY(true) io.SetStdinTTY(true) io.SetStderrTTY(true) + remotesFunc := tt.remotes + if remotesFunc == nil { + remotesFunc = func() (context.Remotes, error) { + return context.Remotes{}, nil + } + } + f := &cmdutil.Factory{ IOStreams: io, GitClient: &git.Client{GitPath: "/fake/path/to/git"}, - Remotes: tt.remotes, + Remotes: remotesFunc, } var gotOpts *SetDefaultOptions