AgentSkillsCN

jj-git-interactive-rebase-to-jj

完整梳理Git交互式变基操作与Jujutsu等效操作之间的转换关系。

SKILL.md
--- frontmatter
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.

Table of Contents

  1. Core Concept: The Paradigm Shift
  2. Complete Operation Mapping
  3. Visual Comparison Workflows
  4. Beyond Git: Jujutsu's Advanced Capabilities
  5. Practical Examples
  6. Quick Reference Tables

Core Concept: The Paradigm Shift

Git's Interactive Rebase Model

mermaid
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:

  1. Enter a special mode (git rebase -i)
  2. Edit a todo list in an editor
  3. Execute all operations sequentially
  4. Handle interruptions with --continue, --abort, or --skip
  5. Cannot make additional changes without restarting

Jujutsu's Direct Editing Model

mermaid
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:

  1. Commands operate directly on any commit in the history
  2. Descendants are automatically rebased when ancestors change
  3. Every operation is atomic and immediately undoable
  4. Conflicts are committed and can be resolved later
  5. 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 RebaseWhat It DoesJujutsu CommandKey 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 messagejj describe -r <commit> -m "message"Can reword any commit directly without entering special mode
edit <commit>Stop to modify commitjj 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 messagejj squash -r <commit> -m "message"Specify message explicitly with -m
fixup -C <commit>Merge, use fixup message, no editjj squash -r <commit>Default behavior when commit has a description
drop <commit>Remove commit entirelyjj 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
breakPause to make changes(not needed)All operations are atomic; never in "rebasing mode"
label <name>Mark a point in historyjj bookmark create <name> -r <commit>Creates a bookmark (jj's term for Git branches)
reset <label>Move to labeled pointjj new <bookmark> or jj edit <bookmark>new creates new commit on top; edit checks out bookmark
merge <branches>Create merge commitjj new <parent1> <parent2> ...Multiple parents create a merge; supports n-way merges

Advanced Operations

OperationGit Interactive RebaseJujutsu CommandNotes
Reorder commitsReorder lines in todo listjj rebase -r <commit> -A <target> or -B <target>-A = insert after; -B = insert before; descendants follow
Split commitMark "edit", reset, stage, commitjj split -r <commit>Opens TUI to select changes for first commit; remainder becomes second
Partial squashStage subset, commit --amendjj squash -i -r <commit>Interactive diff editor to select hunks to move
Move to specific ancestorfixup + reorder + execjj 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 absorbAutomatically distributes working copy changes to appropriate ancestors
Keep empty after squash(not possible)jj squash --keep-emptiedPreserves source commit after moving changes

Conflict Resolution

Git Interactive RebaseJujutsu Equivalent
Rebase stops with conflict markersConflict 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 --abortjj undo or jj op restore <operation>
git rebase --skipjj abandon <commit> (to skip applying a commit)
Resolve before continuingjj new <conflicted>, resolve, jj squash into conflicted commit
git mergetooljj resolve (launches merge tool for each conflict)

Visual Comparison Workflows

Workflow 1: Reordering Commits

Git Interactive Rebase:

mermaid
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:

mermaid
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:

code
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:

code
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 (@)

Or more directly:

bash
# From E, squash everything into C
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:

bash
jj op log              # See all operations performed on repository
jj op show <id>        # Show what an operation changed
jj undo                # Undo last operation (any operation!)
jj op restore <id>     # Restore repo to any previous state

Example:

bash
$ jj log -r @
@  abc123 feat: add feature X

$ jj describe -m "feat: add feature Y"  # Oops, wrong message
$ jj squash                              # Oops, didn't mean to squash
$ jj rebase -d main                      # Oops, wrong base

$ jj op log
@  def456 ... rebase
○  ghi789 ... squash
○  jkl012 ... describe
○  mno345 ... [original state]

$ jj undo                # Undoes rebase
$ jj undo                # Undoes squash
$ jj undo                # Undoes describe - back to original!

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:

bash
# Reword multiple commits simultaneously
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"

# Split multiple commits
jj split -r X
jj split -r Y
jj split -r Z

# All operations execute immediately and independently

3. Absorb - Intelligent Change Distribution

bash
# Make changes to multiple files
echo "fix" >> file1.txt
echo "improvement" >> file2.txt
echo "refactor" >> file3.txt

# Automatically move each change to the commit that last touched those lines
jj absorb

# jj analyzes blame information and distributes changes appropriately

Visual representation:

mermaid
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:

bash
jj log
@  work-in-progress   # Working copy is always a commit
○  finished-feature
○  main

# No "git add" needed - all changes are tracked
# No "git commit --amend" needed - changes automatically amend @
# No "git stash" needed - just create new commit with jj new

5. Conflicted Commits

bash
# After a rebase that would conflict in Git:
jj log
@  abc123 (conflict) attempted merge
○  def456 feature branch
○  ghi789 main

# Work continues normally - conflict is just marked
jj new               # Create new commit on top of conflict
jj new other-commit  # Or work on something else
jj edit abc123       # Come back to resolve conflict later

6. Flexible Targeting

bash
# Move changes between ANY two commits (not just parent/child)
jj squash --from <commit-A> --into <commit-B>

# Insert commit anywhere in history
jj rebase -r X -A Y -B Z    # Insert X after Y and before Z

# Duplicate commits to multiple locations
jj duplicate <commit> -d <dest1>
jj duplicate <commit> -d <dest2>

7. Revsets - Powerful Commit Selection

bash
# Select multiple commits with expressions
jj rebase -s 'author("alice")' -d main          # All commits by Alice
jj abandon 'empty()'                             # All empty commits
jj squash -r 'description(glob:"WIP:*")'        # All WIP commits
jj log -r 'mine() & ~bookmarks()'               # My commits not on bookmarks

8. Evolution Log

Track how a single change evolved:

bash
jj evolog -r <commit>    # See all versions of this commit
jj evolog -p             # With diffs showing what changed each time

# Shows history of amendments, rebases, descriptions, etc.

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:

bash
git rebase -i main
# Manually edit todo list:
# - Reorder commits
# - Mark several commits as "fixup"
# - Mark one as "edit" to split
# Deal with conflicts at each step
git rebase --continue (multiple times)

Jujutsu approach:

bash
# Reword WIP commits
jj describe -r <commit1> -m "proper message"
jj describe -r <commit2> -m "another proper message"

# Squash fixup commits
jj squash -r <fixup-commit1>
jj squash -r <fixup-commit2>

# Reorder if needed
jj rebase -r X -B Y

# Split a commit
jj split -r <large-commit>

# All done! Each command executes immediately
# Use jj undo at any point if you make a mistake

Example 2: Fix a Bug in an Earlier Commit

Git approach:

bash
# Make fix
git add <files>
git commit --fixup=<commit-to-fix>
git rebase -i --autosquash <base>
# Interactive rebase executes

Jujutsu approach:

bash
# Approach 1: Direct edit
jj edit <commit-to-fix>
# Make changes
# Changes automatically amend the commit
jj new @-  # Return to where you were

# Approach 2: From working copy
# Make changes in working copy
jj squash --into <commit-to-fix>

# Approach 3: Interactive selection
jj squash -i --into <commit-to-fix>

Example 3: Rebase Complex Branch Structure

Scenario:

code
    C - D - E (feature-v2)
   /
  B (feature-v1)
 /
A (main)

Want to rebase feature-v2 directly onto A

Git approach:

bash
git rebase --onto A B feature-v2
# OR
git rebase -i A
# Manually drop commits from feature-v1

Jujutsu approach:

bash
# Approach 1: Direct rebase
jj rebase -s C -d A

# Approach 2: Using revset
jj rebase -s 'B..feature-v2' -d A

# Result:
# A - C' - D' - E' (feature-v2)
#  \
#   B (feature-v1)

Example 4: Recover from Mistakes

Scenario: You ran several operations and realized the third operation was wrong.

Git approach:

bash
# Very difficult without reflog expertise
git reflog
git reset --hard HEAD@{5}  # Hope you count correctly
# Lost all subsequent work

Jujutsu approach:

bash
jj op log
#  @  op4 ... latest operation
#  ○  op3 ... the mistaken operation
#  ○  op2 ...
#  ○  op1 ...

jj undo       # Undoes op4
jj undo       # Undoes op3 (the mistake)
jj op restore op4  # Restores op4
# Now have: op1, op2, op4 (op3 was removed)

# Or restore to exact state:
jj op restore <op-before-mistake>

Example 5: Split Commit by File

Git approach:

bash
git rebase -i <commit>^
# Mark commit as "edit"
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:

bash
# By path specification:
jj split -r <commit> file1.txt
# file1.txt goes to first commit
# file2.txt stays in second commit

# Or interactively:
jj split -i -r <commit>
# TUI to select changes

Quick Reference Tables

Operation Categories

Message Editing

TaskJujutsu Command
Change commit messagejj describe -r <commit> -m "message"
Open editor for messagejj describe -r <commit>
Change authorjj describe -r <commit> --author "Name <email>"
Reset author to selfjj describe -r <commit> --reset-author

Commit Combination

TaskJujutsu Command
Squash into parentjj squash -r <commit>
Squash partial into parentjj squash -i -r <commit>
Squash into specific ancestorjj squash --from <commit> --into <ancestor>
Squash between any commitsjj squash --from <src> --into <dest>
Keep empty after squashjj squash -r <commit> --keep-emptied
Auto-squash to right placesjj absorb

Commit Division

TaskJujutsu Command
Split commit interactivelyjj split -r <commit>
Split by pathsjj split -r <commit> <paths>
Create new commit on topjj new <commit>
Move working copy changes to new commitjj commit
Move specific paths to new commitjj commit <paths>

History Rewriting

TaskJujutsu Command
Remove commitjj abandon <commit>
Edit commit directlyjj edit <commit>
Edit commit without checkoutjj diffedit -r <commit>
Duplicate commitjj duplicate <commit>
Duplicate to specific locationjj duplicate <commit> -d <dest>

Commit Reordering & Moving

TaskJujutsu Command
Move commit after targetjj rebase -r <commit> -A <target>
Move commit before targetjj rebase -r <commit> -B <target>
Move commit to new parentjj rebase -r <commit> -d <parent>
Move commit treejj rebase -s <commit> -d <dest>
Move branch to new basejj rebase -b <branch> -d <dest>

Conflict Management

TaskJujutsu Command
See conflicted filesjj status or jj log -r 'conflict()'
Start resolving conflictsjj new <conflicted-commit>
Resolve with merge tooljj resolve
List conflictsjj resolve --list
Move resolution into conflicted commitjj squash

Undo Operations

TaskJujutsu Command
Undo last operationjj undo
See operation historyjj op log
Restore to specific operationjj op restore <operation>
View changes in operationjj op show <operation>
Create reverse commit (git revert)jj revert -r <commit> -d <target>

Common Patterns

"I want to edit commit X"

bash
jj edit X              # Checkout X, changes amend it
# ... make changes ...
# Changes automatically amend X

"I want to add these changes to commit X"

bash
# Make changes in working copy
jj squash --into X

"I want to split commit X by hunks"

bash
jj split -i -r X

"I want to combine commits A, B, and C"

bash
jj squash -r C         # C → B
jj squash -r B         # B → A
# Result: One commit at A with all changes

"I want to reorder A-B-C to A-C-B"

bash
jj rebase -r C -B B

"I want to move commit X to a different branch"

bash
jj rebase -r X -d <other-branch>

"I messed up, undo everything"

bash
jj undo                          # Undo last operation
jj undo                          # Undo one more
jj op restore <operation-id>     # Go back to specific state

Command Flag Reference

jj rebase flags

FlagMeaningExample
-r <revset>Rebase only specified commitsjj rebase -r X -d Y
-s <revset>Rebase commit and descendantsjj 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

FlagMeaningExample
-r <revset>Squash this commit into parentjj squash -r X
--from <revset>Source of changesjj squash --from X --into Y
--into <revset>Destination of changesjj squash --into Y
-iInteractive (select hunks)jj squash -i -r X
-m <message>Set descriptionjj squash -r X -m "msg"
--keep-emptiedKeep empty source commitjj squash -r X --keep-emptied
<paths>Squash only these pathsjj squash file1.txt file2.txt

jj new flags

FlagMeaningExample
<revset>Parent(s) of new commitjj new X or jj new X Y (merge)
-m <message>Set descriptionjj new X -m "Start feature"
-A <revset>Insert afterjj new -A X
-B <revset>Insert beforejj new -B X
--no-editDon't edit new commitjj new X --no-edit

Summary: Why Jujutsu Doesn't Need Interactive Rebase

Git's interactive rebase exists because:

  1. The working copy is not a commit
  2. You need a staging area to prepare commits
  3. History editing requires entering a special mode
  4. Operations are batched and executed sequentially
  5. Conflicts pause the entire process

Jujutsu eliminates these needs by:

  1. Making the working copy a commit (@)
  2. Eliminating the staging area
  3. Making every command operate directly on history
  4. Executing each operation immediately and atomically
  5. Committing conflicts and allowing later resolution
  6. Auto-rebasing descendants of changed commits
  7. 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.