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
77 changes: 35 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@ gh stack add auth-layer
gh stack add api-endpoints
# ... make commits ...

# Push all branches and create/update PRs
# Push all branches
gh stack push

# View the stack
gh stack view

# Open a stack of PRs
gh stack submit
```

## How it works
Expand All @@ -55,7 +58,7 @@ main (trunk)

The **bottom** of the stack is the branch closest to the trunk, and the **top** is the branch furthest from it. Each branch inherits from the one below it. Navigation commands (`up`, `down`, `top`, `bottom`) follow this model: `up` moves away from trunk, `down` moves toward it.

When you push, `gh stack` creates one PR per branch. Each PR's base is set to the branch below it in the stack, so reviewers see only the diff for that layer.
When you submit, `gh stack` creates one PR per branch and links them together as a **Stack** on GitHub. Each PR's base is set to the branch below it in the stack, so reviewers see only the diff for that layer.

### Local tracking

Expand Down Expand Up @@ -253,84 +256,74 @@ gh stack sync

### `gh stack push`

Push all branches in the current stack and create or update pull requests.
Push all branches in the current stack to the remote.

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

Pushes every branch to the remote, then for each branch either creates a new PR (with the correct base branch) or updates the base of an existing PR if it has changed. Uses `--force-with-lease` by default to safely update rebased branches.

When creating new PRs, you will be prompted to enter a title for each one. Press Enter to accept the default (branch name), or use `--auto` to skip prompting entirely.
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.

| Flag | Description |
|------|-------------|
| `--auto` | Use auto-generated PR titles without prompting |
| `--draft` | Create new PRs as drafts |
| `--skip-prs` | Push branches without creating or updating PRs |
| `--remote <name>` | Remote to push to (defaults to auto-detected remote) |

**Examples:**

```sh
gh stack push
gh stack push --auto
gh stack push --draft
gh stack push --skip-prs
gh stack push --remote upstream
```

### `gh stack view`
### `gh stack submit`

View the current stack.
Push all branches and create/update PRs and the stack on GitHub.

```
gh stack view [flags]
gh stack submit [flags]
```

Shows all branches in the stack, their ordering, PR links, and the most recent commit with a relative timestamp. Output is piped through a pager (respects `GIT_PAGER`, `PAGER`, or defaults to `less -R`).
Creates a Stacked PR for every branch in the stack, pushing branches to the remote.

After creating PRs, `submit` automatically creates a **Stack** on GitHub to link the PRs together. If the stack already exists on GitHub (e.g., from a previous submit), new PRs will be added to the top of the stack.

When creating new PRs, you will be prompted to enter a title for each one. Press Enter to accept the default (branch name), or use `--auto` to skip prompting entirely.

| Flag | Description |
|------|-------------|
| `-s, --short` | Compact output (branch names only) |
| `--json` | Output stack data as JSON |
| `--auto` | Use auto-generated PR titles without prompting |
| `--draft` | Create new PRs as drafts |
| `--remote <name>` | Remote to push to (defaults to auto-detected remote) |

**Examples:**

```sh
gh stack view
gh stack view --short
gh stack view --json
gh stack submit
gh stack submit --auto
gh stack submit --draft
```

### `gh stack unstack`
### `gh stack view`

Remove a stack from local tracking and optionally delete it on GitHub.
View the current stack.

```
gh stack unstack [branch] [flags]
gh stack view [flags]
```

If no branch is specified, uses the current branch to find the stack. By default, the stack is removed from both local tracking and GitHub. Use `--local` to only remove the local tracking entry.
Shows all branches in the stack, their ordering, PR links, and the most recent commit with a relative timestamp. Output is piped through a pager (respects `GIT_PAGER`, `PAGER`, or defaults to `less -R`).

| Flag | Description |
|------|-------------|
| `--local` | Only delete the stack locally (keep it on GitHub) |

| Argument | Description |
|----------|-------------|
| `[branch]` | A branch in the stack to delete (defaults to the current branch) |
| `-s, --short` | Compact output (branch names only) |
| `--json` | Output stack data as JSON |

**Examples:**

```sh
# Remove the stack from local tracking and GitHub
gh stack unstack

# Only remove local tracking
gh stack unstack --local

# Specify a branch to identify the stack
gh stack unstack feature-auth
gh stack view
gh stack view --short
gh stack view --json
```

### `gh stack merge`
Expand Down Expand Up @@ -399,8 +392,8 @@ gh stack add auth-middleware
gh stack add api-routes
# ... write code, make commits ...

# 4. Push everything and create PRs
gh stack push
# 4. Push everything and create Stacked PRs
gh stack submit

# 5. Reviewer requests changes on the first PR
gh stack bottom
Expand All @@ -409,7 +402,7 @@ gh stack bottom
# 6. Rebase the rest of the stack on top of your fix
gh stack rebase

# 7. Push the updated stack
# 7. Push the updated branches
gh stack push

# 8. When the first PR is merged, sync the stack
Expand Down Expand Up @@ -450,7 +443,7 @@ gh stack add -Am "Frontend components"
# → feat/02 already has commits, creates feat/03 and commits there

# 7. Push everything and create PRs
gh stack push
gh stack submit
```

Compared to the typical workflow, there's no need to name branches, run `git add`, or run `git commit` separately. Each `gh stack add -Am "..."` does it all.
Expand Down
2 changes: 1 addition & 1 deletion cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ func runInit(cfg *config.Config, opts *initOptions) error {
}

cfg.Printf("To add a new layer to your stack, run `%s`", cfg.ColorCyan("gh stack add"))
cfg.Printf("When you're ready to push to GitHub and open a stack of PRs, run `%s`", cfg.ColorCyan("gh stack push"))
cfg.Printf("When you're ready to push to GitHub and open a stack of PRs, run `%s`", cfg.ColorCyan("gh stack submit"))

return nil
}
2 changes: 1 addition & 1 deletion cmd/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func runMerge(cfg *config.Config, target string) error {

if br.PullRequest == nil {
cfg.Errorf("no pull request found for branch %q", br.Branch)
cfg.Printf(" Run %s to create PRs for this stack.", cfg.ColorCyan("gh stack push"))
cfg.Printf(" Run %s to create PRs for this stack.", cfg.ColorCyan("gh stack submit"))
return ErrSilent
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/merge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestMerge_NoPullRequest(t *testing.T) {

assert.ErrorIs(t, err, ErrSilent)
assert.Contains(t, output, "no pull request found")
assert.Contains(t, output, "gh stack push")
assert.Contains(t, output, "gh stack submit")
}

func TestMerge_AlreadyMerged(t *testing.T) {
Expand Down
Loading
Loading