Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[gh repo clone] Add --no-upstream flag #10608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: trunk
Choose a base branch
from

Conversation

iamazeem
Copy link
Contributor

@iamazeem iamazeem commented Mar 15, 2025

Fixes #8274.

Tests

bin/gh repo clone --help

Output
$ bin/gh repo clone --help 
Clone a GitHub repository locally. Pass additional `git clone` flags by listing
them after `--`.

If the `OWNER/` portion of the `OWNER/REPO` repository argument is omitted, it
defaults to the name of the authenticating user.

When a protocol scheme is not provided in the repository argument, the `git_protocol` will be
chosen from your configuration, which can be checked via `gh config get git_protocol`. If the protocol
scheme is provided, the repository will be cloned using the specified protocol.

If the repository is a fork, its parent repository will be added as an additional
git remote called `upstream`. The remote name can be configured using `--upstream-remote-name`.
The `--upstream-remote-name` option supports an `@owner` value which will name
the remote after the owner of the parent repository.

If the repository is a fork, its parent repository will be set as the default remote repository.

To disable the addition of the `upstream` remote for a forked repository,
use the `--no-upstream` flag. For a non-forked repository, this flag has no effect.


USAGE
  gh repo clone <repository> [<directory>] [flags] [-- <gitflags>...]

FLAGS
      --no-upstream                   Do not set/fetch upstream remote when cloning a fork
  -u, --upstream-remote-name string   Upstream remote name when cloning a fork (default "upstream")

INHERITED FLAGS
  --help   Show help for command

EXAMPLES
  # Clone a repository from a specific org
  $ gh repo clone cli/cli
  
  # Clone a repository from your own account
  $ gh repo clone myrepo
  
  # Clone a repo, overriding git protocol configuration
  $ gh repo clone https://github.com/cli/cli
  $ gh repo clone [email protected]:cli/cli.git
  
  # Clone a repository to a custom directory
  $ gh repo clone cli/cli workspace/cli
  
  # Clone a repository with additional git clone flags
  $ gh repo clone cli/cli -- --depth=1
  
  # Clone a forked repository without the upstream remote
  $ gh repo clone myuser/cli --no-upstream

LEARN MORE
  Use `gh <command> <subcommand> --help` for more information about a command.
  Read the manual at https://cli.github.com/manual
  Learn about exit codes using `gh help exit-codes`

bin/gh repo clone iamazeem/cli --upstream-remote-name upstream --no-upstream

$ bin/gh repo clone iamazeem/cli --upstream-remote-name upstream --no-upstream
specify only one of `--upstream-remote-name` or `--no-upstream`

Usage:  gh repo clone <repository> [<directory>] [flags] [-- <gitflags>...]

Flags:
      --no-upstream                   Do not set/fetch upstream remote when cloning a fork
  -u, --upstream-remote-name string   Upstream remote name when cloning a fork (default "upstream")

$ echo $?
1

bin/gh repo clone iamazeem/cli cli1 -- --depth=1

$ bin/gh repo clone iamazeem/cli cli1 -- --depth=1
Cloning into 'cli1'...
remote: Enumerating objects: 1306, done.
remote: Counting objects: 100% (1306/1306), done.
remote: Compressing objects: 100% (1150/1150), done.
remote: Total 1306 (delta 191), reused 609 (delta 105), pack-reused 0 (from 0)
Receiving objects: 100% (1306/1306), 12.67 MiB | 1.27 MiB/s, done.
Resolving deltas: 100% (191/191), done.
! Fetching upstream of cli/cli
From github.com:cli/cli
 * [new branch]      trunk      -> upstream/trunk
! Repository cli/cli set as the default repository. To learn more about the default repository, run: gh repo set-default --help

$ (cd cli1 && git remote)
origin
upstream

bin/gh repo clone iamazeem/cli cli2 --no-upstream -- --depth=1

$ bin/gh repo clone iamazeem/cli cli2 --no-upstream  -- --depth=1
Cloning into 'cli2'...
remote: Enumerating objects: 1306, done.
remote: Counting objects: 100% (1306/1306), done.
remote: Compressing objects: 100% (1149/1149), done.
remote: Total 1306 (delta 191), reused 608 (delta 106), pack-reused 0 (from 0)
Receiving objects: 100% (1306/1306), 12.67 MiB | 822.00 KiB/s, done.
Resolving deltas: 100% (191/191), done.

$ (cd cli2 && git remote)
origin

