Rebasing and Editing History
jj makes editing history safe and automatic—no more manual conflict resolution from git commit --amend.
The Problem with Git
In git, editing old commits is risky:
# Edit commit from 3 steps ago
git rebase -i HEAD~3
# Manually edit, save, exit
# All subsequent commits have conflicts
# Have to resolve each one manually
jj's Solution: Automatic Descendant Rebasing
When you edit an old commit in jj, all descendants automatically rebase. No manual intervention needed.
Editing Old Commits
git
git rebase -i HEAD~3git add .git commit --amendgit rebase --continuejj
jj edit --change abc123vim file.jsjj new| git | jj |
|---|---|
| git rebase -i HEAD~3 | jj edit --change abc123 |
| git add . | vim file.js |
| git commit --amend | jj new |
| git rebase --continue |
Squashing Commits
git
git rebase -i HEAD~3jj
jj squash @- -m "Combined message"jj squash -r 'descendants(@, 2)' -m "One commit"| git | jj |
|---|---|
| git rebase -i HEAD~3 | jj squash @- -m "Combined message" |
Reordering Commits
git
git rebase -i HEAD~3jj
jj rebase -s abc -d def| git | jj |
|---|---|
| git rebase -i HEAD~3 | jj rebase -s abc -d def |
Moving Commits (Rebase)
git
git rebase --onto new-base old-base branchjj
jj rebase -d new-basejj rebase -s 'descendants(abc, 5)' -d new-base| git | jj |
|---|---|
| git rebase --onto new-base old-base branch | jj rebase -d new-base |
Abandoning Commits
git
git reset --hard HEAD~1jj
jj abandon @-| git | jj |
|---|---|
| git reset --hard HEAD~1 | jj abandon @- |
Undo Any Operation
jj keeps an operation log—nothing is ever truly lost:
git
git refloggit reset --hard HEAD@{1}jj
jj op logjj op undo| git | jj |
|---|---|
| git reflog | jj op log |
| git reset --hard HEAD@{1} | jj op undo |
Practical Example
Let's say you made 5 commits and want to edit the second one:
# Current history
@ Commit 5
◉ Commit 4
◉ Commit 3 ← Want to edit this one
◉ Commit 2
◉ Commit 1
# Edit commit 3
jj edit --change commit3
# Now @ is commit 3
@ Commit 3
◉ Commit 2
◉ Commit 1
# Make your changes
vim file.js
# Finalize the edit
jj describe -m "Updated commit 3"
jj new
# Commits 4 and 5 auto-rebased!
@ (new working copy)
◉ Commit 5 (auto-rebased)
◉ Commit 4 (auto-rebased)
◉ Updated commit 3
◉ Commit 2
◉ Commit 1
NOTE:
This automatic rebasing is one of jj's killer features. No more manual conflict resolution when editing history.
Splitting Commits
git
git reset HEAD~1git add file1git commit -m "Part 1"git add file2git commit -m "Part 2"jj
jj edit @-jj restore file2jj describe -m "Part 1"jj newjj describe -m "Part 2"jj new| git | jj |
|---|---|
| git reset HEAD~1 | jj edit @- |
| git add file1 | jj restore file2 |
| git commit -m "Part 1" | jj describe -m "Part 1" |
| git add file2 | jj new |
| git commit -m "Part 2" | jj describe -m "Part 2" |
Safe History Editing
jj never loses data:
- Every operation is recorded in the operation log
jj op undoreverses any operationjj op restore <id>restores to any previous state- Abandoned commits remain until explicitly deleted
Try It Yourself
# Create 3 commits
echo "a" > a.txt
jj describe -m "Add A"
jj new
echo "b" > b.txt
jj describe -m "Add B"
jj new
echo "c" > c.txt
jj describe -m "Add C"
jj new
# Edit the middle commit
jj edit @-
# Change B
echo "b2" > b.txt
# Finalize
jj describe -m "Add B (updated)"
jj new
# Commit C auto-rebased!
jj log
Key Takeaways
- Editing old commits auto-rebases descendants
jj editmoves @ to target commitjj squashcombines commitsjj rebase -dmoves commits to new basejj op undoreverses any operation- History editing is safe and reversible
Next Steps
Now let's explore jj's first-class conflict handling.