Usage

jj-stack [--help] [--repository REPO] [--config NAME=VALUE] [--config-file PATH] [--debug] [--color WHEN] [--time-output] [--version] [<command> ...]

Create and update stacked GitHub pull requests from your jj changes.

Edit and rearrange changes with jj, then run jj-stack submit to update their PRs. Running jj-stack with no command shows the current stack and its PR status.

Use jj-stack merge when the PRs at the bottom are ready; it waits for GitHub and then updates your local stack. Run jj-stack sync only after a merge it did not wait for.

Core commands

submit
Create or update PRs for a jj stackAliases: sub
view
Check the PR status of one or more jj stacksAliases: status, st, v
list
List the stacks jj-stack is tracking in this repoAliases: ls
merge
Merge pull requests at the bottom of a stack
sync
Update a local stack after GitHub merges or rebases it

Support commands

unstack
Remove a GitHub stack without closing its pull requests
cleanup
Remove unused PR branches, stack overview comments, and saved links
checkout
Check out an existing stack of pull requests
doctor
Check repo setup and GitHub connectivity
in-use
Check whether this repo uses jj-stack

Advanced repair

relink
Reconnect a change to its pull request

Configuration

completion
Print shell completion setup for bash, zsh, or fish

Help

help
Show top-level help, or help for one command

Global options

-h, --help
Show help
--repository REPO
Workspace path to operate on; defaults to the current directory
--config NAME=VALUE
Set a jj config value for this command, such as ui.color=always; repeat for several values
--config-file PATH
Additional jj config file to load; repeat for several
--debug
Enable debug logging
--color WHEN
When to colorize output; possible values: always, never, debug, auto
--time-output
Prefix each output line with elapsed seconds
--version
Show the jj-stack version and exit

Commands

Core commands

submit

Aliases: sub

jj-stack submit [--base REVSET] [--dry-run] [--describe TARGET=FILE | --describe-with HELPER] [--edit | --resume-edit FILE] [--draft | --open] [--label LABELS] [--reviewers USERS] [--team-reviewers TEAMS] [--re-request] [revset]

Create or update GitHub pull requests for the selected stack of changes.

Push the selected changes and create or update one PR per change, in local parent order. Existing PRs stay linked to their changes. Resolve any conflicts before submitting.

The --label, --reviewers, and --team-reviewers flags accept comma-separated values and may be repeated. When passed, they override the corresponding configured defaults for this run.

Common examples:

  • jj-stack submit --dry-run previews the current stack.
  • jj-stack submit creates or refreshes its pull requests.
  • jj-stack submit <head-change-id> selects another stack explicitly.
  • jj-stack submit --base <parent-change-id> <child-head-change-id> submits only the changes after an open parent pull request. Repeat --base whenever you refresh the child stack.
Positional arguments
revset
Stack head to submit; defaults to @ when the working-copy change is described and nonempty, otherwise @-
Options
--base REVSET
Submit changes above this submitted ancestor, using its PR branch as the base; repeat this option on later submits of the dependent stack
--dry-run
Preview submission without pushing branches or changing pull requests
--describe TARGET=FILE
Read a PR body from FILE; TARGET is a change ID, or stack for an overview comment on the head PR
--describe-with HELPER
Generate pull request titles, bodies, and the stack overview with HELPER
--edit
Open planned pull request titles, bodies, and draft states in your editor before submitting
--resume-edit FILE
Reopen a saved editor file instead of generating a new one
--draft
Create new PRs as drafts; use --draft=all to make existing PRs drafts too
--open
Mark submitted PRs ready for review, including existing drafts
--label LABELS
Add labels to the selected PRs; comma-separated or repeat the option
--reviewers USERS
Request reviews by GitHub username; comma-separated or repeat the option
--team-reviewers TEAMS
Request reviews by team slug; comma-separated or repeat the option
--re-request
Request another review from users who last approved or requested changes on an existing pull request
Supplying descriptions

A pull request title comes from a change's subject line, and its body from the rest of the description. When a description has no body, jj-stack uses the repo's pull request template (.github/PULL_REQUEST_TEMPLATE.md, PULL_REQUEST_TEMPLATE.md, or docs/PULL_REQUEST_TEMPLATE.md), or repeats the subject line if no template exists.

Later submits refresh the title and body from the change description, provided both still match the defaults for the last submitted version. Editing either field on GitHub preserves both.

Use --describe CHANGE=FILE to read a PR body from a Markdown file, or --describe stack=FILE to add an overview comment to the head PR of a stack with several changes. Relative paths are resolved from the directory where you run jj-stack.

