Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,13 @@ gh stack sync --prune

### `gh stack push`

Push all branches in the current stack to the remote.
Push active branches in the current stack to the remote.

```
gh stack push [flags]
```

Pushes every branch to the remote using `--force-with-lease --atomic`. This is a lightweight wrapper around `git push` that knows about all branches in the stack. It does not create or update pull requests — use `gh stack submit` for that.
Pushes every active branch (excluding merged and queued branches) in one `git push` using explicit per-branch `--force-with-lease` checks. The update is not atomic: branches whose leases pass may update even if another branch is rejected. Fix the rejected branch and rerun the command; branches already updated will be unchanged. This command does not create or update pull requests — use `gh stack submit` for that.

| Flag | Description |
|------|-------------|
Expand Down
17 changes: 9 additions & 8 deletions cmd/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ func PushCmd(cfg *config.Config) *cobra.Command {

cmd := &cobra.Command{
Use: "push",
Short: "Push all branches in the current stack to the remote",
Long: `Push all branches in the current stack to the remote.

Uses --force-with-lease and --atomic to ensure safe, all-or-nothing pushes.
Merged and queued branches are automatically skipped. This command is safe to
run repeatedly — it will only update branches that have changed.`,
Example: ` # Push all stack branches to the default remote
Short: "Push active branches in the current stack to the remote",
Long: `Push active branches in the current stack to the remote.

Uses explicit per-branch --force-with-lease checks. Updates are not atomic: a
branch may update even if another branch is rejected. Fix the rejected branch
and run the command again; branches already updated will be unchanged.
Merged and queued branches are automatically skipped.`,
Example: ` # Push active stack branches to the default remote
$ gh stack push

# Push to a specific remote
Expand Down Expand Up @@ -77,7 +78,7 @@ func runPush(cfg *config.Config, opts *pushOptions) error {
}
s := stacks[0]

// Push all active branches atomically
// Push all active branches with explicit per-branch leases.
remote, err := pickRemote(cfg, currentBranch, opts.remote)
if err != nil {
if !errors.Is(err, errInterrupt) {
Expand Down
10 changes: 10 additions & 0 deletions docs/src/content/docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ Use `gh stack add <branch-name>` to add a new branch on top of the current stack

You can also add PRs to an existing stack from the GitHub UI — either a brand-new PR or an already-open PR (via the recommendation banner), added to the top of the stack. See [Adding to an Existing Stack](/gh-stack/guides/ui/#adding-to-an-existing-stack) for details.

### How many PRs can a stack contain?

A stack can contain up to **100 pull requests**. If your work requires more than 100 PRs, split it into multiple stacks.

### How can I modify my stack?

Use `gh stack modify` to restructure a stack. It opens an interactive terminal UI where you can reorder, drop, fold (combine), insert, and rename branches — then applies all changes at once. See the [Restructuring Stacks](/gh-stack/guides/modify/) guide for a full walkthrough.
Expand Down Expand Up @@ -119,6 +123,12 @@ jobs:

See the [Webhooks reference](/gh-stack/reference/webhooks/) for the full details on the `stack` object in webhook payloads, or the [REST API reference](/gh-stack/reference/rest-api/) to read the same object on demand from a pull request.

### Why isn't the `stack` object in my `pull_request.opened` webhook?

A pull request is always **created before it's added to a stack**, so the `pull_request.opened` event never includes the `stack` object — at that moment the PR isn't part of any stack yet. The same is true for any other event that fires before the PR joins a stack.

To learn exactly when a PR becomes part of a stack, listen for the `pull_request` event with the **`stacked`** action. It fires when a PR is added to a stack and carries the `stack` object. See the [`stacked` event](/gh-stack/reference/webhooks/#the-stacked-event) in the Webhooks reference for the full payload.

### How can I optimize CI usage for a stack?

Because a workflow runs for every PR in a stack, a large stack can multiply your CI usage. You can use the `stack` fields to selectively run jobs based on the position of the current PR in the stack.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/guides/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ gh stack push

This is equivalent but distinct from updating your branch using a merge commit. The key difference is that after changing a lower branch, rebase maintains a linear commit history so the unique set of commits on each branch have clean diffs.

`gh stack push` then handles the force push safely via `--force-with-lease --atomic`, ensuring either all branches update or none do.
`gh stack push` then handles the force push with explicit per-branch `--force-with-lease` checks. The multi-branch push is not atomic: branches whose leases pass may update even if another branch is rejected. Fix the rejected branch and rerun the command; branches already updated will be unchanged.

For a simpler all-in-one flow, `gh stack sync` combines fetch, rebase, and push into a single command — useful when you just need to pull in the latest upstream changes:

Expand Down
4 changes: 2 additions & 2 deletions docs/src/content/docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ gh stack rebase --committer-date-is-author-date

### `gh stack push`

Push all branches in the current stack to the remote.
Push active branches in the current stack to the remote.

```sh
gh stack push [flags]
Expand All @@ -404,7 +404,7 @@ gh stack push [flags]
|------|-------------|
| `--remote <name>` | Remote to push to (defaults to auto-detected remote) |

Pushes every branch to the remote using `--force-with-lease --atomic`. This is a lightweight wrapper around `git push` that knows about all branches in the stack. It does not create or update pull requests — use `gh stack submit` for that.
Pushes every active branch (excluding merged and queued branches) in one `git push` using explicit per-branch `--force-with-lease` checks. The update is not atomic: branches whose leases pass may update even if another branch is rejected. Fix the rejected branch and rerun the command; branches already updated will be unchanged. This command does not create or update pull requests — use `gh stack submit` for that.

**Examples:**

Expand Down
8 changes: 5 additions & 3 deletions docs/src/content/docs/reference/webhooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ description: Reference for the stacked action and stack object in pull_request w

When a pull request belongs to a stack, GitHub adds a `stack` property to the `pull_request` object in webhook event payloads. This lets apps and integrations inspect the stack's ultimate target branch — not just the direct parent branch of the PR.

The `stack` object is included in the `pull_request` webhook payload for all [pull request lifecycle events](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request) whenever the pull request is part of a stack.

The `stack` object is included in the `pull_request` webhook payload for [pull request lifecycle events](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request) that fire while the pull request is part of a stack.

:::note[The `opened` event never includes a stack]
A pull request is always **created before it is added to a stack**, so the `pull_request` event with the `opened` action never carries a `stack` object — at that point the PR is not yet part of any stack. Listen for the [`stacked` action](#the-stacked-event) to learn exactly when a PR joins a stack and to receive its `stack` object.
:::

## The `stack` Object

Expand Down Expand Up @@ -54,7 +56,7 @@ The `stack` object is **only present** when the pull request belongs to a stack.

## The `stacked` Event

GitHub delivers the `pull_request` event with the `stacked` action when a pull request is **added to a stack**. Because a PR is created before it joins a stack, this is the event to listen for when you need to know exactly when a PR becomes part of a stack.
GitHub delivers the `pull_request` event with the `stacked` action when a pull request is **added to a stack**. Because a PR is always created before it joins a stack, the `opened` event never includes a `stack` object — the `stacked` action is the event to listen for when you need to know exactly when a PR becomes part of a stack.

| | |
|---|---|
Expand Down
8 changes: 5 additions & 3 deletions skills/gh-stack/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ gh stack add -um "Fix auth bug" auth-fix

### Push branches to remote — `gh stack push`

Push all stack branches to the remote.
Push active stack branches to the remote.

```
gh stack push [flags]
Expand All @@ -512,7 +512,8 @@ gh stack push --remote upstream

**Behavior:**

- Pushes all active (non-merged) branches atomically (`--force-with-lease --atomic`)
- Pushes all active (non-merged, non-queued) branches in one non-atomic multi-ref push with explicit per-branch `--force-with-lease` checks
- Some branches may update if another is rejected; fix the rejected branch and rerun the command
- Does **not** create or update pull requests — use `gh stack submit` for that

**Output (stderr):**
Expand Down Expand Up @@ -541,7 +542,8 @@ gh stack submit --auto --open

**Behavior:**

- Pushes all active (non-merged) branches atomically (`--force-with-lease --atomic`)
- Pushes each active (non-merged, non-queued) branch sequentially with explicit per-branch `--force-with-lease` checks; the overall submit is not atomic
- If a later branch push is rejected, earlier branch pushes and PR updates remain; fix the rejection and rerun the same command
- Creates a new PR for each branch that doesn't have one (base set to the first non-merged ancestor branch)
- After creating PRs, links them together as a **Stack** on GitHub (requires the repository to have stacks enabled)
- If every PR in the stack has already been merged, the stack is complete and can't be extended. `submit` automatically forks your unmerged branches into a **new** stack rooted at the trunk and creates it on GitHub, leaving the merged stack untouched.
Expand Down