Is your feature request related to a problem? Please describe.
For a terminal prompt, it's useful to me to be able to see how many commits ahead I am from my nearest bookmark. It reminds me to advance before I push. Currently there's no built-in functionality within JJ to produce this metric. If there was, programs such as starship would be able to leverage it (see further context).
Describe the solution you'd like
A template function like shortest_distance(a, b).
Say I have a bookmark main.
shortest_distance(main, main) # 0jj new main creates change abc
shortest_distance(main, abc) # 1
shortest_distance(abc, main) # 1jj new abc creates change def
shortest_distance(main, def) # 2
shortest_distance(abc, def) # 1Describe alternatives you've considered
jj-starship - a standalone program designed to be integrated into starship - implements this functionality here using the JJ crate.
Additional context
The starship terminal prompt application recently released official integration for JJ. I raised a feature request on the bookmark module for support for showing distance to commit, however because starship only operates by invoking jj (here), instead of pulling it in as a library, it's unable to access this information without a costly second subcommand call and additional parsing legwork. Having a template function for this would make it cheap and easy to add this feature. See also: the feature request on the starship repo from me: https://github.com/starship/starship/issues/7804.
I am a Rust developer, so with guidance I could potentially contribute the PR for this work, if it's decided that this feature is worth having.
Edits:
- changed "revset function" to "template function"
- clarified
distancetoshortest_distanceto avoid ambiguity / multiple distances
Activity
4If such a function is added, the notion of "distance" needs to be clearly and rigorously defined.
The simplest solution is just to define the function "shortest_distance" instead
"revset().len()" looks interesting but if it manifests the whole revset before counting the length that could be painful performance-wise.
It would likely still be a good temporary solution though, especially for most non pathological cases with only a few changes between the closest bookmark and the current change (which should cover the vast majority of starship users)
If such a function is added, the notion of "distance" needs to be clearly and rigorously defined.
The simplest solution is just to define the function "shortest_distance" instead
"revset().len()" looks interesting but if it manifests the whole revset before counting the length that could be painful performance-wise.
Yeah that seems reasonable. I think it could also make sense to do revset_count() to use the same optimizations as --count, but that's a distinct FR.
There is also a question of whether shortest_distance should be a global function or a Commit method. I think global function with two parameters makes more sense..? Not sure if "negative distance" should also be supported (that is, first param should be descendant of second param for positive distance, or vice versa).
@josephlou5 thanks for your detailed reply! While I've been using JJ for a few months at this point, I've yet to need to dive too deeply with the revset language and templates, so sorry for confusing them! I've updated the main issue body and issue title to fix this. I've also clarified distance to shortest distance, reflecting my use case for the feature, where shortest distance is sufficient (as suggested by Poliorcetics).
I see you've linked some other issues for motivation, but I'll just comment here and depend on you to propagate out as needed :)
A couple thoughts:
jj log --countalready mostly does this. I know you said an additional command invocation is not ideal (which makes sense), but just calling it out since you didn't mention it in the FR. (But also see note below.)revset()function to the templating language. This means that you could count the size of a revset by doingrevset("mutable()").len(). This is essentially the same as--count, but does so in the context of a template (and may also be slower because it will probably eagerly fetch all the commits instead of just reporting the size, but this may be an unsubstantiated claim).Note about
--countI show a distance in my prompt (don't use Starship though), so here are some problems I've encountered with counting "distance"...
jj log --countcounts the number of revisions that would have been printed in the graph. If you want the "distance" betweenxandy(assuming they're single commits), then you could just dojj log -r x..y --count(this revset is equivalent to::y ~ ::x = x::y ~ x(note that the RHS is true becausexandyare single commits!); usually "distance" in this context does not include both endpoints). However, this only returns the correct answer ifx..yis a linear chain, which isn't always the case. For example, if you have something like this:Then
jj log -r x..y --countwill return5when in fact you probably wanted3. Maybe you could try to usefirst_ancestorsthen, so that only one path is followed:This will return
3, which is good. But wait, what if the other branch doesn't have the same number of commits?In this case, does "distance" mean
3or2? If you always want the shortest or the longest distance, that's not possible because there's no way to know which path to take, and there are only ways to traverse the first parent edges, and there may be much more complicated graphs betweenxandy. I even think it's probably not generally possible to say "I always want the path throughnor something like that. So basically, you need a proper definition of "distance", or you need to be satisfied with the first parent path.My solution to this (I was actually working on this last night, coincidentally) is to
jj logwithout--countand use a template to output the commit IDs of the parents (parents.map(|p| p.commit_id())). I then walk all the parent edges myself using BFS to find the shortest distance betweenxandy. I haven't done extensive timing tests yet, and it seems to be only a tiny bit slower (~20 ms slower than--count), but I believe--countis supposed to be much faster because it knows you only want the size. But I'm also trying to optimize some other templated output in a singlejj logcall, so it might be making up for that a little bit.Also, this problem is also present with the
revset()templating function that I mentioned above, since it's also using a revset without specifying which paths to take and then just looking at the size of the result.So the conclusions of my wall of text are:
--countorrevset()to get an approximation of "distance".It may also be the case that "distance between commits" is already commonly defined in the VCS world and I'm just unaware. In that case, I hope this rant was at least interesting, even if not useful :p