Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Goal

Where you see how to interpret the conflict markers and you see how a conflict resolution automatically propagates.

Resolving A Conflict by Editing It

The most straightforward and natural way to solve a conflict is by editing the affected files.
In this case, it's a matter of editing the merge Change zo:

@  l        empty
×  zm       My parent is conflicted
×  zo       Merge conflict           <- Resolve the conflict here
├─╮
│ ○  nz        merge-b
○ │  m         merge-a
├─╯
○  vr       Base of merge-a and merge-b
│
~

Notice: you've surely obtained a different Change ID. For convenience, let's call it nz+, meaning the nz's direct child.

Did I Tell You Not To Edit Changes?

If you are tempted to run jj edit zo, please, don't! Remember my heartfelt plea in Don't Edit. Do jj new zo instead. Remember that any change you perform in zo is automatically propagated. This way, you can review your resolution before finalizing it and easily undo any mistake.
So, do yourself a favor and create a Buffer Change:

jj new zo

This is what you get:

@  k        empty                    <- Your Buffer Change
│ ×  zm     My parent is conflicted
├─╯
×  zo       empty Merge conflict     <- The Change to fix
├─╮
│ ○  nz        merge-b
○ │  m         merge-a
├─╯
○  vr       Base of merge-a and merge-b
│
~

Of course, even this Buffer Change will be conflicted. That's exactly the point: you want to resolve the conflict from there, so it must inherit it.

So, here's the idiomatic workflow for attacking conflicts:

  1. Run jj new targeting the conflicted Change.
  2. When you've solved the conflict, jj squash, as per the mini-workflow.
  3. If you want to start over, jj restore.

This is exactly the mini-workflow you saw in Don't Edit.

Resolving The Conflict

Edit the conflicted file file.txt. You will see this content:

Donec neque quam, dignissim in, mollis nec, sagittis eu, wisi.
Phasellus neque orci, porta a, aliquet quis, semper a, massa.
Etiam vel neque nec dui dignissim bibendum.
Nunc porta vulputate tellus.
Nunc eleifend leo vitae magna.
Sed id ligula quis est convallis tempor.
<<<<<<< conflict 1 of 1
%%%%%%% diff from: vrzponzu b12f7845 "Base of merge-a and merge-b"
\\\\\\\        to: mvxxwqoo e1acf668
-We will create a conflict here.
+We will create a conflict here: merge-a added this text.
+++++++ nzsunxwp 0004a47a
We will create a conflict here: merge-b added this text.
>>>>>>> conflict 1 of 1 ends
Nullam rutrum.
Curabitur vulputate vestibulum lorem.
Nullam eu ante vel est convallis dignissim.
Sed bibendum.
Aliquam posuere.

Let's learn how to read the conflict:

<<<<<<< conflict 1 of 1
%%%%%%% diff from: vrzponzu b12f7845 "Base of merge-a and merge-b"
\\\\\\\        to: mvxxwqoo e1acf668
-We will create a conflict here.
+We will create a conflict here: merge-a added this text.
+++++++ nzsunxwp 0004a47a
We will create a conflict here: merge-b added this text.
>>>>>>> conflict 1 of 1 ends

Interpreting Conflicts

The first and the last lines are obviously the conflict, with the total number of conflicts:

<<<<<<< conflict 1 of 1

>>>>>>> conflict 1 of 1 ends

All the lines above and below those boundaries are fine: both parents agree on them.
Between those 2 markers, you will find 2 elements:

  1. How the 1st Change wanted to change the common Base.
  2. The content of the 2nd Change.

Look:

%%%%%%% diff from: vrzponzu b12f7845 "Base of merge-a and merge-b"
\\\\\\\        to: mvxxwqoo e1acf668
-We will create a conflict here.
+We will create a conflict here: merge-a added this text.

Read it as:

%%%%%%% diff from: v "Base of merge-a and merge-b"       <- from the common Base
\\\\\\\        to: mvxxwqoo e1acf668                     <- the first Change merge-a
-We will create a conflict here.                         <- wants to apply this diff.
+We will create a conflict here: merge-a added this text.

Of course, - means deleting a line, + means replacing the line with some other content.

Resolving The Conflict

Here's a possible resolution:

Sed id ligula quis est convallis tempor.
We will create a conflict here: merge-a added this text, merge-b added this text.
Nullam rutrum.

*Very* interestingly, if you run jj st, you will see this message:

Hint: Conflict in parent commit has been resolved in working copy

Cool! That's how Jujutsu tells you:

Hey, pal! If you squash back this change, you fix the conflict.

Let's do that

jj squash

Perfect, it's all clear!
What about the Buffer Change? No worries, it's an empty, messageless Change. It will be automatically abandoned once you move away; it was a temporary working area from the start.

Conflict Resolutions Propagate

Look how beautiful:

@  l        empty
○  zm       My parent is conflicted
○  zo       Merge conflict
├─╮
│ ○  nz        merge-b
○ │  m         merge-a
├─╯
○  vr       Base of merge-a and merge-b
│
~

Not only is zo clear: the conflict resolution propagated to zm and to @ too.

How cool is that?

Resolutions All The Way Down

That's a general rule. If you build on top of a conflict:

×  j     <- also in conflict
×  i     <- also in conflict
×  h     <- also in conflict
×  g     <- also in conflict
×  f     <- also in conflict
×  e     <- also in conflict
×  d     <- conflict
○  c
○  b
○  a

The moment you fix the conflict somewhere, the resolution will be propagated. Say you fixed the conflict in f, you'd get:

○  j
○  i
○  h
○  g
○  f     <- resolved here
×  e     <- conflict
×  d     <- conflict
○  c
○  b
○  a

Of course, ideally you'd fix the conflict where it originated. In this case, since you already have a resolution in f you can jj squash it to d:

○  j
○  i
○  h
○  g
○  f
○  e
○  d
○  c
○  b
○  a

I hope you got the idea.
Ready to see some more challenging cases?