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.7

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.7
File Size Uploaded
jj_stack-0.1.7.tar.gz 491.9 kB Details

Built distribution (wheel)

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

Total release size: 737.5 kB

Release files / jj_stack-0.1.7.tar.gz

Download URL jj_stack-0.1.7.tar.gz
Size 491.9 kB
Tags Source
SHA-256 checksum
How to use checksums
58608950807108aa0a99870f7a847672ee1548dec9a6ed3df5f8783700535674
BLAKE2b-256 checksum
How to use checksums
da195e8b4435789b9f4fbd822de6324e2eb54cbe71f6ec86347c2b3300b2ee54
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","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.7-py3-none-any.whl

Download URL jj_stack-0.1.7-py3-none-any.whl
Size 245.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
53676c3f73cd87e0b6b74528e162d908813e3a6fe1464b5534f621458afa4d88
BLAKE2b-256 checksum
How to use checksums
46fc7c7902970fd02005ee8c57394761c01f1bf1efd4387dab2e10a1ef703f0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","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

This release

0.1.7 This release

2 release files

0.1.6

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