Jujutsu (jj): Next-Generation Version Control
Jujutsu (jj) is a modern version control system that aims to provide a better user experience than Git while maintaining compatibility with Git repositories.
What is Jujutsu?
Jujutsu is designed to address many of Git’s usability issues: - No staging area: Direct commit workflow - Automatic conflict resolution: Better merge handling - Immutable history: Operations create new commits instead of modifying existing ones - Powerful revsets: Advanced commit selection syntax - Git compatibility: Works with existing Git repositories
Key Concepts
Fundamental Differences from Git
- No Index/Staging Area: Changes are committed directly
- Working Copy Commits: Your working directory is always a commit
- Change IDs: Commits have stable identifiers that survive rebasing
- Automatic Rebasing: Operations automatically maintain clean history
Jujutsu vs Git Terminology
Git | Jujutsu | Description |
---|---|---|
HEAD | @ | Current working copy |
Branch | Branch | Named pointer to commit |
Commit | Change | A set of changes with stable ID |
Rebase | Rebase | Automatic history rewriting |
Merge | Merge | Combining multiple parents |
Installation
Method 1: Package Managers
macOS (Homebrew)
brew install jj
Linux (Cargo)
# Install Rust if not already installed
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Install jj
cargo install --git https://github.com/martinvonz/jj.git jj-cli
Ubuntu/Debian (from source)
# Install dependencies
sudo apt update
sudo apt install -y build-essential pkg-config libssl-dev
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Clone and build jj
git clone https://github.com/martinvonz/jj.git
cd jj
cargo build --release
sudo cp target/release/jj /usr/local/bin/
Method 2: Pre-built Binaries
# Download latest release
curl -L https://github.com/martinvonz/jj/releases/latest/download/jj-linux-x86_64.tar.gz | tar xz
sudo mv jj /usr/local/bin/
chmod +x /usr/local/bin/jj
Initial Setup and Configuration
First-Time Configuration
# Set up user information
jj config set --user user.name "Your Name"
jj config set --user user.email "your.email@example.com"
# Configure editor
jj config set --user ui.editor "code --wait" # VS Code
# or
jj config set --user ui.editor "vim" # Vim
# Configure diff tool
jj config set --user ui.diff.tool "code --wait --diff"
# or
jj config set --user ui.diff.tool "vimdiff"
# Configure merge tool
jj config set --user ui.merge-tool "code --wait"
Configuration File
# ~/.jjconfig.toml
[user]
name = "Your Name"
email = "your.email@example.com"
[ui]
editor = "code --wait"
diff-tool = "code --wait --diff"
merge-tool = "code --wait"
pager = "less -FRX"
[colors]
"commit_id prefix" = "blue"
"change_id prefix" = "magenta"
"branch" = "green"
"tag" = "yellow"
"working_copy" = "green bold"
[revsets]
log = "@ | ancestors(remote_branches()..@, 2) | trunk()"
[aliases]
l = ["log", "-r", "(@ | ancestors(remote_branches()..@, 2) | trunk())"]
ll = ["log"]
st = ["status"]
d = ["diff"]
s = ["show"]
Basic Operations
Creating and Initializing Repositories
Initialize New Repository
# Create new jj repository
mkdir my-project
cd my-project
jj init
# Initialize with Git backend (recommended)
jj init --git
Clone Existing Git Repository
# Clone from Git repository
jj git clone https://github.com/user/repo.git
cd repo
# Or clone and convert existing Git repo
git clone https://github.com/user/repo.git
cd repo
jj init --git-repo .
Working with Changes
Making Changes
# Check status
jj status
# View current changes
jj diff
# Create a new change (commit)
jj commit -m "Add new feature"
# Amend current change
echo "more content" >> file.txt
jj commit --amend -m "Add new feature with more content"
# Create new change and continue working
jj new
echo "another change" >> file2.txt
jj commit -m "Another feature"
Viewing History
# Show log (default view)
jj log
# Show detailed log
jj log --verbose
# Show specific range
jj log -r "main..@"
# Show graph
jj log --graph
# Custom log format
jj log --template 'commit_id.short() ++ " " ++ description.first_line() ++ "\n"'
Real-World Workflow Examples
Scenario 1: Feature Development
# Start new feature
cd my-project
jj new main -m "Start user authentication feature"
# Make changes
echo "class UserAuth:" > auth.py
echo " def login(self, username, password):" >> auth.py
echo " pass" >> auth.py
# Commit current state
jj commit -m "Add UserAuth class skeleton"
# Continue development
echo " def logout(self):" >> auth.py
echo " pass" >> auth.py
echo " def validate_user(self, username):" >> auth.py
echo " return True" >> auth.py
# Commit additional changes
jj commit -m "Add logout and validation methods"
# Add tests
echo "import unittest" > test_auth.py
echo "from auth import UserAuth" >> test_auth.py
echo "" >> test_auth.py
echo "class TestUserAuth(unittest.TestCase):" >> test_auth.py
echo " def test_login(self):" >> test_auth.py
echo " auth = UserAuth()" >> test_auth.py
echo " # Test implementation" >> test_auth.py
jj commit -m "Add unit tests for UserAuth"
# View the feature branch
jj log -r "main..@"
Scenario 2: Bug Fix with History Rewriting
# Discover bug in earlier commit
jj log --oneline
# * 3a2b1c9 Add unit tests for UserAuth
# * 2b1c9d8 Add logout and validation methods
# * 1c9d8e7 Add UserAuth class skeleton
# * 9d8e7f6 (main) Initial commit
# Fix bug in the validation method
jj edit 2b1c9d8 # Edit the commit with the bug
echo " def validate_user(self, username):" >> auth.py
echo " return username is not None and len(username) > 0" >> auth.py
# Commit the fix
jj commit --amend -m "Add logout and validation methods (fix validation logic)"
# Return to latest change
jj edit @
# View updated history (automatic rebasing)
jj log -r "main..@"
Scenario 3: Collaborative Development
# Fetch latest changes
jj git fetch
# Rebase current work on latest main
jj rebase -d main
# Push changes
jj git push --branch feature-auth
# Create pull request (using GitHub CLI)
gh pr create --title "Add user authentication" --body "Implements login, logout, and user validation"
# After review, squash commits before merge
jj squash -r "main..@" -m "Add complete user authentication system"
Advanced Features
Revsets (Revision Selection)
Jujutsu uses a powerful query language for selecting commits:
# Current working copy
jj log -r "@"
# All ancestors of current commit
jj log -r "ancestors(@)"
# All descendants of main
jj log -r "descendants(main)"
# Commits between main and current
jj log -r "main..@"
# All branches
jj log -r "branches()"
# Remote branches
jj log -r "remote_branches()"
# Commits by author
jj log -r 'author("john@example.com")'
# Commits with specific message
jj log -r 'description(regex:"fix|bug")'
# Complex queries
jj log -r "(main | @) & ancestors(remote_branches())"
Conflict Resolution
Jujutsu has superior conflict resolution compared to Git:
# Create conflicting changes
jj new main -m "Feature A"
echo "feature A" > feature.txt
jj commit
jj new main -m "Feature B"
echo "feature B" > feature.txt
jj commit
# Merge (creates conflict)
jj merge
# View conflict
jj status
jj diff
# Resolve conflict
echo "feature A and B combined" > feature.txt
jj commit -m "Resolve conflict between features"
# Alternative: Use merge tool
jj resolve --tool
Working with Multiple Changes
# Create multiple parallel changes
jj new main -m "Feature 1"
echo "feature 1" > f1.txt
jj commit
jj new main -m "Feature 2"
echo "feature 2" > f2.txt
jj commit
jj new main -m "Feature 3"
echo "feature 3" > f3.txt
jj commit
# View all changes
jj log --graph
# Combine specific changes
jj merge f1-commit-id f2-commit-id -m "Combine features 1 and 2"
# Reorder changes
jj rebase -d main -s f3-commit-id
Integration with Git Workflows
Git Interoperability
# Work with Git remotes
jj git remote add origin https://github.com/user/repo.git
jj git fetch
jj git push
# Import Git branches
jj git import
# Export to Git
jj git export
# Work with Git hooks
# Jj respects Git hooks in .git/hooks/
Converting Git Repository
# Convert existing Git repo
cd existing-git-repo
jj init --git-repo .
# Import all Git history
jj git import
# Continue working with jj
jj status
jj log
Hybrid Workflow (Git + Jujutsu)
# Use jj for local development
jj new main -m "Local feature development"
# ... make changes ...
jj commit -m "Implement feature"
# Export to Git for pushing
jj git export
git push origin feature-branch
# Or push directly with jj
jj git push --branch feature-branch
Team Collaboration Scenarios
Scenario 1: Code Review Workflow
# Developer A: Create feature
jj new main -m "Add payment processing"
# ... implement feature ...
jj commit -m "Implement payment gateway integration"
jj commit -m "Add payment validation"
jj commit -m "Add payment tests"
# Push for review
jj git push --branch payment-feature
# Developer B: Review and suggest changes
jj git fetch
jj new payment-feature -m "Address review comments"
# ... make changes ...
jj commit -m "Fix validation edge cases"
jj commit -m "Improve error handling"
# Squash before merge
jj squash -r "payment-feature..@" -m "Add payment processing with review fixes"
jj git push --branch payment-feature --force
Scenario 2: Hotfix Workflow
# Critical bug discovered in production
jj new production -m "Hotfix: Fix critical security vulnerability"
# Quick fix
sed -i 's/vulnerable_function/secure_function/g' security.py
jj commit -m "Replace vulnerable function with secure implementation"
# Test the fix
python -m pytest tests/test_security.py
jj commit --amend -m "Fix critical security vulnerability (tested)"
# Deploy hotfix
jj git push --branch hotfix-security
# Merge to main after deployment
jj rebase -d main
jj git push --branch main
Scenario 3: Large Feature with Multiple Developers
# Lead developer: Create feature branch
jj new main -m "Start user management system"
jj commit -m "Add user model and basic structure"
jj git push --branch user-management
# Developer 1: Work on authentication
jj git fetch
jj new user-management -m "Implement user authentication"
# ... implement auth ...
jj commit -m "Add login/logout functionality"
jj git push --branch user-auth
# Developer 2: Work on user profiles
jj new user-management -m "Implement user profiles"
# ... implement profiles ...
jj commit -m "Add user profile management"
jj git push --branch user-profiles
# Lead developer: Integrate features
jj git fetch
jj merge user-auth user-profiles -m "Integrate authentication and profiles"
# Resolve any conflicts
jj status
# ... resolve conflicts if any ...
jj commit -m "Resolve integration conflicts"
# Final integration
jj git push --branch user-management
Advanced Configuration and Customization
Custom Templates
# ~/.jjconfig.toml
[templates]
log_oneline = '''
change_id.short() ++ " " ++
if(description, description.first_line(), "(no description)") ++
if(branches, " (" ++ branches.join(", ") ++ ")")
'''
log_detailed = '''
"Commit: " ++ commit_id.hex() ++ "\n" ++
"Change: " ++ change_id.hex() ++ "\n" ++
"Author: " ++ author.name() ++ " <" ++ author.email() ++ ">\n" ++
"Date: " ++ author.timestamp() ++ "\n" ++
if(branches, "Branches: " ++ branches.join(", ") ++ "\n") ++
"\n" ++ indent(" ", description) ++ "\n"
'''
Custom Commands (Aliases)
# ~/.jjconfig.toml
[aliases]
# Short status
s = ["status"]
# Detailed log with graph
lg = ["log", "--graph", "--template", "log_detailed"]
# Show changes in current branch
current = ["log", "-r", "main..@"]
# Quick commit with message
qc = ["commit", "-m"]
# Amend last commit
amend = ["commit", "--amend"]
# Create new change and commit
nc = ["new", "-m"]
# Show diff for specific change
show = ["diff", "-r"]
# Rebase current branch on main
sync = ["rebase", "-d", "main"]
Integration with Development Tools
VS Code Integration
// .vscode/settings.json
{
"jujutsu.enable": true,
"jujutsu.path": "/usr/local/bin/jj",
"git.enabled": false,
"scm.defaultViewMode": "tree"
}
Shell Integration
# ~/.bashrc or ~/.zshrc
# Jujutsu prompt integration
function jj_prompt() {
if jj root >/dev/null 2>&1; then
local branch=$(jj log -r @ --no-graph --template 'branches.join(" ")')
local change_id=$(jj log -r @ --no-graph --template 'change_id.short()')
if [ -n "$branch" ]; then
echo " (jj:$branch)"
else
echo " (jj:$change_id)"
fi
fi
}
# Add to PS1
PS1='${PS1}$(jj_prompt)'
# Useful aliases
alias jst='jj status'
alias jl='jj log'
alias jd='jj diff'
alias jc='jj commit'
alias jn='jj new'
Performance and Optimization
Repository Maintenance
# Check repository health
jj debug check
# Optimize repository
jj debug reindex
# Clean up unreferenced changes
jj debug gc
# Repository statistics
jj debug stats
Large Repository Handling
# Configure for large repositories
jj config set --repo core.preload-index false
jj config set --repo core.fsmonitor true
# Shallow clone for large repositories
jj git clone --depth 1 https://github.com/large/repo.git
# Sparse checkout
jj sparse set path/to/needed/files
Migration Strategies
Gradual Migration from Git
# Phase 1: Parallel usage
# Keep using Git for team collaboration
# Use jj for local development
# Phase 2: Team adoption
# Train team on jj basics
# Use jj for feature branches
# Continue using Git for main branch
# Phase 3: Full migration
# Move all development to jj
# Use jj git commands for remote operations
# Eventually move to native jj hosting (when available)
Migration Script
#!/bin/bash
# migrate-to-jj.sh
REPO_URL=$1
REPO_NAME=$(basename "$REPO_URL" .git)
if [ -z "$REPO_URL" ]; then
echo "Usage: $0 <git-repo-url>"
exit 1
fi
echo "Migrating $REPO_URL to Jujutsu..."
# Clone with Git
git clone "$REPO_URL" "${REPO_NAME}-git"
cd "${REPO_NAME}-git"
# Initialize jj in the same directory
jj init --git-repo .
# Import all Git history
jj git import
# Set up remote
jj git remote add origin "$REPO_URL"
# Create jj-specific configuration
cat > .jjconfig.toml <<EOF
[ui]
default-command = "log"
[revsets]
log = "@ | ancestors(remote_branches()..@, 10) | heads(remote_branches())"
[aliases]
sync = ["git", "fetch", "--all-remotes"]
push = ["git", "push"]
EOF
echo "Migration completed!"
echo "Repository is now ready for jj usage"
echo "Run 'jj status' to get started"
Troubleshooting Common Issues
Issue 1: Conflict Resolution
# When conflicts occur
jj status # Shows conflicted files
# View conflict markers
jj diff
# Resolve manually or with tool
jj resolve --tool
# Or edit files manually and commit
vim conflicted_file.txt
jj commit -m "Resolve conflict"
Issue 2: Lost Changes
# View all changes (including abandoned)
jj log --revisions 'all()'
# Restore abandoned change
jj new <abandoned-change-id>
# Or use operation log
jj op log
jj op restore <operation-id>
Issue 3: Git Synchronization Issues
# Force import from Git
jj git import --force
# Reset to match Git state
jj git export
git reset --hard origin/main
jj git import
# Check Git/jj consistency
jj debug check-git
This comprehensive guide covers Jujutsu from basic concepts to advanced usage, providing practical examples for teams transitioning from Git to this modern version control system.