Skip to content

Commit d77d2fe

Browse files
committed
update readme
1 parent 5937a75 commit d77d2fe

1 file changed

Lines changed: 357 additions & 1 deletion

File tree

README.md

Lines changed: 357 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,358 @@
11
# gh-stack
2-
A GitHub CLI extension to manage stacked branches and PRs
2+
3+
A GitHub CLI extension for managing stacked branches and pull requests.
4+
5+
Stacked PRs break large changes into a chain of small, reviewable pull requests that build on each other. `gh stack` automates the tedious parts — creating branches, keeping them rebased, setting correct PR base branches, and navigating between layers.
6+
7+
## Installation
8+
9+
```sh
10+
gh extension install github/gh-stack
11+
```
12+
13+
Requires the [GitHub CLI](https://cli.github.com/) (`gh`) v2.0+.
14+
15+
## Quick start
16+
17+
```sh
18+
# Start a new stack from the default branch
19+
gh stack init
20+
21+
# Create the first branch and start working
22+
gh stack add auth-layer
23+
# ... make commits ...
24+
25+
# Add another branch on top
26+
gh stack add api-endpoints
27+
# ... make commits ...
28+
29+
# Push all branches and create/update PRs
30+
gh stack push
31+
32+
# View the stack
33+
gh stack view
34+
```
35+
36+
## How it works
37+
38+
A **stack** is an ordered list of branches where each branch builds on the one below it. The bottom of the stack is based on a **trunk** branch (typically `main`).
39+
40+
```
41+
main (trunk)
42+
└── auth-layer → PR #1 (base: main)
43+
└── api-endpoints → PR #2 (base: auth-layer)
44+
```
45+
46+
When you push, `gh stack` creates one PR per branch. Each PR's base is set to the branch below it in the stack (**branch-chaining**), so reviewers see only the diff for that layer.
47+
48+
### Local tracking
49+
50+
Stack metadata is stored in `.git/gh-stack` (a JSON file, not committed to the repo). This tracks which branches belong to which stack and their ordering. Rebase state during interrupted rebases is stored separately in `.git/gh-stack-rebase-state`.
51+
52+
## Commands
53+
54+
### `gh stack init`
55+
56+
Initialize a new stack in the current repository.
57+
58+
```
59+
gh stack init [branches...] [flags]
60+
```
61+
62+
Creates an entry in `.git/gh-stack` to track stack state. In interactive mode (no arguments), prompts you to name branches and offers to use the current branch as the first layer. When explicit branch names are given, creates any that don't already exist (branching from the trunk). The trunk defaults to the repository's default branch unless overridden with `--base`.
63+
64+
Enables `git rerere` automatically so that conflict resolutions are remembered across rebases.
65+
66+
| Flag | Description |
67+
|------|-------------|
68+
| `-b, --base <branch>` | Trunk branch for the stack (defaults to the repository's default branch) |
69+
| `-a, --adopt` | Adopt existing branches into a stack instead of creating new ones |
70+
71+
**Examples:**
72+
73+
```sh
74+
# Interactive — prompts for branch names
75+
gh stack init
76+
77+
# Non-interactive — specify branches upfront
78+
gh stack init feature-auth feature-api feature-ui
79+
80+
# Use a different trunk branch
81+
gh stack init --base develop feature-auth
82+
83+
# Adopt existing branches into a stack
84+
gh stack init --adopt feature-auth feature-api
85+
```
86+
87+
### `gh stack add`
88+
89+
Add a new branch on top of the current stack.
90+
91+
```
92+
gh stack add [branch]
93+
```
94+
95+
Creates a new branch at the current HEAD, adds it to the top of the stack, and checks it out. Must be run while on the topmost branch of a stack. If no branch name is given, prompts for one.
96+
97+
**Examples:**
98+
99+
```sh
100+
gh stack add api-routes
101+
gh stack add # prompts for name
102+
```
103+
104+
### `gh stack checkout`
105+
106+
Discover and check out an entire stack from a pull request or branch.
107+
108+
```
109+
gh stack checkout <pr-or-branch> [flags]
110+
```
111+
112+
Accepts a PR number, PR URL, or branch name. Traces the chain of PRs to discover the full stack, fetches all branches, and saves the stack to local tracking.
113+
114+
> **Note:** This command is not yet implemented. Running it prints a notice.
115+
116+
| Flag | Description |
117+
|------|-------------|
118+
| `--no-switch` | Fetch and track the stack without switching to the target branch |
119+
120+
**Examples:**
121+
122+
```sh
123+
gh stack checkout 42
124+
gh stack checkout feature-auth
125+
gh stack checkout https://github.com/owner/repo/pull/42
126+
```
127+
128+
### `gh stack rebase`
129+
130+
Pull from remote and do a cascading rebase across the stack.
131+
132+
```
133+
gh stack rebase [branch] [flags]
134+
```
135+
136+
Fetches the latest changes from `origin`, then ensures each branch in the stack has the tip of the previous layer in its commit history. Rebases branches in order from trunk upward. If a branch's PR has been squash-merged, the rebase automatically switches to `--onto` mode to correctly replay commits on top of the merge target.
137+
138+
If a rebase conflict occurs, the operation pauses and prints the conflicted files with line numbers. Resolve the conflicts, stage with `git add`, and continue with `--continue`. To undo the entire rebase, use `--abort` to restore all branches to their pre-rebase state.
139+
140+
| Flag | Description |
141+
|------|-------------|
142+
| `--downstack` | Only rebase branches from trunk to the current branch |
143+
| `--upstack` | Only rebase branches from the current branch to the top |
144+
| `--continue` | Continue the rebase after resolving conflicts |
145+
| `--abort` | Abort the rebase and restore all branches to their pre-rebase state |
146+
147+
| Argument | Description |
148+
|----------|-------------|
149+
| `[branch]` | Target branch (defaults to the current branch) |
150+
151+
**Examples:**
152+
153+
```sh
154+
# Rebase the entire stack
155+
gh stack rebase
156+
157+
# Only rebase branches below the current one
158+
gh stack rebase --downstack
159+
160+
# Only rebase branches above the current one
161+
gh stack rebase --upstack
162+
163+
# After resolving a conflict
164+
gh stack rebase --continue
165+
166+
# Give up and restore everything
167+
gh stack rebase --abort
168+
```
169+
170+
### `gh stack sync`
171+
172+
Fetch, rebase, push, and sync PR state in a single command.
173+
174+
```
175+
gh stack sync
176+
```
177+
178+
Performs a safe, non-interactive synchronization of the entire stack:
179+
180+
1. **Fetch** — fetches the latest changes from `origin`
181+
2. **Fast-forward trunk** — fast-forwards the trunk branch to match the remote (skips if diverged)
182+
3. **Cascade rebase** — rebases all stack branches onto their updated parents (only if trunk moved). If a conflict is detected, all branches are restored to their original state and you are advised to run `gh stack rebase` to resolve conflicts interactively
183+
4. **Push** — pushes all branches (uses `--force-with-lease` if a rebase occurred)
184+
5. **Sync PRs** — syncs PR state from GitHub and reports the status of each PR
185+
186+
**Examples:**
187+
188+
```sh
189+
gh stack sync
190+
```
191+
192+
### `gh stack push`
193+
194+
Push all branches in the current stack and create or update pull requests.
195+
196+
```
197+
gh stack push [flags]
198+
```
199+
200+
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.
201+
202+
| Flag | Description |
203+
|------|-------------|
204+
| `-f, --force` | Force-push branches |
205+
| `--draft` | Create new PRs as drafts |
206+
| `--dry-run` | Show what would be pushed without actually pushing |
207+
208+
**Examples:**
209+
210+
```sh
211+
gh stack push
212+
gh stack push --force
213+
gh stack push --draft
214+
gh stack push --dry-run
215+
```
216+
217+
### `gh stack view`
218+
219+
View the current stack.
220+
221+
```
222+
gh stack view [flags]
223+
```
224+
225+
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`).
226+
227+
| Flag | Description |
228+
|------|-------------|
229+
| `-s, --short` | Compact output (branch names only) |
230+
| `-w, --web` | Open all associated PRs in the browser |
231+
232+
**Examples:**
233+
234+
```sh
235+
gh stack view
236+
gh stack view --short
237+
gh stack view --web
238+
```
239+
240+
### `gh stack unstack`
241+
242+
Remove a stack from local tracking and optionally delete it on GitHub.
243+
244+
```
245+
gh stack unstack [branch] [flags]
246+
```
247+
248+
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.
249+
250+
| Flag | Description |
251+
|------|-------------|
252+
| `--local` | Only delete the stack locally (keep it on GitHub) |
253+
254+
| Argument | Description |
255+
|----------|-------------|
256+
| `[branch]` | A branch in the stack to delete (defaults to the current branch) |
257+
258+
**Examples:**
259+
260+
```sh
261+
# Remove the stack from local tracking and GitHub
262+
gh stack unstack
263+
264+
# Only remove local tracking
265+
gh stack unstack --local
266+
267+
# Specify a branch to identify the stack
268+
gh stack unstack feature-auth
269+
```
270+
271+
### `gh stack merge`
272+
273+
Merge a stack of PRs.
274+
275+
```
276+
gh stack merge <pr>
277+
```
278+
279+
Merges the specified PR and all PRs below it in the stack.
280+
281+
> **Note:** This command is not yet implemented. Running it prints a notice.
282+
283+
### Navigation
284+
285+
Move between branches in the current stack without having to remember branch names.
286+
287+
```sh
288+
gh stack up [n] # Move up n branches (default 1)
289+
gh stack down [n] # Move down n branches (default 1)
290+
gh stack top # Jump to the top of the stack
291+
gh stack bottom # Jump to the bottom of the stack
292+
```
293+
294+
Navigation commands clamp to the bounds of the stack — moving up from the top or down from the bottom is a no-op with a message. If you're on the trunk branch, `up` moves to the first stack branch.
295+
296+
**Examples:**
297+
298+
```sh
299+
gh stack up # move up one layer
300+
gh stack up 3 # move up three layers
301+
gh stack down
302+
gh stack top
303+
gh stack bottom
304+
```
305+
306+
### `gh stack feedback`
307+
308+
Share feedback about gh-stack.
309+
310+
```
311+
gh stack feedback [title]
312+
```
313+
314+
Opens a GitHub Discussion in the [gh-stack repository](https://github.com/github/gh-stack) to submit feedback. Optionally provide a title for the discussion post.
315+
316+
**Examples:**
317+
318+
```sh
319+
gh stack feedback
320+
gh stack feedback "Support for reordering branches"
321+
```
322+
323+
### Placeholder commands
324+
325+
The following commands are planned but not yet implemented. Running them prints a notice and suggests using `gh stack feedback` to share your interest.
326+
327+
`remove` · `modify` · `reorder` · `move` · `fold` · `squash` · `rename` · `split`
328+
329+
## Typical workflow
330+
331+
```sh
332+
# 1. Start a stack
333+
gh stack init
334+
gh stack add auth-middleware
335+
336+
# 2. Work on the first layer
337+
# ... write code, make commits ...
338+
339+
# 3. Add the next layer
340+
gh stack add api-routes
341+
# ... write code, make commits ...
342+
343+
# 4. Push everything and create PRs
344+
gh stack push
345+
346+
# 5. Reviewer requests changes on the first PR
347+
gh stack bottom
348+
# ... make changes, commit ...
349+
350+
# 6. Rebase the rest of the stack on top of your fix
351+
gh stack rebase
352+
353+
# 7. Force-push the updated stack
354+
gh stack push --force
355+
356+
# 8. When the first PR is merged, sync the stack
357+
gh stack sync
358+
```

0 commit comments

Comments
 (0)