Working with Conflicts
One of JJ’s most revolutionary features is how it handles conflicts. Unlike Git where conflicts block your workflow, JJ treats conflicts as first-class data that you can commit, work around, and resolve whenever convenient.
How Conflicts Work in JJ
In Git, conflicts are errors that halt operations until resolved.
In JJ, conflicts are just another state:
- ✅ You can commit conflicted files
- ✅ You can create new commits on top of conflicts
- ✅ You can resolve conflicts later—or never
- ✅ Resolving once propagates through stacks
Research shows 15 out of 20 major JJ resources highlight this as a transformative feature.
Creating a Conflict (Example)
Let’s deliberately create a conflict to see how JJ handles it:
# Start from mainjj new main -m "Feature A"
# Create a file with version Aecho "version A" > file.txt
# Create another commit that conflictsjj new -m "Feature B"echo "version B" > file.txt
# JJ creates a conflict in the current commit (@)jj status# Shows: "file.txt has conflicts"What happened:
- Feature A set
file.txtto “version A” - Feature B tried to change it to “version B”
- JJ recorded both versions as conflicting
Living with Conflicts
Here’s what makes JJ revolutionary: you can keep working.
# You have a conflictjj status# "file.txt has conflicts"
# But you can create new work anyway!jj new -m "Feature C"vim other-file.txt # Work on something else
# View your stackjj log# @ Feature C# ○ Feature B (has conflicts)# ○ Feature A# ○ mainIn Git, the conflict would halt everything. In JJ, Feature B is marked as conflicted, but you’re free to continue.
Viewing Conflicts
See which commits have conflicts:
# Check current commitjj status
# View all commits - conflicted ones are markedjj log -r stack
# See the conflict markerscat file.txtJJ’s conflict markers are more informative than Git’s:
<<<<<<< Conflict 1 of 1+++++++ Contents of side #1version B%%%%%%% Changes from base to side #2-version A+version B>>>>>>> Conflict 1 of 1 endsThe %%%%%%% section shows what changed from the base—helping you understand both sides of the conflict.
Conflict Propagation Through Stacks
This is where JJ’s approach becomes powerful: conflicts propagate through descendant commits.
# Create a stackjj new main -m "Database schema"echo "table users" > schema.sql
jj new -m "Add auth"echo "auth code" > auth.js
jj new -m "Add UI"echo "ui code" > ui.js
# Go back and edit database schemajj edit <schema-commit>echo "DIFFERENT table users" > schema.sql
# This creates a conflict in schema.sql# The conflict propagates to descendants if they touched the same file
# Go to the topjj stack-topjj log -r stack# Shows which commits inherited conflictsThe key insight: Once you resolve the conflict in the base commit, JJ automatically updates all descendants.
As one developer exclaimed: “YOU JUST FIX THE CONFLICT ONCE, FOR ALL YOUR PULL REQUESTS. IT’S ACTUALLY AMAZING.” (Sandy Maguire)
Resolving Conflicts
When you’re ready to resolve (or when you need to—like before pushing to CI):
# Navigate to the conflicted commitjj edit <conflicted-commit-id>
# Open the conflict resolverjj resolveThis opens your configured merge tool (like vimdiff, meld, or vscode) showing:
- Base version
- Your version
- Their version
- Merged result
Make your choices and save. JJ automatically:
- Updates the commit to mark the conflict as resolved
- Rebases all descendant commits
- Propagates the resolution through the stack
When to Resolve Conflicts
In Git, you must resolve immediately. In JJ, you have options:
Resolve Now
When you want clean history for pushing:
# Sync brings in changes that conflictjj stack-sync
# Resolve before pushingjj resolvejj stack-submitResolve Later
When working on multiple things and conflicts don’t block you:
# Conflict in commit Ajj status# "file.txt has conflicts"
# Keep working on commit Bjj new -m "Unrelated feature"vim other-file.js# No problem!
# Resolve commit A when convenientjj edit <commit-A>jj resolveResolve Never
If a commit will be squashed or abandoned:
# Experimental commit with conflictsjj new -m "Try approach X"# ... conflicts ...
# Decide not to use itjj abandon @# Conflict gone with the commitExample: Editing Mid-Stack Creates Conflict
A common scenario:
# You have a stackjj log# @ commit-3 "Add API"# ○ commit-2 "Add models"# ○ commit-1 "Add database schema"# ○ main
# Edit the middle commitjj edit commit-2vim src/models.js # Make breaking changes
# This might conflict with commit-3 since it uses the models# JJ automatically rebases commit-3 and marks conflicts if any
# Go to commit-3 to seejj edit commit-3jj status# May show conflicts if your changes to models break the API
# Resolvejj resolve# Fix the conflicts in the API to match new model
# Done! The stack is consistentConflict Resolution Tips
Use jj undo for Bad Resolutions
Made the wrong choice while resolving?
# Resolved conflictsjj resolve# ... made wrong choices ...
# Undo the resolutionjj undo# Conflict state restored, can resolve againLazyJJ’s Claude Integration
If you have Claude CLI set up:
# AI-assisted conflict resolutionjj claude-resolveClaude will help resolve conflicts by understanding both sides and suggesting resolutions.
Preview Conflicts Before Resolving
See the conflict markers without opening the resolver:
cat conflicted-file.txtThis lets you understand the conflict before deciding whether to resolve now or later.
Conflicts in Stacked PRs
A killer workflow:
# You have 3 stacked PRsjj log# @ PR-3 "Add UI"# ○ PR-2 "Add models"# ○ PR-1 "Add database schema"# ○ main
# Main gets updated, conflicts with PR-1jj stack-sync# PR-1 now has conflicts
# Resolve PR-1jj edit <PR-1-commit>jj resolve# Fix conflicts
# Go back to topjj stack-top
# Push updatesjj stack-submitWhat JJ did automatically:
- Marked PR-1 as conflicted when syncing
- Let you keep working on PR-3 despite PR-1’s conflicts
- When you resolved PR-1, rebased PR-2 and PR-3
- Propagated the resolution through the entire stack
In Git/Graphite, you’d resolve PR-1, then manually rebase PR-2, resolve its conflicts, manually rebase PR-3, resolve its conflicts. In JJ, one resolution propagates.
Why This is Better Than Git
| Git | JJ |
|---|---|
| Conflicts block operations | Conflicts are just data |
| Must resolve before continuing | Can work on other commits |
| Rebase stops at each conflict | Records all conflicts, resolves when ready |
| Resolve same conflict in each branch | Resolve once, propagates to descendants |
| Fear of complex rebases | Confident in stack manipulation |
Common Questions
”Can I push conflicted commits to GitHub?”
Technically yes (JJ will push the conflict markers), but your CI will likely fail. Best practice: resolve before pushing.
”What if I want to see all conflicted commits?”
jj log -r "conflict()"This shows only commits with conflicts.
”Can I resolve conflicts differently in different branches?”
If branches diverged before the conflict, yes—each has its own conflict state. But in a stack, resolving the base propagates forward.
”What if Claude’s resolution is wrong?”
jj undo # Undo Claude's resolutionjj resolve # Resolve manuallyThe operation log makes experimentation safe.
Next Steps
Now that you understand first-class conflicts:
- Learn about Mid-Stack Editing - conflict-creating operations
- Read Syncing with Remote - when conflicts commonly occur
- See the Operation Log - your safety net for resolution mistakes
- Understand the Mental Model - why this approach makes sense
First-class conflicts are one of JJ’s superpowers. Embrace them!
Ernesto Jiménez