Skip to content

Design: separate a stacked PR's merge target from its base #157

Description

@bmcdonnell-fb

From Overview / What Is a Stack?:

A pull request stack consists of two or more pull requests in the same repository where:

  • The first (bottom) pull request targets the main branch (e.g., main).
  • Each subsequent pull request targets the branch of the PR below it.
   ┌── feat/frontend     → PR #3 (base: feat/api-endpoints)  ← top
  ┌── feat/api-endpoints → PR #2 (base: feat/auth-layer)
 ┌── feat/auth-layer     → PR #1 (base: main)               ← bottom
main (trunk)

[endquote]

Merging to the base branch is fundamentally wrong for this. In your example here, api-endpoints is not logically part of auth-layer, so should not merge to it. You should separate "base" and "target" (e.g. main, super-feature, etc). Normally all branches in a stack will merge to the same target.

You already track "target" as distinct from base - your own Overview states each PR is evaluated for rules and protections using "its final target branch (e.g., main), not the branch it directly targets." So the concept exists; it's just confined to rule evaluation and discarded everywhere else. Promote it to a first-class merge destination.

Result should be something like:

$ git log --all --graph --decorate --pretty=oneline --abbrev-commit
*   043bf03 (HEAD -> main) Merge pull request #2 from username/feat/2
|\
| * 88624f8 (feat/2) Feature 2
* | 39f3460 Merge pull request #1 from username/feat/1
|\|
| * 0f1e961 (feat/1) Feature 1
|/
* 04a9dac Initial commit

Related, I made the same point to Git Butler, but they're constrained by the existing GitHub implementation. But you are GitHub, now deciding the next implementation - not constrained.

Along with this, the implementation should display and enforce "below" branches in the stack as prerequisites. feat/2 should be blocked from merging until feat/1 has merged. You'll have to make this explicit, instead of just a side effect of merging to the base branches.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions