Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Goal

You see how moving changes from a source Change to a target Change might imply abandoning the source.

Squashing Changes

The way jj squash parameter defaults interact is quite clever.The basic syntax is:

jj squash -f A -t B FILES
  • If you omit the list of files, then all the changes in the Change are moved.
  • If you omit either -f or -t, then the Current Change @ will be implied.

So:

jj squash -f z

moves the edits from z to your current working copy.
If you do:

jj squash -t x

your edits move to x, no matter where it sits in your history, either in the past or in the future, in the same branch or elsewhere.

This technique is truly powerful: it lets you edit something here and send it somewhere else when you're done.
For example, if you notice an unrelated typo while working on a feature, fix it on the spot and squash it where it belongs.
Or, say you spot something that needs to be done in a different branch. You can write the change immediately and then move it there with a jj squash. In a sense, this is the basic move that lets you work on multiple features simultaneously.
These moves are so idiomatic that Jujutsu offers a "macro command": absorb. I'll cover it in the following pages with practical exercises.

Abandoning Changes

When the source Change is left empty after a move, Jujutsu automatically abandons it. For example, say you have:

$ jj log -r "n::zx"

○  zx
○  v
○  s
○  n

If you squash v into n:

Solution

jj squash -f v -t s

then you end up with:

○  zx
○  s
○  n

What Happens To Messages?

As with Git, Jujutsu always preserves information. If squashing results in deleting a Change, and that Change has a description, Jujutsu will stop and interactively let you define one. You will typically merge all the original messages into one.
Otherwise, you can always set the message with -m, or use --use-destination-message / -u.

Quiz Time!

Check out this sloppy mistake:

$ jj show yo- --git

--- a/src/App.js
+++ b/src/App.js
...
+    <button
+      className="square"
+      onClick={handleClic}
...

Oops, Clic instead of Click. Have I fixed it in yo-? Not at all! I committed a fix on top of it, with the very expressive message "Fix":

$ jj show yo --git

--- a/src/App.js
+++ b/src/App.js
@@ -11,7 +11,7 @@
   return (
     <button
       className="square"
-      onClick={handleClic}
+      onClick={handleClick}
     >

Terrible. Help me, please: get rid of that noisy yo Change and make yo correct:

Solution

One way to do that is:

jj edit yo
jj squash -u

Remember? -u is the short for --use-destination-message.

Or, without moving away from your Current Change:

jj squash -f yo -t n -u ```

Just jj squash

If you run the bare jj squash, without any argument, then:

  • it will move all the changes (indeed, no files are specified)
  • from the Current Change to its parent.
  • Should your Current Change have no message, it will be abandoned and a fresh new one will be immediately created.

Say you are on the tip of a branch:

@  sw           empty
○  zx           Click alternates Xs and Os main
○  v            Click sets a X
○  sn           Square has state

You code, editing @. Then you run jj squash.
Can you see where this goes? It's the equivalent of having the Git index!

Let me show you how this works.