Skip to content

Support parallel execution of merge queue for PRs in the same stack #281

@joshlf

Description

@joshlf

Gemini's take:


Based on your goal to parallelize the Merge Queue for stacked PRs while enforcing dependency failures, I have brainstormed two designs.

The core constraint of GitHub's Merge Queue is that a PR must target the queue's branch (e.g., main) to enter. Therefore, the "Cascading" model (B targets A) must be temporarily abandoned during the merge phase.

Design 1: Just-in-Time (JIT) Retargeting with Sentinel Checks

This design modifies the lifecycle of a GHerrit stack to allow all PRs to target main strictly for the purpose of merging, while adding a safety mechanism to prevent broken chains.

1. The Strategy

Instead of waiting for PR A to land before retargeting B, GHerrit will retarget the entire stack to main at the moment of enqueueing.

  • Review Phase: Keep current behavior. A targets main, B targets A.
  • Merge Phase: When the user runs gherrit submit (or triggers via comment), GHerrit performs the following simultaneously:
  1. Updates B and C to target main. (Note: Since B physically contains A's commits, this is a valid git operation).
  2. Adds A, B, and C to the Merge Queue.

2. The "Sentinel" Action (Safety Mechanism)

Since B and C now technically target main directly, GitHub treats them as independent. If A fails tests and is ejected, GitHub would normally try to run B on its own. Since B contains A's code (and thus A's bugs), B would eventually fail, but this wastes CI time and is not "fail-fast."

To solve this, inject a lightweight Sentinel GitHub Action that runs on the merge_group event.

  • Logic:
  1. Parse the gherrit-meta tag in the PR body to find the parent PR ID.
  2. Use the GitHub API to check the status of the Parent PR.
  3. Fail Immediately if the Parent PR is NOT merged AND NOT present in the current merge_group.
  • Result: If A fails and is removed, B's Sentinel check sees that A is gone. B fails instantly. C's Sentinel check sees B failed. C fails instantly.

3. Constraints

  • Merge Strategy: You must use "Rebase and Merge" or "Create a Merge Commit" for the repository. You cannot use "Squash and Merge."

  • Why? B contains A's commits. If A is squashed on merge, main receives Commit_A_Squashed. B attempts to land with Commit_A_Original. This results in duplicate commits or conflicts. "Rebase and Merge" ensures git detects the duplicate patch-IDs and applies B cleanly.

  • Diff UX: For a brief moment during the merge queue processing, B will target main and show A's commits in its file diffs. This is acceptable as the PR is already approved and "in flight," but it may look confusing if a user checks the PR link during this window.


Design 2: The "Bundle" (Super-PR) Approach

If the "Squash and Merge" constraint or the complexity of JIT retargeting is too high, this alternative achieves parallelism by collapsing the stack.

1. The Strategy

When the user requests a merge for the stack A -> B -> C:

  1. GHerrit creates a synthetic "Bundle PR" (S) targeting main.
  2. S is pushed to a phantom branch that matches the head of C. (Effectively, S contains A+B+C).
  3. S enters the Merge Queue.
  4. All tests run on S (effectively testing A, B, and C in parallel).

2. Reconciliation

Once S merges:

  • GHerrit detects the merge.
  • GHerrit updates the original PRs (A, B, C) to mark them as "Merged" (or closes them manually) and links them to the landed commit(s).

3. Pros & Cons

  • Pros: Works with Squash and Merge (the whole stack becomes one squash commit, or user can choose). Guarantees atomic success/failure (if A breaks, S breaks, so B and C never land).
  • Cons: Loss of granularity in GitHub's "Merged PRs" list (you see one "Stack Merge" PR instead of 3 individual PRs).

Recommendation

Design 1 is the robust solution that preserves your current "Gerrit-style" granular history and PR/commit 1:1 mapping. It effectively tricks GitHub into parallelizing the tests while the Sentinel Action reimplements the dependency graph that GitHub ignores.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions