Undo and Time Travel
jj's operation log makes it impossible to lose work—every action is recorded and reversible.
The Operation Log
Every jj command is recorded in the operation log:
$ jj op log
d5f3b08fe3ea5 2 minutes ago checkout
1c8e94a3f2c1b 4 minutes ago new
e7d3a0b5d4c2e 6 minutes ago describe
a2b5c8d9e0f1a 8 minutes ago checkout
Compare to git's reflog:
git reflogjj op log| git | jj |
|---|---|
| git reflog | jj op log |
Undoing Operations
git reset --hard HEAD@{1}jj op undo| git | jj |
|---|---|
| git reset --hard HEAD@{1} | jj op undo |
Multiple Undos
# Undo last 3 operations
jj op undo --count 3
Restoring Specific States
git reset --hard HEAD@{5}jj op restore abc123| git | jj |
|---|---|
| git reset --hard HEAD@{5} | jj op restore abc123 |
Viewing Past States
You can view the repository as it was at any operation:
# Show log at past operation
jj op log -at <operation-id>
# Show status at past operation
jj op show <operation-id>
Common Undo Scenarios
Accidentally abandoned work:
# Oops, abandoned wrong commit
jj abandon @-
# Undo it
jj op undo
# Commit is back
jj log
Bad rebase:
# Rebase went wrong
jj rebase -d bad-base
# Undo entire rebase
jj op undo
Mistaken merge:
# Merged wrong branch
jj merge wrong-branch
# Undo
jj op undo
Time Travel Debugging
The operation log is like a time machine:
# Something broke, when did it happen?
jj op log
# Jump back to before it broke
jj op restore <good-operation-id>
# Verify bug isn't there
jj log
jj show @
# Come back to present
jj op restore <latest-operation-id>
Comparing States
# Compare current state with past
jj diff --from <past-operation>
# See what changed between two operations
jj diff --from <op1> --to <op2>
Operation IDs vs Commit IDs
- Operation ID: Identifies a command in the operation log
- Commit ID: Identifies a specific commit
- Change ID: Stable identifier for a logical change
# Undo an operation (sequence of commits)
jj op undo <operation-id>
# View a specific commit
jj show <commit-id>
# Track a change across rebases
jj log -r change(<change-id>)
Immutable Operations
Some operations can't be undone (for safety):
jj git push # Pushed to remote—can't undo
jj git fetch # Fetched from remote—can't undo
But you can undo their local effects:
# After a problematic fetch
jj git fetch
jj op undo # Undoes the local ref updates
Practical Workflow
# Experiment freely
jj new
echo "radical change" > core.js
jj describe -m "Experiment"
jj new
# Doesn't work out
jj op undo
# Try different approach
jj new
echo "conservative change" > core.js
jj describe -m "Safe refactor"
jj new
Operation Log Size
The operation log grows but jj handles it efficiently:
# Operations are cheap
# Thousands of operations = still fast
# Clean old operations if needed
jj op log --limit 100
Git Comparison Summary
| Task | Git | jj |
|------|-----|----|
| Undo last action | git reset --hard HEAD@{1} | jj op undo |
| View history | git reflog | jj op log |
| Restore state | git reset --hard <ref> | jj op restore <id> |
| Undo multiple | Complex git dancing | jj op undo --count N |
| Time limit | 90 days default | Never expires |
Try It Yourself
# Make some changes
echo "a" > a.txt
jj describe -m "Add A"
jj new
echo "b" > b.txt
jj describe -m "Add B"
jj new
# Undo last operation
jj op undo
# b.txt is gone
# Undo again
jj op undo
# a.txt is gone
# Redo by going forward
jj op log # Find the operations
jj op restore <id> # Jump to any point
Key Takeaways
- Every jj operation is recorded
jj op undoreverses the last operationjj op restorejumps to any past state- Operation log never expires
- Safe experimentation—always can undo
- More reliable than git reflog
Next Steps
You've now mastered the core jj concepts. Let's look at some advanced workflows.