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.
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 commit -m <msg> |
Commit staged changes and restack children |
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 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 |
GitHub integration
| Command | Description |
|---|---|
ez push |
Push current branch only and create/update its PR |
ez push --title "..." --body "..." |
Push and set PR title/body on creation |
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-edit --title "..." --body "..." |
Edit the PR for the current branch |
ez merge |
Merge the bottom PR of the stack via GitHub |
Inspection
| Command | Description |
|---|---|
ez log |
Show the full stack with branch names, commit counts, and PR status |
ez status |
Show the current branch and its position in the stack |
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.