jj
jj copied to clipboard
FR: add a revset function for the default branch of a remote
Is your feature request related to a problem? Please describe. i have a git alias which shows me commits that are only on my branch, not yet merged to master:
[alias]
branch-log = !git log $(git merge-base HEAD origin/HEAD)..
i can almost replicate this with jj: jj log -r 'main@origin..@'
only shows the commits between origin/main
and HEAD. but it doesn't work if the branch on the remote is called something else. i can specify the common cases:
jj log -r '(remote_branches(main, origin) | remote_branches(master, origin))..@'
and in practice that will almost always work. but it would be nice to make that reliable.
Describe the solution you'd like
add a new function to https://github.com/martinvonz/jj/blob/main/docs/revsets.md#functions that returns the default branch of the remote - perhaps remote_branches(@, origin)
, by analogy with origin/HEAD? that doesn't conflict with a named branch, and it means that @origin
is a cute shortcut for it.
Describe alternatives you've considered
- add a standalone command which prints the revlist a
--revisions
flag resolved to. that seems useful regardless; but without an equivalent oforigin/HEAD
it doesn't actually solve my problem. - do bash shenanigans to detect if this is backed by a git repo and run
git rev-parse --symbolic-full-name origin/HEAD
; this doesn't work because jj aliases (AFAICT?) don't support running arbitrary code, only jj subcommands.
Additional context jj 0.13.0-81b0e3bf3bf6f230740e93532b9a9767e8a4ef6e
Note that there is trunk()
which refers to the HEAD of the default branch of the remote named origin
. You can also override trunk()
yourself to point to foobar@remote
. So that's a convenient one; for instance, as an alternative to your branch-log
I tend to use the following revset I call "open()
" (that I plug all the time, sorry!), in order to look at all my open patch series: (mine() ~ ::trunk()) ~ heads(empty())
; trunk()
has been very convenient for when some repos use master
and others use main
...
Maybe trunk()
should just be capable of taking an argument which is the remote to get the default HEAD/branch of? I'm not sure if we should overload it.
i didn't know about trunk! that sounds like roughly what i want, but the exact behavior is not what i expect:
trunk(): Resolves to the head commit for the trunk branch of the remote named origin or upstream. The branches main, master, and trunk are tried. If more than one potential trunk commit exists, the newest one is chosen. If none of the branches exist, the revset evaluates to root().
i would expect it to use origin/HEAD
if using a git backend, or fall back to the current behavior if the backend doesn't support getting the default branch for some reason. "guessing and checking" makes me a little uncomfortable; i've run into repos in the past that have main
that is not the default branch.
I'd like to second this, I originally thought that trunk()
was implemented by using origin/HEAD
, instead of being part of a manual list of remotes/branches to check.
I think a new revset function would be really helpful, and I can help to work on implementing this if we decide on the correct syntax (eg. remote_default_branch([remote])
or maybe someway to extend remote_branches()
)? Or just overriding trunk()
as suggested above might do the trick as well.
jj's internal view doesn't know about <remote>/HEAD
, so we'll need to think that about first. It's a bit special because <remote>/HEAD
is a symbolic ref whereas jj's refs (including local HEAD@git
) are all peeled.
It might be easier to set up trunk()
to point to the remote head branch on jj git clone
/jj git init
.
Ah, I see what you mean—looking through the code briefly, it seems like jj doesn't explicitly handle symbolic refs, it just depends on all refs being resolved to a commit ID (correct me if I'm wrong).
Will that be part of the scope for jj
?
Alternatively I can do what you suggest: just configure a repository specific alias for trunk()
to set the default branch for the remote on jj git clone
/jj git init
. (This doesn't handle adding of new remotes, but I'm thinking updating the configuration on each jj git fetch
will not be ideal since it will override user-specific customizations, if any).
I think there are two main questions to consider:
- Should
HEAD@remote
be created as remote branches?- If yes, then
trunk()
can be set automatically toremote_branches(exact:"HEAD")
for example. - Otherwise, perhaps the custom revset-alias would be the best solution.
- If yes, then
- Does jj want to support Git symbolic refs?
- As mentioned this might be more complicated to handle. Since symbolic refs aren't commonly used apart from
.git/HEAD
and.git/refs/remotes/[remote]/head
, we could just treatHEAD@remote
as a typical branch and depend on the underlying Git tools (gix/git2) to resolveHEAD@remote
to a peeled ref upon every fetch.
- As mentioned this might be more complicated to handle. Since symbolic refs aren't commonly used apart from
jj doesn't explicitly handle symbolic refs, it just depends on all refs being resolved to a commit ID
Correct.
jj
doesn't have a current/active branch (which is represented as a symbolic HEAD
in Git), too. There's lengthy discussion related to that in #2338. If the discussion concluded we would need a distinction between symbolic and peeled HEAD
, it might make sense to apply a similar data model to remote HEAD
s. I have no idea right now.
just configure a repository specific alias for
trunk()
to set the default branch for the remote onjj git clone
/jj git init
. (This doesn't handle adding of new remotes, but I'm thinking updating the configuration on eachjj git fetch
will not be ideal since it will override user-specific customizations, if any).
Agreed. It should be one-time action.
I think there are two main questions to consider:
* Should `HEAD@remote` be created as remote branches? * If yes, then `trunk()` can be set automatically to `remote_branches(exact:"HEAD")` for example. * Otherwise, perhaps the custom revset-alias would be the best solution.
Maybe no (at least in the underlying data model)? <remote>/HEAD
isn't an independent branch, but is a state of the branch referred to by the <remote>/HEAD
, I think. If <remote>/HEAD
appeared as a usual branch, user could set up tracking local branch in jj.
* Does jj want to support Git symbolic refs?
I don't think it will become a general concept. HEAD
s might be special cased, but I honestly don't know.
I started working on #3205, which updates the repo's trunk()
alias on initial clone to the remote's default branch, but coming back to it I do think it might be valuable to have a revset function for the remote's default branch. Adding an alias feels like a sub-optimal workaround since users can add new remotes and want to find out what the latest revision on that remote's default branch is as well.
I do think it might be valuable to have a revset function for the remote's default branch.
Perhaps, the revset syntax can be HEAD@<remote>
just like HEAD@git
. The underlying data model will need some adjustment. Maybe git_head
will be moved to RemoteView
(where remote_views["git"].head
is HEAD@git
)? I'm not sure whether the git_head
should be a symbolic ref.
https://github.com/martinvonz/jj/blob/8600750fceafbf489d42a99b36b1f48bbc1e416b/lib/src/op_store.rs#L266-L285 https://github.com/martinvonz/jj/blob/8600750fceafbf489d42a99b36b1f48bbc1e416b/docs/design/tracking-branches.md#proposed-data-model
Circling back to this after doing some more research, it seems like refs/remotes/origin/HEAD
is actually a special symbolic ref which is only added when running git clone
. Adding new remotes via git remote add X
and git fetch X
doesn't automatically populate refs/remotes/X/HEAD
. In light of discovering this, I think going back to the original suggestion of just setting repo-level trunk()
on clone would be the best option, since this is also effectively what git clone
does when populating ref/remotes/origin/HEAD
. We can definitely come back to revisit this in the future if this has greater demand, or eg. we start supporting symbolic refs.
(Sorry for the wishy-washiness, I will re-open my above PR once it's completed.)