jujutsu
A Git-compatible version control system with a cleaner mental model, first-class conflict handling, and a powerful history rewriting workflow.
jujutsu (invoked as jj) is a version control system that is fully compatible
with Git repositories but replaces Git's user-facing model with a cleaner, more
consistent one. It is designed around the idea that history rewriting should be
easy and safe — not something you fear.
Unlike Git, there is no index (staging area), no detached HEAD confusion, and merge conflicts are stored as a first-class part of the repository state rather than something that blocks you from doing anything else.
Features
- No index — every change to your working directory is automatically tracked
as part of the current "change". No more
git add, no more accidentally leaving things unstaged. - First-class conflicts — merge conflicts are stored in the repository and can be rebased, shelved, and passed around like any other commit. You are never blocked from committing or rebasing just because a conflict exists.
- Powerful history rewriting —
jj rebase,jj squash,jj split, andjj abandonmake rearranging commits trivial and safe - Change IDs — every commit has a stable "change ID" that survives rebases and amendments, so you always know which logical change you're talking about even as its commit hash changes
- Anonymous branches — you don't have to name a branch to work on something.
Named branches (
bookmarksin jj) are optional labels, not required navigation tools - Git backend — uses a real Git repository under the hood, so you can interoperate with any Git hosting (GitHub, GitLab, etc.) without friction
- Revsets — a powerful expression language for selecting sets of commits, used consistently across all commands
Installation
cargo install jj-cli
Or via your package manager:
# macOS
brew install jj
# Arch Linux
pacman -S jujutsu
# Debian 13+ / Ubuntu
apt install jj
# Fedora
dnf install jujutsu
# Nix
nix-env -iA nixpkgs.jujutsu
Official .deb and .rpm packages are also available on the
releases page.
Getting Started
You can initialize a new repo or co-locate jj inside an existing Git repo:
# Start a new jj-native repo
jj git init myproject
# Use jj inside an existing Git repo (non-destructive)
cd existing-git-repo
jj git init --colocateUsage
# See the current status (replaces git status)
jj status
# See the commit log (beautiful graph by default)
jj log
# Describe the current change (set its commit message)
jj describe -m "feat: add login page"
# Create a new empty change on top of the current one
jj new
# Squash the current change into its parent
jj squash
# Split the current change interactively into two
jj split
# Rebase the current change onto a different parent
jj rebase -d main
# Push to a Git remote
jj git push
# Fetch from a Git remote
jj git fetchThe Working Copy is Always a Commit
In Git, uncommitted changes exist in a liminal space (the working tree and
index) that is separate from commits. In jj, your working directory changes
are always part of a real commit — the "working-copy commit" — which is
automatically amended as you edit files. This means:
- You can never lose work by checking out something else
jj logalways shows your in-progress work- Operations like
jj rebasework equally well on in-progress changes
Revsets
jj has a powerful expression language for selecting commits:
# Show commits reachable from main but not yet on origin
jj log -r 'main..@'
# Show all commits that touch a specific file
jj log -r 'file("src/main.rs")'
# Show commits with a specific description pattern
jj log -r 'description(glob:"feat:*")'Tips
- Run
jj logoften — the output is compact, colorful, and always includes your in-progress work - Use
jj undoto undo any operation — jj keeps a full operation log, so nothing is ever truly lost - The
jj tuicommand (via thejj-tuiplugin) provides an interactive interface similar togitui