WordPress / WordPress/contributor-toolkit

The conflicts the app shows are the conflicts Git would show

Open
#351 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement real-world-git
Dominant language
JavaScript
Stars
36
Forks
13
Avg merge
23h 19m
Merged PRs (30d)
72

Description

Updated 15 September 2026. Written before #364 was decided. The bundled Git is now a real Git binary, merge-tree is in the code, and the pull-request half shipped in v1.1.0-beta.2 by a different route. The spikes below are settled and the remaining scope is one decision, stated at the end.

The gap

When the app refuses a patch, its conflict report agrees with nobody. The same pull request produces one conflict count in the app, a different one on GitHub, and a third on a contributor's own command line — not because any of them is buggy, but because the app answers a different question. Its apply matches diff regions by neighbourhood against today's checkout, with no base; Git merges three ways from a common ancestor. A region whose own lines are untouched fails because a neighbour changed. #226 made those failures well-described; this issue makes them stop being wrong.

Under #350's contract this is the piece with the strictest acceptance bar: the conflicts the app shows are the conflicts Git would show. Same files, same regions. A contributor who takes the app's conflict report to their mentor, or to the pull request's author, must be describing the same problem Git describes.

The idea

Replace the two-way apply, for anything that has a base, with a real three-way merge: the common ancestor from history, the contributor's side, and the incoming side, merged by the bundled Git implementation, writing standard conflict markers into the files where the sides genuinely disagree. A clean three-way result is applied; a conflicted one is presented as Git presents it — in the tree, resolvable, honest.

This engine is shared. Trunk updates evaluating standing work, pull request branches brought forward, Trac attachments applied against the trunk they were written on — every flow that today either force-resets or refuses becomes a caller of the same merge.

Spikes first — settled

#364 was decided in favour of shipping a real Git binary, which retires the first two questions by construction, and the third is answered by the clone. Recorded rather than deleted, so the reasoning stays readable:

  • Markers reach the working tree on conflict, and the index state. Moot: the engine is Git.
  • Fidelity against real cases. Moot: the engine is Git, so fidelity is identity.
  • The merge base, and the fallback where the clone lacks it. Answered: a new site clones the whole history without old file contents (#403), so the base comes from the repository and no fallback is needed. mergeTree passes it explicitly as --merge-base.

Where the engine already is

git merge-tree --write-tree -z --merge-base= is in src/git-read.cjs, and ticket-branches.js calls it to decide whether ticket work still fits a moved trunk (#418). Its verdict is Git's verdict, for that flow.

Two of the three callers this issue named are also settled, by routes other than the one proposed here:

  • A pull request no longer reaches the checkout as a diff at all. It is fetched as its own branch (#458, shipped in v1.1.0-beta.2), so there is no two-way apply left to make honest.
  • A Trac attachment is still applied with git apply --whitespace=nowarn -p1 — no base, no three-way. It is Git's own verdict rather than a neighbourhood match, which is most of what this issue was asking for, but it is not a merge. #356 is where that construction is retired.

What is actually left

One question, and it is a judgement call rather than work: is the bar met? "The conflicts the app shows are the conflicts Git would show" is satisfied wherever the app now speaks, because Git is what speaks. If that is enough, this closes and #356 carries the remainder. If the bar meant specifically a three-way merge everywhere a base exists, it stays open and is one step: git apply --3way for attachments that carry a base.

Deliberately not proposed

Byte-identical output to Git's diff machinery. Git's merge uses heuristics that shift hunk boundaries in pathological cases; matching its verdicts — which files, which regions — is the bar. Matching its bytes is not, and chasing it would be a treadmill.

Related

  • #350 — the contract this enforces
  • #226 — the diagnosis this succeeds; its recorded discussion designed most of this
  • #290 — the first caller
  • #349 — release branches, another eventual caller

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading src/git-read.cjs and ticket-branches.js to understand the existing merge-tree integration. Review #356 and the settled work described for #364, #403, #418, and #458. Done means deciding whether Git-backed conflict reporting already meets the stated bar; any remaining attachment merge work belongs in #356.

Written by the indexing model from the issue text.

Assessment

Tech stack
git, javascript
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.