Docs

Search Vex documentation

Search public documentation, commands, and release notes.

Browse documentation

Repositories and Changes

Submit and stacking

How vex submit turns work into a reviewable Change, and how to stack dependent changes from a Vex-native checkout or a plain Git clone.

On this page

vex submit turns local work into a reviewable Change. It never rewrites the target branch itself — landing does that, separately, once review and CI gates pass. A stack is just what you get when one submitted Change is built directly on another: Vex tracks the relation and lands the stack in dependency order, and where the repository is GitHub-synced, projects each Change to its own pull request with the parent's branch as the base rather than one flat diff against trunk.

There are two ways to reach vex submit, and this page covers both:

  • A Vex-native checkout — cloned with vex clone, working against the JJ change graph directly. This is the deep, first-class path: stacking is implicit in how you build commits.
  • A plain Git clone — no native Vex workspace at all, just git clone and an ordinary branch. vex submit still works here, and branches can still stack, but the relationship has to be named explicitly.

The submit model

Regardless of which mode you're in, vex submit does the same three things: import the exact content you built, create or update the Vex Change and its patchset, and (where the repository is GitHub-synced) project a matching pull request. Re-submitting the same work appends a patchset to the existing Change rather than creating a new one — vex submit is safe to run again after a small fix, a rebase, or a retried network call.

Stacking from a Vex-native checkout

In a Vex-native checkout there is no separate "stack" command to create one. A stack is just the ordinary JJ change graph: vex new starts the next change on top of wherever you are, so a chain of vex new calls is already a stack before you submit anything.

Terminal
vex new main -m "feat: add the storage layer"
# ...edit files...
vex new -m "feat: use the storage layer in the API"
# ...edit files...
vex submit -r 'main..@' --target main

vex submit -r 'main..@' submits every change in that range as its own Change, each one still carrying its place in the graph. Vex numbers parents before children, so the change numbers themselves read in dependency order. Submitting a single change at a time works the same way — vex submit --target main from @ submits just the current change, and Vex still detects its parent from the JJ graph if that parent was submitted too.

Inspect and manage a stack's identity with vex stack:

Terminal
vex stack list                # active graph-derived stacks in this repository
vex stack show '#42'          # the stack a given Change belongs to
vex stack name my-feature     # give a stack a stable bookmark alias

If the target branch moves while your stack is still open, vex sync fetches it and restacks your submitted changes on top, then resubmits — pass --no-restack to pull and resubmit without rewriting anything locally, and --restack to say so explicitly. A GitHub-synced repository projects each Change in the stack to its own pull request, base branch chained to the parent's branch automatically.

Submitting (and stacking) from a plain Git clone

vex submit also works from an ordinary git clone — no .jj directory, no native Vex workspace. Push a branch, then submit it:

Terminal
git clone https://github.com/<org>/<repo>.git
git checkout -b feature/widget
# ...commit work...
git push origin feature/widget
vex submit --target main

Vex re-fetches the exact branch head itself (it does not trust a locally-computed diff), imports the commits between the target and your branch, and creates the Change and pull request. Push again and re-run vex submit to append a patchset — the branch is adopted, never rewritten out from under you.

Stacking is explicit in this mode. There is no local graph for Vex to read a parent out of, so name the parent branch with --target:

Terminal
git checkout -b feature/widget-styling feature/widget
# ...commit work built on top of feature/widget...
git push origin feature/widget-styling
vex submit --target feature/widget

Vex resolves feature/widget to the Change that already adopted it, and the new Change inherits that Change's landing target rather than trying to land onto feature/widget itself — the pull request Vex creates uses feature/widget as its base, so it renders as a genuinely stacked PR, not a flat diff against main.

Two things this mode requires that a Vex-native checkout does not:

  • Submit bottom-up. The parent branch has to already be submitted (and pushed) before you submit a branch stacked on it — Vex needs to find an existing Change for the parent to attach to.
  • Rebase forward, don't fall behind. A stacked branch has to be built on its parent's current pushed head. If the parent branch has moved since you branched from it, vex submit rejects the child with a stack_parent_not_at_tip error rather than importing a stale diff — rebase your branch onto the parent's latest commit, push, and submit again.

There is no local restack for this mode: when a parent branch changes, git rebase the child branch yourself and push, the same as you would with any other Git remote.

Commands, side by side

TaskVex-native checkoutPlain Git clone
Start the next change in a stackvex newgit checkout -b <branch> <parent-branch>
Submit one changevex submit --target mainvex submit --target main
Submit a whole stack at oncevex submit -r 'main..@' --target mainnot available — submit each branch after its parent
Stack on another submitted changeautomatic (JJ parent)vex submit --target <parent-branch>
Update after a small fixedit, then vex submit againcommit, git push, then vex submit again
Pull the target and reconcilevex sync --target maingit pull on the target, then resubmit
Inspect a stackvex stack show '#42'vex change show '#42' (walk parent_change)
Landvex land '#42'vex land '#42'

Landing a stack

Landing is identical once a Change exists, in either mode: vex land '#N' requests server-verified landing for that Change and its submitted-together closure — every open ancestor it depends on lands first, in order. vex land '#N' --dry-run prints the computed closure and gate state without requesting anything; --when-ready queues the change and lets the server land it once CI and approvals clear instead of refusing while they're outstanding.

A child cannot land ahead of its parent. If the parent is still open, landing the child lands the whole stack up to that point in one queued request — you do not need to land each Change in a stack by hand.