ez
Stacked PRs for GitHub.
ez is a fast, lightweight CLI for managing stacked pull requests on GitHub. It shells out to git and gh so there's nothing magical happening under the hood — just the tools you already know, orchestrated intelligently.
Why stacked PRs?
Large pull requests are hard to review. Stacked PRs let you break work into a chain of small, focused branches where each branch builds on the one below it:
main
└── feat/auth-types ← PR #1 (data models)
└── feat/auth-api ← PR #2 (API routes, depends on #1)
└── feat/auth-ui ← PR #3 (frontend, depends on #2)
Reviewers see small diffs. You keep working without waiting. When PR #1 merges, ez rebases the rest of the stack automatically.
The problem is that git doesn't know about stacks. Rebasing, reordering, and keeping GitHub PRs pointed at the right base branch is tedious and error-prone. ez handles all of that for you.
Quick start
# Install
# Initialize in any git repo
# Start building a stack
# ... make changes ...
# ... make changes ...
# Push the entire stack and open PRs for all branches
That's it. Two PRs, correctly chained, with GitHub base branches set automatically.
Using ez with AI Agents
ez is designed to work well in automated and agentic contexts:
- JSON output —
ez status --jsonandez log --jsonemit machine-readable data that agents can parse without screen-scraping. - Non-interactive flags —
ez checkout <name>orez checkout 42bypasses the TUI picker.ez commit --if-changedexits cleanly when there's nothing to commit. - Autostash —
ez sync --autostasheliminates thegit stash && ... && git stash popdance. - Structured exit codes — each failure mode has a distinct exit code (rebase conflict = 3, stale remote = 4, usage error = 5, unstaged changes = 6) so agents can take the right recovery action.
Install the ez skill
If your agent supports Skills, install the repo's ez-workflow skill directly from GitHub:
That installs the instructions from SKILL.md, which teaches the agent when to use ez instead of raw git/gh commands in an ez-managed repo.
Quick install check
&&
Key agent patterns
# Parse current branch state
# → {"branch":"feat/x","parent":"main","pr_number":42,"children":[],"needs_restack":false,...}
# Only commit if there are staged changes
# Sync without a manual stash dance
# Get just the PR URL for scripting
# Find branches that need restacking
|
For agents that support local instruction files, AGENTS.md contains a compact install-and-usage guide. For skills-based agents, install ez-workflow with npx skills add and use that skill directly.
Agent setup prompt
Copy-paste this into any AI coding agent to get ez installed and configured:
Install ez-stack, a CLI for stacked PRs on GitHub, and set it up in this repo.
Steps:
- Install the binary:
cargo install ez-stack(or update:ez update) - Verify it works:
ez --version - Initialize in this repo:
ez init - Add these rules to the project's CLAUDE.md (or equivalent agent instructions file):
## ez-stack
This repo uses `ez` for stacked PRs. When `.git/ez/stack.json` exists, use `ez` instead of raw git/gh commands:
- `ez create <name>` instead of `git checkout -b`
- `ez commit -m "msg"` instead of `git commit` (auto-restacks children, shows diff stat)
- `ez commit -m "msg" -- path1 path2` to stage specific files
- `ez diff --stat` instead of `git diff main...HEAD --stat`
- `ez parent` instead of parsing `ez status` for the parent branch
- `ez push` instead of `git push` / `gh pr create`
- `ez submit` to push the entire stack
- `ez sync --autostash` to sync with trunk
Key flags: `--json` on status/log for machine output, `--if-changed` on commit to no-op when nothing staged, `--from <base>` on create to skip checkout.
Output: every command appends `[ok | 45ms]` or `[exit:3 | 120ms]` to stderr.
Exit codes: 0=ok, 2=gh error, 3=conflict, 4=stale ref, 5=usage error, 6=unstaged changes.
Commands
Stack creation & editing
| Command | Description |
|---|---|
ez init |
Initialize ez in the current repository |
ez create <name> |
Create a new branch stacked on the current branch |
ez create <name> -m "msg" |
Create branch and commit staged changes in one step |
ez create <name> -am "msg" |
Create branch, stage all tracked changes, and commit |
ez create <name> --from <base> |
Create branch from a specific base without checking it out first |
ez commit -m <msg> |
Commit staged changes, restack children, show diff stat |
ez commit -m "subj" -m "body" |
Multi-paragraph commit (repeated -m, like git) |
ez commit -m <msg> -- <paths> |
Stage specific paths and commit |
ez commit -m <msg> --if-changed |
Commit only if there are staged changes (no-op otherwise) |
ez amend |
Amend the last commit and restack children |
ez delete [<name>] |
Delete a branch from the stack and restack |
ez move --onto <branch> |
Reparent the current branch onto another branch |
Syncing & rebasing
| Command | Description |
|---|---|
ez sync |
Fetch trunk, detect merged PRs, clean up, and restack |
ez sync --dry-run |
Preview what sync would do without making changes |
ez sync --autostash |
Stash uncommitted changes before sync, restore after |
ez restack |
Rebase each branch onto its parent |
Navigation
| Command | Description |
|---|---|
ez up |
Check out the branch above the current one |
ez down |
Check out the branch below the current one |
ez top |
Check out the top of the stack |
ez bottom |
Check out the bottom of the stack |
ez checkout |
Interactively select a branch to check out |
ez checkout <name> |
Switch directly to a branch by name (non-interactive) |
ez checkout <number> |
Switch directly to a branch by PR number (non-interactive) |
GitHub integration
| Command | Description |
|---|---|
ez push |
Push current branch only and create/update its PR |
ez push --title "..." --body "..." |
Push and set/update PR title and body |
ez push --base <branch> |
Push and override the PR base branch |
ez submit |
Push all branches in the stack and create/update all PRs |
ez pr |
Open the current branch's PR in the browser |
ez pr-link |
Print the PR URL to stdout (pipeable) |
ez pr-edit |
Edit the PR body in $EDITOR |
ez pr-edit --title "..." --body "..." |
Edit the PR title/body directly |
ez draft |
Mark the current PR as a draft |
ez ready |
Mark the current PR as ready for review |
ez merge |
Merge the bottom PR of the stack via GitHub |
Inspection & diffing
| Command | Description |
|---|---|
ez log |
Show the full stack with branch names, commit counts, and PR status |
ez log --json |
Show the full stack as a JSON array (machine-readable) |
ez status |
Show the current branch and its position in the stack |
ez status --json |
Show current branch info as JSON (machine-readable) |
ez diff |
Show diff of current branch vs parent (what the PR reviewer sees) |
ez diff --stat |
Show only the diffstat summary |
ez diff --name-only |
Show only changed file names |
ez parent |
Print the parent branch name to stdout (pipeable) |
ez branch |
List all branches with PR numbers and worktree paths |
Updating
| Command | Description |
|---|---|
ez update |
Update ez to the latest version |
ez update --check |
Check for updates without installing |
ez update --version v0.1.12 |
Install a specific version |
ez push vs ez submit
ez push |
ez submit |
|
|---|---|---|
| Scope | Current branch only | All branches from trunk to current |
| PRs | Creates/updates PR for current branch | Creates/updates PRs for all branches |
| When to use | Iterating on a single branch | First push of a stack, or after restacking all branches |
Note: Running
gh pr createafterez pushwill fail —ez pushalready created the PR.
Example workflow
Here's a complete session building a three-branch stack:
# 1. Start from main
&&
# 2. Create the first branch in the stack
# 3. Stack a second branch on top
# 4. Stack a third branch on top
# 5. See the full stack
# main
# ├── feat/auth-types (1 commit)
# │ ├── feat/auth-api (1 commit)
# │ │ ├── feat/auth-middleware (1 commit) ← you are here
# 6. Push everything and open PRs
# Creates 3 PRs:
# feat/auth-types → main
# feat/auth-api → feat/auth-types
# feat/auth-middleware → feat/auth-api
# 7. After feat/auth-types is reviewed and merged on GitHub:
# Fetches main (which now includes auth-types),
# rebases auth-api onto main, rebases auth-middleware onto auth-api,
# deletes the merged feat/auth-types branch,
# and updates PR base branches on GitHub.
ez create with a commit message
ez create accepts -m to commit staged changes in one step:
# equivalent to: ez create feat/auth && ez commit -m "add auth module"
Use -a to stage all tracked changes automatically (like git commit -a):
# equivalent to: git add -A && ez create feat/auth -m "add auth module"
How it works
ez is intentionally simple in its architecture:
- No custom git internals. Every git operation is a call to the
gitCLI. Every GitHub operation goes throughgh. You can always see exactly what happened by reading your git log. - Stack metadata is stored in
.git/ez/stack.json— a single JSON file tracking branch order, parent relationships, and associated PR numbers. It's local to your repo and ignored by git. - Restacking uses
git rebase --ontoto move each branch in the stack onto its updated parent. This is the same operation you'd do by hand;ezjust does it for every branch in the right order. - PR management calls
gh pr createandgh pr editto set base branches so GitHub shows the correct, minimal diff for each PR in the stack.
Stack metadata format
Prerequisites
- git 2.38+
- gh (GitHub CLI), authenticated via
gh auth login - A GitHub repository with push access
Installation
From crates.io
From source
Install script (recommended)
|
To install a specific version:
|
GitHub releases
Pre-built binaries for Linux and macOS are available on the Releases page.
Contributing
Contributions are welcome! Please read CONTRIBUTING.md for development setup, code style, and how to submit changes.
License
MIT. See LICENSE for details.