This is my private fork of Modular's stack-pr, maintained at jansel/stack-pr. It creates a GitHub pull request for each commit in a stack, with each PR based on the previous one so reviewers see only that commit's changes.
This fork includes upstream changes through
57e425c,
plus these workflow changes:
- Default submission: running
stack-prwith no arguments submits the stack and prints the resulting PRs. - Preserve PR descriptions: submission keeps existing PR bodies and refreshes
their cross-links. Use
--update-bodyto replace bodies from commit messages; upstream uses--keep-bodyto opt into preserving them. Titles still update. - Interactive rebases: submit and land commands can run while paused during
git rebase -i. The fork temporarily saves and restores the rebase state, including in linked worktrees. Upstream rejects submission during a rebase. Resolve conflicts and commit changes before submitting. Proposed upstream in PR #88: Make submit work withgit rebase -i. - Land the entire stack:
land-allmerges from bottom to top, rebasing each remaining PR just before its turn. It watches required CI, confirms each merge, and resumes after interruptions by skipping already merged PRs. Upstream'sland.style=disablesetting disables bothlandandland-allhere. - Visible PR URLs: each PR's full URL appears on its own line, including when terminal hyperlinks are disabled. Multi-PR descriptions label the stack “oldest at bottom.”
- Restricted-fetch bugfix: stack branches are fetched explicitly, including
when
git remote set-branches origin mainrestricts normal fetches. This fixes branch-number reuse that could overwrite existing stack branches; proposed upstream in PR #86. - Upgrade from GitHub:
stack-pr upgradeinstalls the latestmainfrom this fork into the current installation's Python environment. - Failure recovery: submissions restore the exact stash they created and restore PRs temporarily made draft, including after errors or Ctrl-C.
The upstream merge also brings the config command, repository-root config
lookup, optional usage tips, target-branch diagnostics, temporary draft status
while updating existing PRs, and quieter output.
The fork requires Python 3.10+, has no Python runtime dependencies, and uses
updated build and development tools. It is distributed from GitHub; installing
stack-pr by name from PyPI installs the upstream distribution.
You need Python 3.10 or newer, Git, and the GitHub CLI. Authenticate an account that can access this private fork and the repositories where you want to use it:
gh auth login
gh auth setup-gitUse this command for both installation and upgrades, in your chosen Python environment:
python -m pip install --upgrade "git+https://github.com/jansel/stack-pr.git@main"For an isolated installation:
python3 -m venv ~/.venvs/stack-pr
~/.venvs/stack-pr/bin/python -m pip install --upgrade "git+https://github.com/jansel/stack-pr.git@main"
export PATH="$HOME/.venvs/stack-pr/bin:$PATH"Add the PATH line to your shell startup file to keep the command available.
Once installed, update with:
stack-pr upgradeCheck the installed version and source commit with:
stack-pr --versionThe version comes from the installed package's metadata, independent of the repository you run it in. GitHub installations also report the exact source commit recorded by pip.
This runs python -m pip install --upgrade using the Python interpreter that
runs stack-pr, targeting this fork's main branch. It works from any directory,
requires Git access to the fork, and reports pip's exit status if installation
fails. It does not need a working checkout or gh at runtime if Git credentials
are already configured.
For local development, see CONTRIBUTING.md.
Create a feature branch and make one commit per desired PR:
git fetch origin
git switch -c my-feature origin/main
# Edit files, then create a commit for each logical change.
git add path/to/file
git commit -m "First change"
# Repeat for the next change.Inspect the stack, then create or update its PRs:
stack-pr view
stack-pr submit # `stack-pr` and `stack-pr export` also submitThe output shows the newest commit first, with visible URLs:
Stack:
* cc932b71 (#439, 'jansel/stack/103' -> 'jansel/stack/102'): Improve navigation
https://github.com/jansel/example/pull/439
* 3475c898 (#438, 'jansel/stack/102' -> 'main'): Fix the coffee dispenser
https://github.com/jansel/example/pull/438
Amend or reorder commits locally, then run stack-pr submit again. Existing PR
bodies are preserved; use stack-pr submit --update-body when you want to replace
them with the current commit messages.
To rebase onto the latest target branch:
git fetch origin
git rebase origin/main
stack-pr submitOnce reviewed, land one PR or the entire stack:
stack-pr land
# Or:
stack-pr land-allland merges the bottom PR and rebases the remaining stack. land-all lands
bottom to top, rebasing each later PR just before merging it. It waits for
required CI and confirms that GitHub has merged each PR before advancing.
GitHub's merge requirements still apply; missing approvals, failed required
checks, and merge conflicts stop the command.
You can also merge the bottom PR through GitHub, then fetch and rebase your local branch before resubmitting the remaining stack.
| Command | Behavior |
|---|---|
submit / export |
Create or update one PR per commit; the default with no arguments. |
view |
Inspect commits and linked PRs without pushing or rewriting commits. |
land |
Merge the bottom PR and rebase the rest. |
land-all |
Watch required CI and merge the stack; rerun to resume. |
abandon |
Remove stack metadata and delete the associated stack branches. |
config section.key=value |
Write a configuration setting. |
upgrade |
Install the latest version of this fork from GitHub. |
--version |
Print the installed version and source commit. |
Stack commands operate on BASE..HEAD, targeting origin/main by default.
When --base is omitted, the base is the merge base of HEAD and the remote
target. Generated branches are named $USERNAME/stack/$ID; let stack-pr
manage these branches and the stack-info metadata in commit messages.
These options apply to submit, view, land, land-all, and abandon:
| Option | Meaning / default |
|---|---|
-R, --remote |
Git remote; origin. |
-B, --base |
Base revision; inferred from the remote target. |
-H, --head |
Head revision; HEAD. |
-T, --target |
Remote target branch; main. |
--hyperlinks / --no-hyperlinks |
Toggle terminal hyperlinks; full URLs always remain visible. |
--show-tips / --no-show-tips |
Toggle usage tips after commands. |
-V, --verbose |
Show additional progress and subprocess output. |
--branch-name-template |
Branch template; $USERNAME/stack. Supports $USERNAME, $BRANCH, and $ID; /$ID is appended if absent. |
Examples:
stack-pr view -B HEAD~5
stack-pr submit -B HEAD~5
stack-pr submit -H HEAD~2
stack-pr view --no-hyperlinks --no-show-tips--update-body: replace PR bodies from commit messages. By default, keep the current bodies and only refresh cross-links and titles.-d,--draft: create new PRs as drafts.--draft-bitmask: choose draft status for new PRs from oldest to newest, such as0010for a four-PR stack. The mask length must match the stack;--drafttakes precedence.--reviewer: comma-separated reviewer handles; defaults to$STACK_PR_DEFAULT_REVIEWERor the config file.-s,--stash: temporarily stash tracked uncommitted changes during submission. Restores those changes and their staged status without consuming unrelated stashes. If restoration conflicts, the stash is retained for recovery.
stack-pr land-all --wait 30 --retries 10--wait is the interval in seconds between checks of CI and merge status.
--retries is the number of rechecks after the initial check, so 10 permits
up to 11 polling rounds per PR. Both values must be nonnegative, including in
the config file. Increase --retries for longer CI or merge queues.
The command waits for pending required checks and GitHub's mergeability calculation. It requests a merge once ready and waits for confirmation, including when GitHub queues the PR. Known temporary transport errors can be retried; merge conflicts, failed or cancelled required checks, drafts, missing approvals, and permission errors stop the command.
If interrupted or stopped, rerun the same land-all command. It skips the
already merged PRs at the bottom of the stack and continues with the remaining
PRs. Closed but unmerged PRs and PRs merged out of stack order require fixing
the stack before proceeding.
For local rebase conflicts, stack-pr aborts the rebase it started, returns to
the original branch, and reports the PR URL and conflicting files. Resolve the
conflicts locally, resubmit the remaining changes, then rerun land-all.
Unlike repeatedly calling land, each later PR is rebased only when its turn
arrives, rather than after every earlier merge.
Defaults are read from .stack-pr.cfg at the Git repository root, including
when invoked from a subdirectory. STACKPR_CONFIG overrides the path.
stack-pr config common.hyperlinks=False
stack-pr config common.show_tips=False
stack-pr config repo.target=main
stack-pr config 'repo.branch_name_template=$USERNAME/$BRANCH'
stack-pr config land-all.wait=60Example configuration:
[common]
verbose=False
hyperlinks=True
show_tips=True
draft=False
stash=False
[repo]
remote=origin
target=main
reviewer=GithubHandle1,GithubHandle2
branch_name_template=$USERNAME/$BRANCH
[land]
style=bottom-only
[land-all]
wait=30
retries=10Set land.style=disable to disable both landing commands and require merging
through GitHub. Use stack-pr <command> --help for command-specific options.