Undoing mistakes
Reset your progress
Reset your progress
To reset your progress to the start of this chapter, run the following command:
curl https://s2p2.github.io/jj-for-everyone/reset.sh | bash -s undo
cd ~/jj-tutorial/repo
Jujutsu keeps a log of every operation performed on the repository, not just the commit history. This lets you easily undo mistakes.
For example, let's fix a hastily written commit message. First, we'll check our current state.
cd ~/jj-tutorial/repo
jj log
@ pzsymsll alice@local 2025-09-10 20:52:56 1be9c292 │ (empty) (no description set) ◆ lyquuqkl bob@local 2025-09-10 20:52:56 main git_head() 2ae165d3 │ Add submission instructions │ ○ vtnrtqsn alice@local 2025-09-10 20:52:56 push-vtnrtqsnrzqx d517a86d ├─╯ WIP: Add for loop (need to fix syntax) ◆ tlwyrrmy alice@local 2025-09-10 20:52:56 ffc6841f │ (empty) Merge code and documentation for hello-world ~
Making a Mistake
Let's add more greetings and create a commit with a poor description.
echo 'print("Hallo, Welt!")' >> hello.py
echo 'print("Bonjour, le monde!")' >> hello.py
jj commit -m "code improvements"
@ yxmkmosm alice@local 2025-09-10 20:53:46 d28a8261 │ (empty) (no description set) ○ pzsymsll alice@local 2025-09-10 20:53:46 git_head() 050b543b │ code improvements ◆ lyquuqkl bob@local 2025-09-10 20:52:56 main 2ae165d3 │ Add submission instructions │ ○ vtnrtqsn alice@local 2025-09-10 20:52:56 push-vtnrtqsnrzqx d517a86d ├─╯ WIP: Add for loop (need to fix syntax) ◆ tlwyrrmy alice@local 2025-09-10 20:52:56 ffc6841f │ (empty) Merge code and documentation for hello-world ~
Undoing the mistake
To fix this, we restore the repository to its previous state with jj undo.
jj undo
@ pzsymsll alice@local 2025-09-10 20:53:46 94a7ca39 │ (no description set) ◆ lyquuqkl bob@local 2025-09-10 20:52:56 main git_head() 2ae165d3 │ Add submission instructions │ ○ vtnrtqsn alice@local 2025-09-10 20:52:56 push-vtnrtqsnrzqx d517a86d ├─╯ WIP: Add for loop (need to fix syntax) ◆ tlwyrrmy alice@local 2025-09-10 20:52:56 ffc6841f │ (empty) Merge code and documentation for hello-world ~
We're back to the state before the bad commit. Now we can commit again with a better message.
jj commit -m "Print German and French greetings as well"
@ npnpqkvz alice@local 2025-09-10 20:54:33 8f11c4f5 │ (empty) (no description set) ○ pzsymsll alice@local 2025-09-10 20:54:33 git_head() df82ee49 │ Print German and French greetings as well ◆ lyquuqkl bob@local 2025-09-10 20:52:56 main 2ae165d3 │ Add submission instructions │ ○ vtnrtqsn alice@local 2025-09-10 20:52:56 push-vtnrtqsnrzqx d517a86d ├─╯ WIP: Add for loop (need to fix syntax) ◆ tlwyrrmy alice@local 2025-09-10 20:52:56 ffc6841f │ (empty) Merge code and documentation for hello-world ~
Undoing and Redoing Multiple Operations
jj undo works like Ctrl+Z; you can run it multiple times to go further back. It even undoes file system changes, because jj snapshots your workspace before running a command.
Let's undo the last three operations: the good commit, the file modifications, and the jj new main command from the previous chapter.
the good commit:
jj undo
@ pzsymsll alice@local 2025-09-10 20:53:46 94a7ca39 │ (no description set) ◆ lyquuqkl bob@local 2025-09-10 20:52:56 main git_head() 2ae165d3 │ Add submission instructions │ ○ vtnrtqsn alice@local 2025-09-10 20:52:56 push-vtnrtqsnrzqx d517a86d ├─╯ WIP: Add for loop (need to fix syntax) ◆ tlwyrrmy alice@local 2025-09-10 20:52:56 ffc6841f │ (empty) Merge code and documentation for hello-world ~
the file modifications:
# hello.py
print("Hello, world!")
print("Hallo, Welt!")
print("Bonjour, le monde!")
jj undo
# hello.py
print("Hello, world!")
jj log to check:
@ pzsymsll alice@local 2025-09-10 20:52:56 1be9c292 │ (empty) (no description set) ◆ lyquuqkl bob@local 2025-09-10 20:52:56 main git_head() 2ae165d3 │ Add submission instructions │ ○ vtnrtqsn alice@local 2025-09-10 20:52:56 push-vtnrtqsnrzqx d517a86d ├─╯ WIP: Add for loop (need to fix syntax) ◆ tlwyrrmy alice@local 2025-09-10 20:52:56 ffc6841f │ (empty) Merge code and documentation for hello-world ~
One last time: jj new main command from the previous chapter.
jj undo
@ wkqpyqlm alice@local 2025-09-10 20:52:56 640e22bf │ (empty) (no description set) │ ◆ lyquuqkl bob@local 2025-09-10 20:52:56 main 2ae165d3 │ │ Add submission instructions │ │ ○ vtnrtqsn alice@local 2025-09-10 20:52:56 push-vtnrtqsnrzqx d517a86d │ ├─╯ WIP: Add for loop (need to fix syntax) │ ◆ tlwyrrmy alice@local 2025-09-10 20:52:56 ffc6841f ╭─┤ (empty) Merge code and documentation for hello-world │ │ │ ~ │ ◆ uvxzzlmr bob@local 2025-09-10 20:52:55 git_head() 60e09461 │ Document hello.py in README.md ~
The jj redo command re-applies undone operations, like Ctrl+Shift+Z. Let's use it to get back to our most recent state.
jj redo
jj redo
jj redo
@ npnpqkvz alice@local 2025-09-10 20:54:33 8f11c4f5 │ (empty) (no description set) ○ pzsymsll alice@local 2025-09-10 20:54:33 git_head() df82ee49 │ Print German and French greetings as well ◆ lyquuqkl bob@local 2025-09-10 20:52:56 main 2ae165d3 │ Add submission instructions │ ○ vtnrtqsn alice@local 2025-09-10 20:52:56 push-vtnrtqsnrzqx d517a86d ├─╯ WIP: Add for loop (need to fix syntax) ◆ tlwyrrmy alice@local 2025-09-10 20:52:56 ffc6841f │ (empty) Merge code and documentation for hello-world ~
Try jj redo one more time, you'd get an error that there's nothing to redo.
Important Considerations
-
Overwriting History: If you
undooperations and then create a new one, the undone states become inaccessible viajj redo. This is similar to standard undo/redo stacks. However, unlike most graphical applications, Jujutsu has advanced commands to recover these "hidden" states, which is a topic for another day. -
Remote Operations: Do not
jj undoa push operation (e.g.,jj git push). It only affects your local repository and can cause conflicts with the remote. If you do it by accident, immediatelyjj redoit. -
Local History: The operation log is local to your machine and is not cloned or pushed.
jj undoonly works on the history created in your local repository clone.