martinvonz / jj

A Git-compatible VCS that is both simple and powerful
https://martinvonz.github.io/jj/
Apache License 2.0
8.17k stars 272 forks source link

FR: `reorder` AKA `histedit` command (better tools for reordering commits part 2) #1531

Open ilyagr opened 1 year ago

ilyagr commented 1 year ago

In terms of the implementation, this would follow ~#1530~ https://github.com/martinvonz/jj/issues/1188#issuecomment-1416682924.

A jj reorder command would be very similar to hg histedit/git rebase --interactive. Apart from reordering, it could also support squashes (both "roll" and "fold") and abandoning commits, just like the hg/git versions. I see no need for it to support splits/edits; it's simpler to edit or split commits one by one before or after reordering.

Aside on names: I like the name reorder better than histedit, even if we do support more than reordering, but histedit is also a fine name. The main downside is that we tend to use real English words for jj commands, and it would be nice to continue this tradition. However, there are already exceptions.

Optional in first version: For merge commits, the reorder command could be more clever than swap. jj reorder rev would demand that the intermediate commits (rev:@) form a line and that rev itself is not a merge commit. Then, it makes sense to reorder merge commits in this line while preserving all but one parent of these merge commits.

Other optional features: jj reorder rev1 --to rev2 could be synonymous to jj reorder --from rev1 --to rev2. jj reorder without a revision specified could take either 10 last commits or find the first merge commit ancestor of @ and take the line up to it, not inclusive. The number 10 could be adjustable, e.g. jj reorder -n 20 could be legal.

We could support a nice TUI interface. For git, there exist "sequence editors" (example) which we could support. We could also make one. (The one I linked to is GPL, but even if we have to make our own, that'd be easier than making a TUI diff editor)

dbarnett commented 1 year ago

+1 to start simple, but eventually I think it's worth supporting split if it's going to support fold, or something to conveniently solve the case when I have a stray line of changes in one commit I want to move down into a child.

Another feature I'd love eventually: hint in the sequence editor if a reorder is going to cause merge conflicts.

ilyagr commented 1 year ago

something to conveniently solve the case when I have a stray line of changes in one commit I want to move down into a child

At the moment, I feel that jj reorder followed by jj split is simply more convenient, but I could be wrong. In particular, it would be nice if we could make sure the user is aware of the relevant change ids.

Another feature I'd love eventually: hint in the sequence editor if a reorder is going to cause merge conflicts.

Ideally, this would be solvable by either jj undo or another jj reorder after you get the undesired conflicts. In practice, there could be bugs where two reorders give a different result than one reorder (if I understand correctly, according to Pijul's author, these are impossible to avoid with a snapshot-based system). We'd very much want to fix those, but it could be an adventure since it likely requires keeping more information than just the snapshots around for commits.

ilyagr commented 1 year ago

Now, the plan is to not do #1530's swap command. Instead, https://github.com/martinvonz/jj/issues/1188#issuecomment-1416682924 becomes "part 1" of this.

ilyagr commented 9 months ago

An update on my personal feelings on this: I now think that we should implement jj rebase -r ... --after/--before from #1188. Then, we can think about how many of the use-cases for an interactive rebase are covered by that and how many are left. I have a suspicion that, for my use-cases at least, non-interactive rebase --after might be good enough.

Some of us are also excited about a GUI tool similar to Sapling's ISL, which might also make more sense than an interactive rebase.

epage commented 7 months ago

What is this were just a -i/--interactive flag on jj log?

I can understand the desire for a TUI but I feel like I could be really efficient with a git-like editor UI. Maybe we could have room for both (jj log --editor)?

RE need for interactive support: For myself, this is about speed and confidence: I can just move things around and I'm done compared to getting the right IDs, writing the exact command needed, and wondering if I got the right things.

For myself, I'd find a GUI taking me out of my flow.

thoughtpolice commented 7 months ago

By "git-like" editor UI you mean the flat file UX? I wouldn't mind just having the file equivalent of git rebase -i, it can be kind of fast if you already know exactly what you want and use editor keystrokes, but I've never thought it was very good at all. One of the problems in Git's case is that you quickly "lose the forest for the trees" (no pun intended) because e.g. it's not clear if one of your modifications will cause conflicts, then you have to go through the dance of fixing the conflict and rebase --continue over and over.

I think a major advantage of a TUI "sequence editor" like the one Ilya linked is that it is much more viable to handle actual trees, not just single-head series. A lot of our commands generalize well over trees, and we often want to talk about them since they're so cheap to create, so I think this is desirable. For example, given the following tree with two heads:

A -> B -> C
      \
       -> D -> E

What if you wanted to reorder "A, and all its descendents" AKA the descendents(A) revset? Well, in the Git UX with just a flat file I think it's weird, you can do something like specify the merge verb with interactive rebase, but I'm not sure there's a direct way to just specify child/parent relationships. For example, what if I wanted to make D a child of C while retaining the D -> E edge? Handling this would actually make it more like a tree editor, not a sequence editor.

A thing with the format used by git rebase -i is that there isn't really too much parsing happening, the interface is more like "give me a list of abstract objects to put in order", so there isn't much 'syntax.' It's fairly low level. I don't know if we want to get in the business of inventing a new format to parse or anything, either.

Another advantage of a "tree editor" like this in our model is that a TUI could show you the resulting speculative tree, including conflicts. For example, if I selected commit C, and moved it "underneath" B changing the relationship to C -> B, the resulting "speculative graph log" could immediately show that a conflict arises from that, presumably because B and C don't commute:

     x    x
A -> C -> B
          \   x    x
           -> D -> E

where the x markers are red conflict indicators, like we have in jj log today. Now you have a global view of the entire change series, as you make each modification. Hiding it behind an editor buffer obscures these kinds of things, I think. One of the amazing things with jj's transactional model is that these kinds of changes are just transactions that can stay "pending" and we don't commit them; sort of like "read your own writes" in an MVCC database where you may do a query inside a transaction that depends on seeing the results of a previous query, but the entire transaction can be aborted at any time.

I think using some kind of modal UX, Kakoune/Helix/Vim style inputs, would make this very fast and efficient.

So, I think a flat-file editor UI like git rebase -i or sl histedit is a good idea. But I think we can do so, so much better, too (and I would hope that having a raw flat file option wouldn't take the wind out of implementing a TUI, but it is probably much much work admittedly.)