| name | jj-git-interactive-rebase-to-jj |
| description | Complete reference for translating git interactive rebase operations to Jujutsu equivalents. |
| disable-model-invocation | true |
Complete Reference: Git Interactive Rebase to Jujutsu
A comprehensive guide for Git users transitioning to Jujutsu, mapping all interactive rebase operations to their jj equivalents and beyond.
Diamond / development-join applicability.
The single-chain examples below treat @ as a freely-mutable content commit.
That model does not apply inside a development join (the diamond workflow).
Inside a join, @ is a shared, always-empty [wip] commit sitting directly on a multi-parent join, and that [wip] is the stable coordination surface that makes N concurrent editors safe by construction.
In this repo a pushed wip deploy bookmark additionally tracks @, so machines rebuild from it.
Inside a join never jj describe @, never jj rebase -r @ / --revisions @, and never bare jj squash -r @ (the form at the squashing example below advances @ through a chain and consumes the [wip]).
Route changes downward while leaving @ empty in place with jj squash --from @ --insert-after <target> -m "..." --keep-emptied [-- <paths>] (or --insert-before), jj absorb (documented below as the blame-routed down-route), or jj split keeping the wip.
Canonical rule, the single-shared-[wip] invariant, and rationale live in ~/.claude/skills/jj-version-control/diamond-workflow.md; join mechanics in ~/.claude/skills/jj-version-control/SKILL.md; non-interactive command execution in ~/.claude/skills/jj-workflow/SKILL.md.
Table of Contents
- Core Concept: The Paradigm Shift
- Complete Operation Mapping
- Visual Comparison Workflows
- Beyond Git: Jujutsu's Advanced Capabilities
- Practical Examples
- Quick Reference Tables
Core Concept: The Paradigm Shift
Git's Interactive Rebase Model
graph TD
A[Start Interactive Rebase] --> B[Edit Todo List]
B --> C[Process Each Commit]
C --> D{Conflict?}
D -->|Yes| E[Stop, Resolve, Continue]
D -->|No| F[Next Commit]
E --> F
F --> G{More Commits?}
G -->|Yes| C
G -->|No| H[Complete Rebase]
In Git, interactive rebase is a batch operation where you:
- Enter a special mode (
git rebase -i)
- Edit a todo list in an editor
- Execute all operations sequentially
- Handle interruptions with
--continue, --abort, or --skip
- Cannot make additional changes without restarting
Jujutsu's Direct Editing Model
graph TD
A[Identify Target Commit] --> B[Execute Direct Command]
B --> C[Descendants Auto-Rebase]
C --> D[Operation Complete]
D --> E{Want to Undo?}
E -->|Yes| F[jj undo]
E -->|No| G[Continue Working]
F --> A
In Jujutsu, there is no special mode:
- Commands operate directly on any commit in the history
- Descendants are automatically rebased when ancestors change
- Every operation is atomic and immediately undoable
- Conflicts are committed and can be resolved later
- You never leave the normal working state
The fundamental insight: In jj, every commit operation is non-interactive by default, making "interactive rebase" as a concept obsolete.
Complete Operation Mapping
Basic Operations
| Git Interactive Rebase | What It Does | Jujutsu Command | Key Differences |
|---|
pick <commit> | Keep commit as-is | (automatic) | All commits are "picked" by default; use jj new <commit> to build on top |
reword <commit> | Edit commit message | jj describe -r <commit> -m "message" | Can reword any commit directly without entering special mode |
edit <commit> | Stop to modify commit | jj edit <commit> | Checks out commit immediately; changes automatically amend it |
squash <commit> | Merge into parent (keep both messages) | jj squash -r <commit> | Moves changes from commit into parent; descendants auto-rebase |
fixup <commit> | Merge into parent (discard message) | jj squash -r <commit> | jj doesn't distinguish fixup from squash; both discard the source message by default |
fixup -c <commit> | Merge, use fixup message | jj squash -r <commit> -m "message" | Specify message explicitly with -m |
fixup -C <commit> | Merge, use fixup message, no edit | jj squash -r <commit> | Default behavior when commit has a description |
drop <commit> | Remove commit entirely | jj abandon <commit> | Abandons commit; descendants rebased onto grandparent(s) |
exec <command> | Run shell command at this point | (no direct equivalent) | Use jj log + shell script, or test after each change and jj undo if needed |
break | Pause to make changes | (not needed) | All operations are atomic; never in "rebasing mode" |
label <name> | Mark a point in history | jj bookmark create <name> -r <commit> | Creates a bookmark (jj's term for Git branches) |
reset <label> | Move to labeled point | jj new <bookmark> or jj edit <bookmark> | new creates new commit on top; edit checks out bookmark |
merge <branches> | Create merge commit | jj new <parent1> <parent2> ... | Multiple parents create a merge; supports n-way merges |
Advanced Operations
| Operation | Git Interactive Rebase | Jujutsu Command | Notes |
|---|
| Reorder commits | Reorder lines in todo list | jj rebase -r <commit> -A <target> or -B <target> | -A = insert after; -B = insert before; descendants follow |
| Split commit | Mark "edit", reset, stage, commit | jj split -r <commit> -- <paths> -m "msg" | Non-interactive: pass explicit paths and -m. Bare jj split (no paths) opens a diff TUI and hangs non-interactive Bash; jj split <paths> opens the editor twice (extracted commit plus remainder), so pre-stage the remainder via jj describe -r <commit> -m "<remainder>" first. See jj-workflow "Non-interactive command execution". |
| Partial squash | Stage subset, commit --amend | jj squash -i -r <commit> | Interactive diff editor to select hunks to move |
| Move to specific ancestor | fixup + reorder + exec | jj squash --from <commit> --into <ancestor> | Can target any ancestor, not just parent |
| Move between any commits | (not possible) | jj squash --from <src> --into <dest> | Works on any two commits in history |
| Edit without checkout | (not possible) | jj diffedit -r <commit> | Edit commit's diff directly in editor without checking out |
| Auto-absorb changes | (not possible) | jj absorb | Automatically distributes working copy changes to appropriate ancestors |
| Keep empty after squash | (not possible) | jj squash --keep-emptied | Preserves source commit after moving changes |
Conflict Resolution
| Git Interactive Rebase | Jujutsu Equivalent |
|---|
| Rebase stops with conflict markers | Conflict is committed; marked as "conflict" in log |
git add <file> | (automatic - jj tracks all files) |
git rebase --continue | (not needed - never in special mode) |
git rebase --abort | jj undo or jj op restore <operation> |
git rebase --skip | jj abandon <commit> (to skip applying a commit) |
| Resolve before continuing | jj new <conflicted>, resolve, jj squash into conflicted commit |
git mergetool | jj resolve (launches merge tool for each conflict) |
Visual Comparison Workflows
Workflow 1: Reordering Commits
Git Interactive Rebase:
sequenceDiagram
participant U as User
participant G as Git
participant E as Editor
U->>G: git rebase -i HEAD~5
G->>E: Open todo list editor
E->>U: Show:<br/>pick A<br/>pick B<br/>pick C
U->>E: Edit to:<br/>pick A<br/>pick C<br/>pick B
E->>G: Save and close
G->>G: Execute rebase
G->>U: Complete (or stop on conflict)
Jujutsu Direct Command:
sequenceDiagram
participant U as User
participant J as Jujutsu
U->>J: jj rebase -r C -B B
J->>J: Move C before B
J->>J: Auto-rebase descendants
J->>U: Complete immediately
Workflow 2: Squashing Multiple Commits
Git:
A - B - C - D - E (HEAD)
Goal: Combine C, D, E into one commit
git rebase -i HEAD~5
# Change todo list:
pick A
pick B
pick C
squash D
squash E
Jujutsu:
A - B - C - D - E (@)
Goal: Same
jj squash -r @ # E squashed into D, @ now points to D
jj squash -r @ # D squashed into C, @ now points to C
# Result: A - B - C (@)
Single-chain cleanup only.
In a development join @ is the shared empty [wip] sitting on the multi-parent join, so jj squash -r @ would consume that [wip] into its parent and re-point @ — emptying the editing surface concurrent actors write to and, in this repo, dragging the pushed wip deploy bookmark.
There, route changes downward with jj squash --from @ --insert-before <target> --keep-emptied -m "..." (or --insert-after <target>), which leaves @ empty and in place, or jj absorb.
See ~/.claude/skills/jj-version-control/diamond-workflow.md.
Or more directly:
jj squash --from @ --into C
jj squash --from @- --into C
Beyond Git: Jujutsu's Advanced Capabilities
These features have no equivalent in Git's interactive rebase and represent jj's advanced capabilities.
1. Operation Log - Complete History Tracking
What Git has:
- Per-ref reflog (
git reflog)
- Limited to tracking HEAD and branch movements
- Cannot undo complex multi-ref operations atomically
What Jujutsu has:
jj op log
jj op show <id>
jj undo
jj op restore <id>
Example:
$ jj log -r @
@ abc123 feat: add feature X
$ jj describe -m "feat: add feature Y"
$ jj squash
$ jj rebase -d main
$ jj op log
@ def456 ... rebase
○ ghi789 ... squash
○ jkl012 ... describe
○ mno345 ... [original state]
$ jj undo
$ jj undo
$ jj undo
2. Parallel History Editing
Git limitation: Can only edit one commit at a time in interactive rebase mode.
Jujutsu capability: Edit multiple commits in parallel:
jj describe -r A -m "Fix typo in A"
jj describe -r B -m "Fix typo in B"
jj describe -r C -m "Fix typo in C"
jj describe -r X -m "<remainder>"
jj split -r X -- <paths> -m "<extracted>"
jj describe -r Y -m "<remainder>"
jj split -r Y -- <paths> -m "<extracted>"
3. Absorb - Intelligent Change Distribution
echo "fix" >> file1.txt
echo "improvement" >> file2.txt
echo "refactor" >> file3.txt
jj absorb
Visual representation:
graph LR
A[Working Copy<br/>Changes to<br/>file1, file2, file3] -->|jj absorb| B[Changes<br/>distributed<br/>automatically]
B --> C[Commit X<br/>gets file1 changes]
B --> D[Commit Y<br/>gets file2 changes]
B --> E[Commit Z<br/>gets file3 changes]
4. Working Copy Commit
The working copy is always a commit:
jj log
@ work-in-progress
○ finished-feature
○ main
5. Conflicted Commits
jj log
@ abc123 (conflict) attempted merge
○ def456 feature branch
○ ghi789 main
jj new
jj new other-commit
jj edit abc123
6. Flexible Targeting
jj squash --from <commit-A> --into <commit-B>
jj rebase -r X -A Y -B Z
jj duplicate <commit> -d <dest1>
jj duplicate <commit> -d <dest2>
7. Revsets - Powerful Commit Selection
jj rebase -s 'author("alice")' -d main
jj abandon 'empty()'
jj squash -r 'description(glob:"WIP:*")'
jj log -r 'mine() & ~bookmarks()'
8. Evolution Log
Track how a single change evolved:
jj evolog -r <commit>
jj evolog -p
Practical Examples
Example 1: Clean Up a Feature Branch
Scenario: You have 10 commits with several "WIP" commits, typo fixes, and changes that should be reordered.
Git approach:
git rebase -i main
git rebase --continue (multiple times)
Jujutsu approach:
jj describe -r <commit1> -m "proper message"
jj describe -r <commit2> -m "another proper message"
jj squash -r <fixup-commit1>
jj squash -r <fixup-commit2>
jj rebase -r X -B Y
jj describe -r <large-commit> -m "<remainder>"
jj split -r <large-commit> -- <paths> -m "<extracted>"
Example 2: Fix a Bug in an Earlier Commit
Git approach:
git add <files>
git commit --fixup=<commit-to-fix>
git rebase -i --autosquash <base>
Jujutsu approach:
jj edit <commit-to-fix>
jj new @-
jj squash --into <commit-to-fix>
jj squash -i --into <commit-to-fix>
Example 3: Rebase Complex Branch Structure
Scenario:
C - D - E (feature-v2)
/
B (feature-v1)
/
A (main)
Want to rebase feature-v2 directly onto A
Git approach:
git rebase --onto A B feature-v2
git rebase -i A
Jujutsu approach:
jj rebase -s C -d A
jj rebase -s 'B..feature-v2' -d A
Example 4: Recover from Mistakes
Scenario: You ran several operations and realized the third operation was wrong.
Git approach:
git reflog
git reset --hard HEAD@{5}
Jujutsu approach:
jj op log
jj undo
jj undo
jj op restore op4
jj op restore <op-before-mistake>
Example 5: Split Commit by File
Git approach:
git rebase -i <commit>^
git reset HEAD^
git add file1.txt
git commit -m "Part 1"
git add file2.txt
git commit -m "Part 2"
git rebase --continue
Jujutsu approach:
jj describe -r <commit> -m "Part 2"
jj split -r <commit> -- file1.txt -m "Part 1"
Quick Reference Tables
Operation Categories
Message Editing
| Task | Jujutsu Command |
|---|
| Change commit message | jj describe -r <commit> -m "message" |
| Open editor for message | jj describe -r <commit> |
| Change author | jj describe -r <commit> --author "Name <email>" |
| Reset author to self | jj describe -r <commit> --reset-author |
Commit Combination
| Task | Jujutsu Command |
|---|
| Squash into parent | jj squash -r <commit> |
| Squash partial into parent | jj squash -i -r <commit> |
| Squash into specific ancestor | jj squash --from <commit> --into <ancestor> |
| Squash between any commits | jj squash --from <src> --into <dest> |
| Keep empty after squash | jj squash -r <commit> --keep-emptied |
| Auto-squash to right places | jj absorb |
Commit Division
| Task | Jujutsu Command |
|---|
| Split commit (non-interactive) | jj split -r <commit> -- <paths> -m "msg" (pre-stage remainder with jj describe -r <commit> -m; bare jj split / -i open a TUI and hang non-interactive shells) |
| Split by paths | jj split -r <commit> <paths> |
| Create new commit on top | jj new <commit> |
| Move working copy changes to new commit | jj commit |
| Move specific paths to new commit | jj commit <paths> |
History Rewriting
| Task | Jujutsu Command |
|---|
| Remove commit | jj abandon <commit> |
| Edit commit directly | jj edit <commit> |
| Edit commit without checkout | jj diffedit -r <commit> |
| Duplicate commit | jj duplicate <commit> |
| Duplicate to specific location | jj duplicate <commit> -d <dest> |
Inside a development join: keep @ as the empty [wip] | Do not describe @ / rebase -r @ / bare squash -r @; route down with jj squash --from @ --insert-after <target> -m "..." --keep-emptied, jj absorb, or jj split (keep wip). See ~/.claude/skills/jj-version-control/diamond-workflow.md. |
Commit Reordering & Moving
| Task | Jujutsu Command |
|---|
| Move commit after target | jj rebase -r <commit> -A <target> |
| Move commit before target | jj rebase -r <commit> -B <target> |
| Move commit to new parent | jj rebase -r <commit> -d <parent> |
| Move commit tree | jj rebase -s <commit> -d <dest> |
| Move branch to new base | jj rebase -b <branch> -d <dest> |
Conflict Management
| Task | Jujutsu Command |
|---|
| See conflicted files | jj status or jj log -r 'conflict()' |
| Start resolving conflicts | jj new <conflicted-commit> |
| Resolve with merge tool | jj resolve |
| List conflicts | jj resolve --list |
| Move resolution into conflicted commit | jj squash |
Undo Operations
| Task | Jujutsu Command |
|---|
| Undo last operation | jj undo |
| See operation history | jj op log |
| Restore to specific operation | jj op restore <operation> |
| View changes in operation | jj op show <operation> |
| Create reverse commit (git revert) | jj revert -r <commit> -d <target> |
Common Patterns
"I want to edit commit X"
jj edit X
"I want to add these changes to commit X"
jj squash --into X
"I want to split commit X by paths"
jj describe -r X -m "<remainder>"
jj split -r X -- <paths> -m "<extracted>"
"I want to combine commits A, B, and C"
jj squash -r C
jj squash -r B
"I want to reorder A-B-C to A-C-B"
jj rebase -r C -B B
"I want to move commit X to a different branch"
jj rebase -r X -d <other-branch>
"I messed up, undo everything"
jj undo
jj undo
jj op restore <operation-id>
Command Flag Reference
jj rebase flags
| Flag | Meaning | Example |
|---|
-r <revset> | Rebase only specified commits | jj rebase -r X -d Y |
-s <revset> | Rebase commit and descendants | jj rebase -s X -d Y |
-b <revset> | Rebase branch (revs from base) | jj rebase -b X -d Y |
-d <revset> | Destination (new parent) | jj rebase -r X -d Y |
-A <revset> | Insert after (and rebase children) | jj rebase -r X -A Y |
-B <revset> | Insert before (and rebase children) | jj rebase -r X -B Y |
jj squash flags
| Flag | Meaning | Example |
|---|
-r <revset> | Squash this commit into parent | jj squash -r X |
--from <revset> | Source of changes | jj squash --from X --into Y |
--into <revset> | Destination of changes | jj squash --into Y |
-i | Interactive (select hunks) | jj squash -i -r X |
-m <message> | Set description | jj squash -r X -m "msg" |
--keep-emptied | Keep empty source commit | jj squash -r X --keep-emptied |
<paths> | Squash only these paths | jj squash file1.txt file2.txt |
jj new flags
| Flag | Meaning | Example |
|---|
<revset> | Parent(s) of new commit | jj new X or jj new X Y (merge) |
-m <message> | Set description | jj new X -m "Start feature" |
-A <revset> | Insert after | jj new -A X |
-B <revset> | Insert before | jj new -B X |
--no-edit | Don't edit new commit | jj new X --no-edit |
Summary: Why Jujutsu Doesn't Need Interactive Rebase
Git's interactive rebase exists because:
- The working copy is not a commit
- You need a staging area to prepare commits
- History editing requires entering a special mode
- Operations are batched and executed sequentially
- Conflicts pause the entire process
Jujutsu eliminates these needs by:
- Making the working copy a commit (
@)
- Eliminating the staging area
- Making every command operate directly on history
- Executing each operation immediately and atomically
- Committing conflicts and allowing later resolution
- Auto-rebasing descendants of changed commits
- Providing operation-level undo for any mistake
The result: Every operation you'd use interactive rebase for in Git is a direct, immediate command in jj. You never enter a special mode, never need to continue or abort, and can undo anything instantly.
This document serves as a complete reference for transitioning from Git's interactive rebase to Jujutsu's direct commit editing model.