- Posted
jujutsudevelopmentguide
How Do I...
Maybe it's just me, but when I first pick up a new tool and I don't yet have any muscle memory, I don't have a broad view of what commands are available, and I'm missing all the shortcuts and aliases that I can use with whatever it's replacing, I can feel a little "trapped by the tool". I'm much slower at everything I try to do, I'm not quite sure what side effects some of the commands I run will have, and I have to go look at the documentation for anything beyond the basics.
So this part is intended to be less of a linear tutorial and more like a quick reference handbook for common tasks. You might find this useful if you read part 1 and decided to try out Jujutsu, and you've picked up the basic set of commands, but you still don't feel like you're ready to start using it in earnest. Hopefully this will help to bridge the gap by broadening your view of what's available and connecting it to what you already know how to do with Git.
...See More of the Log?
jj log takes an -r argument, and you
can give it an expression in Jujutsu's revset
language.
See everything on the main branch:
jj log -r '::main'
See everything on the main branch and leading up to your current revision:
jj log -r '::main | ::@'
See all the revisions you authored:
jj log -r 'mine()'
See everything:
jj log -r ..
...Edit an Existing Revision?
This is the equivalent of something like a git commit --amend or what you'd do with a git rebase -i (but with jj it's also a way to navigate around, because editing a revision will move you to
that place in the tree).
To directly edit an existing revision in place:
jj edit <revision ID>
Or you can create a new revision on top of the one you want to edit, make changes, and then squash those changes into it:
jj new <base revision>
# Make changes, then...
jj squash
...Insert Between Two Revisions?
You can create a new revision after
(-A) or before (-B) another revision, which will insert it into the history rather than
branching off on a new tangent.
jj new -A <revision ID>
jj new -B <revision ID>
To illustrate this, let's say you have these revisions:
○ wtpxmkup blog@maddie.wtf 19 seconds ago git_head() 14d876d1 │ Third original revision ○ qxqkllpu blog@maddie.wtf 25 seconds ago b0d0cc60 │ Second original revision ○ uvtplnry blog@maddie.wtf 29 seconds ago cd980411 │ First original revision ~
If you were to run jj new u, (without either -A or -B, so saying "create a new revision on
top of u"), you'd get this result:
@ ouksmvko blog@maddie.wtf 11 seconds ago 3a5609b6 │ (empty) (no description set) │ ○ wtpxmkup blog@maddie.wtf 59 seconds ago 14d876d1 │ │ Third original revision │ ○ qxqkllpu blog@maddie.wtf 1 minute ago b0d0cc60 ├─╯ Second original revision ○ uvtplnry blog@maddie.wtf 1 minute ago git_head() cd980411 │ First original revision ~
The new revision, o, has been created on top of u, but is on a separate diverging branch of
revisions from the others.
Instead, if you ran jj new -A u ("create a new revision after u"), you'd get this:
○ wtpxmkup blog@maddie.wtf 6 seconds ago 6871540d │ Third original revision ○ qxqkllpu blog@maddie.wtf 6 seconds ago 6c461c50 │ Second original revision @ mltnnmks blog@maddie.wtf 6 seconds ago 625a0faf │ (empty) (no description set) ○ uvtplnry blog@maddie.wtf 2 minutes ago git_head() cd980411 │ First original revision ~
The new revision, m this time, has been inserted after u—so, still a child of u, but also a
parent of all the revisions that were children of u before.
-B is along the same lines—you would have gotten exactly the same result if you ran jj new -B q.
...Get Rid of Changes?
If you're editing a revision and you want to get rid of the changes you made to a certain file in that revision, you can "restore" the changes:
jj restore path/to/file
You can also get rid of all changes in the revision, but keep the revision around:
jj restore
If you want to get rid of the changes and get rid of the revision, you can abandon it:
jj abandon
Restores can be of the form "restore the version of this file from a particular revision", which by default is the previous revision, but can be an older one or an unrelated one:
jj restore --from main path/to/file
This form of the command is the equivalent of git checkout main -- path/to/file.
...Split a Revision?
There's a split command:
jj split <revision ID>
This will open an interactive TUI editor to let you choose which changes you want to go into the first new revision. Everything not ticked in that editor will go into the second revision.
This command defaults to splitting the current revision, so not providing any arguments is
equivalent to jj split -r @.
...Delete a Bookmark (Branch)?
First delete it locally:
jj bookmark delete the-bookmark-name
Then you can push the deletion:
jj git push --deleted
...Go Back to Git?
You basically can just start running Git commands again at any time with no destructive effects. The only thing is that running Jujutsu commands will put you into detached HEAD mode, so you'll want to check out a branch:
git checkout some-branch-name
...Undo a Mistake?
Jujutsu maintains a log of all operations that occurred (a bit like Git's reflog), and you can look at it:
jj op log
You can undo the latest operation:
jj op undo
Or you can provide that command with the ID of an operation that you found in the op log:
jj op undo <operation ID>
If you want to undo multiple operations, you can restore the repository to its state at any particular operation in the log:
jj op restore <operation ID>
...Look at a Diff?
Jujutsu's diff is similar to Git's.
jj diff -r <revision ID>
This also defaults to the current revision (@) if you don't provide an -r argument:
jj diff
You can provide it file glob patterns to look at the diff to specific files:
jj diff src/bin/**.rs
...Cherry-Pick a Change?
Jujutsu calls this operation "duplicating" a revision. You can do it in one step if you know either the revision ID or the commit ID of the revision you want to duplicate, and if you know where you want it to go.
jj duplicate <source ID> --destination <ID of destination base rev>
To make this a bit clearer, let's say there's a revision called a that you want to cherry-pick on
top of a revision called b. If you run jj duplicate a --destination b, Jujustu will create a new
revision called c on top of b, and it will contain a copy of the changes from a.
If you want to insert the cherry-picked revision between two other
revisions, you can provide the same -A (after) or -B (before)
argument to jj duplicate that you can give to jj new and other commands instead of
--destination:
jj duplicate <source ID> -A <rev to insert it after>
# or...
jj duplicate <source ID> -B <rev to insert it before>
...Rebase Changes?
Jujutsu's rebase is significantly
more user-friendly than Git's. Let's say we have a branch called my-cool-feature, whose first
revision is a, and we want to rebase it onto main (maybe we just updated the latest version of
main from the remote with jj git fetch).
First, to initiate the rebase, we can do this:
jj rebase --source a --destination main
This will move a and all of its children onto the destination, main. This is actually most like
a git rebase --onto.
Jujutsu can alternatively figure out which revisions you want it to move based on the tip of the branch rather than the root:
jj rebase --branch my-cool-feature --destination main
# or (you don't have to use a branch name)
jj rebase --branch b --destination main
After you've performed the rebase, conflicts
might have been created. Just jj edit the conflicted revisions, working from base to tip, and fix
the conflicts manually (or you can jj new onto each conflicted revision, fix the conflicts in the
new revision that's created, and then jj squash the resolution changes in).
...Merge?
Merges are just revisions with more than one parent, and jj new takes multiple arguments for the
parents of the revision. To merge a branch a into a branch b, run:
jj new b a
Then you'll probably want to write a message for the new merge revision, with:
jj describe