If X is a merge commit, `jj rebase -s X -d X- -d another-revision` should work
Currently, it results in Error: Revset "X-" resolved to more than one revision
That's actually on purpose, so you don't create a merge commit when you didn't mean to. I see your usecase, however. What I had imagined for cases like was to use a separate argument (--multiple-destinations <revsets> or something). I'm not sure which is better. Perhaps we should be less careful because we have undo functionality. It could be a flag instead of a separate option (but keep things like jj diff --from X --to Y in mind, since that would then require two separate flags if we ever thought of a way of making that display the diff between two revsets).
Whatever we decide, we should keep other commands in mind. Should jj new <revset> be allowed, for example? Should jj squash -r <revset> be allowed? That would certainly be useful (we may want to validate that the revset is contiguous, however).
I'm leaning towards simply allowing revsets for these commands (as you suggested for rebase) and relying on undo for recovering if it wasn't what the user meant.
I see your point. I think undo would have to be improved before being relied upon for this purpose. It's hard to say exactly what the best design would be, but some possibilities are an undo tree with multiple undos possible, or git-branchless-style interactive undo, or a more user-friendly jj op log command that makes it easy to see only the relevant info.
I think --multiple-destinations and --multiple-revisions, perhaps shortened to -D and -R sound like a good idea (as long as these short options aren't taken).
A slightly cutesy alternative would be to have --froms, --tos, --destinations, --revisions.
With this philosophy, jj new <revset> would need to be allowed, or replaced with jj new -r rev -R revset.
Another possible alias for --destinations (with an s) is --ds. Then, -D would be free for something else. Not sure if such aliases are supported by clap.
I think
undowould have to be improved before being relied upon for this purpose.
Because you don't trust it or because it's hard to figure out which operation to undo?
It's hard to say exactly what the best design would be, but some possibilities are an undo tree with multiple undos possible
Multiple undos are already possible (but it's generally easier to use jj op restore to go back multiple operations), but you're right that the operation log is not a tree. When you undo an operation, the operation log is not rewound; an inverse operation is instead added to the log (more like hg backout than hg strip).
or
git-branchless-style interactive undo
Yes, that would be nice :)
or a more user-friendly
jj op logcommand that makes it easy to see only the relevant info.
I've meant to add a jj op show <operation> (and jj op log -p) or something like that, but I'm not sure what a good output format would be. It should probably tell you something about which commits were added and removed, and which refs were updated.
I think
--multiple-destinationsand--multiple-revisions, perhaps shortened to-Dand-Rsound like a good idea (as long as these short options aren't taken).
-R is taken (it's a global argument for specifying which repo to work on). Mercurial (and git-branchless, I think) supports one or many commits to -r and I don't think I've heard about issues with that, so maybe it's not really a problem. I suspect that making the user use different options for one of more revisions is more annoying. My concern was mostly with commands that create merge commits if many revisions are specified (like jj new/merge/checkout, jj rebase -d).
Because you don't trust it or because it's hard to figure out which operation to undo?
The latter 100%. I think the low-level op-log implementation is amazing, and the addition of a "restore" operation onto the op log when you restore is a great idea. (For undo, things are a bit more confusing unless it's equivalent to a restore)
This probably belongs in a separate thread, but I'm currently thinking that adding the following features would make a huge difference:
jj op log -n 15to limit the length of the output.jj op log -m rebaseto restrict only to operations that include "rebase" in their description.- Let's say X is the rebase operation hash. I not only should be able to do
jj op restore Xto restore to after that operation (as already implemented), but I should also be able tojj op undo Xto restore to before it happened. Alternatively, the latter could be spelledjj op restore X-instead, but I think usingundohere is much more intuitive. (revset expressions for op log are a neat concept, but I'm not sure if they're that useful)
This ignores concurrent operations, but that's a separate topic. jj op undo X should simply fail if X is a "merge" of concurrent operation. There could be a separate operation to deal specifically with concurrency and separate accommodations for how to prevent jj op log -m rebase from hiding concurrency, to be finalized later.
UPDATE:
I've meant to add a
jj op show <operation>...
That would be neat, and could be quite powerful when recovering from weird concurrency behavior. However, 90% of the time there's enough info for me in the jj op log description, at least so long as I also have watch jj log open in another pane.
UPDATE 2: Now that I think of it, jj --color=always op log | grep -C 1 rebase | head -n 40 gives me more or less all the information I need. It doesn't roll off the tongue, though.
UPDATE 3: I just realized that jj op undo OPERATION is already valid syntax, documented as <OPERATION>: The operation to undo. This is actually a bit confusing. I guess it's like splitting the op log after the OPERATION, doing whatever happens after it on one concurrent branch and the opposite of OPERATION on the other concurrent branch? I suppose this can be rather powerful, but I only ever use jj op restore since that is easier to wrap my mind around.
Maybe I'm overthinking it, but at the moment it seems like it would be clearer if after jj undo OPERATION, the op log showed the concurrent branches I described above instead of the mere addition of an "undo" operation.
This probably belongs in a separate thread, but I'm currently thinking that adding the following features would make a huge difference:
jj op log -n 15to limit the length of the output.jj op log -m rebaseto restrict only to operations that include "rebase" in their description.- Let's say X is the rebase operation hash. I not only should be able to do
jj op restore Xto restore to after that operation (as already implemented), but I should also be able tojj op undo Xto restore to before it happened. Alternatively, the latter could be spelledjj op restore X-instead, but I think usingundohere is much more intuitive. (revset expressions for op log are a neat concept, but I'm not sure if they're that useful)
I have felt the same way about "opsets" :)
UPDATE 2: Now that I think of it,
jj --color=always op log | grep -C 1 rebase | head -n 40gives me more or less all the information I need. It doesn't roll off the tongue, though.
You may want to add ui.color = "always" to your ~/.jjconfig.toml. I have that set so I can pipe the output to a pager (until we've implemented issue #9). I know not everyone likes pagers, but they do address your usecase pretty well (searching for "rebase" in the output and viewing the output near it).
UPDATE 3: I just realized that
jj op undo OPERATIONis already valid syntax, documented as<OPERATION>: The operation to undo. This is actually a bit confusing. I guess it's like splitting the op log after the OPERATION, doing whatever happens after it on one concurrent branch and the opposite of OPERATION on the other concurrent branch?
jj [op] undo is like git revert/hg backout/jj backout but on an operation instead of on a commit, i.e. it's a three-way merge that applies the reverse diff onto another commit/operation. It's effectively the same as what you describe, but with a linear operation log instead.
I suppose this can be rather powerful, but I only ever use
jj op restoresince that is easier to wrap my mind around.
Yes, same here :)
Maybe I'm overthinking it, but at the moment it seems like it would be clearer if after
jj undo OPERATION, the op log showed the concurrent branches I described above instead of the mere addition of an "undo" operation.
Yes, it could be implemented that way, just like git revert/hg backout/jj backout could be implemented that way. I'm not sure if that would be better or why git and hg don't implement them that way.
A few additional thoughts:
-
Specifying a revset with multiple commits in a single argument is especially important for
-r, when that supports multiple commits as described in #1158 . -
hgdoes not seem to have any restriction on specifying multiple commits in a single arguments, at least according to the docs I read.
I can think of the following approaches here (designed for a future where all of -r/-s/-d/-b can be repeated):
-
Have a
--multiflag that disables the safety check that each-r/-s/-d/-bargument resolves to a single commit. -
Have a
--multiple-destinations, abbreviated to--md(note the two-), that is like-dbut supports revsets with multiple commits. Unfortunately, as Martin explains above, we cannot use-Rand therefore-Dprobably doesn't make much sense. I decided I like--md,--mr, etc. slightly more than--ds,--rs, and-ss, but it doesn't really matter. -
(I think this is not supported by
clap, would be nice otherwise) Have a--multiflag that affects only the following-r/-s/-d/-bflag.This might be possible with current clap if we moved from "derived" clap to "builder" clap, since that allows some access to the information of how arguments were ordered. It would be messy and is probably not worth it.
I'm leaning towards simply allowing revsets for these commands (as you suggested for rebase) and relying on undo for recovering if it wasn't what the user meant.
I think this is still my preference. What do you think? You didn't mention that option, but I'm not sure if you just forgot about it or if you think it's a bad idea.
I somehow missed the fact that this was your preference.
I could certainly live with that, but I think I would prefer option 1 above, at least at first. We could thus try it out.
I'd also prefer this until all the kinks with undo are worked out. In fact, having a safeguard until I feel I can completely trust the undo command is the one thing I feel stronger about. https://github.com/martinvonz/jj/issues/922 would be a nightmare if I accidentally rebase everything.
We could perhaps have the option to make it the default. I probably wouldn't make it the default for myself.
In the long-term, it's likely best to do as you suggested. I think we'll (I'll?) be more confident of that after living with --mutli for a bit. These are certainly the easiest options to implement.
I could certainly live with that, but I think I would prefer option 1 above, at least at first. We could thus try it out.
Sure, sounds good.
OK, this should be easy to do then. Thanks!