Skip to main content

jj-stack: manage stacked GitHub PRs with jj

jj-stack turns a linear series of local jj changes into a stack of GitHub pull requests. Rewrite, split, squash, or reorder the changes with jj, then run jj-stack submit to update GitHub. Existing PRs follow their change IDs, keeping comments and review history together.

Quick start

You need Python 3.14 or newer, jj 0.45.1 or newer, and a repo on github.com where you can push branches and open pull requests. jj-stack uses GITHUB_TOKEN, then GH_TOKEN, then your GitHub CLI login for authentication.

Install with uv:

uv tool install jj-stack

Submit your first stack

Start with a linear series of local jj changes on top of trunk(). Authenticate with gh auth login, or supply a token in GITHUB_TOKEN or GH_TOKEN. Check the repo setup and apply the safe local fixes:

jj-stack doctor --fix

Inspect the stack that ends at your working copy:

jj-stack

Create one GitHub PR per local change:

jj-stack submit

Revise the changes locally with jj and rerun jj-stack submit whenever the stack is ready to refresh. Use jj-stack list to see every tracked stack in the repo.

Mental model

Your local jj history determines which changes form a stack and their order. On GitHub, each change gets a stable PR branch and a PR. The bottom PR targets trunk by default, and each PR above it targets the PR branch below:

Local changes:  trunk() <- A     <- B     <- C
GitHub PRs:     main    <- PR #1 <- PR #2 <- PR #3

Each PR's diff shows only the changes it adds on top of its base, so reviewers can consider one change at a time. jj-stack manages the PR branches for you, and they normally stay out of your local bookmark view.

When you rewrite a change, its change ID still connects it to the same PR. Submitting again updates that PR and the PRs for any dependent changes, preserving their discussions.

To select a stack, pass the change ID of its head (the top change). jj-stack follows the head's parents back to trunk to find the rest. If you don't name a head, it uses your working copy when it has both a description and changes, or its parent otherwise. After editing a lower change, pass the head's ID to jj-stack submit so the update includes the changes above your edit.

Everyday workflow

  1. Write code as a series of local jj changes.
  2. Run jj-stack submit.
  3. Revise, add, remove, or reorder the changes locally as reviews come in.
  4. Run jj-stack submit again to refresh GitHub.
  5. Run jj-stack merge when the PRs at the bottom are ready. Pass --pull-request <pr> to stop at an earlier PR, and --method to choose a merge method your repo allows. The command waits for GitHub, including its merge queue, then updates your local stack and remaining PRs.
  6. If you used --no-wait, interrupted the wait, or merged through GitHub, run jj-stack sync <head-change-id> after GitHub finishes.

view, submit, merge, and sync accept a change ID when you need to select a stack other than the one ending at the working copy.

See the user guide for drafts, descriptions, merge queues, cleanup, and working with multiple stacks.

Optional setup

Invoke it as jj stack

Add a command alias to your user configuration with jj config edit --user:

[aliases]
stack = ["util", "exec", "--", "jj-stack"]

For tab completion of both jj-stack and jj stack, add the output of jj-stack completion to your shell startup file:

eval "$(jj-stack completion zsh --jj-alias stack)"

bash and fish work the same way. See Configuration for more setup options.

Other installation options

pipx provides another isolated installation:

pipx install jj-stack

You can also use pip inside an activated virtual environment:

python -m pip install jj-stack

To upgrade an installation made with uv, run uv tool upgrade jj-stack. If the command is not on your shell PATH, run uv tool update-shell.

Learn more

For all flags and aliases, use the built-in help:

jj-stack --help
jj-stack <command> --help
jj-stack help --all

Coding agent integration

Install the bundled skill to give coding agents instructions for working with jj-stack:

gh skill install bos/jj-stack jj-stack

The skill source and evaluation notes are included in this repo.

Development

With uv, jj, and just installed, run just to list the development workflows. See CONTRIBUTING.md for setup and validation instructions.

Metadata

Release files for jj-stack 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jj-stack 0.1.6
File Size Uploaded
jj_stack-0.1.6.tar.gz 476.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jj-stack 0.1.6
File Interpreter ABI Platform
jj_stack-0.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 718.9 kB

Release files / jj_stack-0.1.6.tar.gz

Download URL jj_stack-0.1.6.tar.gz
Size 476.4 kB
Tags Source
SHA-256 checksum
How to use checksums
d0f48325560d0ae78e799926d5dcf833bf1f8120865bee2c07aa77543bbf0456
BLAKE2b-256 checksum
How to use checksums
ff7c6a63e391bfccd614d71d36153c58537312c9ecc74fc33110b095317bca85
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / jj_stack-0.1.6-py3-none-any.whl

Download URL jj_stack-0.1.6-py3-none-any.whl
Size 242.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c18a7a1991f483db9e19c28d93e5abaf6d9ffac798463531db00727a45f6bac0
BLAKE2b-256 checksum
How to use checksums
c2b64e9f783f4d90020454cbe59abca6b7d634888cf947d7820ece8215329c8b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.1.7

2 release files

This release

0.1.6 This release

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page