FR: `resolve` flag to select the first conflicted commit on a branch
Is your feature request related to a problem? Please describe. Since conflict resolutions are propagated to child commits, it's often only necessary to resolve a single commit's conflict after a rebase. Even when there are multiple resolutions necessary, it's typically best to start with the original conflict and work forward, and it will usually not be necessary to resolve every commit in the branch.
Describe the solution you'd like
A new option for resolve, probably -b, --branch, that would take a rev (typically a branch name) and find the oldest ancestor with a conflict, and begin resolution on that commit.
Describe alternatives you've considered
Since resolution doesn't require the resolve command, it may be good to have an option on edit to automatically select the oldest conflict, for people who don't want to use resolve. This could be something like jj edit --conflicted <branch>.
As an enhancement, jj resolve -b <branch> could automatically run a sequence of resolve operations, progressing forward along the branch history as each commit is resolved. It would still start with the oldest conflict, of course.
I like this idea, and it actually seems quite easy to implement (in its most basic version). Like jj rebase -b, jj resolve -b would be an alias for jj resolve -r complicated_revset.
The complicated_revset should be a revset alias, do that it can be used with jj edit or jj new (I prefer the latter when resolving conflicts).
TODO: a good name for the revset. The best I have at the moment is the verbose oldest_ancestor_with_conflicts.
Thinking out loud about a draft revset, based on https://martinvonz.github.io/jj/prerelease/revsets/:
conflictroot(x) = latest(roots( (::x~conflict())..x ))
This is equivalent to latest(roots( ::x ~ ::(::x~conflict()) )), I believe.
I haven't tested it yet, but it seems correct, if perhaps cryptic. It should also error out if no commit in x is conflicted.
I've mentioned this before: having some general next(/prev) function such that you can write jj new 'next(conflict() & stack())' would be pretty useful, where next(x) = heads(@: & x:), or something like that. It's relevant not only for conflicts, but if, for example, jj run becomes able to record exit codes, you might ask to jump to the first commit with a failing run/test.
I think you mean roots(), not heads(). In fact, wouldn't latest(roots( conflict() & stack() )) work just as well? Here, there will usually only be one root, so latest will usually be a no-op.
Your intuition is interestingly different than the intuition for my revset. I was thinking "take the connected components of conflict() that x intersects with". So, if x itself is not conflicted, nothing happens. You are suggesting taking the oldest revision in the current stack. For your jj run example, your version is probably better. For conflict resolution, they will be the same in most cases, but when they differ, I think I prefer mine.
I think you mean roots(), not heads(). In fact, wouldn't latest(roots( conflict() & stack() )) work just as well? Here, there will usually only be one root, so latest will usually be a no-op.
Oops, yes, I did mean roots. Actually, in this case, specifying stack is unnecessary if I define next to include @; I had just habitually included it out of performance concerns (but it should be efficient to query conflicts() when combined with x::). Possibly the best interpretation actually includes only descendants(children(x)) rather than descendants(x). Ideally I would be able to write jj new 'next(conflicts())' to mean "go to the next conflict" without needing a more specialized conflictroot function.
Your intuition is interestingly different than the intuition for my revset. I was thinking "take the connected components of conflict() that x intersects with". So, if x itself is not conflicted, nothing happens. You are suggesting taking the oldest revision in the current stack. For your jj run example, your version is probably better. For conflict resolution, they will be the same in most cases, but when they differ, I think I prefer mine.
I don't think I understand the difference. In both cases, won't evaluation of the revset produce an empty set because x is not conflicted?