Divio type: Explanation
This document explains jujutsu’s two most powerful features for multi-agent development: automatic rebasing and non-blocking conflicts. Understanding these concepts helps you leverage jj’s advantages in Spec Kitty workflows.
In software development, changes often depend on other changes:
WP01: Add User model
↓
WP02: Build User API (uses User model)
↓
WP03: Create User UI (uses User API)
When WP01 changes after WP02 has started, WP02 becomes “outdated”—it was built on an old version of WP01. In git, this requires manual rebasing.
Git represents history as a chain of commits:
main: A ─── B ─── C
\
WP01: D ─── E
\
WP02: F ─── G
When commit E is amended (WP01 changes during review), git doesn’t automatically update F and G:
main: A ─── B ─── C
\
WP01: D ─── E' (amended)
\
WP02: F ─── G (still based on old E)
You must manually rebase:
cd .worktrees/###-feature-WP02/
git rebase ###-feature-WP01
# Resolve conflicts if any
# Continue rebase
jj tracks change IDs separately from commit IDs. When you modify a change, jj knows to update all dependent changes:
Before amendment:
WP01: change_id=abc → commit_id=E
WP02: change_id=def → commit_id=G (parent: abc)
After amendment:
WP01: change_id=abc → commit_id=E' (new commit, same change)
WP02: change_id=def → commit_id=G' (automatically rebased)
When you run spec-kitty sync (or jj rebase), dependent changes are automatically updated:
cd .worktrees/###-feature-WP02/
spec-kitty sync
# WP02 is now based on the updated WP01 - automatically
In a multi-agent workflow with 5 work packages:
| Scenario | Git Manual Steps | jj Auto Steps |
|---|---|---|
| WP01 amended | Rebase WP02, WP03, WP04, WP05 | 0 (automatic) |
| WP02 amended | Rebase WP04, WP05 | 0 (automatic) |
| WP03 amended | Rebase WP04, WP05 | 0 (automatic) |
For complex dependency trees, git requires O(n) manual operations. jj requires zero.
Git conflicts halt progress:
git rebase main
# CONFLICT (content): Merge conflict in src/config.py
# error: could not apply abc1234... Add new config
# Resolve all conflicts manually, then run "git rebase --continue"
Until you resolve the conflict, you cannot:
The agent (or developer) is blocked.
jj stores conflicts in the file without blocking:
jj rebase -d main
# Rebased 1 commits
# New conflicts appeared in these commits:
# abc12345 Add new config
The conflict markers are in the file, but jj continues:
# src/config.py
<<<<<<< Conflict 1 of 1
+++++++ Contents of side #1
DEBUG = True
TIMEOUT = 30
------- Contents of base
DEBUG = False
TIMEOUT = 60
+++++++ Contents of side #2
DEBUG = False
TIMEOUT = 45
>>>>>>> Conflict 1 of 1 ends
You can:
When ready to resolve:
# Edit the file to remove conflict markers
vim src/config.py
# Or use a merge tool
jj resolve src/config.py
# The file is now resolved
jj status
# No conflicts
Consider an agent implementing WP02 (API endpoints). A sync brings in WP01 changes that conflict with config.py:
Git behavior:
git pull --rebaseconfig.pyjj behavior:
spec-kitty syncconfig.py (stored)Understanding jj’s model helps explain why these features work:
Git tracks commits. Each commit is a snapshot with a parent pointer:
commit abc123
parent: def456
tree: (file contents)
message: "Add feature"
Commits are immutable. Amending creates a new commit; the old one is orphaned.
jj tracks changes. Each change has an immutable ID but can point to different commits:
change abc123
current_commit: ghi789
previous_commits: [def456, xyz...]
description: "Add feature"
When you amend, the change ID stays the same but points to a new commit. Dependent changes (which reference the change ID, not the commit ID) automatically see the update.
jj status to see unresolved conflicts.| Aspect | Git | jj |
|---|---|---|
| Rebase trigger | Manual command | Automatic on sync |
| Conflict behavior | Blocks until resolved | Stored in files |
| Change tracking | By commit hash | By change ID |
| Amend effect | Creates orphan | Updates dependents |
| History | Reflog (hard to use) | Operation log (clear) |
| Undo | git reset --hard (loses data) |
jj undo (safe) |
Some operations do require conflict resolution:
jj’s non-blocking conflicts delay resolution, not eliminate it. The advantage is when you resolve—at a convenient time, with full context, rather than immediately when they appear.