@iamazeem iamazeem requested a review from a team as a code owner March 15, 2025 08:22
@iamazeem iamazeem requested a review from andyfeller March 15, 2025 08:22
@cliAutomation cliAutomation added the external pull request originating outside of the CLI core team label Mar 15, 2025
@andyfeller
Copy link
Member

Thanks for opening up this pull request, @iamazeem! ❀️

Right now, we are a bit understaffed and juggling some high priority items, so I'm going to be a little slow following up on PRs. Just wanting to give you heads up. πŸ™‡

@andyfeller
Copy link
Member

@iamazeem : just want you to know I'm catching up on this PR today, stay tuned and sincere thank you for patience! πŸ™‡

Copy link
Member

@andyfeller andyfeller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iamazeem : In and of itself, these changes seem straightforward with some room for wording here and there. However, I think there is a larger problem with forked repositories without upstream that we didn't consider previously.

I'm going to bring these up for guidance. πŸ™‡

Comment on lines 64 to 66

To disable the addition of the %[1]supstream%[1]s remote for a forked repository,
use the %[1]s--no-upstream%[1]s flag. For a non-forked repository, this flag has no effect.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest: I'd like to attempt rephrasing a few of these sections to be smaller and easier to read.

			If the repository is a fork, its parent repository will be added as an additional git remote named
			%[1]supstream%[1]s and set as the default remote unless %[1]s--no-upstream%[1]s flag is specified.

			The remote name can be configured using the %[1]s--upstream-remote-name%[1]s flag, which supports
			the %[1]s@owner%[1]s syntax to name it after the owner of the parent repository.

as opposed to all of this:

If the repository is a fork, its parent repository will be added as an additional
git remote called %[1]supstream%[1]s. The remote name can be configured using %[1]s--upstream-remote-name%[1]s.
The %[1]s--upstream-remote-name%[1]s option supports an %[1]s@owner%[1]s value which will name
the remote after the owner of the parent repository.
If the repository is a fork, its parent repository will be set as the default remote repository.
To disable the addition of the %[1]supstream%[1]s remote for a forked repository,
use the %[1]s--no-upstream%[1]s flag. For a non-forked repository, this flag has no effect.

@@ -186,7 +202,7 @@ func cloneRun(opts *CloneOptions) error {
}

