Skip to content

ci-mode: refuse to squash a pull request that merges in another line of history - #10

Merged
georgeh0 merged 1 commit into
mainfrom
merge-pr-guard
Oct 24, 2026
Merged

georgeh0 merged 1 commit into
mainfrom
merge-pr-guard

Conversation

@georgeh0

Copy link
Copy Markdown
Member

Summary

The queue squashes, so an upstream merge must land as a merge commit around it (CI.md § Merge commits). Until now nothing enforced that, and cocoindex-plus#541, #549 and #557 were squashed (cocoindex-io/cocoindex-plus#561). This makes the queue refuse such a PR on its own: no label, nothing to remember.

  • ci-mode, on a merge group: fetches the queued PR's history and its base, commits only (--filter=tree:0; about 3 MB and 2 s for cocoindex-plus). It fails when the PR contains a merge whose first parent is on the base and whose second parent is not. That merge brings in a line of history that a squash would drop. The check fails before any test job, so the queue removes the PR cheaply. It needs no new permission (contents: read). If the check cannot run, the merge group fails.
  • CI.md § Merge commits: states the rule and the shapes that pass.
  • apply_rulesets.py: enables merge methods before applying the rulesets and disables them after. A merge-queue rule may only name a method the repository allows, so Merge queue: land every pull request as a merge commit; allow only merge commits #8's order failed with HTTP 422 when switching back to SQUASH. Already applied from this branch to restore cocoindex-plus; --check is clean.

Test plan

  • The step script, against real cocoindex-plus PRs with the bases they had when they landed:
    • Fail: #530, #541, #549, #557 and #565 (all OSS merges), and #550 (stacked on #549 before #549's merge commit landed).
    • Pass: #550 on a base that has #549's merge commit, and #556, #563 and #566.
  • Synthetic shapes: a pull-style merge passes; main merged into a feature branch passes; an unlanded branch merged onto a fresh branch fails; a missing PR ref fails.
  • shellcheck, pytest scripts, conformance.
  • Live after v1 moves: a merge-shaped scratch PR must leave the queue, and a normal PR must land squashed.

🤖 Generated with Claude Code

…of history

The queue squashes, and a squash keeps only a merge's first parent, so an
upstream merge must land as a merge commit around the queue (CI.md, Merge
commits). Three cocoindex-plus merge PRs went through the queue anyway and
lost their upstream parents (#541, #549, #557; cocoindex-plus#561).

On a merge_group event, ci-mode now fetches the queued pull request's
history and its base, commits only (`--filter=tree:0`, about 3 MB and 2 s
for cocoindex-plus), and fails when the pull request contains a merge whose
first parent is on the base and whose second parent is not. It needs no
label and no new permission (`contents: read`). It fails before any test
job, so the queue removes the pull request cheaply. Merging the base into a
branch, or a pull-style merge of a branch with itself, passes. A failed
lookup fails the merge group.

Checked on real cocoindex-plus pull requests with the bases they had:
#530, #541, #549, #557, #565 (OSS merges) and #550 (stacked on #549's
unlanded merge) fail; #550 on a base with #549's merge commit, #556, #563
and #566 pass; synthetic pull-style, base-merged and unlanded-branch shapes
behave as described.

apply_rulesets.py also enables merge methods before it applies the rulesets
and disables them after: a merge-queue rule may only name a method the
repository allows, so #8's order failed going back to SQUASH (HTTP 422).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@georgeh0
georgeh0 merged commit c8a77d8 into main Oct 24, 2026
2 checks passed
@georgeh0
georgeh0 deleted the merge-pr-guard branch October 24, 2026 22:53
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