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

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.5
File Size Uploaded
jj_stack-0.1.5.tar.gz 457.9 kB Details

Built distribution (wheel)

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

Total release size: 693.5 kB

Release files / jj_stack-0.1.5.tar.gz

Download URL jj_stack-0.1.5.tar.gz
Size 457.9 kB
Tags Source
SHA-256 checksum
How to use checksums
30d4b926ce3cbeb196f8040b81d8c0cdbf3e316f516abc35387533d8df2f0eea
BLAKE2b-256 checksum
How to use checksums
6b75c1ea5b3d99fc5e40e7a3e70ea3b031d826140a124dcb1e6ce244ba86cb3a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","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.5-py3-none-any.whl

Download URL jj_stack-0.1.5-py3-none-any.whl
Size 235.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e4241b2612c5f84de681443f3e3d64befb3d447e000468a499cb8de8fdf7831f
BLAKE2b-256 checksum
How to use checksums
1c12aa0fe1fae558d2139d31aca31794c5c6247a61813a0ae0c289d6c9e3b13b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","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

0.1.6

2 release files

This release

0.1.5 This release

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