Jujutsu VCS - Getting Started Guide
Jujutsu (jj) is a new version control system that's been picking up a fair amount of interest. While Git has dominated for nearly two decades, Jujutsu promises a simpler, more intuitive approach to version control.
Table of Contents
Do we really need a new VCS?
I'll be honest: I was skeptical. A VCS claiming to be "so much easier and more powerful than Git"? Git works fine for most daily workflows, I don't find the command-line all that complex, and the advanced scenarios tend to be rare.
Funny thing, years ago I was equally skeptical of a new VCS when I wrote the post "Don't be a git, just use subversion". In it I argued that Git's complex branching and non-linear nature wasn't worth it for most developers. Here's the Hacker News discussion calling me an idiot.
I'm not a complete luddite. I jumped from CVS to Subversion immediately when it launched.
So, learning from my previous bad take and seeing Jujutsu gaining some traction, I decided to give it a fair shot.
The verdict: I'm a convert. It took me a few weeks to grok it, but now it is all I use.
Getting Started
Most Jujutsu tutorials dive straight into advanced features, but let's start with the essentials. Here's how to try it alongside your existing Git projects without disruption.
Installation and Setup
Install Jujutsu:
brew install jj # macOS
# or your package manager of choice
Initialize in an existing Git repo:
jj git init --colocate
This creates a Jujutsu repository that coexists with your Git repo. You can switch between both tools seamlessly.
Note: The examples use inline messages using -m "Comment" just like in git, if you omit it will open your EDITOR and enable full comments. Strive to write better commit messages than these examples.
Automatic Staging
In Git, you make changes, add them to a staging area, and then commit; in Jujutsu you create a working space first, then any changes made are automatically included until you make a new working space.
My Daily Workflow
Here's the pattern I've settled into:
1. Create a new working space
jj new
2. Describe what you're working on
jj desc -m "Add user authentication feature"
Note: This can be modified anytime, I like to describe early to focus the change
3. Make your changes
- Files are automatically staged as you modify them
- Everything respects your
.gitignorefile - If something unwanted gets added:
jj file untrack [FILE]
4. Start the next change
jj new # Previous working space becomes a commit
That's it. Your previous working space automatically becomes a commit in both Jujutsu and Git.
Real Example: Adding This Article
Let me show you exactly how I used Jujutsu to add this article to my blog:
Step 1: Create empty working space
jj new
Aside - Explaining the status message
The jj status output looks a bit gnarly and has more than a typical git status or log. This shows two lines, the working copy and the parent. Explaining the working copy line:
@symbol represents "here" - your current position in the commit history. This can be used as a reference, the@-means the item before@ymzurouris the change ID, a unique identifier Jujutsu assigns to track this set of changes. This will not change regardless of your edits to that change set.- The
7a959934is the git commit hash of this change in the repo, this will change as you make changes in the changeset. (empty)- No files have been modified in this working copy.(no description set)- No commit message has been written yet.
Step 2: Create article which gets automatically staged:
Step 3: Add description
jj desc -m "Add jujutsu article"
Step 4: Create next working space
jj new
The previous working space is now a proper Git commit (verified with git log):
Note: Pay attention to the Jujutsu change ID which stays the same throughout all the changes above, and the git commit hash which changes with each change until the next working space is created.
After two more changes (adding images and updating the home page), here's what jj log shows:
The real power here is that you can go back and edit any previous commit with jj edit [changeID] or reorder them with jj rebase. But let's keep things simple for now.
Jujutsu supports creating a new space and describing in one step using jj new -m "Description". You can use this as a shortcut instead of two commands: jj new and then jj desc -m "".
Working with Bookmarks (Git Branches)
Jujutsu calls Git branches "bookmarks" – they work similarly, but with some key differences that make the distinction worthwhile. I'll call it out below.
In the log above, you'll see:
trunk: Your local branchtrunk@origin: The remote branch
I started with a local commit that hadn't been pushed yet, which is why they're different.
Creating a Feature Branch
To move these commits to a feature branch, create a bookmark pointing to the specific change:
jj bookmark set -r tylvvqpn add/jujutsu
Note: I could use jj bookmark set -r t add/jujutsu with just the t to refer to the change id, since that is the shortest unique string to refer. This is why the color syntax highlights the first letter or tow.
Pushing and Creating Pull Requests
jj git push -b add/jujutsu --allow-new
This pushes your bookmark to GitHub, and you'll get a link to create a PR. If you go to the repo in GitHub you will see the typical new branch PR creation prompt. You can create the PR.
Adding More Commits
To add more commits to your feature branch, use the same workflow:
jj new- Create new working space- Make changes
jj commit -m "more updates to article"- Commit the change
One of the key differences between jujutsu and git, you aren't "on a branch" with new changes continue on that branch like in git. For jj, the bookmark points at a change id and you need to repoint your bookmark to include the new change:
jj bookmark set -r @- add/jujutsu
The @- shortcut refers to the change before your current working revision (marked by @).
Now your local bookmark differs from the remote. Push up your change:
jj git push
When your PR is done and merged on GitHub, sync your local repo:
jj git fetch
Solo Development
If you're working alone without PRs, just use the trunk bookmark:
- Repoint trunk to your desired revision
- Push to GitHub
- Done!
This looks like:
jj bookmark set -r CHANGE-ID trunk
jj git push
If the change id is current change use -r @, see my aliases below for how I
simplify this flow.
Stash Equivalent
The stash equivalent using Jujutsu is easier than git because everything stays visible. With git stash it feels hidden away and I'll forget what I stashed and what happens when I restore.
Approach: Using jj you create a new changeset before your current working copy, make your change, and then move back to your current working copy. This looks like the following:
A simple example, I'm editing this article and want to update the footer on my site.
Current state, I created a change with description: "Update jujutsu: add stash"
To create a new changset based off the parent (not my current state) use: jj new @-.
However, this will create a fork, since both my current change (updating the article), and my new change (updating the footer) will have the same parent. This is fine, but will require resolving later.
So to avoid that extra step later, I create the fix changeset to be created after the parent using: jj new -A @- -m "Update footer"
You can see the new change was added after the previous parent, and before current change. I can now make an update to the footer.
And when I'm done go back to editing the new article using: jj edit tltlyrpq
I could then mark that change as trunk and push up to deploy.
jj bookmark set -r yvwynsxy trunk
jj git push
Split and Squash
Split and Squash are two commands in Jujutsu to modify changes. Split will break a change into multiple changes, and squash does the opposite takes multiple changes and squashes into one.
Split
Here's an example on how to use jj split to break a change into multiple changes.
If you don't specify a change, jj split will start an interactive TUI. In that interface, the files marked will become a new change, those unmarked will stay in the current.
In a similar scenario to the stash above, let's say I made a change to my base template while I was in the middle of editing this article. I forgot to create a new change for it first. This jj status shows both files have changes:
So if I run jj split it will launch the TUI, I will select the file I want to split first (the change to the base template) by tapping space bar, and then press C to confirm.
After the split, you can see this jj log shows there are now two different changes:
Squash
Use jj squash to take multiple changes and merge them. By default, jj squash will take the current commit @ and squash into its parent.
Here's an example with two commits, one name father, one named son.
Running: jj squash -m "I am father and son will squash the son into the father. If you don't specify the message, it will open your EDITOR to add.
So now after, jj status shows just the single commit:
Useful Tips and Configuration
Quick Commit Shortcut
Instead of jj desc followed by jj new, use:
jj commit -m "Your description"
Increase File Size Limit
Jujutsu's default 1MB file limit is restrictive. Update ~/.config/jj/config.toml:
[snapshot]
max-new-file-size = "10MiB"
Essential Recovery Commands
jj undo- Undo the last operationjj abandon [changeID]- Remove a changejj log- See your change historyjj status- Current working space status
Aliases
Create aliases in the config file ~/.config/jj/config.toml
Here are my aliases. They are basically just shortcuts, since all fetches, inits, and pushes are git fetches, git inits, and git pushes, just remove the git part. Likewise for track, all tracks are bookmark tracks.
[aliases]
fetch = ["git", "fetch"]
init = ["git", "init", "--colocate"]
mark = ["bookmark", "set", "-r", "@"]
push = ["git", "push", "--allow-new"]
track = ["bookmark", "track"]
With the aliases in place my workflow to make a change on trunk is:
jj new # create new space if it doesn't exist
# ... make changes ...
jj desc -m "Describe change"
jj mark trunk # mark current rev as trunk
jj push # push up change
The push will update the remote and create the local as immutable and create a new space ready to go.
Working with GitHub
Jujutsu works alongside git and GitHub, which makes it low risk to try out, you can use it yourself without having to require your team, coworkers, or anyone else to also use it. It integrates seamlessly so they won't even know.
Creating a Pull Request on GitHub
# clone the repo from GitHub
git clone git@github.com:mkaz/jotit
# track the trunk from origin
jj bookmark track trunk@origin
# ... make your changes ...
# describe your commit
jj desc -m "I did some things"
# create a local bookmark (branch)
jj bookmark set -r @ fix/things
# push branch up to GitHub
jj git push --allow-new
The push command will show the link to create a Pull Request, or you can go to the repo in your browser and GitHub will show the new branch with the option to create a pull request.
Updating Pull Request on GitHub
To update your PR on GitHub using Jujutsu is quite similar to updating trunk and pushing up. Make the change, mark the change id as the new bookmark, and push to GitHub.
# ... make changes ...
# describe your commit
jj desc -m "I made more changes"
# update bookmark to this change
jj bookmark set -r @ fix/things
# push updated branch up
jj git push
Pull in Change from GitHub
Let's suppose someone else made a change to your branch on GitHub and you want to pull in their change.
# pull in changes from GitHub
jj git fetch
# move your current state to be based off updated fix/things
jj rebase -d fix/things
Bring Local Repo Current with GitHub
After your PR, or others have been merged, here's how to bring your local repo current with GitHub.
# pull in changes from GitHub
jj git fetch
# move your current workspace to be based off trunk
jj rebase -d trunk
Summary
Here's a few areas what I like About Jujutsu, since first writing this article to try it out, I've since switched and use it 100% of the time. I'm a convert.
Less Mental Overhead
No more git add, git mv, or git rm commands. File operations just work without the extra staging. So much easier, but requires to pay attention to what is in your directory. Make sure you use .gitignore to avoid adding unwanted files.
Lightweight Commit Creation
Starting a new commit with jj new feels effortless. Since descriptions are optional and editable, there's no pressure to get everything perfect upfront.
Flexible Changes
Jujutsu's super powers. I can:
- Edit previous commits with
jj edit [changeID] - Split mixed changes with
jj split - Squash related changes together
Real example: While writing this article, I made unrelated site updates. When I forgot to switch contexts, I used jj split to separate the article changes into their own commit, then squashed them with my previous article work.
Learning Strategy: Start Simple
Here's how I recommend you get started learning Jujutsu. Start small with either a side project or even create a sandbox repo just to play in. No risk.
- Start with the core workflow shown above
- Gradually add complexity as you encounter new needs
- Keep lifelines handy:
jj undoandjj abandonfor when things go wrong
The key is experimentation. It requires playing with and using to appreciate.
Note: I recommend disabling any git features in your IDE, since git is in the background for storage, it is possible git extensions can get confused or change things in unexpected ways.
Resources
Tutorials and Documentation
- Steve's Jujutsu Tutorial - Excellent beginner-friendly walkthrough
- Official Jujutsu Documentation - Comprehensive reference
- Git Command Table - Side-by-side comparison of Git vs Jujutsu commands
Getting Help
jj help- Built-in command referencejj help [command]- Detailed help for specific commands- Jujutsu Discussions - Community support