Use --edit to edit the planned titles, bodies, and draft states before anything is pushed. Save and close the editor to continue. Invalid text or an editor error stops submission before any branches or PRs change. If submission fails, the editor file is kept. Retry the same command with --resume-edit FILE instead of --edit. The file must still name exactly the selected changes.

The editor comes from jj's ui.editor, then $VISUAL, then $EDITOR. Neither --edit nor --resume-edit can be combined with --describe-with.

With --describe-with HELPER, jj-stack runs helper --pr <change-id> once per PR and helper --stack <revset> once for a stack with several changes. Each call must print a JSON object with string title and body fields.

view

Aliases: status, st, v

jj-stack view [--pull-request PR] [--json] [--verbose] [revset ...]

Compare the local and GitHub state of the selected jj stacks.

Show submitted and unsubmitted changes with their current PR state. Long stacks are summarized; use --verbose to show every change, plus unresolved review threads and failed or pending checks with links.

PR state comes from GitHub and stack order comes from local history. This command does not fetch. Run jj git fetch first if you need to update local trunk(). Pass several revsets or repeat --pull-request to inspect several stacks in one run.

Common examples:

  • jj-stack view inspects the stack ending at @ when the working-copy change is described and nonempty, otherwise @-.
  • jj-stack view --pull-request 123 finds the full local stack containing that PR.
  • jj-stack view <change-id> finds the full local stack containing that change.

In terminals with hyperlink support, click a PR label to open it on GitHub. The PR in the "Submitted stack" heading links to the topmost submitted PR.

Positional arguments
revset
Select a stack by its head revset or by any change ID it contains. Combine either with --pull-request; defaults to the current stack
Options
-p, --pull-request PR
Inspect the full stack containing this PR number or URL; repeat to inspect several stacks
--json
Output stack status as JSON
-v, --verbose
Show every change, plus unresolved review threads and check results with links

list

Aliases: ls

jj-stack list [--json]

List the stacks jj-stack is tracking in this local repo.

Each row shows the head change ID, stack size, PR state, and head description. Stacks without any submitted changes and stacks that exist only on GitHub are not listed.

Orphaned PRs are listed separately: their local changes are no longer in any stack. The orphan rows show saved PR links without checking their current GitHub state. To close them and remove their unused branches, stack overview comments, and saved links, use jj-stack cleanup --pull-request orphans --close.

For local stacks, PR state comes from GitHub and stack order comes from local history. This command does not fetch. Run jj git fetch first if you need to update local trunk().

In terminals with hyperlink support, click the PR label in a row to open it on GitHub. A count such as 5 PRs links to the topmost PR in that stack.

Options
--json
Output tracked stacks and orphaned PRs as JSON

merge

jj-stack merge [--dry-run] [--no-wait] [--pull-request PR] [--method METHOD] [revset]

Ask GitHub to merge pull requests at the bottom of a stack.

Starting at the bottom of the stack, jj-stack selects consecutive open, non-draft pull requests. Each must still match the commit that was last submitted; GitHub decides whether reviews, checks, conflicts, and repo rules allow the merge.

The command waits for GitHub to finish, including through a merge queue. It then fetches trunk, removes the merged changes from the local stack, rebases any remaining changes onto the updated trunk, and updates their existing pull requests.

Use --no-wait to return once GitHub accepts the request, or press Ctrl-C to stop waiting; neither cancels the request. After GitHub finishes, run jj-stack sync <head-change-id>.

For a direct merge, one that GitHub performs without a merge queue, --method chooses among the merge methods the repo allows. Without it, the command uses jj-stack.merge_method from your jj config, or the repo's only allowed method, and otherwise prefers rebase, then squash, then a merge commit. If several methods are allowed and the stack contains signed commits, choose one explicitly: merging can discard signatures. A merge queue chooses its own method and ignores --method.

Common examples:

  • jj-stack merge --dry-run previews the merge without changing GitHub.
  • jj-stack merge asks GitHub to merge the ready PRs at the bottom of the stack.
  • jj-stack merge --pull-request 123 --method squash selects PR 123 as the last PR to merge and chooses the merge method explicitly.
Positional arguments
revset
Stack head to merge; defaults to @ when the working-copy change is described and nonempty, otherwise @-. To merge only the bottom portion, use --pull-request instead
Options
--dry-run
Preview the merge without asking GitHub to merge anything
--no-wait
Return after GitHub accepts the merge request; run jj-stack sync after it merges
-p, --pull-request PR
Merge this PR and all PRs below it; the PRs above it stay open and are updated after the merge
--method METHOD
GitHub merge method for a direct merge: merge, rebase, or squash

