diff --git a/README.md b/README.md
index 5b77062..f9a9e68 100644
--- a/README.md
+++ b/README.md
@@ -2,40 +2,14 @@
[](https://github.com/luqven/gh-stack/actions/workflows/ci.yml)
-> This README and tool were originally written by [@timothyandrew](https://github.com/timothyandrew/gh-stack). I highly recommend reading his blog post on stacked-PR workflows [here](https://0xc0d1.com/blog/git-stack/).
+Manage stacked pull requests on GitHub.
-I use this tool to help manage stacked pull requests on Github, which are notoriously difficult to manage manually. Here are a few examples:
+## Features
-- https://0xc0d1.com/blog/git-stack/
-- https://stackoverflow.com/questions/26619478/are-dependent-pull-requests-in-github-possible
-- https://gist.github.com/Jlevyd15/66743bab4982838932dda4f13f2bd02a
-
-This tool assumes that:
-
-- All PRs in a single "stack" all have a unique identifier in their title (I typically use a Jira ticket number for this).
-- All PRs in the stack live in a single GitHub repository.
-- All remote branches that these PRs represent have local branches named identically.
-
-It then looks for all PRs containing this identifier and builds a dependency graph in memory.
-
-With this graph built up, the tool can:
-
-- Add a markdown table to the PR description (idempotently) of each PR in the stack describing _all_ PRs in the stack.
-- Log a simple list of all PRs in the stack (+ dependencies) to stdout.
-- Automatically update the stack + push after making local changes.
-- **Land an entire stack** by squash-merging the topmost approved PR and closing the rest.
-
----
-
-- [gh-stack](#gh-stack)
- - [Installation](#installation)
- - [Usage](#usage)
- - [Examples](#examples)
- - [Strategy](#strategy)
- - [Disclaimer](#disclaimer)
- - [Releasing](#releasing)
- - [Contributors](#contributors)
- - [Credits](#credits)
+- **Visualize** your stack with `gh-stack log`
+- **Annotate** PR descriptions with stack metadata tables
+- **Rebase** entire stacks after local changes
+- **Land** a stack by squash-merging the top PR and closing the rest
## Installation
@@ -44,322 +18,153 @@ brew tap luqven/gh-stack
brew install gh-stack
```
-If you cloned this repository, you can build and install from source with:
+
+Build from source
```bash
-# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
-
-# Configure `PATH`
export PATH="$HOME/.cargo/bin:$PATH"
-
-# Install `gh-stack`
-cd gh-stack
cargo install --force --path .
```
+
-## Usage
-
-> Note: If you don't have a personal access token, you can generate one here:
-> https://github.com/settings/tokens. Give your token `repo` scope permissions.
+## Setup
```bash
-# Set the environment variable for the Github API token
-$ export GHSTACK_OAUTH_TOKEN=''
+export GHSTACK_OAUTH_TOKEN='' # repo scope required
+# Optional: override auto-detected repository
+export GHSTACK_TARGET_REPOSITORY='owner/repo'
+```
-# Optional: The repository is auto-detected from your git remote.
-# You can override it with an environment variable or the -r flag:
-$ export GHSTACK_TARGET_REPOSITORY=''
+You can also set these in a `.gh-stack.env` file.
-# You can also set these in a `.gh-stack.env` file in the project root.
-```
+## Commands
+
+### log
+
+Visualize your stack. [Learn more](docs/log.md)
```bash
-$ gh-stack
+gh-stack log 'STACK-ID'
+gh-stack log 'STACK-ID' --short # compact list view
+```
-USAGE:
- gh-stack
+
+Example output
-FLAGS:
- -h, --help Prints help information
+```
+◉ feat/part-3 (current)
+│ 2 hours ago
+│
+│ a1b2c3d - Add validation logic
+│ f4e5d6c - Update tests
+│
+◯ feat/part-2
+│ 3 hours ago
+│
+│ 1a2b3c4 - Implement core feature
+│
+◯ feat/part-1
+│ 5 hours ago
+│
+│ 9z8y7x6 - Initial scaffolding
+│
+◯ main
+```
+
-SUBCOMMANDS:
- annotate Annotate the descriptions of all PRs in a stack with metadata about all PRs in the stack
- autorebase Rebuild a stack based on changes to local branches and mirror these changes up to the remote
- land Land a stack by squash-merging the topmost approved PR and closing the rest
- log Print a visual tree of all pull requests in a stack
- rebase Print a bash script to STDOUT that can rebase/update the stack (with a little help)
+### annotate
-# Print a description of the stack to stdout (auto-detects repository from git remote)
-$ gh-stack log 'stack-identifier'
+Add a markdown table to each PR description. [Learn more](docs/annotate.md)
-# Idempotently add a markdown table summarizing the stack
-# to the description of each PR in the stack
-$ gh-stack annotate 'stack-identifier'
+```bash
+gh-stack annotate 'STACK-ID'
+gh-stack annotate 'STACK-ID' --badges # shields.io badges (public repos)
+gh-stack annotate 'STACK-ID' --ci # skip confirmation
+```
-# Override auto-detected repository with -r flag
-$ gh-stack annotate 'stack-identifier' -r ''
+
+Example output
-# Same as above, but with a custom title prefix.
-$ gh-stack annotate 'stack-identifier' -r '' --prefix '#'
+This adds a table to each PR description:
-# Same as above, but precede the markdown table with the
-# contents of `filename.txt`.
-$ gh-stack annotate 'stack-identifier' -p filename.txt
+```markdown
+### Stack: STACK-ID
-# Same as above, but with shields.io status badges (requires public repo).
-# By default, annotations use GitHub's native PR autolinking which works
-# with both public and private repositories.
-$ gh-stack annotate 'stack-identifier' --badges
+| PR | Title | Base |
+|:--:|:------|:----:|
+| #103 | [STACK-ID] Add validation | #102 |
+| #102 | [STACK-ID] Implement feature | #101 |
+| #101 | [STACK-ID] Initial scaffolding | main |
+```
-# Automatically update the entire stack, both locally and remotely.
-# WARNING: This operation modifies local branches and force-pushes.
-$ gh-stack autorebase 'stack-identifier' -C /path/to/repo
+GitHub auto-links PR numbers, showing status on hover.
+
-# Same as above, but skips confirmation step.
-$ gh-stack autorebase 'stack-identifier' -C /path/to/repo --ci
+### land
-# Land the entire stack (squash-merges topmost approved PR, closes the rest)
-$ gh-stack land 'stack-identifier'
+Squash-merge the topmost approved PR and close the rest. [Learn more](docs/land.md)
-# Preview what would happen without making changes
-$ gh-stack land 'stack-identifier' --dry-run
+```bash
+gh-stack land 'STACK-ID'
+gh-stack land 'STACK-ID' --dry-run # preview changes
+gh-stack land 'STACK-ID' --count 2 # only land bottom 2 PRs
+gh-stack land 'STACK-ID' --no-approval # skip approval check
+```
-# Skip approval requirement check
-$ gh-stack land 'stack-identifier' --no-approval
+### autorebase
-# Only land the bottom N PRs in the stack
-$ gh-stack land 'stack-identifier' --count 2
+Rebuild and push a stack after local changes. [Learn more](docs/autorebase.md)
-# Emit a bash script that can update a stack in the case of conflicts.
-# WARNING: This script could potentially cause destructive behavior.
-$ gh-stack rebase 'stack-identifier'
+```bash
+gh-stack autorebase 'STACK-ID' -C /path/to/repo
+gh-stack autorebase 'STACK-ID' -C /path/to/repo --ci # skip confirmation
```
-### Examples
-
-_This is a quick overview of the ways this tool could be used in practice._
-
-1. Write some code, create local commits/branches:
-
- ```bash
- $ git checkout -b first
- # Write code
- $ git add -A; git commit -m 'first'
-
- $ git checkout -b second
- # Write code
- $ git add -A; git commit -m 'second #1'
- # Write code
- $ git add -A; git commit -m 'second #2'
-
- $ git checkout -b third
- # Write code
- $ git add -A; git commit -m 'third'
- ```
-
-2. Your Git tree now looks like:
-
- ```bash
- * 42315c4 U - (third) third
- |
- * 6db2c28 U - (second) second #2
- |
- * 5746a83 U - second #1
- |
- * e845ded U - (first) first
- |
- * 8031011 U - initial commit
- ```
-
-3. Push each branch:
-
- ```bash
- $ git push origin first:first second:second third:third
- * [new branch] first -> first
- * [new branch] second -> second
- * [new branch] third -> third
- ```
-
-4. Create a PR for each new branch (starting at `first`), and:
-
- - Ensure that all the PRs have a common identifier in their title (I'll use `[EXAMPLE-17399]` here). ~This identifier (currently) is required to be unique across all GitHub repositories accessible to you (including _all_ public repositories).~
- - It is recommended that you use the `-r` flag to specify the repository you want gh-stack to search for PRs in. Otherwise, gh-stack will search all repositories accessible to you. This can result in matches from multiple repositories that are unrelated to the stack.
- - Set the `base` for each PR to the branch preceding it. Here, `first`'s PR is set to merge into `master`, `second`'s PR is set to merge into `first`, and `third`'s PR is set to merge into `second`.
-
-5. Log all PRs in the stack:
-
- ```bash
- # Tree view (default)
- $ gh-stack log 'EXAMPLE-13799' -r 'example_user/example-repo'
- ◉ third (current)
- │ 2 hours ago
- │
- ◯ second
- │ 2 hours ago
- │
- ◯ first
- │ 2 hours ago
- │
- ◯ master
-
- # Compact list view
- $ gh-stack log 'EXAMPLE-13799' -r 'example_user/example-repo' --short
- #1: [EXAMPLE-13799] PR for branch `first` (Base)
- #2: [EXAMPLE-13799] PR for branch `second` (Merges into #1)
- #3: [EXAMPLE-13799] PR for branch `third` (Merges into #2)
- ```
-
-6. Annotate all PRs with information about the stack:
-
- ```bash
- $ gh-stack annotate 'EXAMPLE-13799' -r 'example_user/example-repo'
- 1: [EXAMPLE-13799] PR for branch `first`
- 2: [EXAMPLE-13799] PR for branch `second`
- 3: [EXAMPLE-13799] PR for branch `third`
- Going to update these PRs ☝️ Type 'yes' to continue: yes
- Done!
- ```
-
- This (idempotently) adds a table like this to the description of every PR in the stack:
-
- ```markdown
- ### Stacked PR Chain: EXAMPLE-13799
- | PR | Title | Merges Into |
- |:--:|:------|:-----------:|
- |#1|[EXAMPLE-13799] PR for branch `first`|-|
- |#2|[EXAMPLE-13799] PR for branch `second`|#1|
- |#3|[EXAMPLE-13799] PR for branch `third`|#2|
- ```
-
- GitHub automatically converts `#1`, `#2`, `#3` to clickable links. Hovering over them shows PR details including current status.
-
- For public repositories, you can use `--badges` to add shields.io status badges:
-
-
-7. Make changes to a branch that rewrites commits in some way (amend, remove a commit, combine commits):
-
- ```bash
- $ git checkout first
- # Do some work
- $ git add -A; git commit --amend -m 'amended first'
- ```
-
- History has now diverged, and this will cause conflicts with dependent PRs when `first` is (force-)pushed.
-
- ```bash
- * e7cb9c6 U - (HEAD -> first) amended first
- |
- | * 42315c4 N - (origin/third, third) third
- | |
- | * 6db2c28 N - (origin/second, second) second #2
- | |
- | * 5746a83 N - second #1
- | |
- | * e845ded N - (origin/first) first
- |/
- |
- * 8031011 U - (origin/master, master) initial commit
- ```
-
-8. Use the `autorebase` subcommand to fix this inconsistency (it requires a path to a local checkout of the repository):
-
- ```bash
- $ gh-stack autorebase --project /tmp/test EXAMPLE-13799
- Checking out Commit { id: 803101159653bf4bf92bf098e577abc436458b17, summary: "initial commit" }
-
- Working on PR: "first"
- Cherry-picking: Commit { id: e7cb9c6cdb03374a6c533cbf1fc23a7d611a73c7, summary: "amended first" }
-
- Working on PR: "second"
- Cherry-picking: Commit { id: 5746a83aed004d0867d52d40efc9bd800b5b7499, summary: "second #1" }
- Cherry-picking: Commit { id: 6db2c2817dfed244d5fbd8cbb9b8095965ac9a05, summary: "second #2" }
-
- Working on PR: "third"
- Cherry-picking: Commit { id: 42315c46b42044ebc4b57a995a75b97699f4855a, summary: "third" }
-
- ["b45e5838a93b33411a5f0c9f726bc1987bc71ff5:refs/heads/first", "93170d2199ed9c2ae30d1e7492947acf477fb035:refs/heads/second", "a85a1931c44c3138d993128591af2cad2ef6c68d:refs/heads/third"]
- Going to push these refspecs ☝️ Type 'yes' to continue: yes
- Enumerating objects: 12, done.
- Counting objects: 100% (12/12), done.
- Delta compression using up to 8 threads
- Compressing objects: 100% (8/8), done.
- Writing objects: 100% (11/11), 907 bytes | 453.00 KiB/s, done.
- Total 11 (delta 3), reused 0 (delta 0)
- remote: Resolving deltas: 100% (3/3), done.
- To github.com:timothyandrew/test.git
- + e845ded...b45e583 b45e5838a93b33411a5f0c9f726bc1987bc71ff5 -> first (forced update)
- + 6db2c28...93170d2 93170d2199ed9c2ae30d1e7492947acf477fb035 -> second (forced update)
- + 42315c4...a85a193 a85a1931c44c3138d993128591af2cad2ef6c68d -> third (forced update)
-
- Updating local branches so they point to the new stack.
-
- + Branch first now points to b45e5838a93b33411a5f0c9f726bc1987bc71ff5
- + Branch second now points to 93170d2199ed9c2ae30d1e7492947acf477fb035
- + Branch third now points to a85a1931c44c3138d993128591af2cad2ef6c68d
- All done!
- ```
-
- - This restores local history to a flat list and pushes the tip of each branch up to update the PRs themselves.
-
- ```bash
- * a85a193 N - (HEAD, origin/third, third) third
- |
- * 93170d2 N - (origin/second, second) second #2
- |
- * 61f64b6 N - second #1
- |
- * b45e583 N - (origin/first, first) amended first
- |
- * 8031011 U - (origin/master, master) initial commit
- ```
-
- - If conflicts are encountered, `autorebase` will pause and allow you to fix the conflicts before resuming.
-
-## Strategy
-
-This is a quick summary of the strategy the `autorebase` subcommand uses:
-
-1. Find the `merge_base` between the local branch of the first PR in the stack and the branch it merges into (usually `develop`). This forms the boundary for the initial cherry-pick. This is a heuristic and is not suitable for all situations, especially when changes have already been pushed or PRs are merged directly on GitHub. Accept an explicit boundary for the initial cherry-pick to avoid ambiguity here.
-2. Check out the commit/ref that the first PR in the stack merges into (usually `develop`). We're going to cherry-pick the entire stack onto this commit.
-3. Cherry-pick all commits from the first PR (stopping at the cherry-pick boundary calculated in 1.) onto `HEAD`.
-4. Move the _local_ branch for the first PR so it points at `HEAD`.
-5. The _remote tracking_ branch for the first PR becomes the next cherry-pick boundary.
-6. Repeat steps 3-5 for each subsequent PR until all PRs have been cherry-picked over.
-7. Push all refs at once by passing multiple refspecs to a single invocation of `git push -f`.
+### rebase
-## Disclaimer
+Generate a bash script for manual rebasing. [Learn more](docs/rebase.md)
-Use at your own risk (and make sure your git repository is backed up), especially because:
+```bash
+gh-stack rebase 'STACK-ID' > rebase.sh
+```
-- This tool works for my specific use case, but has _not_ been extensively tested.
-- The `autorebase` command is in an experimental state; there are possibly edge cases I haven't considered.
+## Workflow
-## Releasing
+1. Create branches that build on each other
+2. Push and create PRs with a shared identifier (e.g., `[TICKET-123]`)
+3. Set each PR's base to the branch below it
+4. Use `gh-stack annotate` to add stack tables
+5. After rebasing, use `gh-stack autorebase` to sync
+6. Use `gh-stack land` when ready to merge
-Releases are automated via GitHub Actions. To create a new release:
+## Requirements
-```bash
-# Update version in Cargo.toml, then:
-git add Cargo.toml
-git commit -m "chore: release v0.x.0"
-git tag v0.x.0
-git push origin master v0.x.0
-```
+- All PRs in a stack share a unique identifier in their title
+- All PRs live in a single GitHub repository
+- Remote branches have matching local branch names
-This triggers the release workflow which:
-1. Runs tests
-2. Builds universal macOS binary (x86_64 + arm64)
-3. Builds Linux binary
-4. Creates GitHub Release with binaries attached
-5. Opens PR to update the Homebrew formula
+## Troubleshooting
-## Contributors
+See [docs/troubleshooting.md](docs/troubleshooting.md) for common issues and solutions.
-Contributors are encouraged to submit pull requests to improve the tool. Please stick to semantic versioning and don't submit pull requests that break the tool.
+## Disclaimer
+
+Use at your own risk. The `autorebase` command modifies git history and force-pushes.
+
+## Contributing
See [AGENTS.md](AGENTS.md) for coding guidelines.
-## Credits
+## Releasing
+
+```bash
+# Update version in Cargo.toml
+git commit -m "chore: release vX.Y.Z"
+git tag vX.Y.Z
+git push origin master vX.Y.Z
+```
+
+---
-This README and tool were originally written by [@timothyandrew](https://github.com/timothyandrew/gh-stack). I highly recommend reading his blog post [here](https://0xc0d1.com/blog/git-stack/).
+Originally created by [@timothyandrew](https://github.com/timothyandrew/gh-stack). See his [blog post on stacked PRs](https://0xc0d1.com/blog/git-stack/).
diff --git a/docs/annotate.md b/docs/annotate.md
new file mode 100644
index 0000000..3f6265d
--- /dev/null
+++ b/docs/annotate.md
@@ -0,0 +1,61 @@
+# gh-stack annotate
+
+Add a markdown table to each PR description showing the full stack.
+
+## Usage
+
+```bash
+gh-stack annotate 'STACK-ID'
+gh-stack annotate 'STACK-ID' --badges # shields.io badges (public repos)
+gh-stack annotate 'STACK-ID' --ci # skip confirmation prompt
+gh-stack annotate 'STACK-ID' --prefix '#' # remove prefix from titles
+gh-stack annotate 'STACK-ID' -p file.md # prepend file contents
+```
+
+## Output
+
+Each PR in the stack gets a table added to its description:
+
+```markdown
+### Stack: STACK-ID
+
+| PR | Title | Base |
+|:--:|:------|:----:|
+| #103 | [STACK-ID] Add validation | #102 |
+| #102 | [STACK-ID] Implement feature | #101 |
+| #101 | [STACK-ID] Initial scaffolding | main |
+```
+
+GitHub auto-links PR numbers. Hovering shows PR status.
+
+## Flags
+
+| Flag | Description |
+|------|-------------|
+| `--badges` | Use shields.io status badges (public repos only) |
+| `--ci` | Skip confirmation prompt |
+| `--prefix` | Characters to strip from PR titles in the table |
+| `-p`, `--prelude` | File to prepend before the table |
+| `-r`, `--repository` | Override repository (owner/repo) |
+| `-o`, `--origin` | Git remote name (default: origin) |
+| `-e`, `--excl` | Exclude PR by number (repeatable) |
+
+## How it works
+
+1. Finds all PRs with the identifier in their title
+2. Builds a dependency graph from PR base branches
+3. Generates a markdown table
+4. Updates each PR description (idempotent)
+
+The annotation is idempotent - running it multiple times updates the existing table rather than adding duplicates.
+
+## When to use
+
+- After creating all PRs in a stack
+- After adding or removing PRs from a stack
+- After PR status changes (to update badges)
+
+## See also
+
+- [log](log.md) - Visualize the stack
+- [land](land.md) - Merge the stack
diff --git a/docs/autorebase.md b/docs/autorebase.md
new file mode 100644
index 0000000..9815b2e
--- /dev/null
+++ b/docs/autorebase.md
@@ -0,0 +1,71 @@
+# gh-stack autorebase
+
+Rebuild and push an entire stack after local changes.
+
+## Usage
+
+```bash
+gh-stack autorebase 'STACK-ID' -C /path/to/repo
+gh-stack autorebase 'STACK-ID' -C /path/to/repo --ci # skip confirmation
+gh-stack autorebase 'STACK-ID' -C /path/to/repo -b # cherry-pick boundary
+```
+
+## How it works
+
+1. Checks out the base branch (e.g., `main`)
+2. Cherry-picks commits from each PR in stack order
+3. Updates local branches to point at new commits
+4. Force-pushes all branches at once
+
+This reconstructs a clean, linear stack from your local changes.
+
+## Flags
+
+| Flag | Description |
+|------|-------------|
+| `-C`, `--project` | Path to local repository (required) |
+| `--ci` | Skip confirmation prompt |
+| `-b`, `--initial-cherry-pick-boundary` | Stop initial cherry-pick at this SHA |
+| `-o`, `--origin` | Git remote name (default: origin) |
+| `-r`, `--repository` | Override repository (owner/repo) |
+| `-e`, `--excl` | Exclude PR by number (repeatable) |
+
+## Conflict handling
+
+If a conflict occurs during cherry-picking:
+
+1. The process pauses
+2. Resolve conflicts manually
+3. Stage resolved files with `git add`
+4. Continue with `git cherry-pick --continue`
+
+## Example
+
+After amending a commit in the middle of your stack:
+
+```bash
+# Your local history diverged from remote
+git checkout feat/part-1
+git commit --amend -m "Updated message"
+
+# Rebuild and sync the entire stack
+gh-stack autorebase 'STACK-ID' -C .
+```
+
+## Warnings
+
+- **Force-pushes** to all branches in the stack
+- Back up your work before running
+- Collaborators will need to reset their local branches
+
+## When to use
+
+- After amending commits
+- After interactive rebase
+- After resolving conflicts with upstream
+- After reordering commits
+
+## See also
+
+- [log](log.md) - Verify stack structure after rebase
+- [rebase](rebase.md) - Generate a rebase script for manual control
diff --git a/docs/land.md b/docs/land.md
new file mode 100644
index 0000000..4cfe4c3
--- /dev/null
+++ b/docs/land.md
@@ -0,0 +1,63 @@
+# gh-stack land
+
+Merge an entire stack by squash-merging the topmost approved PR and closing the rest.
+
+## Usage
+
+```bash
+gh-stack land 'STACK-ID'
+gh-stack land 'STACK-ID' --dry-run # preview without changes
+gh-stack land 'STACK-ID' --count 2 # only land bottom 2 PRs
+gh-stack land 'STACK-ID' --no-approval # skip approval check
+```
+
+## How it works
+
+1. Orders the stack from base to top
+2. Finds the topmost PR that can be merged (approved, not draft)
+3. Squash-merges that PR into its base
+4. Closes all PRs below it with a comment linking to the merge
+
+This works because each PR contains all commits from PRs below it. Squash-merging the top PR lands all changes at once.
+
+## Flags
+
+| Flag | Description |
+|------|-------------|
+| `--dry-run` | Preview what would happen without making changes |
+| `--count N` | Only land the bottom N PRs in the stack |
+| `--no-approval` | Skip the approval requirement check |
+| `-r`, `--repository` | Override repository (owner/repo) |
+| `-o`, `--origin` | Git remote name (default: origin) |
+| `-e`, `--excl` | Exclude PR by number (repeatable) |
+
+## Requirements
+
+- PRs must be approved (unless `--no-approval`)
+- Draft PRs block landing
+- The PR being merged must pass branch protection rules
+
+## Example
+
+```
+Stack before:
+ #103 [STACK-ID] Part 3 (approved)
+ #102 [STACK-ID] Part 2 (approved)
+ #101 [STACK-ID] Part 1 (approved, base: main)
+
+After `gh-stack land 'STACK-ID'`:
+ #103 squash-merged into main
+ #102 closed (comment: "Landed via #103")
+ #101 closed (comment: "Landed via #103")
+```
+
+## When to use
+
+- All PRs in the stack are approved
+- CI is passing on the top PR
+- Ready to merge to main/master
+
+## See also
+
+- [log](log.md) - Check stack status before landing
+- [annotate](annotate.md) - Update PR descriptions
diff --git a/docs/log.md b/docs/log.md
new file mode 100644
index 0000000..5378b42
--- /dev/null
+++ b/docs/log.md
@@ -0,0 +1,71 @@
+# gh-stack log
+
+Visualize your stack's structure and status.
+
+## Usage
+
+```bash
+gh-stack log 'STACK-ID'
+gh-stack log 'STACK-ID' --short # compact list view
+gh-stack log 'STACK-ID' --include-closed # show closed/merged PRs
+gh-stack log 'STACK-ID' --no-color # disable colors and unicode
+gh-stack log 'STACK-ID' -C /path/to/repo # specify repo path
+```
+
+## Output
+
+The default tree view shows:
+
+```
+◉ feat/part-3 (current)
+│ 2 hours ago
+│
+│ a1b2c3d - Add validation logic
+│ f4e5d6c - Update tests
+│
+◯ feat/part-2
+│ 3 hours ago
+│
+◯ feat/part-1 (draft)
+│ 5 hours ago
+│
+◯ main
+```
+
+- `◉` marks the current branch
+- `◯` marks other branches
+- Commits are shown when run from a git repo
+- Timestamps show when each PR was last updated
+- Draft PRs are labeled
+
+The `--short` flag shows a compact list:
+
+```
+#103: [STACK-ID] Add validation (Merges into #102)
+#102: [STACK-ID] Implement feature (Merges into #101)
+#101: [STACK-ID] Initial scaffolding (Base)
+```
+
+## Flags
+
+| Flag | Description |
+|------|-------------|
+| `--short`, `-s` | Compact list format instead of tree |
+| `--include-closed` | Show branches with closed/merged PRs |
+| `--no-color` | Disable colors and unicode characters |
+| `-C`, `--project` | Path to local repository |
+| `-r`, `--repository` | Override repository (owner/repo) |
+| `-o`, `--origin` | Git remote name (default: origin) |
+| `-e`, `--excl` | Exclude PR by number (repeatable) |
+
+## When to use
+
+- Before rebasing to understand stack structure
+- To check which PRs are open, merged, or draft
+- To see recent commits on each branch
+- To verify stack order before landing
+
+## See also
+
+- [annotate](annotate.md) - Add stack tables to PR descriptions
+- [land](land.md) - Merge the stack
diff --git a/docs/rebase.md b/docs/rebase.md
new file mode 100644
index 0000000..87147bf
--- /dev/null
+++ b/docs/rebase.md
@@ -0,0 +1,45 @@
+# gh-stack rebase
+
+Generate a bash script to manually rebase a stack.
+
+## Usage
+
+```bash
+gh-stack rebase 'STACK-ID' > rebase.sh
+chmod +x rebase.sh
+./rebase.sh
+```
+
+## How it works
+
+Outputs a bash script with git commands to:
+
+1. Check out each branch in order
+2. Rebase onto the previous branch
+3. Handle the stack reconstruction step-by-step
+
+This gives you full control over the rebase process.
+
+## Flags
+
+| Flag | Description |
+|------|-------------|
+| `-e`, `--excl` | Exclude PR by number (repeatable) |
+
+## When to use
+
+- When `autorebase` doesn't fit your workflow
+- When you need to inspect/modify the rebase steps
+- For debugging stack issues
+- When you want to dry-run before executing
+
+## Warnings
+
+- The generated script may need manual adjustments
+- Review the script before executing
+- Back up your work first
+
+## See also
+
+- [autorebase](autorebase.md) - Automatic stack rebuilding
+- [log](log.md) - Verify stack structure
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
new file mode 100644
index 0000000..a03e9a2
--- /dev/null
+++ b/docs/troubleshooting.md
@@ -0,0 +1,137 @@
+# Troubleshooting
+
+Common issues and solutions.
+
+## Authentication
+
+### "Bad credentials" error
+
+Your GitHub token is invalid or expired.
+
+```bash
+# Check your token
+echo $GHSTACK_OAUTH_TOKEN
+
+# Generate a new token at https://github.com/settings/tokens
+# Required scope: repo
+export GHSTACK_OAUTH_TOKEN=''
+```
+
+### "You didn't pass GHSTACK_OAUTH_TOKEN"
+
+The environment variable isn't set.
+
+```bash
+export GHSTACK_OAUTH_TOKEN=''
+
+# Or add to .gh-stack.env in your project root
+echo "GHSTACK_OAUTH_TOKEN=" > .gh-stack.env
+```
+
+## Stack Detection
+
+### "No PRs found matching 'IDENTIFIER'"
+
+- Verify the identifier exists in PR titles
+- Check you're searching the right repository
+- Use `-r owner/repo` to specify the repository explicitly
+
+```bash
+# Debug: search manually
+gh pr list --search 'IDENTIFIER in:title'
+```
+
+### PRs from wrong repository
+
+The identifier matched PRs in multiple repositories.
+
+```bash
+# Specify repository explicitly
+gh-stack log 'STACK-ID' -r 'owner/repo'
+
+# Or set the environment variable
+export GHSTACK_TARGET_REPOSITORY='owner/repo'
+```
+
+### Stack order is wrong
+
+PRs must have their base branch set correctly:
+
+- Bottom PR: base is `main` (or your default branch)
+- Each PR above: base is the branch below it
+
+## Autorebase
+
+### Conflicts during cherry-pick
+
+```bash
+# 1. Resolve conflicts in your editor
+# 2. Stage resolved files
+git add
+
+# 3. Continue cherry-picking
+git cherry-pick --continue
+```
+
+### "The --project argument is required"
+
+```bash
+gh-stack autorebase 'STACK-ID' -C /path/to/repo
+# Or from the repo directory:
+gh-stack autorebase 'STACK-ID' -C .
+```
+
+### Local branches out of sync after autorebase
+
+```bash
+# Fetch latest from remote
+git fetch origin
+
+# Reset local branch to remote
+git checkout
+git reset --hard origin/
+```
+
+## Land
+
+### "PR #X requires approval"
+
+Get approval on the PR, or skip the check:
+
+```bash
+gh-stack land 'STACK-ID' --no-approval
+```
+
+### "PR #X is a draft and blocks landing"
+
+Mark the PR as ready for review on GitHub, then retry.
+
+### Merge failed
+
+Check branch protection rules on the repository. The PR may need:
+
+- Passing CI checks
+- Required reviewers
+- Up-to-date branch
+
+## Log
+
+### Tree view shows only trunk branch
+
+Closed/merged PRs are hidden by default.
+
+```bash
+gh-stack log 'STACK-ID' --include-closed
+```
+
+### No commits shown in tree view
+
+Run from inside a git repository, or specify the path:
+
+```bash
+gh-stack log 'STACK-ID' -C /path/to/repo
+```
+
+## Still stuck?
+
+Open an issue: https://github.com/luqven/gh-stack/issues
diff --git a/img/annotate.png b/img/annotate.png
deleted file mode 100644
index 54dee18..0000000
Binary files a/img/annotate.png and /dev/null differ
diff --git a/img/complete-1.png b/img/complete-1.png
deleted file mode 100644
index 8a35e1e..0000000
Binary files a/img/complete-1.png and /dev/null differ
diff --git a/img/complete-2.png b/img/complete-2.png
deleted file mode 100644
index 6fc4619..0000000
Binary files a/img/complete-2.png and /dev/null differ
diff --git a/img/complete-3.png b/img/complete-3.png
deleted file mode 100644
index 0513720..0000000
Binary files a/img/complete-3.png and /dev/null differ
diff --git a/img/conflict.png b/img/conflict.png
deleted file mode 100644
index e8eed80..0000000
Binary files a/img/conflict.png and /dev/null differ
diff --git a/img/feature-1.png b/img/feature-1.png
deleted file mode 100644
index 3166414..0000000
Binary files a/img/feature-1.png and /dev/null differ
diff --git a/img/feature-2.png b/img/feature-2.png
deleted file mode 100644
index f26c393..0000000
Binary files a/img/feature-2.png and /dev/null differ
diff --git a/img/feature-3.png b/img/feature-3.png
deleted file mode 100644
index fa5ea3f..0000000
Binary files a/img/feature-3.png and /dev/null differ
diff --git a/img/initial.png b/img/initial.png
deleted file mode 100644
index 7015078..0000000
Binary files a/img/initial.png and /dev/null differ
diff --git a/img/postconflict.png b/img/postconflict.png
deleted file mode 100644
index 7f3d144..0000000
Binary files a/img/postconflict.png and /dev/null differ
diff --git a/img/tree.png b/img/tree.png
deleted file mode 100644
index 11e6ca6..0000000
Binary files a/img/tree.png and /dev/null differ