jj-stack finds stacks by following the parent relationships in jj. You do not need to create bookmarks for individual changes or for the stack as a whole.

How jj-stack finds a stack

Suppose your local history looks like this:

flowchart BT
  T["trunk()"] --> A["A"] --> B["B"] --> C["C"]

If C is selected as the head, jj-stack walks back through its parents until it reaches trunk(). The stack contains A, B, and C; the change at trunk() is the base and is not part of the stack.

By default, view, submit, merge, and sync select @ as the head, or @- if @ is empty or has no description. To select a different stack, pass its head change ID:

jj-stack view <head-change-id>
jj-stack submit <head-change-id>

jj-stack list shows the head change ID of each tracked stack. A change ID continues to identify the same logical change after you edit or rebase it, even though its Git commit ID changes.

A bookmark does not divide a stack

Now suppose the bookmark feature-b points to B:

---
config:
  block:
    padding: 32
---
block-beta
  columns 3
  C["C"] space:2
  B["B"] space FEATURE["feature-b"]
  A["A"] space:2
  T["trunk()"] space:2
  T --> A
  A --> B
  B --> C
  FEATURE -.-> B
  style FEATURE fill:none,stroke:none,font-family:monospace

Selecting C still selects A, B, and C. The bookmark neither divides the stack nor changes which pull request belongs to each change.

Passing the bookmark as an argument selects B as the head:

jj-stack view feature-b
jj-stack submit feature-b

These commands select A and B. C is above the selected head and is left out.

view can find the stack containing a change

jj-stack view treats a bare change ID differently from a bookmark or other revset. A change ID asks for the complete stack containing that change. In the example above:

jj-stack view <B-change-id>

shows A, B, and C, while:

jj-stack view feature-b

shows only A and B because the bookmark selects B as the head.

If two visible stack heads descend from B, both stacks contain it. In that case, view stops and asks for a more precise selection. You can pass the change ID of your intended head, or pass a bookmark or other revision expression that resolves to that head.

For submit, select the stack by its head change ID. A middle change or a bookmark selects only the lower part of the stack, and submit stops if GitHub already groups the whole stack as one.

merge follows the same rules, so a middle change selects only the lower part of the stack, and merge stops in the same case. To merge only the bottom of a stack, name the last PR to merge with --pull-request <pr> instead. See merge and sync.

PR branches are separate from your bookmarks

jj-stack creates and updates the Git branches that GitHub needs for PRs, normally with names beginning with jj-stack/. These are separate from your ordinary bookmarks, even when a bookmark points to the same change.

The managed PR branches normally stay out of local bookmark output. Do not create, move, or delete bookmarks whose names start with jj-stack/ yourself. See Configuration if you need a different prefix; choose it before your first submit.

When a bookmark makes a change immutable

jj-stack submits visible, mutable changes. With jj’s default immutability rules, a local bookmark does not make its target immutable, but an untracked remote bookmark can. jj-stack respects your immutable_heads() configuration.

An immutable base at trunk() is expected. If a change inside the stack is immutable, jj-stack stops before submitting or rewriting it.

Use jj bookmark list --all-remotes to see whether a remote bookmark points to the change. If so, handle that bookmark through your normal jj workflow. For example, track it if it is a branch you intend to work on, or move your mutable changes onto the intended base with jj.

If bookmarks whose names start with jj-stack/ appear in your local bookmark output, run jj-stack doctor --fix to remove them and keep them out of future fetches. If the change is still immutable, inspect other remote bookmarks, tags, and your immutable_heads() configuration before deciding how to proceed.