PricingSign inDownload

Blog / git

Two agents changed the same code: resolve the merge conflict

Two coding agents on two branches edited the same function. How to see what each meant, let an agent merge both, and check its work before you commit.

On this page
  1. The setup
  2. Show the base, not just both sides
  3. See what each agent meant
  4. Let the agent merge both
  5. Check the merge with one command
  6. Fewer conflicts next time
  7. Where Joxo fits
  8. Frequently asked questions
  9. How do I see the original code in a git merge conflict?
  10. Can Claude Code resolve merge conflicts?
  11. How do I review how a merge conflict was resolved?
  12. How do I undo a merge that went wrong?
  13. Try it with your team

You ran two agents in parallel, each on its own branch. One added a discount to the checkout total. The other made quantity optional and rounded the total to cents. Both are good changes. Both touched the same three lines. The second merge stops with a conflict.

Merge conflicts between agents are harder than between people, because there's nobody to ask what they meant. The commit message and the diff are all you have. This post shows how to get the most out of those, how to have an agent do the merge, and the one command that shows you exactly what it decided.

Everything below was run in a scratch repo with git 2.43 and Claude Code 2.1.318.

The setup#

Both branches start from this:

js
function total(items) {
  let sum = 0;
  for (const item of items) sum += item.price * item.qty;
  return sum;
}

Branch agent-a added a discount parameter and a test for it. Branch agent-b used item.qty ?? 1, rounded with Math.round(sum * 100) / 100, and added a test for 0.1 + 0.2. We merged agent-a into main, then agent-b:

CONFLICT (content): Merge conflict in price.js
CONFLICT (content): Merge conflict in price.test.js
Automatic merge failed; fix conflicts and then commit the result.

Show the base, not just both sides#

By default git shows only the two sides of a conflict. That hides the most useful fact: what the code looked like before either agent touched it. Turn on the three-way view, then redraw the conflicts you already have:

bash
git config merge.conflictStyle zdiff3
git checkout --conflict=zdiff3 price.js price.test.js
function total(items, discount = 0) {
  let sum = 0;
<<<<<<< ours
  for (const item of items) sum += item.price * item.qty;
  return sum * (1 - discount);
||||||| base
  for (const item of items) sum += item.price * item.qty;
  return sum;
=======
  for (const item of items) sum += item.price * (item.qty ?? 1);
  return Math.round(sum * 100) / 100;
>>>>>>> theirs
}

Now each change reads as a change. Ours left the loop alone and multiplied the result by (1 - discount). Theirs changed the loop and wrapped the result in a rounding. Both can be kept. Without the base section you would just see two different loops and two different return lines, and it would look like a choice.

zdiff3 needs git 2.35 or later. On older versions use diff3, which does the same with a little more repeated text.

See what each agent meant#

List only the commits that touched the conflicting files, from both sides:

bash
git log --merge --oneline
d5dded9 Add discount to total
7169d30 Default qty to 1 and round to cents

git log --merge -p adds each one's diff. This is where good commit messages from your agents pay off. If yours write "update price.js", add a line to your instruction file asking them to say why in every commit message, because the next merge depends on it.

Let the agent merge both#

With the base and the intent in view, the merge is easy to describe. Give the agent both, and keep the commit for yourself:

bash
claude -p "This repo is mid-merge with conflicts. Both sides are wanted. Read each side's commit with git log --merge -p, resolve every conflict so both changes work together, run node price.test.js, and stop before committing. Then list, one line per file, what you kept from each side." --allowedTools "Bash,Read,Edit"

It resolved both files, ran the tests, staged the files and reported:

- price.js
  - From ours (discount commit): the discount = 0 parameter and the (1 - discount) multiplier.
  - From theirs (qty/rounding commit): item.qty ?? 1 and rounding to cents. I round after
    applying the discount, so the final total is what gets rounded.
- price.test.js
  - From ours: the total(..., 0.5) === 10 discount assertion.
  - From theirs: the 0.1 + 0.2 === 0.3 rounding assertion with the default qty.

Notice the one real decision in there: round after the discount, not before. Neither branch made that choice, because neither knew about the other. The merge is where it gets made, so it's what you should check.

Three parts of that prompt do the work:

  • "Both sides are wanted." Without it, an agent may pick one side and drop the other cleanly, which passes the tests the remaining side brought.
  • "Run the tests." Both sides' tests have to pass on the merged code, not just their own branch.
  • "Stop before committing." You read the result first.

Check the merge with one command#

Reading a merge commit is usually awkward: plain git show on a merge prints a combined diff with two columns of + and - against both parents. Git 2.36 added a view made for this. After you commit, run:

bash
git show --remerge-diff HEAD
-<<<<<<< d5dded9 (Add discount to total)
-  for (const item of items) sum += item.price * item.qty;
-  return sum * (1 - discount);
-||||||| cd76a23
-  for (const item of items) sum += item.price * item.qty;
-  return sum;
-=======
   for (const item of items) sum += item.price * (item.qty ?? 1);
-  return Math.round(sum * 100) / 100;
->>>>>>> 7169d30 (Default qty to 1 and round to cents)
+  return Math.round(sum * (1 - discount) * 100) / 100;

It replays the merge with its conflicts and diffs that against what was committed, so you see only the decisions: what was dropped from each side, and the one new line written to join them. It works on anyone's merge commit, so use it when reviewing a teammate's agent's merge too.

If something's wrong before you've committed, git merge --abort puts everything back as it was before the merge.

Fewer conflicts next time#

Conflicts between agents mostly come from two of them being sent at the same code without knowing it. A few habits cut them down:

  • Merge one branch, then update the other before the second agent finishes. A small conflict early beats a big one at the end.
  • Give agents different files, not different features, when the features meet in one function.
  • Say who's on what. A short shared note listing which agent owns which part of the code lets the second agent see the first one's claim before it starts.

Where Joxo fits#

Joxo shows every agent on a project who else is working in the same files, so the second agent hears about the first before it writes, not at merge time. Setup is one line pasted into your agent.

Frequently asked questions#

How do I see the original code in a git merge conflict?#

Set git config merge.conflictStyle zdiff3. Conflicts then show a base section between the two sides. For conflicts already in your files, redraw them with git checkout --conflict=zdiff3 <file>.

Can Claude Code resolve merge conflicts?#

Yes. Tell it both sides are wanted, have it read git log --merge -p for the intent, run the tests, and stop before committing so you can check its choices.

How do I review how a merge conflict was resolved?#

Run git show --remerge-diff <merge commit>. It shows only what the person or agent changed while resolving: what was dropped from each side and any new lines.

How do I undo a merge that went wrong?#

Before committing, git merge --abort returns to the state before the merge. After committing, and before pushing, git reset --hard ORIG_HEAD does the same.