spr: Stacked Pull Requests on GitHub

Lobsters Hottest Tools

Summary

spr is a CLI tool that converts each commit on a Git branch into a separate pull request on GitHub, enabling stacked PRs without manual branch management.

<p><a href="https://lobste.rs/s/txnyjt/spr_stacked_pull_requests_on_github">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 05/18/26, 04:26 AM

ejoffe/spr

Source: https://github.com/ejoffe/spr

logo License: MIT Build Release

Each commit becomes a pull request. Stop juggling branches.

git spr manages stacked pull requests on GitHub so you don’t have to. Write commits on a single branch, and spr turns each one into its own pull request – kept in sync, correctly ordered, and ready to merge.

terminal cast

Why stacked PRs?

  • Small PRs get reviewed faster. A 50-line change gets meaningful feedback; a 500-line change gets “looks good.”
  • No more branch gymnastics. Stop creating feature-part-1, feature-part-2, rebasing one onto the other, and resolving conflicts between them.
  • Ship incrementally. Land the database migration today, the API tomorrow, the UI the day after – each reviewed and merged independently.
  • Works with native GitHub. No extra services, no custom merge bots. Just pull requests and branches, managed for you.

Quick Start

Install via brew, nix, or download a binary:

brew install ejoffe/tap/spr        # macOS/Linux
nix profile install github:ejoffe/spr   # Nix

Then use it like normal git – just replace git push + manual PR creation with git spr update:

git commit -m "Add user authentication"
git commit -m "Add login page"
git commit -m "Add session management"

git spr update   # creates 3 pull requests, stacked in order
git spr status   # show status of your stack
git spr merge    # merge everything that's ready

That’s it. Each commit is a PR. Amend a commit and run git spr update again to sync changes.

Commands

CommandAliasesDescription
git spr updateu, upCreate and update pull requests for commits in the stack
git spr statuss, stShow status of open pull requests
git spr mergeMerge all mergeable pull requests
git spr amendaAmend a commit in the stack
git spr editeEdit a commit in the stack (interactive rebase)
git spr syncSynchronize local stack with remote
git spr checkRun pre-merge checks (configured by mergeCheck)
git spr versionShow version info

Global flags: --detail (show status bit headers), --verbose (log git commands and GitHub API calls), --debug, --profile

Installation

Brew

brew tap ejoffe/homebrew-tap
brew install ejoffe/tap/spr

Nix

nix profile install github:ejoffe/spr

Or run without installing:

nix run github:ejoffe/spr

Apt

echo "deb [trusted=yes] https://apt.fury.io/inigolabs/ /" | sudo tee /etc/apt/sources.list.d/inigolabs.list
sudo apt update
sudo apt install spr

Binary

Download pre-compiled binaries from the releases page.

From source

make bin   # requires goreleaser; binaries output to dist/

Usage Guide

Workflow

Commit your changes to a branch as you normally do. Every commit becomes a pull request.

git add feature_1.go
git commit -m "Feature 1"
git add feature_2.go
git commit -m "Feature 2"
git add feature_3.go
git commit -m "Feature 3"

git spr update

The commit subject becomes the PR title; the commit body becomes the PR description. There’s no need to create branches or call git pushgit spr update handles everything.

Work in progress: Prefix a commit message with WIP to skip PR creation for that commit. Remove the prefix when you’re ready.

Updating pull requests

Run git spr update to sync your entire stack. New commits get new PRs; amended commits update existing PRs automatically.

> git spr update
[⌛❌✅❌] 60: Feature 3
[✅✅✅✅] 59: Feature 2
[✅✅✅✅] 58: Feature 1
FlagAliasDescription
--count-cUpdate a specific number of PRs from the bottom of the stack
--reviewer-rAdd reviewers to newly created pull requests
--no-rebase--nrDisable rebasing (also supports SPR_NOREBASE env var)

Amending commits

Stage your changes, then use git spr amend to pick which commit to amend:

> git add feature_2.go
> git spr amend
 3 : 5cba235d : Feature 3
 2 : 4dc2c5b2 : Feature 2
 1 : 9d1b8193 : Feature 1
Commit to amend [1-3]: 2

Use --update (-u) to automatically run git spr update after amending.

Editing commits

Use git spr edit to start an interactive rebase session on a specific commit:

> git spr edit
 3 : 5cba235d : Feature 3
 2 : 4dc2c5b2 : Feature 2
 1 : 9d1b8193 : Feature 1
Commit to edit [1-3]: 2

Finish with git spr edit --done (add -u to also update). Cancel with git spr edit --abort.

