Editing history without holding my breath
Part 7 of 9.
The three moves this lesson covers - start new work, edit existing work, start new work from a point in the past - map onto things I already did constantly in Git. The difference is how much I used to brace for them.
jj new closing out the current working copy and opening an empty one is unremarkable,
basically git commit with the staging step removed. The one that changed how I work is
jj edit <change-id>. Fixing something two commits back used to mean git rebase -i,
picking edit on the right line, making the fix, git rebase --continue, and hoping
nothing downstream conflicted along the way. With jj, jj edit just moves the working
copy onto that commit. Any file I touch amends it directly. Descendants get rebased
automatically the next time jj needs to show me the graph. No detached-HEAD state, no
multi-step rebase to abandon halfway through if I change my mind - jj undo covers that.
Starting new work from a past commit instead of @ was the one that felt most
unfamiliar coming from Git, mostly because Git makes the equivalent (checking out an old
commit, then branching from it) feel like a special, slightly risky operation. In jj it’s
just jj new <old-change-id>, and the log immediately shows a fork - two real heads
sharing an ancestor, neither privileged over the other. Nothing forces me to decide right
away whether that second line of work becomes a bookmark, gets merged back, or gets
abandoned. That deferred-decision property is what I didn’t expect to value as much as I
do: the moment “let me just quickly check this from main instead” comes up mid-task, it’s
a single command instead of a stash-and-switch dance.