jujutsu

A Git-compatible version control system with a cleaner mental model, first-class conflict handling, and a powerful history rewriting workflow.

Screenshot of jujutsu

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 rewritingjj rebase, jj squash, jj split, and jj abandon make 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 (bookmarks in 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 --colocate

Usage

# 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 fetch

The 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 log always shows your in-progress work
  • Operations like jj rebase work 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 log often — the output is compact, colorful, and always includes your in-progress work
  • Use jj undo to undo any operation — jj keeps a full operation log, so nothing is ever truly lost
  • The jj tui command (via the jj-tui plugin) provides an interactive interface similar to gitui