How Are Conflicts Handled?
Important
To satisfy your curiosity, you look under Jujutsu’s hood, and grasp what “conflicts are first-class citizens” means.
Two Philosophies
Git and Jujutsu fundamentally differ in what they think a conflict is.
To begin with, in Git there is no such thing as a “conflicted commit”. A conflict is not a Git object, but a state blocking operations until completely resolved.
In Jujutsu, conflicts are part of history, and can be manipulated like Changes. Operations creating conflicts don’t fail; conflicts are recorded for you to resolve later. Best of all, “editing broken files” isn’t the only way out.
But “being first-class citizen” is a very vague concept. Let’s try to get a better grasp of it.
What’s Inside A Commit
In both Git and Jujutsu a non-conflicted commit holds:
- Pointers to its parents (that is, its position in history).
- A pointer to exactly
1Tree: a coherent photograph of the project’s filesystem.

From here, Git and Jujutsu dramatically diverge.
Conflicts in Git
All Git operations are atomic: they either succeed or roll back leaving the repository exactly as it was. Except during conflicts. When merging fails, Git cannot forge a coherent Tree, so it is stuck halfway through of an incomplete transaction. So it:
- Gives up creating the next dommit.
- Blocks the repository.
- Hands the problem over to you.
Git does it best to help you solve the conflict, spreading helpers in 3 places:
-
In the index: for each conflicted file, 3 versions (
:1ancestor,:2ours,:3theirs). -
In the working copy: this is the only time when Git violates its own rules and tampers with your files, filling them with conflict markers
<<<<<<<,=======, and>>>>>>>. -
In
.git: a bunch of state files likeMERGE_HEADto keep track of the transaction.
In the meanwhile, your repo remains an exceptional no-fly zone.
Conflicts in Jujutsu
Jujutsu doesn’t panic. It doesn’t even inject conflict markers on disk. It creates the Change anyway. But how?
-
Position? Easy, the parents are known.
-
Content? All Jujutsu knows is that a consistent Tree must be somehow derived combining the Parent 1’s Tree, the Parent 2’s Tree and the Base’s Tree.
Why not point to all of them? After all: nothing prevents a Change from targeting more than one Tree, like in Git.
Conflicted Changes
Yep. A Change internall hold always a collection of Trees. That’s the trick.
- If that collection contains
1Tree only, then it’s an unconflicted Change. - If there’s more than
1, then there is merge to complete.
This normalization lets Jujutsu treat all Changes consistently, no matter the conflicts.
The Algebra Of Conflicts
A convenient way to represent the Trees referenced by a Change is as
an algebraic expression with + (apply) and - (diff) signs:
T − Fis the diff between the content of ChangeTand the content of ChangeF, so the changes to apply fromFto get toT.
○ T ─╮
│ │ T - F
○ F ←╯
F+diffmeans applying the editsdiffon top of a the content of ChangeF, getting back a new ChangeT.
○ T ─╮
+ diff ═══⇒ │ │ T - F = diff
○ F ○ F ←╯
A failed merge between X and Y is stored as Y + (X − B). It
means:
Apply the diff
(X - B)ontoY.
In the happy scenario, Jujutsu will execute that operation, converging to a conflictless single Tree.
It it can’t reduce this to 1 Tree, it records [X, Y, B] meaning: “I
need to do Y + (X − B), help me.”
A Concrete Example
Say greetings.txt exists in B, X and Y with these content:
○ Y hello mundo
│ ○ X hola world
├─╯
○ B hello world
Merging X and Y you get:
× M <- conflict (we would like "hola mundo")
├─╮
│ ○ Y hello mundo
○ │ X hola world
├─╯
○ B hello world
Both changes touch the same line, so you get a conflict. M records
X + (Y − B). Jujutsu failed to reduce it, but M exists anyway as
an ordinary Change. You can jj edit M to resolve the conflict
whenever you like.
Simplifications
If you apply the diff (X - B) to its natural base B you get:
B + (X - B)
The grade school student in you recalls that B and -B cancel, so
this simplifies algebraically to:
X
Wrongly Applied Diffs
This lets you interpret a conflict as those cases where you cannot simplify algegraic expressions, or:
A conflict is a diff applied to the wrong base.
The diff (X - B) applies cleanly to base B:
B + (X - B) = X
Apply it to the wrong base:
Y + (X - B)
and you might get a conflict. Jujutsu stores [Y, X, B] hoping you
either edit files, or move Changes so algebra simplifies the conflict
away.1
Markers Are An Illusion
When you visit a Conflicted Change, you’ll find conflict markers. Yet
conflict markers don’t exist in Jujutsu’s repositories: they are
generated on-the-fly when you visit a Conflicted Change.
When you save, Jujutsu parses them back into logical states.
Because they are dynamic, you can choose between 3 built-in styles, including the original Git’s one.
-
How can possibly algebra simplify the conflict away? If you at curious, check out Back to Grade School! ↩