A Short Guide to Jujutsu (Jj) for Git Users
Jujutsu (jj) is a modern, Git-compatible version control system from Google designed to be simple and powerful. It aims to make complex workflows effortless, especially cleaning up commit history. If you’ve ever felt that Git was hard or required too much discipline, jj might be for you.
The jj Philosophy
- Effortless History: With
jj, you don’t need perfect commits from the start. Work freely, and clean up your history later with powerful commands likejj split,jj squash, andjj edit. - No Staging Area: The working copy is always “live.” There is no need for
git addorgit stash. To work on something new, you just check out a different revision. - Free Checkpointing:
jjrecords every operation. You can undo almost anything with the magicjj undocommand.
1. Installation and Configuration
First, install jj on your system.
- Linux/macOS (Homebrew):
1brew install jj - Windows (Scoop/Winget):
1 2 3scoop install jj # OR winget install --id Jujutsu.Jujutsu - NixOS:
1 2 3environment.systemPackages = with pkgs; [ jujutsu ]; # OR nix profile install nixpkgs#jujutsu - For other methods, see the official installation guide.
After installing, configure your user details. This is a one-time setup.
|
|
2. Creating Repositories
- Clone a Git repo: This is the most common way to start. It creates a
jjrepo that is linked to the Git remote.1jj git clone https://github.com/user/repo.git - Use
jjin an existing Git repo:1 2cd my-existing-git-repo jj git init --colocate - Create a new repo:
1jj git init my-new-repo
3. Git User’s Cheat Sheet: Common Operations
If you’re used to Git, here’s how to translate common commands to the jj way of thinking.
| Git Command | Jujutsu (jj) Equivalent | Notes |
|---|---|---|
git status |
jj st |
Shows modified files in the working copy. |
git log |
jj log |
Shows the revision history. Much more powerful! |
git add . |
(Not needed) | Changes are automatically part of the working-copy revision. |
git commit -m "..." |
jj describe -m "..." |
Describes the current revision. No staging needed. |
git checkout -b my-feature |
jj new -m "my-feature" |
Creates a new revision for you to work on. |
git checkout main |
jj new main |
Creates a new empty change on top of main to start new work. |
git commit --amend |
jj describe -m "new msg" or just edit files |
Amend the message with describe, or edit files and they are auto-amended. |
git rebase -i HEAD~3 |
jj log, then jj edit, jj squash, jj split, jj rebase |
jj provides dedicated, safer commands for each interactive rebase action. |
git stash |
(Not needed) | Just create a new revision with jj new. Your old work is safe. |
git pull --rebase |
jj git fetch then jj rebase -d main@origin |
Explicitly fetch and then rebase your stack of changes. |
git push origin my-feature |
jj git push --bookmark my-feature |
Pushes the revision pointed to by the bookmark. |
4. The Core Workflow: Your Working Copy is a Commit
The biggest mental shift from Git is that your working copy is always part of a commit. In jj, there is a special “working-copy commit,” often shown with a ~ in the log. It has no commit message and is where your uncommitted changes live.
When you run jj new, you create a new, empty working-copy commit. When you run jj describe, you give the current working-copy commit a message and “close” it, and jj creates a new, empty working-copy commit for you to continue working.
- Start a new change: Use
jj newto create a new, empty revision to work on. Your changes will be automatically saved to it.1 2jj new # Now, edit your files - Describe your change: When you’re ready, describe the revision. You can do this at any time.
1jj describe -m "My new feature" - Check your status: Get in the habit of running these commands all the time to see what’s happening.
1 2 3jj st # See modified files jj log # See the history of revisions jj diff # See code changes in the current revision - Push a change: after cleanup, move your bookmark and push
1 2jj bookmark move master --to=@ jj git push --bookmark master
5. Understanding Revision IDs vs. Commit IDs
This is a crucial concept to grasp. Run jj log to see them.
- Revision ID (e.g.,
wuloypwt): A local, permanent ID for a set of changes. It never changes, even if you edit the commit. It’s how you refer to revisions injjcommands. - Commit ID (e.g.,
182d3ce4): This is the Git SHA. It represents the entire state of the worktree. It will change every time you edit the revision (e.g., by rebasing or amending the message). This is what you see in GitHub.
6. Navigating and Editing History (The Magic)
jj makes history manipulation safe and easy. There is no need for git stash.
- Edit a past revision: Just use
jj editwith the revision ID. Your working copy will update to that revision, and you can start making changes.Use1jj edit <revision-id> # e.g., jj edit wuloypwt@for the current revision,@-for its parent, and@+for a child. - Fixing Mistakes:
- The magic undo button: This reverts the last operation (a commit, a rebase, etc.). You can even run it multiple times.
1jj undo - The operation log: To go further back in time, view the log of all operations and restore to a previous state.
1 2jj op log jj op restore <operation-id>
- The magic undo button: This reverts the last operation (a commit, a rebase, etc.). You can even run it multiple times.
- Cleaning up commits:
- Split a commit: If a commit does too much, split it into smaller ones.
1jj split - Combine commits: To merge a revision into its parent, use
jj squash.1jj squash
- Split a commit: If a commit does too much, split it into smaller ones.
7. A Concrete Example: From Messy to Clean
Let’s see how this works in practice.
-
Start a new feature on top of
main:1 2jj new main -m "feat: Add user login button" # You add the HTML for the button. -
Add the styling, but as a new change:
1 2jj new -m "style: Style the login button" # You add the CSS.Your log now looks like this (simplified):
1 2 3 4 5o style: Style the login button | o feat: Add user login button | o (main branch) -
Realize you forgot something in the first commit. No problem! Just edit it directly.
1 2 3jj edit <revision-id-of-login-button> # Add the missing 'aria-label' attribute to the HTML. # The change is automatically saved to that revision. -
Decide to combine the two commits. A button and its style belong together.
1 2 3 4jj new . # Go back to the top commit jj squash # The "style" commit is now merged into the "feat" commit. # jj will ask you to write a new, combined commit message.Your log is now clean:
1 2 3o feat: Add styled user login button | o (main branch) -
Now you are ready to push.
1 2jj bookmark create login-feature -r @ jj git push --bookmark login-feature
8. Branching and Remotes
In jj, branches are just pointers (called “bookmarks”) to revisions. They don’t automatically move forward when you create a new commit. You must explicitly create, move, and push them.
- Start a new line of work:
1 2# Creates a new, empty revision on top of main jj new main - Do your work: Make some edits, then describe the commit.
1 2 3jj describe -m "A new feature" # You might continue with more commits jj new -m "Another commit for the feature" - Create a bookmark (branch): Before you push, create a bookmark to give your line of work a name. The
-r @flag means “give the name to the current revision.”1jj bookmark create my-feature -r @ - List bookmarks: To see all bookmarks (local and remote), use
jj bookmark list.1jj bookmark list - Pull latest changes: First, fetch from the remote. To make
jjautomatically update your localmainbookmark whenorigin/mainchanges, you can “track” the remote branch. Then, rebase your stack of changes onto the main branch.1 2 3jj git fetch jj bookmark track main@origin jj rebase -d origin/main - Push your branch: The command for pushing a bookmark is
jj git push --bookmark <name>.1 2 3 4 5# Push a specific bookmark. This is the most common push command. jj git push --bookmark my-feature # Alternatively, push a specific change, which will create a branch for it jj git push --change . - Checking out a colleague’s branch: To review or work on someone else’s changes, first fetch the latest from the remote. Then, find the change you want and create a new working copy at that revision.
1 2 3 4 5 6jj git fetch # Find the revision from the remote branch in the log jj log -r "all()" # Check it out. This creates a new working-copy commit at that revision. jj new <revision-id-of-their-change>
9. Merging and Tidying Up (Without a PR)
When you own the repository or have direct push access to the main branch (main or master), you can merge your changes and clean up your feature branch directly from the command line, skipping the GitHub pull request process entirely.
- Update your local
mainbranch: Make sure you have the latest changes from the remote.1 2 3jj git fetch jj new main jj git pull - Rebase your feature branch: Go to your feature branch and rebase it on top of the latest
main.1 2jj new my-feature jj rebase -d main - Merge into
main: Move to themainbranch and fast-forward it to the tip of your feature branch.1 2jj new main jj rebase -s my-feature # Fast-forwards main to my-feature - Push
mainto the remote:This command will push the updated1jj git pushmainbranch. - Delete your feature branch: Once merged, you no longer need the bookmark or the remote branch.
1 2 3 4 5# Delete the local bookmark jj bookmark delete my-feature # Delete the remote branch jj git push --delete my-feature
10. Important Considerations & Downsides
- Force-Pushing is Normal:
jjworks by rewriting history, so it force-pushes changes. This can make reviewing a pull request commit-by-commit on GitHub difficult, as old commits will change. - State is Local: The undo history and the status of revisions are stored locally on your machine. If you clone your repo on a second computer, you won’t have the undo history from the first. A revision created on machine A will be “immutable” (you can’t use
jj split, etc.) on machine B. Push your branches before switching computers to avoid losing work. - Using Git in Parallel: It’s safe to run read-only
gitcommands (likegit status). For any action that modifies history (commit,rebase,push), use thejjcommand to avoid confusion.
Also check out “The jj VCS Workshop” by Jimmy Koppel and of course the jj-vcs site.