| name | jujutsu |
| description | Guide to Jujutsu (jj) version control system. Use when working with commits, branches, pull requests, PRs, version control, rebasing, pushing, or when the user mentions jj, git, or version control operations. |
Jujutsu (jj) Version Control Guide
Jujutsu is a modern, Git-compatible version control system. This project uses jj colocated with git.
Key Differences from Git
| Concept | Git | Jujutsu |
|---|
| Staging area | Explicit git add | None - working copy IS a commit |
| Branches | Named refs | Bookmarks (auto-follow rewrites) |
| Stash | Separate stash stack | Not needed - just use commits |
| Amend | git commit --amend | Just edit files, or use jj squash |
| Identity | Commit ID only | Change ID (stable) + Commit ID |
Essential Commands
| Task | Command |
|---|
| Status | jj status or jj st |
| Diff | jj diff |
| Log | jj log |
| Commit & continue | jj commit -m "message" |
| Update message | jj describe -m "message" |
| New empty commit | jj new |
| Squash into parent | jj squash |
| Undo last operation | jj undo |
| Fetch from remote | jj git fetch |
| Push to remote | jj git push |
| Create & push bookmark | jj git push --named name=@ |
| Push existing bookmark | jj git push --bookmark name |
Working Copy Model
The working copy (@) is always a commit. File changes are automatically tracked - no staging required.
parent commit
↓
@ (working copy) ← your edits go here automatically
Quick Git-to-Jujutsu Translation
| Git | Jujutsu |
|---|
git status | jj st |
git diff | jj diff |
git log | jj log |
git add . && git commit -m "msg" | jj commit -m "msg" |
git push | jj git push |
git pull | jj git fetch then jj rebase -d main@origin |
git checkout -b branch | jj new main then jj bookmark set branch |
git branch | jj bookmark list |
git stash | jj new (just start new commit) |
git blame | jj file annotate |
Jujutsu Workflows
Common development workflows and patterns for jj.
Understanding the Working Copy Model
In jj, there's no staging area. The working copy IS a commit that updates automatically:
main
│
├── feature-commit (parent)
│ │
│ └── @ (working copy - your current edits)
When you edit files, they're immediately part of the working copy commit. This is fundamentally different from git.
Basic Development Workflow
Starting New Work
jj git fetch
jj rebase -d main@origin
jj new main -m "Add new feature"
jj describe -m "Implement feature X"
jj new
The Staging Commit Pattern (Recommended)
Work with two commits: a "staging" commit for completed work and the working copy for in-progress changes.
jj new main -m "Feature: user authentication"
jj new
jj squash
jj git push --bookmark feature-branch
Quick Single Commit
For simple changes that don't need the staging pattern:
jj new main
jj commit -m "Fix typo in README"
Modifying History
Editing a Previous Commit
jj log
jj edit <change-id>
jj new <where-you-were>
All descendant commits are automatically rebased.
Squashing Commits Together
jj squash
jj squash -r <change-id>
jj squash --into <change-id>
jj squash -i
Splitting a Commit
jj split
Reordering Commits
jj rebase -d <new-parent>
jj rebase -r <change-id> -d <new-parent>
jj rebase -s <change-id> -d <new-parent>
Working with Bookmarks (Branches)
Creating a Feature Branch
jj new main
jj bookmark set feature/my-feature
jj commit -m "Part 1"
jj commit -m "Part 2"
jj bookmark move feature/my-feature --to @-
Pushing to Remote
jj git push --named feature/my-feature=@
jj git push --bookmark feature/my-feature
jj git push
Creating Pull Requests
Full PR Workflow (Recommended)
jj log
jj git push --named feature/my-feature=@
gh pr create --title "My feature" --body "Description" --base main --head feature/my-feature
Alternative: Create Bookmark First
jj bookmark create -r @ feature/my-feature
jj bookmark track feature/my-feature@origin
jj git push --bookmark feature/my-feature
Key Points
- --named flag: Creates bookmark, tracks it, and pushes in one step (recommended)
- Multiple commits: The bookmark points to the tip; all ancestor commits up to main are included
- Subsequent pushes: Just use
jj git push --bookmark name
Updating a PR
jj edit <change-id>
jj git push --bookmark feature/my-feature
Updating from Main
jj git fetch
jj rebase -d main@origin
Handling Conflicts
Conflicts in jj are first-class citizens - they're stored in commits and don't block operations.
When Conflicts Occur
jj status
jj resolve
jj resolve src/main.rs
jj resolve --tool=:ours
jj resolve --tool=:theirs
Viewing Conflict Markers
Conflict markers in files look like:
<<<<<<< Conflict 1 of 1
%%%%%%% Changes from base to side #1
-old line
+modified in first branch
+++++++ Contents of side #2
modified in second branch
>>>>>>>
Undo and Recovery
Undoing Operations
jj undo
jj op log
jj op restore <operation-id>
Recovering Abandoned Commits
jj op log
jj op restore <operation-id>
Stacked Changes / Multiple PRs
Creating a Stack
jj new main -m "Refactor: extract helper function"
jj bookmark set refactor-helper
jj new
jj describe -m "Feature: use helper for new feature"
jj bookmark set feature-new
jj new
jj describe -m "Docs: document new feature"
jj bookmark set docs-feature
Pushing Stacked PRs
jj git push --bookmark refactor-helper --allow-new
jj git push --bookmark feature-new --allow-new
jj git push --bookmark docs-feature --allow-new
Updating a Stack After Review
jj edit <refactor-helper-change-id>
jj git push --bookmark refactor-helper
jj git push --bookmark feature-new
jj git push --bookmark docs-feature
Colocated Repository Tips
When using jj colocated with git (.jj and .git both exist):
git status
git log
jj commit
jj git import
jj git export
Common Patterns Summary
| Goal | Commands |
|---|
| Start feature | jj new main -m "Feature" |
| Save progress | jj commit -m "WIP" or jj squash |
| Fix old commit | jj edit <id>, fix, jj new |
| Update from main | jj git fetch && jj rebase -d main@origin |
| Push feature | jj bookmark set X && jj git push --bookmark X |
| Undo mistake | jj undo |
Additional References