WordPress / WordPress/contributor-toolkit
The conflicts the app shows are the conflicts Git would show
Nobody has claimed this yet.
- 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-treeis 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.
mergeTreepasses 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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