Syncing

Use git spr sync to pull remote changes into your local stack. Useful after PRs have been merged or updated on GitHub.

Merging

Use git spr merge instead of the GitHub UI to merge in the correct order:

> git spr merge
MERGED #58 Feature 1
MERGED #59 Feature 2
MERGED #60 Feature 3
[✅❌✅✅] 61: Feature 4

spr finds the top mergeable PR in the stack, combines all commits up to it into a single PR, merges it, and closes the intermediate PRs. This avoids triggering redundant CI runs.

Use --count N to merge only the bottom N pull requests.

Merge status bits

Each PR shows four status bits:

[✅❌✅✅] 61: Feature 4
 │  │  │  └─ stack: all PRs below are ready
 │  │  └──── conflicts: no merge conflicts
 │  └─────── approval: PR is approved
 └────────── checks: CI checks pass
Bit
Checkspendingfailedpassnot required
Approvalnot approvedapprovednot required
Conflictshas conflictsno conflicts
Stackblocked belowall clear

Configure check and approval requirements with requireChecks, requiredChecks, and requireApproval in .spr.yml. When requiredChecks lists specific check names, only those checks are evaluated – all others are ignored. This is useful when optional checks (e.g. linters, deploy previews) would otherwise cause the status to show as failed.

Starting a new stack

Create a new branch from the latest pushed state:

git checkout -b new_stack @{push}

Configuration

Configuration is created automatically on first run. Repository config lives in .spr.yml at the repo root; user config lives in ~/.spr.yml.

Repository configuration (.spr.yml)
SettingTypeDefaultDescription
requireChecksbooltrueRequire checks to pass in order to merge
requiredCheckslistList of check names that must pass. When set, only these checks are evaluated; all others are ignored
requireApprovalbooltrueRequire PR approval in order to merge
githubRepoOwnerstrGitHub owner (auto-detected from git remote)
githubRepoNamestrGitHub repository name (auto-detected from git remote)
githubRemotestroriginGit remote name to use
githubBranchstrmainTarget branch for pull requests
githubHoststrgithub.comGitHub host (update for GitHub Enterprise)
mergeMethodstrrebaseMerge method: rebase, squash, or merge
mergeQueueboolfalseUse GitHub merge queue
prTemplateTypestrstackPR template: stack, basic, why_what, or custom
prTemplatePathstrPath to custom PR template file (auto-sets type to custom)
prTemplateInsertStartstrMarker in custom template for commit body insertion start
prTemplateInsertEndstrMarker in custom template for commit body insertion end
mergeCheckstrCommand to run with git spr check before merging
forceFetchTagsboolfalseFetch tags during git spr update
showPrTitlesInStackboolfalseShow PR titles in stack description within PR body
branchPushIndividuallyboolfalsePush branches one at a time instead of atomically
defaultReviewerslistReviewers to add to every new pull request

Example .spr.yml:

requireChecks: true
requiredChecks:
  - "ci/test"
  - "ci/build"
requireApproval: true
mergeMethod: squash
defaultReviewers:
  - teammate
User configuration (~/.spr.yml)
SettingTypeDefaultDescription
showPRLinkbooltrueShow full pull request URL
shortPRLinkboolfalseShow clickable PR-<number> instead of full URL
showCommitIDboolfalseShow first 8 characters of commit hash
logGitCommandsboolfalseLog git commands to stdout
logGitHubCallsboolfalseLog GitHub API calls to stdout
statusBitsHeaderbooltrueShow status bit type headers
statusBitsEmojisbooltrueUse emoji status bits
createDraftPRsboolfalseCreate new PRs as drafts
preserveTitleAndBodyboolfalseDon’t overwrite PR title and body on update
noRebaseboolfalseSkip rebasing on git spr update
deleteMergedBranchesboolfalseDelete branches after PRs are merged
branchPrefixstrsprPrefix for spr-managed branch names

How it compares

spr is similar to Graphite, ghstack, and Gerrit’s stacked review model – but works purely with GitHub’s native pull requests. No extra service, no custom merge bot, no lock-in.

Contributing

Found a bug? Open an issue. Pull requests are welcome.

If you find spr useful, a star helps others discover it.

License

MIT License

Similar Articles

Show HN: Haystack – Review the PRs that need human attention

Hacker News Top

Haystack is a new tool that replaces the GitHub PR review system with a queue that triages pull requests into safe-to-merge, needs-fixes, or needs-human-review buckets, helping teams cope with the surge in PRs from coding agents.