// If the repo is a fork, add the parent as an upstream remote and set the parent as the default repo.
if canonicalRepo.Parent != nil {
if !opts.NoUpstream && canonicalRepo.Parent != nil {
Copy link
Member

@andyfeller andyfeller Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: I believe these changes create a situation where forked repositories are not resolved properly via remotes as gh repo view fails. πŸ€”

I want to raise this up with the other maintainers as I'm unsure if we still need to set remote resolution even though there is only 1 remote.

Demo

Repository: https://github.com/andyfeller/cli

$ make clean && make
rm -rf bin share
go build -trimpath -ldflags "-X github.com/cli/cli/v2/internal/build.Date=2025-04-14 -X github.com/cli/cli/v2/internal/build.Version=v2.68.1-23-g1bb599c1e " -o bin/gh ./cmd/gh

$ alias gh=$(realpath bin/gh)               
$ cd ~/Documents/workspace/andyfeller

$ gh repo clone andyfeller/cli --no-upstream
Cloning into 'cli'...
remote: Enumerating objects: 56037, done.
remote: Total 56037 (delta 0), reused 0 (delta 0), pack-reused 56037 (from 1)
Receiving objects: 100% (56037/56037), 29.54 MiB | 22.13 MiB/s, done.
Resolving deltas: 100% (38350/38350), done.

$ cd cli
$ cat .git/config 
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
[remote "origin"]
	url = https://github.com/andyfeller/cli.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "trunk"]
	remote = origin
	merge = refs/heads/trunk

$ gh repo view
X No default remote repository has been set. To learn more about the default repository, run: gh repo set-default --help

please run `gh repo set-default` to select a default remote repository.

Looking at GH_DEBUG=api output below, the RepositoryNetwork GraphQL query points to logic that resolves the remotes:

Expand for full GH_DEBUG=api gh repo view output

$ GH_DEBUG=api gh repo view
[git remote -v]
[git config --get-regexp ^remote\..*\.gh-resolved$]
* Request at 2025-04-14 11:44:42.424889 -0400 EDT m=+0.084322709
* Request to https://api.github.com/graphql
> POST /graphql HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
> Content-Length: 388
> Content-Type: application/json; charset=utf-8
> Graphql-Features: merge_queue
> Time-Zone: America/New_York
> User-Agent: GitHub CLI v2.68.1-23-g1bb599c1e

GraphQL query:
fragment repo on Repository {
    id
    name
    owner { login }
    viewerPermission
    defaultBranchRef {
      name
    }
    isPrivate
  }
  query RepositoryNetwork {
    viewer { login }
    
    repo_000: repository(owner: "andyfeller", name: "cli") {
      ...repo
      parent {
        ...repo
      }
    }
    
  }
GraphQL variables: null

< HTTP/2.0 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Mon, 14 Apr 2025 15:44:42 GMT
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept-Encoding, Accept, X-Requested-With
< X-Accepted-Oauth-Scopes: repo
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: C7FB:2D65EB:521533D:A3DF8F7:67FD2D6A
< X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
< X-Oauth-Scopes: admin:org, gist, repo, workflow
< X-Ratelimit-Limit: 5000
< X-Ratelimit-Remaining: 5000
< X-Ratelimit-Reset: 1744645618
< X-Ratelimit-Resource: graphql
< X-Ratelimit-Used: 476
< X-Xss-Protection: 0

{
  "data": {
    "viewer": {
      "login": "andyfeller"
    },
    "repo_000": {
      "id": "R_kgDOMUqY2A",
      "name": "cli",
      "owner": {
        "login": "andyfeller"
      },
      "viewerPermission": "ADMIN",
      "defaultBranchRef": {
        "name": "trunk"
      },
      "isPrivate": false,
      "parent": {
        "id": "MDEwOlJlcG9zaXRvcnkyMTI2MTMwNDk=",
        "name": "cli",
        "owner": {
          "login": "cli"
        },
        "viewerPermission": "ADMIN",
        "defaultBranchRef": {
          "name": "trunk"
        },
        "isPrivate": false
      }
    }
  }
}

* Request took 478.958417ms
X No default remote repository has been set. To learn more about the default repository, run: gh repo set-default --help

please run `gh repo set-default` to select a default remote repository.

cli/context/context.go

Lines 61 to 109 in 408e21e

func (r *ResolvedRemotes) BaseRepo(io *iostreams.IOStreams) (ghrepo.Interface, error) {
if r.baseOverride != nil {
return r.baseOverride, nil
}
if len(r.remotes) == 0 {
return nil, errors.New("no git remotes")
}
// if any of the remotes already has a resolution, respect that
for _, r := range r.remotes {
if r.Resolved == "base" {
return r, nil
} else if r.Resolved != "" {
repo, err := ghrepo.FromFullName(r.Resolved)
if err != nil {
return nil, err
}
return ghrepo.NewWithHost(repo.RepoOwner(), repo.RepoName(), r.RepoHost()), nil
}
}
if !io.CanPrompt() {
// we cannot prompt, so just resort to the 1st remote
return r.remotes[0], nil
}
repos, err := r.NetworkRepos(defaultRemotesForLookup)
if err != nil {
return nil, err
}
if len(repos) == 0 {
return r.remotes[0], nil
} else if len(repos) == 1 {
return repos[0], nil
}
cs := io.ColorScheme()
fmt.Fprintf(io.ErrOut,
"%s No default remote repository has been set. To learn more about the default repository, run: gh repo set-default --help\n",
cs.FailureIcon())
fmt.Fprintln(io.Out)
return nil, errors.New(
"please run `gh repo set-default` to select a default remote repository.")
}

cli/context/context.go

Lines 130 to 162 in 408e21e

// NetworkRepos fetches info about remotes for the network of repos.
// Pass a value of 0 to fetch info on all remotes.
func (r *ResolvedRemotes) NetworkRepos(remotesForLookup int) ([]*api.Repository, error) {
if r.network == nil {
err := resolveNetwork(r, remotesForLookup)
if err != nil {
return nil, err
}
}
var repos []*api.Repository
repoMap := map[string]bool{}
add := func(r *api.Repository) {
fn := ghrepo.FullName(r)
if _, ok := repoMap[fn]; !ok {
repoMap[fn] = true
repos = append(repos, r)
}
}
for _, repo := range r.network.Repositories {
if repo == nil {
continue
}
if repo.Parent != nil {
add(repo.Parent)
}
add(repo)
}
return repos, nil
}

@andyfeller
Copy link
Member

Bringing up #8274 (comment) internally to see if we can get movement on this PR. Thank you again for your patience as I know this has been dragging on πŸ™‡

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
external pull request originating outside of the CLI core team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

gh repo clone without upstream
3 participants