Skip to content

Re-parent with the vendored git-merge-onto instead of the merge dance#56

Merged
Phlogistique merged 6 commits into
mainfrom
use-git-merge-onto
Jun 26, 2026
Merged

Re-parent with the vendored git-merge-onto instead of the merge dance#56
Phlogistique merged 6 commits into
mainfrom
use-git-merge-onto

Conversation

@Phlogistique

@Phlogistique Phlogistique commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Re-parents each child PR with a single git-merge-onto call (the merge equivalent of git rebase --onto) instead of the five-command dance update_direct_target used: merge the merged branch, merge the pre-squash target, merge -s ours the squash, commit-tree a three-parent commit, reset. That sequence only ever reconstructed one operation: a merge of the squash commit with the base forced to merge-base(HEAD, merged branch). git-merge-onto is vendored as a zero-dependency file run with python3, so the action needs no network download.

Conflict: the action commits and pushes nothing on a conflict, leaving the head at its pre-conflict commit (still mergeable, so the resuming synchronize event still fires). The pre-push of the clean half is gone, and the comment asks for a single uvx git-merge-onto origin/<trunk> origin/<merged> (re-home onto the live trunk, since any newer-trunk conflicts must be resolved before the PR can merge anyway).

Resume: the continuation no longer re-runs the merge. Re-parenting an already-resolved head against the old-parent base would re-open the hunk the user just fixed, because that forced base predates their resolution. It now checks the pushed head contains the squash, then retargets and drops the label; if it does not, the run fails so the user sees they need to look again. update_direct_target is used only by the squash-merge path now.

Removes the conflict-matrix e2e scenario: with one merge there is no base-vs-trunk distinction, and the "follow-up trunk conflict on continuation" case it covered cannot happen, since the single merge brings the trunk content in the first pass.

Phlogistique and others added 6 commits June 26, 2026 16:06
update_direct_target re-homed each child with five git commands (merge the
merged branch, merge the pre-squash target, merge -s ours the squash,
commit-tree a three-parent commit, reset). That is one re-parent: a merge of
the squash commit with the base forced to merge-base(HEAD, merged branch).
Replace it with a single git-merge-onto call, vendored as a zero-dependency
file run with python3 so the action needs no download.

The two-merge conflict path collapses: on a conflict the action now commits
and pushes nothing (the pre-push of the clean half is gone), and the comment
asks for one `uvx git-merge-onto`. Drops the conflict-matrix e2e scenario,
whose base-vs-trunk distinctions and follow-up-trunk-conflict case only
existed because there were two merges.

git-merge-onto: https://github.com/scortexio/git-merge-onto

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRBBuBCbXvdmtRQiSMixVL
astral-sh/setup-uv publishes no moving `v8` major tag, so `@v8` fails to
resolve. Pin the exact v8.2.0 commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRBBuBCbXvdmtRQiSMixVL
"Merge updates from <target> and squash commit" was vague; the squash
commit lives on the target, and the branch this commit exists because of is
the merged one. Say "<target> and <merged branch>".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRBBuBCbXvdmtRQiSMixVL
The human-facing conflict comment now says
`uvx git-merge-onto origin/<trunk> origin/<merged>` instead of pinning the
squash SHA. If trunk advanced since the parent landed, the user resolves
those conflicts now (they would have to before merging anyway) and gets
readable branch names. The action's own call keeps SQUASH_COMMIT, the stable
pin that keeps the resume a no-op.

Also fix the e2e's get_conflict_comment: it still grepped the old plural
"merge conflicts" heading after the single-merge rewrite made it "a merge
conflict", so it found the (correctly posted) comment as 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRBBuBCbXvdmtRQiSMixVL
continue_after_resolution re-ran the re-parent through update_direct_target to
"confirm" it. But git-merge-onto's forced base is the old parent, where the
lines the user just resolved still differ from the trunk, so the re-merge
re-raised the very conflict they had fixed; the no-op skip never fired because
the conflict (rc=1) precedes it. (Not a merge-onto bug: "theirs is an ancestor"
cannot mean "no-op", or a down-move that must drop the old parent's content
would wrongly skip.)

The resume only needs to confirm the user's pushed head contains the squash,
then retarget and drop the label. Replace the re-merge with that ancestry
check. update_direct_target is now used only by the squash-merge path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRBBuBCbXvdmtRQiSMixVL
If the user pushes without finishing the re-parent, the head lacks the squash;
make the run fail with an actionable message instead of retrying silently, so
they notice and look again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRBBuBCbXvdmtRQiSMixVL
@Phlogistique Phlogistique marked this pull request as ready for review June 26, 2026 15:18
@Phlogistique Phlogistique merged commit ab4f3bc into main Jun 26, 2026
3 checks passed
@github-actions github-actions Bot deleted the use-git-merge-onto branch June 26, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant