Jujutsu's rebase Has Three Different "Move" Verbs
Today I got bitten by jj rebase. I ran jj rebase -r uqxnyrku -o main
expecting the descendants to come along, the way git rebase moves a chain.
They didn’t. The descendant got reparented onto uqxnyrku’s old parent,
disconnecting it from the commit I’d just moved.
The Git muscle-memory model, “rebase moves the chain”, doesn’t translate
directly. jj makes you say which chain:
| Flag | Moves |
|---|---|
-r REV | Just that single commit. Descendants get reparented to its old parent. |
-s REV | That commit and all descendants. The whole subtree comes along. |
-b REV | The entire branch containing REV (everything between trunk and REV). |
-r snipping a commit out and leaving its descendants behind is occasionally
what you want (extracting one commit from a stack to relocate it) but it’s
almost never what Git muscle memory expects. -s is the verb that matches
Git’s default rebase: “move this and everything on top of it.”
Recovering from the wrong one
Because change IDs are stable across moves, the orphaned descendant was cheap to reattach with a second rebase:
jj rebase -r xkqrkqmt -o uqxnyrku # reattach the orphan
Gotchas / Notes
- When migrating from Git, default to
jj rebase -s REV -o <dest>unless you specifically want to extract a single commit from its descendants. -ris the precision tool;-sis the everyday one. The accident above came from reaching for-rbecause it’s first in the help output.