Handle merge-commit merges, skip rebase merges with a comment#49
Merged
Conversation
The squash-merge fan-out retargeted every updated child PR onto the target branch and only afterwards pushed the new heads, batched into a single non-atomic push together with the merged-branch deletion. If the push failed (e.g. someone pushed to a child mid-run, rejecting the plain push) or a pr edit died partway through the loop, set -e aborted the run with PRs already retargeted but their heads stale - and unlike the conflict-resume path there is no label to re-trigger the action, so nothing ever repaired them. Apply the ordering the resume path already uses: push the updated heads first, then flip the bases, and delete the merged branch last (deleting a PR's base branch closes the PR, so every child must be off it first). A failed push now leaves the PRs untouched on their old base. The unit test captures the run transcript and asserts the push -> retarget -> delete order; it fails against the previous code. Also corrects the README: pushes are plain, not forced, and branch deletion is its own final step. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
…g-check-3njvdx # Conflicts: # README.md
Since #40 the conflict comment's fast-forward step reads `git merge --ff-only origin/<branch>`, which assert_conflict_comment_merges picks up with its `^git merge` grep, so the extracted commands never match the expected conflict merges. Skip the --ff-only line when extracting. Also trim the new comments in the fan-out push/retarget/delete sequence. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
The fix for the --ff-only line breaking assert_conflict_comment_merges moved to a separate PR; the e2e job here stays red until that lands and main is merged back in. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
A head branch can carry several PRs (one per base), so gh calls keyed by branch name can comment, label, or retarget the wrong one. Every gh call that acts on a specific PR now uses the PR number: the fan-out carries number/branch pairs from gh pr list, and the conflict-resolved run gets PR_NUMBER from the event payload via action.yml. The payload also already carries the PR's base branch, so the resume takes it from a new PR_BASE variable instead of querying the API; the resume test's gh mock no longer answers baseRefName queries, so a reintroduced lookup fails loudly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
…ber-addressing-3njvdx
The action triggers on any merged PR, but its merge sequence assumes the squash shape: SQUASH_COMMIT~ as "target just before the merge" and an -s ours record of the squash. A real merge commit satisfies neither - and needs no fixing at all, since history is not rewritten and stacked children keep correct diffs. Detect the second parent and bail out before touching anything. Rebase merges remain indistinguishable from squashes in the payload and are processed as before. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX
A full skip left child PRs based on the deleted-but-not-deleted merged branch forever: nothing retargeted them and nothing deleted the branch, the two things the action exists to automate. The children's heads need no rewriting (the merge commit carries the parent's commits into the target branch), so retarget them as-is and delete the branch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GitHub records the merge method nowhere (payload, REST, GraphQL), so detect it: a rebase leaves a patch-equivalent copy of every PR commit on the target, a multi-commit squash does not. Single-commit PRs merge identically either way and keep taking the squash path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the patch-id heuristic: ask which PR introduced the commit just below the merge sha. An older PR or none means squash; this PR means a rebase copy. The association is computed asynchronously, so an empty answer is only trusted once the merge sha itself is associated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The squash sequence assumes
SQUASH_COMMIT~is the target just before the merge, which only holds for squashes. GitHub records the merge method nowhere (payload, REST, GraphQL), so the action infers it and now handles each shape:/commits/{sha}/pulls, "the merged pull request that introduced the commit"). A squash introduces a single commit, so the commit below the merge sha belongs to an older PR or to none; a rebase introduces a copy of each PR commit, so the commit below still belongs to this PR (verified against real merges on a scratch repo). The association is computed asynchronously, ~15-30s after the merge in testing, so an empty answer is only trusted once the merge sha itself is associated; on API failure or timeout the run aborts rather than guessing. Not supported: the copies are new commits, so a child retargeted as-is would show the parent's changes in its diff, and the squash sequence can raise spurious conflicts against the intermediate copies (observed in simulation). The action comments on each child PR that the stack must be updated manually and leaves everything alone, including the merged branch.A PR whose commits are all merge commits would defeat the rebase detection, but GitHub refuses to rebase-merge those (
rebaseable: false, verified on a scratch repo).Stacked on #45.
🤖 Generated with Claude Code
https://claude.ai/code/session_01JHvKryT4QUpHYdNq9YEQxX