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 let jj-stack update the matching PRs.

Quick start

Requirements

  • Python 3.14 or newer
  • uv
  • jj 0.44.0 or newer
  • GitHub authentication

Install

Install jj-stack from PyPI:

uv tool install jj-stack

To upgrade, rerun that command with --force. If the command is not on your shell PATH, run uv tool update-shell.

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

eval "$(jj-stack completion zsh)"

bash and fish work the same way.

To invoke it as jj stack and complete that alias too, see Configuration.

Submit your first stack

Start with a linear series of local jj changes on top of trunk(). In a new repo, check the 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

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

jj-stack/add-ui-...         -> PR #3 (base: jj-stack/add-api-...)
jj-stack/add-api-...        -> PR #2 (base: jj-stack/refactor-model-...)
jj-stack/refactor-model-... -> PR #1 (base: main)
main                        -> trunk

The PR branches normally stay out of your local bookmark view. When you rewrite a change, jj-stack updates that change's existing PR branch and PR, along with the PR branches and PRs for dependent changes.

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 changes at the bottom are ready.
  6. After a queued or externally initiated merge finishes, run jj-stack sync <head-change-id>.

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.

Learn more

The built-in help is the canonical flag reference:

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

Coding agent integration

Install the bundled skill to teach coding agents to work with local jj stacks and refresh their GitHub PRs safely:

gh skill install bos/jj-stack jj-stack

See the skill source. In my evaluations with Codex and Claude Code, agents with the jj-stack skill succeeded in 11/12 scenarios versus 6/12 without it, with one critical error versus four, using 60% fewer failed command attempts and 18% fewer tool calls. (The critical error was due to Claude Haiku understanding a rule and ignoring it. I haven't figured out how to get smaller Claude models to behave better, and I don't personally use them.)

Performance

Although jj-stack is written in Python, this does not significantly affect its speed. The real determinants of its performance are the GitHub API and the jj command.

The GitHub API is slow; a single roundtrip takes many hundreds of milliseconds. jj-stack reduces its impact with:

  • GraphQL batch requests where possible
  • concurrent use of the GitHub REST API
  • periodic audits that its queries are minimal in extent

In pursuit of good performance, jj-stack also batches calls to jj and minimizes the amount of work those calls must do.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jj_stack-0.1.0.tar.gz (393.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jj_stack-0.1.0-py3-none-any.whl (208.8 kB view details)

Uploaded Python 3

File details

Details for the file jj_stack-0.1.0.tar.gz.

File metadata

  • Download URL: jj_stack-0.1.0.tar.gz
  • Upload date:
  • Size: 393.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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}

File hashes

Hashes for jj_stack-0.1.0.tar.gz
Algorithm Hash digest
SHA256 212be1c0174e940f85203b5045df51df065400d8c55c0dd35d1e5e50b56c62d7
MD5 f752b8e7fadf1808c512ba726dcc8809
BLAKE2b-256 fe223d5bc73d36d1c7233d917e287bf403ad8229bb6823128ba21d752e90c20a

See more details on using hashes here.

File details

Details for the file jj_stack-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: jj_stack-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 208.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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}

File hashes

Hashes for jj_stack-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 57533876e9e1499bafd94ff2b51eb815860285633cff7fb3f87fd54123737486
MD5 e5a5688fda9a3e4708cad27a35bb34f1
BLAKE2b-256 3bcbd6b6f366e507838a554837d417fcbf0e65a642b0b5694633bdf25e8a93ed

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page