I think that the default behaviour of jj git clone should be changed to only clone the default branch.
I think this should be done for the following reasons:
- I often clone large repos. Because the default behaviour of
jj git cloneis to pull down every branch, this is often a (very) slow and (very) space-consuming operation. - There is currently no way for
jj git cloneto clone only the default branch of a repository, without knowing its name. Given that repositories online are equally split betweenmainandmaster, this presents a problem. - There is currently no difference between
jj git cloneandjj git clone --branch *, so changing the behaviour ofjj git clonewould remove redundancy in the CLI. (And those who prefer the existing behaviour can simply aliasjj git clonetojj git clone --branch *in their shell.)
Activity
18Is this about what the default value for --branch is?
The help for jj help git clone indicates that you could use --branch (main | master), but it doesn't state what the default value is.
(And those who prefer the existing behaviour can simply alias
jj git clonetojj git clone --branch *in their shell.)
I mean, this is not possible via jj aliases (can't alias built-in commands) and also would be quite strange as a shell wrapper (pass everything to /usr/bin/jj EXCEPT for jj git clone without arguments), so I don't think this is a sound point.
Also, requesting defaults to change is usually contentious. I would have no problem with this FR if you just wanted a way to fetch only the default branch (which seemed to be what you wanted, as stated in Discord), but it feels strange to me that this FR is explicitly requesting that (1) this behavior be added, AND (2) it should be made the default. I have personally never had a situation where I ONLY want the default branch to be pulled into the repo, and it would be a bad user experience (for me) to do jj git clone, oops I forgot --branch '*', ugh, run jj git fetch --branch '*'. On the other hand, if you are cloning a repo where you only want the default branch, you likely know that before you do the clone, and so can pass --default-branch-only or whatever. And the same "you could alias this" argument applies in that direction as well (putting aside my objections above).
Also, you said "I think this should be done [because]... I often clone large repos". A single data point is not really a reason to change the default behavior for such a core command. Again, this FR is fine, but I don't think it's reasonable to ask this to be the default.
Disclaimer: I am not a maintainer, and these opinions are my own.
@joyously Using --branch (main | master) pretty clearly doesn't pull down the default branch... I run across repositories whose default is something other than main or master only occasionally, but it's an incorrect solution.
It's not entirely about what the default value for --branch is exactly, no. That is one of the issues -- the default of * is bad when cloning large repos -- but it's more so that, there is no way to express that you want to clone only the default branch in Jujutsu alone.
@josephlou5 Oh, that is true. I already override the jj command so it is just a matter of adding a case there, but that's not an abbreviation, indeed it's a pain to add an alias to a subcommand (in Jujutsu and in Fish). The reason I am requesting a change to the default, rather than solely an addition of behaviour, is that given the existing branch pattern syntax -- which is literal upon the branch name, i.e. m*, m|a, etc (I forget where/if it is documented), there is no way to simply express "default branch", and any addition of support is going to somewhat clash with the existing syntax.
I don't actually think it should be added as anything other than a default because of this, i.e., I don't think there's a nice way to write it down in branch syntax. Sorry, I should have explained this in my initial description!
With regards to large repos, I don't think I'm alone here. I have recently run jj being extremely slow at cloning repos when cloning a couple of programming languages, https://github.com/racket/racket/ and https://github.com/leanprover/lean4 in particular. These are large repos.... but they're not particularly large repos! I think cloning repos about this size is extremely common.
The reason I am requesting a change to the default, rather than solely an addition of behaviour, is that given the existing branch pattern syntax -- which is literal upon the branch name, i.e.
m*,m|a, etc (I forget where/if it is documented), there is no way to simply express "default branch", and any addition of support is going to somewhat clash with the existing syntax.I don't actually think it should be added as anything other than a default because of this, i.e., I don't think there's a nice way to write it down in branch syntax. Sorry, I should have explained this in my initial description!
There are already multiple cases of conflicting options throughout the CLI, such as:
$ jj diff -r @ --from @ --to @
error: the argument '--revisions <REVSETS>' cannot be used with:
--from <REVSET>
--to <REVSET>
Usage: jj diff --revisions <REVSETS> [FILESETS]...
For more information, try '--help'.So this would likely just be a matter of marking --branch as conflicting with --default-branch-only (or whatever flag name). In this Discord message Stephen also says that git branch --single-branch --branch <name> is supported, so I think it's also reasonable that they're marked as NOT conflicting, and any other --branch value just gets added on in the same way. Note that the branches option is already marked as Option, which means we can differentiate between "flag was not given" and "flag had these values" (this is also where the pattern syntax is documented): https://github.com/jj-vcs/jj/blob/03b5dd3df201f1587afede28c0554784b1b0778b/cli/src/commands/git/clone.rs#L111-L129
So it is not the case that the --branch flag defaults to --branch '*' if not given. It is quite easy to then do:
- If only
--default-branch-onlyis given, then fetch the default branch only. - If
--default-branch-onlyis given and--branchis given, then fetch the default branch plus any specified (can also special case for--branch '*'ignoring--default-branch-only). - If only
--branchis given, use that pattern. - If no branch options are given, default to
--branch '*'(fetch all bookmarks; this is also the current behavior).
One may also argue that there should be a config for this, such that jj git fetch in an existing repo also only fetches the default branch without needing to explicit set it in remotes.<name>.fetch-bookmarks (see config docs). But I could also imagine that if --default-branch-only is given, then the command will populate this config in the repo config, similar to how it does that automatically for revset-aliases.'trunk()'. But anyway.
With regards to large repos, I don't think I'm alone here. I have recently run
jjbeing extremely slow at cloning repos when cloning a couple of programming languages, https://github.com/racket/racket/ and https://github.com/leanprover/lean4 in particular. These are large repos.... but they're not particularly large repos! I think cloning repos about this size is extremely common.
Fair enough, you are certainly not the only one cloning large repos, but I don't know if "large repos exist and people clone them" means the default behavior should change. Arguably it is more annoying to have to jj git fetch --branch '*' after the clone than it is to jj git clone <link>, oh it's fetching all branches, ctrl-c and try again with --default-branch-only. (This is my own opinion of course.)
There are already multiple cases of conflicting options throughout the CLI
That's true! However, I think conflicting flags are a bad design... it adds a lot of redundancy to the CLI. Cutting that down was one of the main things I changed in my wrapper (see jj zen rebase in particular). That --branch * exists already and functions the same as the current default is part of why I think "default branch" should replace * as the default.
So it is not the case that the
--branchflag defaults to--branch '*'if not given.
Hmm? Reading the quoted documentation, I think it is indeed the case. "If not present, all branches are fetched and the repository's default branch is used as parent of the working-copy change." This is the same as --branch *, right?
Arguably it is more annoying to have to
jj git fetch --branch '*'after the clone than it is tojj git clone <link>, oh it's fetching all branches,ctrl-cand try again with--default-branch-only. (This is my own opinion of course.)
A problem here is that Jujutsu does not tell you it's cloning a bunch of branches until after it's cloned them.
So it is not the case that the
--branchflag defaults to--branch '*'if not given.Hmm? Reading the quoted documentation, I think it is indeed the case. "If not present, all branches are fetched and the repository's default branch is used as parent of the working-copy change." This is the same as
--branch *, right?
Sorry, I wasn't clear. Yes, in terms of behavior, not providing --branch ("if not present") will have the effect of fetching all branches. However, some flags are given explicit "default values", such as --remote=origin: https://github.com/jj-vcs/jj/blob/03b5dd3df201f1587afede28c0554784b1b0778b/cli/src/commands/git/clone.rs#L73-L75
You can also see https://docs.jj-vcs.dev/latest/cli-reference/#jj-git-clone:~:text=Default%20value%3A%20origin:
--remote <REMOTE_NAME>— Name of the newly created remoteDefault value:
origin
So what I meant was "--branch does not default to * as a literal value". My point with that was "we can change the behavior of the 'if --branch is not present' logic", since the flag will have a default value of None instead of "*" (which in theory you could not differentiate between "default value" and "user provided the same as the default value"). So that's what I described in the bullet points above.
Arguably it is more annoying to have to
jj git fetch --branch '*'after the clone than it is tojj git clone <link>, oh it's fetching all branches,ctrl-cand try again with--default-branch-only. (This is my own opinion of course.)A problem here is that Jujutsu does not tell you it's cloning a bunch of branches until after it's cloned them.
Hm okay, that's unfortunate. Then change "oh it's fetching all branches" to "it's taking a long time... is it fetching all branches?" :')
True, except that I expect fetching a large repository to already take a long time 😭
Regarding the conflicting options discussion: We have the option jj git clone --branch NAME, but it behaves as git clone --branch NAME --single-branch --no-tags does. We combined three git features into one:
| Behavior | Git command | Jujutsu equivalent |
|---|---|---|
| Fetch every branch, check out default | git clone | jj git clone |
| Fetch every branch, check out specific | git clone --branch NAME | jj git clone --branch NAME --branch '*' |
| Fetch just default branch | git clone --single-branch | Not implemented |
| Fetch just one branch, no tags | git clone --single-branch --branch NAME --no-tags | jj git clone --branch NAME |
Maybe we should change the behavior of --branch to be like git first, then we can add --single-branch too?
Also, if jj git clone with no arguments meant "fetch the default branch only", that means there would be no way to specify "fetch the default branch plus some other branches". You would either need to add some magic string for --branch _DEFAULT_BRANCH_NOT_REAL_BRANCH or you would have to say "it's impossible to fetch default branch + a specific branch in a single command". Both of those sound undesirable to me.
Maybe we should change the behavior of
--branchto be like git first, then we can add--single-branchtoo?
jj git clone --branch is already documented with:
If there are multiple matching branches, the first exact branch name is used as the working-copy parent.
So doesn't that already match git clone --branch behavior? Therefore I think maybe the second row in your table is not correct? (I haven't tested it myself though.)
I totally missed the request to make jj git clone default to only cloning the default branch. I think it should be possible, but I don't think it should be the default.
When we clone a single branch, we write it into the repo config:
[remotes.origin]
fetch-bookmarks = "main"To then fetch another bookmark, you need to either:
- Add it to this pattern, then run
jj git fetch - Run
jj git fetch -b NAME, then remember to either use that specifically orjj git fetch --tracked
Neither of those seem like a good default behavior to me. I think it's more common to be okay fetching the whole repo, I think we should require people who want the single-branch behavior opt in to it.
@jennings Where are you finding this repo config file? Neither jj git clone <repo> nor jj git clone --branch <foo> <repo> produce files with a repo config like that anywhere I can find...
@jennings Where are you finding this repo config file? Neither
jj git clone <repo>norjj git clone --branch <foo> <repo>produce the behaviour you describe for me.
https://www.jj-vcs.dev/latest/config/#default-bookmarks-and-tags-to-fetch
$ jj git clone git@github.com:jennings/pulldash --branch main
Fetching into new repo in "/Users/stephen/pulldash"
remote: Enumerating objects: 4269, done.
remote: Total 4269 (delta 605), reused 525 (delta 443), pack-reused 3489 (from 3)
bookmark: main@origin [new] tracked
Setting the revset alias `trunk()` to `main@origin`.
Working copy (@) now at: zxrlrlxs 6bb70ca9 (empty) (no description set)
Parent commit (@-) : snwsmlwo 00ccfcb0 main | feat: keep the reviewer sidebar in place while the timeline scrolls
Added 183 files, modified 0 files, removed 0 files
$ cd pulldash
$ jj config list remotes
remotes.origin.fetch-bookmarks = "main"
remotes.origin.fetch-tags = "~*"Maybe we should change the behavior of
--branchto be like git first, then we can add--single-branchtoo?
jj git clone --branchis already documented with:If there are multiple matching branches, the first exact branch name is used as the working-copy parent.
So doesn't that already match
git clone --branchbehavior? Therefore I think maybe the second row in your table is not correct? (I haven't tested it myself though.)
Just tested, confirmed:
$ jj git clone $REPO --branch josephlou5-smuxyozsrkyu
...
$ cd repo
$ jj log -r @ -r @-
@ wmylyqms 17:33:32
│ (∅) (no description set)
◆ smuxyozs [942ae1f7] 7 months ago josephlou5-smuxyozsrkyu@origin 2026-02-28 15:17:57
│
~
$ jj config list --repo
remotes.origin.fetch-bookmarks = "josephlou5-smuxyozsrkyu"
remotes.origin.fetch-tags = "~*"$ jj git clone $REPO --branch josephlou5-smuxyozsrkyu --branch '*'
...
$ cd repo
$ jj log -r @ -r @-
@ uouyxyvt 17:36:23
│ (∅) (no description set)
◆ smuxyozs [942ae1f7] 7 months ago josephlou5-smuxyozsrkyu@origin 2026-02-28 15:17:57
│
~
$ jj config list --repo
remotes.origin.fetch-bookmarks = "josephlou5-smuxyozsrkyu | *"
remotes.origin.fetch-tags = "~*"$ jj git clone $REPO --branch '~*'
Fetching into new repo in "..."
Nothing changed.
$ cd repo
$ jj log
@ uszvpqpp 17:39:12
│ (∅) (no description set)
◆ zzzzzzzz root() 00000000
$ jj config list --repo
remotes.origin.fetch-bookmarks = "~*"
remotes.origin.fetch-tags = "~*"The second command fetched all branches, but checked out the specified one because it was first in the list. So I think the second line of your table is possible with jj (though IIUC the exact semantics of git clone --branch are not mirrored here), and the only thing that doesn't work today is "fetch only the default branch" since there's no way to specify that with a glob pattern to --branch.
$ jj git clone $REPO --branch josephlou5-smuxyozsrkyu --branch '*'The second command fetched all branches, but checked out the specified one because it was first in the list. So I think the second line of your table is possible with jj
That does not at all seem intended to me, that feels like an accident of implementation. If you specify EDIT: This is documented.--branch foo --branch * I would say jj is justified checking out any branch, since you specified all branches with the *.
I don't particularly care about being able to replicate git clone --branch NAME because it's easy to just check out a different commit after cloning. But I do think it makes sense to enable the functionality of --single-branch: only transfer commits from a single branch from the remote, and arrange for jj git fetch to only transfer that in the future.
That does not at all seem intended to me, that feels like an accident of implementation. If you specify
--branch foo --branch *I would say jj is justified checking out any branch, since you specified all branches with the*.
Well it's explicitly documented, as I quoted in https://github.com/jj-vcs/jj/issues/10324#issuecomment-6022072209, so it seems quite intended. Whether or not it's actually intuitive is debatable, but that's how it works today.
Ah, so it is! I think the UI could be improved, but I suppose it works.
In my personal CLI wrapper (https://codeberg.org/apropos/jj-zen), I am accomplishing this by shelling out to Git to directly query the upstream repository with
git ls-remote --symref "$url" HEAD | awk '/^ref:/{sub("refs/heads/","",$2); print $2; exit}'.The standard Git CLI also includes a
--single-branchthat uses the remote's HEAD to determine the default branch.