sync

jj-stack sync [--dry-run] [--pull-request PR] [--all] [revset]

Update a local stack after GitHub merges or rebases its pull requests.

jj-stack sync fetches trunk, removes obsolete local copies of merged changes, rebases the remaining changes, updates their existing pull requests, and cleans up unused PR branches, stack overview comments, and saved links. It never creates pull requests.

Run it after GitHub's Rebase stack action, or after a merge that jj-stack merge did not wait for finishes on GitHub; when merge waits, it performs this update itself. While a selected PR is still queued, sync leaves the stack unchanged.

After a Rebase stack action, sync checks that the PR order and contents match, rebases your original changes, and updates the PR branches with commits that retain their jj change IDs.

Sync stops if it would discard local edits or cannot determine which local changes and PRs to update. The error explains what needs attention. If a rebase produces conflicts, the local rebase stays in place but the affected PRs are not updated. Resolve the conflicts with jj, then run jj-stack submit <head-change-id>.

jj-stack sync --all updates every local stack affected by a completed merge and cleans up merged PRs whose local changes are gone. A blocked stack does not prevent it from syncing independent stacks. It does not handle Rebase stack actions; use jj-stack sync <head-change-id> for those.

Use plain jj rebase when trunk merely advanced and GitHub did not rewrite the commits.

Positional arguments
revset
Stack head to sync; defaults to @ when the working-copy change is described and nonempty, otherwise @-; cannot be combined with --pull-request
Options
--dry-run
Preview the sync without changing your local stack, PRs, branches, or saved links
-p, --pull-request PR
Sync the complete local stack containing this pull request number or URL
-a, --all
Sync every stack affected by a completed merge, including merged PRs whose local changes are gone; cannot be combined with a selector

Support commands

unstack

jj-stack unstack [--dry-run] [--local] [--pull-request PR] [--stack NUMBER] [revset]

Remove a GitHub stack without closing its pull requests.

The PRs keep their base branches and dependencies. Local changes and saved pull request links stay in place. Submitting the same local stack again recreates the GitHub stack.

With a revset or pull request, unstack uses the matching local stack. Use --stack <number> when the GitHub stack no longer corresponds to a single local stack.

--local only forgets jj-stack's saved pull request links. It does not change GitHub, close pull requests, delete PR branches, or modify local changes.

Positional arguments
revset
Stack head to unstack; defaults to @ when the working-copy change is described and nonempty, otherwise @-; cannot be combined with --pull-request or --stack
Options
--dry-run
Preview separating the GitHub stack or forgetting saved links locally
--local
Only forget saved pull request links; do not change GitHub
-p, --pull-request PR
Select the local stack linked to this pull request number or URL
--stack NUMBER
Separate this GitHub stack even when no matching local stack is available

cleanup

jj-stack cleanup [--dry-run] [--close] [--pull-request PR] [revset]

Clean up closed or merged pull requests.

This removes unused PR branches, stack overview comments, and saved pull request links. It keeps your local changes and other comments on GitHub. For merged PRs, run jj-stack sync first to update the local stack.

With no selector, it checks the whole repo. A revset limits cleanup to one local stack; --pull-request selects one tracked pull request, and --pull-request orphans selects every tracked pull request whose local change is gone. Add --close to a --pull-request selection to retarget those open pull requests to trunk and close them before cleanup. To close a whole stack, first run jj-stack unstack, then close each PR from the top of the stack downward.

Without --close, open pull requests are left alone.

Cleanup keeps a branch while another PR still uses it as its base (an open PR, or a closed PR that GitHub could still reopen), or while an unmerged PR in a GitHub stack needs it. The message names the PR or stack to update before retrying cleanup.

Positional arguments
revset
Revset selecting the stack to clean up; omit it to check every tracked pull request; cannot be combined with --pull-request
Options
--dry-run
Preview cleanup without closing PRs or removing anything
--close
Close selected open pull requests before cleanup; requires --pull-request
-p, --pull-request PR
Clean up this tracked pull request, or use orphans for every tracked pull request whose local change is gone

checkout

jj-stack checkout [--pull-request PR | --revset REVSET | --pick]

Check out an existing stack of pull requests submitted with jj-stack.

Use this command to continue work you submitted from another machine or checkout. It fetches any missing commits, saves their pull request links, and runs jj edit on the selected change. If a PR's version differs from your local version, checkout keeps both and explains how to resolve the difference.

