Jujutsu (jj) Quick Reference
A concise cheat sheet for daily Jujutsu VCS operations.
🚀 Getting Started
Initial Setup
jj git init # Initialize a new repo (Git-backed)
jj git clone <url> # Clone a Git repository
# Example: Clone and start working
jj git clone https://github.com/user/repo.git
cd repo
jj log # View commit history
jj new main # Start new work from main
First Commits
# Create your first change
jj new main # Start from main
jj describe -m "Add README" # Set description
echo "# My Project" > README.md
# Changes automatically tracked!
jj status # See what changed
jj diff # See exact changes
# Create another commit
jj new # New commit on top of current
jj describe -m "Add config"
echo "{}" > config.json
# View your work
jj log # See commit history
Basic Status Checks
jj status # Show working copy status
jj log # View commit graph
jj show # Show current commit details
jj diff # Show uncommitted changes
# Example: Check your work
jj status # See modified files
jj diff src/app.ts # See changes in specific file
jj log -r "@~::" # See last few commits
📝 Daily Workflow
Creating Changes
jj new # Create new empty change on top of current
jj new <commit> # Create new change on top of specific commit
jj new main # Start work from main bookmark
jj describe # Edit current change description
jj describe -m "message" # Set description directly
# Example: Start new feature
jj new main
jj describe -m "EX-123: Add user authentication"
# Make your changes...
Working with Files
jj diff # Show uncommitted changes
jj diff -r <commit> # Show changes in a specific commit
jj diff --from main # Show changes since main
jj diff --from main --to @ # Same as above (explicit)
jj restore <file> # Discard changes to file
jj restore . # Discard all changes
jj restore --from <commit> <file> # Restore file from specific commit
# Examples:
jj restore package.json # Undo changes to package.json
jj restore 'src/**/*.test.ts' # Restore all test files
jj restore --from main src/ # Reset src/ to main's version
Viewing History
jj log # Show commit graph
jj log -r <commit> # Show specific commit
jj log -r "main..@" # Commits between main and current
jj log --limit 10 # Show last 10 commits
jj log -r "mine()" # Show only your commits
jj show <commit> # Show commit details and diff
jj show # Show current commit (same as jj show @)
# Examples:
jj log -r "description(bug)" # Find commits with "bug" in description
jj log -r "@---::" # Last 3 commits and current
jj log --summary # Show file changes summary
Quick commit operations
# Amend current commit (just make changes and they're included)
vim src/file.ts
# Changes automatically tracked in current commit
# Create commit with specific files only
jj new
vim file1.ts file2.ts
jj squash file1.ts # Only file1 goes to previous commit
# file2.ts stays in current commit
# Undo last change
jj undo # Undo most recent jj operation
🔀 Managing Commits
Splitting Changes
Split current commit into multiple commits:
# Method 1: Split using paths (recommended)
# You have changes to fileA.ts, fileB.ts, fileC.ts all in one commit
jj split fileA.ts fileB.ts # First commit gets fileA + fileB
# Remaining fileC.ts stays in second commit
# Method 2: Interactive split (opens diff editor)
jj split # Interactively choose hunks for first commit
# Example: Split UI and data changes
jj split src/components/ src/ui/ # UI in first commit
# Everything else in second commit
# Example: Create empty first commit, move files selectively
jj split -- # Creates empty first commit
jj squash fileA.ts fileB.ts # Move specific files to that commit
Copying Changes Between Commits
Copy specific files from one commit to another:
# Copy file from commit X to current working copy
jj restore --from <commit> <file>
# Example: Copy config.ts from commit abc123
jj restore --from abc123 src/config.ts
# Copy multiple files
jj restore --from abc123 src/config.ts src/utils.ts
# Copy entire directory
jj restore --from abc123 src/components/
Move changes from current commit to parent:
# Move specific files to parent commit
jj squash <file>
# Example: Move only config.ts to parent
jj squash config.ts
# Other files remain in current commit
# Move multiple files
jj squash config.ts utils.ts
# Move all matching pattern
jj squash 'src/**/*.test.ts'
Move changes from parent to current:
# Get specific files from parent
jj restore --from @- <file>
# Example: Pull database.ts from parent into current
jj restore --from @- src/database.ts
Copy changes from any commit to another (not current):
# Create new commit from destination
jj new <destination-commit>
# Copy files from source
jj restore --from <source-commit> <files>
jj describe -m "Copied changes from <source>"
Squashing Commits
Squash current commit into parent:
jj squash # Move ALL changes to parent, abandon current
jj squash -m "New message" # Squash and update parent description
# Example workflow:
# Before: parent(EX-851) -> current(fix typo)
jj squash
# After: parent(EX-851 + typo fix) -> new empty commit
Squash specific commit into its parent:
jj squash -r <commit> # Squash specific commit
# Example: Squash commit abc123 into its parent
jj squash -r abc123
Squash only specific files:
jj squash <file1> <file2> # Only move these files to parent
# Example: Only move the README changes
jj squash README.md
# Other changes stay in current commit
Rebasing Commits
Rebase current commit to new parent:
jj rebase -d <destination> # Move current commit to new parent
# Example: Move current commit onto main bookmark
jj rebase -d main
# Example: Move current commit onto specific commit
jj rebase -d abc123
Rebase a specific commit and its descendants:
jj rebase -s <source> -d <destination>
# Example: Move feature branch (xyz789) onto updated main
jj rebase -s xyz789 -d main
# xyz789 and all commits on top of it move to main
Rebase only specific commits (no descendants):
jj rebase -r <commit> -d <destination>
# Example: Move just commit abc123 (not its children)
jj rebase -r abc123 -d main
Rebase onto parent's parent (skip one commit):
# Useful when you want to remove the parent commit
jj rebase -d @-- # Rebase onto grandparent
Resolving Conflicts
Check for conflicts:
jj status # Shows if there are conflicts
jj log -r 'conflicts()' # List all commits with conflicts
jj diff # View conflicted files with markers
Resolve conflicts manually:
# 1. View conflicted files
jj status
# 2. Edit files and remove conflict markers manually:
# <<<<<<< Side #1 (Conflict 1 of 1)
# your changes
# |||||||
# base version
# =======
# their changes
# >>>>>>> Side #2 (Conflict 1 of 1 ends)
# 3. Changes are auto-tracked - just save the file
# 4. Verify resolution
jj diff # Check your changes
jj status # Should show no conflicts
Resolve by choosing one side entirely:
# Take "their" version (destination/target of rebase)
jj restore --from <destination-commit> <file>
# Take "our" version (source of rebase)
jj restore --from <source-commit> <file>
# Example: During rebase onto main, take main's package.json
jj restore --from main package.json
# Example: Keep your version of config.ts
jj restore --from @- config.ts
Resolve conflicts in specific files:
# For package.json conflicts, often use theirs
jj restore --from main package.json yarn.lock
# For code files, manually merge then verify
vim src/conflicted-file.ts # Edit and resolve
jj diff # Verify resolution
# For binary files, choose one version
jj restore --from main assets/logo.png
View conflict details:
jj show # Show current commit with conflict markers
jj show @ # Same as above
jj diff -r @ # See all changes including conflicts
jj diff --from <commit> # Compare with specific commit
Abort and start over:
jj undo # Undo the operation that caused conflict
jj op log # See what you can undo
Continue after resolving:
# No special command needed!
# Once conflicts are resolved (jj status shows no conflicts)
# Continue with your normal workflow:
jj new # Create next commit
jj describe # Update description
# Or just make more changes to current commit
Undoing & Editing
jj undo # Undo last operation
jj undo --op <operation> # Undo specific operation (see jj op log)
jj abandon <commit> # Abandon a commit (remove from history)
jj edit <commit> # Start editing a past commit
🏷️ Bookmarks & Branches
jj bookmark list # List all bookmarks
jj bookmark create <name> # Create bookmark at current commit
jj bookmark set <name> -r <commit> # Move bookmark to specific commit
jj bookmark delete <name> # Delete a bookmark
jj new <bookmark> # Create new change on top of bookmark
🔄 Git Integration
Syncing with Git
jj git fetch # Fetch from Git remote
jj git push # Push to Git remote
jj git push --bookmark <name> # Push specific bookmark
jj git push --change <commit> # Push specific change (creates branch)
Working with Pull Requests
# Create PR branch from your changes
jj bookmark create feature-xyz
jj git push --bookmark feature-xyz
# Update PR after review feedback
# Make changes in your commits (split, squash, edit, etc.)
jj git push --bookmark feature-xyz --force
# Sync with updated main
jj git fetch
jj rebase -s <your-feature> -d main
jj git push --bookmark feature-xyz --force
Remote Branch Workflow
# Fetch colleague's branch
jj git fetch --branch their-feature
# Create local bookmark to track it
jj bookmark create their-feature -r <their-commit>
# Make changes on top
jj new their-feature
# Do your work
jj bookmark create my-additions
# Push your additions
jj git push --bookmark my-additions
Git Remotes
jj git remote list # List Git remotes
jj git remote add <n> <url> # Add a Git remote
# Work with multiple remotes
jj git fetch --remote origin
jj git fetch --remote upstream
jj rebase -d origin/main # Rebase onto origin's main
🎯 Common Patterns
Split mixed changes into clean commits
# Starting state: One commit with UI and API changes mixed
jj split 'src/components/**' 'src/ui/**' # UI in first commit
# API changes in second commit
# Or using multiple splits:
jj split src/api/ # API changes in first commit
jj split src/components/ # UI in second commit
# Everything else in third commit
Reorganize commits - move specific changes
# Move database.ts from commit C to commit A
# Before: A -> B -> C(has database.ts) -> @
jj new A # Go to commit A
jj restore --from C src/database.ts # Copy file from C
jj squash # Add it to A
jj new C # Go to commit C
jj restore --from @- src/database.ts # Remove file from C
Work on feature while main moves forward
# Your feature branch is behind main
jj rebase -s <your-feature-commit> -d main
# If you have multiple commits in feature:
jj rebase -s <first-feature-commit> -d main
# All descendants automatically come along
# Example with bookmark:
jj rebase -s feature-branch -d main
Cherry-pick specific changes from another branch
# Copy specific files from commit abc123 onto current branch
jj restore --from abc123 src/feature.ts src/utils.ts
jj describe -m "Cherry-pick feature from abc123"
# Copy entire commit:
jj new # Create new commit
jj restore --from abc123 . # Copy all changes
jj describe -m "Cherry-pick abc123"
Fix a mistake in a past commit
jj edit <commit-to-fix> # Edit the past commit
# Make your changes to files
jj describe -m "Fix: corrected typo" # Update description
jj new <where-you-were> # Jump back to where you were
# Example: Fix commit 3 commits back
jj edit @--- # Edit grandparent's parent
vim src/bug.ts # Fix the bug
jj new @ # Jump back to current
Amend/add to previous commit without editing it
# Add forgotten file to parent commit
jj restore --from @- forgotten.ts # Get file to current commit
# Edit forgotten.ts
jj squash forgotten.ts # Move it to parent
# Or move everything:
# Make all your fixes
jj squash # Move all changes to parent
Create clean commit history for PR
# You have: main -> WIP1 -> WIP2 -> fix -> WIP3 -> @
# 1. Squash all WIP commits
jj squash -r WIP1 # Squash WIP1 into main
jj squash -r WIP2 # Squash WIP2 into main
jj squash -r WIP3 # Squash WIP3 into main
# 2. Now you have: main -> fix -> @
# Split if needed:
jj edit fix
jj split src/feature-a/ # Feature A
jj split src/feature-b/ # Feature B
# Rest stays
Extract feature into separate branch
# Current: A -> B -> C -> D -> E (@)
# Want: Branch from B containing D's changes
jj new B # Create new commit from B
jj bookmark create feature-x # Create bookmark
jj restore --from D . # Copy changes from D
jj describe -m "Feature X"
# Now you have:
# A -> B -> C -> D -> E (@)
# \-> feature-x
Revert a commit (create opposite changes)
# Revert commit abc123
jj new # Create new commit
jj restore --from abc123~ . # Restore to before abc123
jj describe -m "Revert abc123"
# Or manually:
jj new # Create new commit
jj restore --from @- file.ts # Get old version
jj describe -m "Revert changes to file.ts"
🔍 Useful Tips
- No staging area: Changes are automatically tracked
- @ symbol: Refers to current working copy commit
- @- symbol: Refers to parent of working copy
- Change IDs: Persistent across rebases (start with
k,l,m, etc.) - Revset syntax: Use
jj log -r "pattern"for powerful queries - Operation log: Every command is logged and can be undone with
jj undo
Operation Log (Your Safety Net)
jj op log # View all operations you've performed
jj op log --limit 10 # Last 10 operations
jj undo # Undo last operation
jj undo --op <operation-id> # Undo specific operation
# Example: You accidentally abandoned commits
jj op log # Find the operation
jj undo --op abc123 # Restore everything
Revset Examples
jj log -r "main..@" # Commits between main and current
jj log -r "description(fix)" # Commits with "fix" in description
jj log -r "@-" # Parent commit
jj log -r "all()" # All commits
jj log -r "mine()" # Your commits
jj log -r "conflicts()" # Commits with conflicts
Working with Multiple Changes
# View your current stack
jj log -r "@::@---" # Current + 3 parents
# Create parallel branches
jj new main # Branch A from main
jj new main # Branch B from main (separate)
# Work on specific change without losing current
jj new <other-commit> # Jump to work on other commit
jj new <original-commit> # Jump back when done
🔧 Troubleshooting
"Uncommitted changes would be lost"
# You're trying to switch commits but have changes
# Option 1: Create new commit with changes
jj new
jj describe -m "WIP: save my work"
# Option 2: Abandon changes
jj restore .
"The given commits are not in a linear sequence"
# You're trying to squash non-adjacent commits
# Use rebase to make them adjacent first
jj rebase -r <commit-to-squash> -d <target-parent>
jj squash -r <commit-to-squash>
"No repo found"
# Make sure you're in a jj repository
jj status
# If not initialized:
jj git init
Accidentally abandoned important commit
# View operation log
jj op log
# Undo the abandon operation
jj undo --op <operation-id>
Push rejected (branch diverged)
# Fetch latest changes
jj git fetch
# Rebase your changes on top
jj rebase -d <remote-bookmark>
# Push again
jj git push
💡 Real-World Scenarios
"I made changes to the wrong commit"
# You edited files but they should be in the parent commit
jj squash # Move all changes to parent
# Or move specific files only
jj squash src/config.ts # Move just this file to parent
"I want to combine these three commits"
# Combine commits A -> B -> C into A
jj squash -r B # B merges into A
jj squash -r C # C merges into A (now parent)
"I need to split this commit into two separate commits"
# Split by files/directories
jj split src/api/ src/models/ # API/models in first, rest in second
# Split by creating empty commit first
jj split -- # Empty first commit
jj squash src/api/ # Move API to first
# Rest stays in second commit
"I want my feature on the latest main"
jj git fetch # Get latest main
jj rebase -s my-feature -d main # Rebase feature onto main
# Or if you're on the feature:
jj rebase -d main # Rebase current commit to main
"I need to update my PR with review changes"
# Make changes to address review
vim src/file.ts
jj describe -m "Address review feedback"
# Or amend existing commit:
vim src/file.ts
jj squash # Add to previous commit
# Push updated branch
jj git push --bookmark feature-branch --force
"I committed debugging code by mistake"
# Remove file from current commit
jj restore src/debug.ts
rm src/debug.ts
# Remove file from previous commit
jj edit @- # Edit parent
jj restore src/debug.ts # Remove it
rm src/debug.ts
jj new @ # Go back to where you were
"I need to extract one feature into a separate PR"
# Current: main -> A -> B(feature) -> C(other) -> @
jj new A # Branch from A
jj bookmark create feature-branch
jj restore --from B . # Copy feature changes
jj describe -m "Feature X"
jj git push --bookmark feature-branch
"I accidentally deleted important commits"
jj op log # Find the operation
jj undo --op <operation-id> # Restore everything
# Or just:
jj undo # Undo last operation
"I want to try something without affecting my current work"
# Save current work
jj new # New commit (current work saved in @-)
jj describe -m "WIP: experiment"
# Try things...
# Discard experiment:
jj abandon @ # Remove experiment commit
jj new @- # Go back to before experiment
# Keep experiment:
jj describe -m "Successful experiment"
"Merge conflict while rebasing"
# Conflict appears during rebase
jj status # See conflicted files
# Option 1: Fix manually
vim src/conflicted.ts # Edit and resolve
jj status # Verify resolved
# Option 2: Take one side
jj restore --from main src/conflicted.ts # Take theirs
# or
jj restore --from @- src/conflicted.ts # Keep yours
# Continue working (no special command needed)
⚙️ Configuration
# Set your identity
jj config set --user user.name "Your Name"
jj config set --user user.email "you@example.com"
# Set editor
jj config set --user ui.editor "code --wait"
📚 Quick Command Reference
| Command | Description |
|---|---|
jj new |
Create new change |
jj new <commit> |
Create change from specific commit |
jj describe |
Edit description |
jj diff |
Show changes |
jj log |
View history |
jj split |
Split change interactively |
jj squash |
Move changes to parent |
jj squash <file> |
Move specific files to parent |
jj squash -r <commit> |
Squash specific commit |
jj rebase -d <dest> |
Rebase current to destination |
jj rebase -s <src> -d <dest> |
Rebase source + descendants |
jj restore --from <commit> <file> |
Copy file from commit |
jj undo |
Undo last operation |
jj edit <commit> |
Edit a past commit |
jj abandon <commit> |
Remove commit from history |
jj bookmark |
Manage bookmarks |
jj git fetch/push |
Sync with Git |
jj status |
Show status (including conflicts) |
Special Symbols
| Symbol | Meaning |
|---|---|
@ |
Current working copy commit |
@- |
Parent of working copy |
@-- |
Grandparent of working copy |
<commit>~ |
Parent of specified commit |
<commit>~2 |
Grandparent of specified commit |
Learn more: jj help or jj help <command>