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

Rebasing

Now that Bob is happy with his second commit, he tries to push it to the remote:

jj new
jj bookmark move main --to @-
jj git push

As you might've already guessed, Bob gets the same error message as Alice did earlier. He also decides to follow the hint of fetching from the remote (after watching some cat videos):

jj git fetch

Here's what Bob's log looks like now:

@  ztyvwqll bob@local 2025-07-22 21:29:52 93f27644(empty) (no description set)smswwyok bob@local 2025-07-22 21:29:27 main?? main@git git_head() e583fa6e
│  Add submission instructions
│   twywpklt alice@local 2025-07-22 21:27:24 main?? main@origin 606959ce
╭─┤  (empty) Combine code and documentation for hello-world
│ │
│ ~
│
  quolxwkk bob@local 2025-07-22 21:22:22 8d538390
│  Document hello.py in README.md
~

It's basically the same situation. Bob's and Alice's changes have branched-off into different directions. To merge them back together, Bob could do the same thing Alice did and create a merge commit. However, Bob doesn't like merge commits. Preferring straight lines in his log, he decides to take a different approach from Alice.

Bob is going to pretend like he made his changes on top of Alice's changes all along. His most recent commit should have Alice's commit as its parent, because that will result in a linear history.

There is a Jujutsu command just for this purpose: It's called rebase. As the name implies, it takes commits from one "base" (some ancestor) and moves them on top of a different "base". jj rebase on its own doesn't work though, it needs to know the destination of the operation. In this case, Bob wants to move his commit on top of the state of the remote main bookmark, i.e. Alice's commit. Therefore, he runs the command:

jj rebase --destination main@origin

What does the log say?

@  ztyvwqll bob@local 2025-07-22 21:30:38 e911e4c7(empty) (no description set)smswwyok bob@local 2025-07-22 21:30:38 main* git_head() 02bd1179
│  Add submission instructions
  twywpklt alice@local 2025-07-22 21:27:24 main@origin 606959ce(empty) Combine code and documentation for hello-world
~

Splendid, that's exactly what Bob wanted. Jujutsu even figured out that the main bookmark should probably point to Bob's new commit. All that's left to do is to rerun:

jj git push

What Bob has just done is a paradigm shift: He has rewritten history. The history used to say that Bob's second commit descended from his first one, but now it says it descended from Alice's commit. The extent of this revisionism is still benign, but as we progress through the book, we will explore the dark arts of history manipulation ever more deeply.

TRUTH ITSELF SHALL BEND TO YOUR WILL

...ahem, where were we?

I've glossed over a small detail. I said the rebase command moves commit from a one base to a new one. What is that previous base? When using the --destination flag (or -d for short), Jujutsu will select the first shared ancestor of your working copy and the new base as the old base to rebase from. In simple terms, the rebase command moves only those commits that aren't on the destination branch yet. In our example, there was only one commit to move.

Creating a merge commit and rebasing are both valid ways of recombining changes that branched-off into different directions. They both have advantages and disadvantages. Some people care more about one aspect than another, so they end up having strong opinions about which approach is best. Here's a hopefully balanced overview of the main trade-off:

advantagedisadvantage
mergePreserves the history exactly as it happened.Can result in a tangled, hard-to-read history.
rebaseResults in an easy-to-read, linear history.Lies about the order in which things happened.

Once you have determined the correct opinion about which one is better, please let everybody on the internet know about your important discovery!