Skip to content

Merge queue: land every pull request as a merge commit; allow only merge commits - #8

Merged
georgeh0 merged 1 commit into
mainfrom
queue-merge-commits
Oct 24, 2026
Merged

georgeh0 merged 1 commit into
mainfrom
queue-merge-commits

Conversation

@georgeh0

Copy link
Copy Markdown
Member

Summary

One formal path for every pull request, with no special case for upstream merges (cocoindex-io/cocoindex-plus#561).

  • The queue lands every pull request as a merge commit (rulesets/repo/merge-queue.json: merge_method: MERGE). A pull request keeps its own history, so an upstream merge keeps its upstream parent and goes through the queue like any change, tested by the queue's full run.
  • A queue repository allows only merge commits. apply_rulesets.py now also sets the merge settings from rulesets/repo-settings.json: squash and rebase off, merge commits titled with the PR's title, the PR's body as the message. No GitHub path can squash a pull request, not even an admin's.
  • No bypass for merge commits. Admins bypass only for incidents, with --merge.
  • ci-mode goes back to its version before ci-mode: fail the merge group of a pull request labelled merge-commit #7: with nothing able to squash, the merge-commit label guard has nothing left to guard.
  • CI.md: the queue section, § Merge commits (read main with git log --first-parent, revert with -m 1), the incident bypass, § Enforcement, and the behavior verified in cocoindex-plus today.

Trade-off: a feature PR's branch commits now sit behind the second parent of its merge commit. First-parent history still has one commit per PR, titled <title> (#N) as before.

Already applied to cocoindex-plus with apply_rulesets.py; --check is clean. After merging, move v1 (ci-mode changed).

Test plan

  • pytest scripts (20 passed), ruff, conformance on the starter workflow.
  • apply_rulesets.py --check reported exactly the planned drift (merge_method, allow_squash_merge, the merge commit title and message), then applied it, then reported none.
  • In cocoindex-plus: the OSS merge #565 went through the queue and landed as 0a20e997 (parents 206df9b9, 27d78f4b). bcc8b61e from oss/main is an ancestor of main, and the subject is the PR title with (#565). #566 landed the same way. An admin gh pr merge --squash --admin and --rebase --admin on scratch PR #567 were refused ("Squash merges are not allowed on this repository").

🤖 Generated with Claude Code

…rge commits

An upstream merge must keep its upstream parent. With the queue squashing,
it had to bypass the queue with an admin merge, and twice it went through
the queue instead and lost its parent (cocoindex-plus#549, #557; #561).
#7 then made ci-mode refuse a labelled pull request,
but that still left a special path to remember.

Now there is one path. The queue merges with merge commits
(rulesets/repo/merge-queue.json), and apply_rulesets.py also sets the
merge settings of every queue repository from rulesets/repo-settings.json:
merge commits only, titled with the pull request's title. A pull request
keeps its history, so an upstream merge goes through the queue like any
change, and no merge method can squash it. Admins bypass only for
incidents, with --merge.

- ci-mode: back to the version before #7; the label guard has nothing
  left to guard.
- CI.md: the queue section, Merge commits (first-parent reading, revert
  with -m 1), the incident bypass, Enforcement, and what was verified in
  cocoindex-plus on 2026-10-24.

Already applied to cocoindex-plus with apply_rulesets.py; --check is clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@georgeh0
georgeh0 merged commit 1de23ee into main Oct 24, 2026
2 checks passed
@georgeh0
georgeh0 deleted the queue-merge-commits branch October 24, 2026 20:05
georgeh0 added a commit that referenced this pull request Oct 24, 2026
…#9)

#8 switched the queue to merge commits and disallowed squash, which changed
how every pull request lands. That was not approved: regular pull requests
squash. This restores the queue's SQUASH method and cocoindex-plus's merge
settings as they were before #8 (squash and merge commits allowed, no
rebase, merge commits titled by GitHub's default message), and keeps them
as code in rulesets/repo-settings.json so apply_rulesets.py restores them.

CI.md goes back to the squash queue with the admin merge for a pull request
that must keep its merge commit. A guard that keeps such a pull request out
of the queue follows separately.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
georgeh0 added a commit that referenced this pull request Oct 24, 2026
…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 added a commit that referenced this pull request Oct 24, 2026
…of history (#10)

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>
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