sameenkarim
Joined 14 years ago
Joined 14 years ago
We will support a stack that is fully contained within a single fork, where the entire stack targets the original repo.
For example, a contributor who has a fork (user/buzz) of the original repo (org/buzz) could create the following stack:
``` frontend → PR #3 (base: user/buzz:api-endpoints) api-endpoints → PR #2 (base: user/buzz:auth-layer) auth-layer → PR #1 (base: org/buzz:main) org/buzz:main (trunk) ```
For merging, we have an API but had to move it to a new async method: https://github.github.io/gh-stack/reference/merge-api/
The legacy API was fully synchronous, and since stacks of multiple PRs can often take more than 10s (our global timeout), we had to move to async.
We've had some folks already use this to integrate stacks into their merge queues. The great part is you can land multiple PRs in one atomic operation, and then there's one push to main with all your commits from multiple PRs. So instead of having to rerun the build/deploy for each, it can trigger for the last commit that contains all of the changes.
There's an internal system we have called CPRMC (Create Pull Request Merge Commit) that is used to evaluate whether a PR is "ready" to merge. This covers everything from mergeability (checking for merge conflicts) to rule evaluations (ensuring that approvals match the potential commit that will be created by merge) and more.
This becomes particularly difficult when squash merging a stack of multiple PRs because we have to calculate a series of squashed commits, then associate those back to the rules/reviews. This is relatively easy for the first PR, but for the second PR onwards this gets more complicated because the ancestor commits are squashed and don't exist on the branch as-is. And I won't get into how much more complicated it gets for multi-parent situations lol.
It's something we need to fix and it's the top priority for the team. Our numbers show that 99% of stack merges go through successfully, but we need to get that much higher.
Thank you for being an early user in the preview and bearing with us while we work out these issues!
Excited to release this more broadly so anyone can start stacking: https://gh.io/stacks
Would love to hear any feedback, especially with the UI and CLI. We've got a lot more updates to the PR experience in store!
Also happy to answer questions about the design decisions we made. There's a bunch happening behind the scenes, and it's one of the largest launches in GitHub history covering almost every service from Actions and protection rules to the CLI and mobile apps.
Stacks require users to explicitly indicate that they are opening a PR that should be part of a stack.
git rebase --onto <new_commit_sha_generated_by_squash> <original_commit_sha_from_tip_of_merged_branch> <branch_name>
So for ex in this scenario: PR1: main <- A, B (branch1)
PR2: main <- A, B, C, D (branch2)
PR3: main <- A, B, C, D, E, F (branch3)
When PR 1 and 2 are squash merged, main now looks like: S1 (squash of A+B), S2 (squash of C+D)
Then we run the following: git rebase --onto S2 D branch3
Which rewrites branch3 to: S1, S2, E, F
This operation moves the unique commits from the unmerged branch and replays them on top of the newly squashed commits on the base branch, avoiding any merge conflicts.Everyone will have their own way of structuring stacks, but I've found it great for the agent to plan a stack structure that mirrors the work to be done.
You can also run a combination of these. For ex, use another tool like jj to develop locally, push up the branches, and use the gh CLI to batch create a stack of n PRs, without touching local state.
As we have it designed currently, you would have to wait for CI to pass on the bottom two and then you can merge the bottom two in one step. The top of the stack would then get rebased, which will likely trigger another CI run.
Thanks for the callout - we'll update those docs to make it clear multiple PRs can be merged at once.