My most frequently used Jujutsu VCS commands
A cheat-sheet of jj commands you're most likely to need day-to-day using jj as a git replacement.
by Danver Braganza on 2025-06-21
A bit over three months ago, I switched to using the Jujutsu Version Control
System as my daily-driver frontend to
git. Since then I’ve been able to refine my workflow to the point where I am
much more productive than I was with git. A key component of those
productivity gains was becoming familiar with just a few commands. I’m sharing
these commands with you as a cheat-sheet if you are considering replacing git
with jj. On the 7th of April, 2025, I joined Imbue as a Member of
Technical Staff. At Imbue, git is the standard VCS used by the engineering team.
I had been curious about the Jujutsu Version Control System (jj), and decided
to attempt to make the switch while joining. Jujutsu makes it easy to interoperate and collaborate with others using a git
repository, but memorizing the idiomatic replacements to my existing git
workflow with the equivalent jj commands took a bit of time. Answers to my
questions were availible in many excellent online resources like Steve Klabnik’s
Jujutsu
Tutorial
and the jj reference.
However, this would come at the expense of pulling me out of the flow of the
task I was working on. Over the past 3 months, I have been refining the jj commands that I keep in my
short-term memory, to build a smooth process that is a superior replacement for
the git flow I previously enjoyed. I find myself having to consult the
references extremely rarely–not because I have committed more of jj to memory,
but because I have learned the subset of jj that is likely to come up during the
working day. I believe that Zipfian
distributions rule over many of our
human experiences. In Zipfian distributions, the frequency of any outcome is
inversely proportional to its rank, meaning that the second most-common outcome
occurs roughly half as often as the most common, and so on. In practice, this
means the distribution is dominated by the few most-common outcomes. You might also think of the Pareto Principle or 80-20 rule which states that 80%
of the outputs are caused by 20% of the inputs. What follows are my “20% inputs”
that I found most useful. I generated this list by running the following shell snippet: I chose to duplicate commands without their final argument so that commands that
only varied by the final argument would be grouped together and counted. I then
edited the results by hand, taking out commands that I no longer use or commands
that had been incorrectly truncated. The final result looked like this. I’ve removed the exact counts, but you can
see what amounts to a power law. I was surprised to find that jj edit is my most commonly used command. There
are two main use cases: The first is as a replacement for git switch/git checkout. The second is to jump back in time to fixup a prior revision if I’ve forgotten
something. With git, I would infrequently use git rebase -i to edit a prior
commit. Since that operation was quite tedious, I would only edit previous
commits in severe cases. Because jj reduces the friction to editing prior
changes, I find myself doing it much more often. This is shorthand for jj bookmark create --revision @, and @ itself is
shorthand for the current revision. This is the equivalent to git’s git checkout -b <branchname> or
git switch -c <branchname>, both of which begin a new branch. Bookmarks in jj behave similarly to branches when uploaded and viewed in Git
hosting platforms. There are a few small differences when viewing and editing
them locally, which you can feel your way through. The jj equivalent to git rebase In my workflow, I often want to rebase my current branch onto main. I used to
spell this in various different ways, some of which dynamically depended on the
name of the bookmark I was rebasing. I’ve recently settled on jj rebase -b@ -dmain which does not vary each time I
call it. This is constantly accessible through C-r in my terminal, and it
always does the right thing. I seem to use jj show and jj diff interchangeably to view the changes that
are pending with the current revision. When called with no arguments, they
assume the default argument of -r@ which signifies the current revision. Less frequently, I also use these commands to look at other revisions in the
tree when I just want to view the changes without visiting that revision. I also use jj diff to specifically look at the diff of single files, or to see
changes between revisions, but these occurrences are very rare. I use this command to start a new branch off main. Usually I will follow this up
with jj b c -r@ <branchname>. This corresponds to the named bookmark
workflow
described at that link. Equivalent to git fetch, this updates my local state to be the same as the
remote. A couple of caveats to keep in mind when using this:
* This automatically updates all named, non-owned bookmarks, even the ones I’m
not currently visiting. In particular, this will automatically update main.
* This will automatically delete any bookmarks that have been deleted from the
remote repository. Usually they have been deleted by me, because I merged and
deleted them in Gitlab, but I can imagine this could drop a bookmark that I
wanted. jj restore is an analog to git restore. It allows you to checkout a
file from a given revision. For old-timers who were used to the old
git checkout -- syntax, this is much more intuitive to learn and use. You could theoretically, use jj restore filename --from some-revision --to
other-revision, to restore a file between two revisions without checking them
out yourself. When I’m restoring a file I’ll usually want to examine how that
file is going to interact with the other files around it, and to run tests. I’ll
first move to the destination revision, and then restore to the current revision
(@) which is the default behaviour when --to is not passed. Just like git push, this will push updates to your tracked bookmarks to the
remote repository. I almost always use the flag --allow-new, which allows new bookmarks I’ve
created to be pushed to the remote. In my workflow, there is never an occasion
where I would want to have a branch locally that isn’t pushed up to my remote.
If you need to, jj can be configured to refuse to push certain commits to
git Short for jj branch forget <branchname>, this command will remove the branch
from your local set of bookmarks, while still keeping it in the remote. This is
useful when I need to check out a colleauge’s branch to inspect it, but don’t
want to stay subscribed to edits until that branch closes. This command sets or changes the description for the current revision. Unlike
git, you are allowed to have revisions without descriptions, but jj will
refuse to push them to remotes. Although jj new -m <description> exists, I never use that, and instead choose
to use a second command to set the description. Just like git status, this will get the status of the current revision in jj. Unlike git, in jj you are always working on a revision, and so there’s
never the concept–or worry–of a dirty working copy. Bookmarks in jj do not automatically update. Since my preference is to commit
often and to push early, I’m usually adding commits on top of bookmarks, and
then pushing them. jj tug is an alias that I found online attributed
here.
It updates the closest bookmark ancestor to point to the current revision,
giving me an easy command to type before I jj git push To set it up, I just add the following two commands to my .jjconfig.toml: tug- updates the bookmark to the previous revision, while tug updates it to
the current one. It’s useful to have both versions, since you might be working
on the current revision, and so don’t want to push it just yet. Similarly to git log, this shows the current history of all revisions. I almost always use jj abandon on leaf revisions, to discard a draft of an
idea or a false start. One gotcha to be careful of is that if you jj abandon a
commit with a bookmark, jj will also delete that bookmark. If you then do a jj
git push, this will push that deletion to the remote, potentially closing your
open pull requests. You can abandon revisions that have descendants, in which case all the
descendants will be rebased onto the parent. This will be as if the commit
you’ve abandoned never existed. Personally, I’ve never had to abandon an
intermediate commit, but it’s good to know that it’s possible. Before closing, I wanted to make sure I talked about the following commands
which I’ve fished out of the long tail. Although used less often, I think
they’re worth discussing because they are useful in error recovery or during
setup. This is another alias, defined as jj mine prints out the bookmarks/branches that I am currently working on, when
I want to take stock and switch between them. Short for jj operation undo, jj undo is a lifesaver! I cannot think of an
equivalent for git although having ohshitgit.com on
your browser bookmarks toolbar comes close. Exactly what it says on the tin, jj undo will reverse the last operation you
performed, allowing you to recover from mistakes or unexpected behaviour. As you can see, I don’t have much reason to call this function, but knowing that
it’s there makes me breathe easier. This is the command that gets you started by transforming a local git
repository into a jj repository. If you have read this far, you’re obviously
quite interested in how jj does things. The best way to learn how to use jj is through doing. Let this be the sign
you’ve been waiting for, and give jj a
try!
Fig. 1: Histogram of my most frequent jj commands
A look at each of the commands
jj edit <-r/-b> <ident>
jj b c -r@ <branchname>
jj rebase -b@ -dmain
jj show and jj diff
jj new -r main
jj git fetch
jj restore
jj git push
jj b f <branchname>
jj desc -m <description>
jj st
jj tug-
jj log
jj abandon
Honourable mention
jj mine
jj undo
jj git init --colocate
.png)
![New Glenn Mission NG-2 Webcast [video]](https://www.youtube.com/img/desktop/supported_browsers/edgium.png)