Use --pull-request to bring in a PR and the PRs below it. Select the top PR to check out the whole stack. Use --pick to choose from local and GitHub stacks in an interactive list. Use --revset to edit the head of a stack this checkout already tracks; it confirms that every change has a saved pull request link and does not contact GitHub.

The PRs and their head branches must belong to the repo selected by your Git remote. PR branches must use jj-stack's branch naming scheme with this checkout's configured prefix, normally jj-stack/. If the original checkout used a custom prefix, set the same jj-stack.branch_prefix here first. PRs with head branches in another repository, such as a contributor's fork, are not supported.

Checkout does not rebase changes or modify GitHub. To start a new change on top, run jj new afterward.

In terminals with hyperlink support, the PR beside "Top" in each GitHub stack's --pick entry is a clickable link. Open it to inspect that stack on GitHub before choosing an entry.

Options
-p, --pull-request PR
Pull request to check out, by number or URL
--revset REVSET
Edit the head of a locally tracked stack without contacting GitHub; defaults to @ when the working-copy change is described and nonempty, otherwise @-
--pick
Interactively choose a local or GitHub stack to check out

doctor

jj-stack doctor [--fix]

Check repo setup and GitHub access.

Checks the Git remote, authentication, GitHub access, your permission to push to the repo, stacked pull request support, and GitHub's default branch. It also reports PR bookmarks imported by a fetch and leftovers from an interrupted checkout or sync.

Run jj-stack doctor --fix to configure fetches to skip PR branches, forget untracked PR bookmarks imported by a fetch, and remove checkout or sync leftovers. These repairs affect only this local repo.

The command exits 1 if a check fails and 0 otherwise. Warnings and problems repaired by --fix do not count as failures. The report includes recovery commands where available.

Options
--fix
Apply safe local repairs instead of only reporting problems

in-use

jj-stack in-use

Check whether this repo contains valid jj-stack tracking data.

Use this in scripts to check whether jj-stack has been set up in this repo. It exits silently with 0 when valid tracking data exists, or 1 when there is none. Repo or tracking errors produce a diagnostic and exit 11. The command does not contact GitHub or change local state.

Advanced repair

jj-stack relink [--replace-remote] PR REVSET

Reconnect a change to its pull request.

Use jj-stack relink in these situations:

  • You ran jj-stack unstack --local and now want to use those PRs again. That command removes the local links between changes and PRs, but leaves the PRs open on GitHub. Run jj-stack relink <pr> <change-id> for each PR to restore its link. The same repair applies if you deleted jj-stack's local tracking file.
  • Someone pushed another version to your PR branch, and you want to replace it with your local version. jj-stack submit stops to avoid overwriting their work. After checking the changes on GitHub, run jj-stack relink --replace-remote <pr> <change-id> so the next jj-stack submit can overwrite that version. To keep their work instead, bring it into your repo with jj-stack checkout --pull-request <pr>.

After relinking, run jj-stack submit <head-change-id> to update the stack's PRs. Use the change ID for each PR when relinking, and the top change's ID when submitting the stack. The existing PRs keep their numbers and discussions.

jj-stack relink itself only updates local tracking. jj-stack submit changes GitHub. jj-stack relink reconnects an open PR in this repo to the change it was created from. It cannot transfer a PR to a different change ID, even with --replace-remote.

Positional arguments
PR
Pull request number or URL
REVSET
Local change to link to the pull request
Options
--replace-remote
Link even if the PR branch has changed unexpectedly; the next jj-stack submit overwrites it with the local change

Configuration

completion

jj-stack completion [--jj-alias NAME] {bash,zsh,fish}

Print a shell completion script. Load it from your shell's startup file after any existing completion setup. For zsh, run it after compinit.

If you use a jj stack alias, include --jj-alias stack to complete commands and options after both jj-stack and jj stack. For example:

  • Bash: eval "$(jj-stack completion bash --jj-alias stack)"
  • Zsh: eval "$(jj-stack completion zsh --jj-alias stack)"
  • Fish: jj-stack completion fish --jj-alias stack | source

Omit --jj-alias if you only use the standalone jj-stack command.

Positional arguments
shell
Shell to generate completion support for
Options
--jj-alias NAME
Also complete an existing jj alias that runs jj-stack, such as stack

Help

help

jj-stack help [--all] [command]

Show top-level help or the detailed help for one command. Use --all to show every command and global option in top-level help.

Positional arguments
command
Command to describe
Options
--all
Show every command and global option in top-level help