Expand description
vcs-jj — automate Jujutsu (jj) from Rust by driving the jj CLI.
It shells out to the installed jj binary and parses its templated output
into typed values — so you get jj’s own behaviour and config, not a
reimplementation of the operation log or backend. Async throughout,
structured errors, and mockable. Every command runs inside an OS job (via
processkit) so a jj subprocess tree can never be orphaned, and honours
an optional per-client timeout.
§The surface
JjApi— the object-safe trait every operation lives on. Depend on&dyn JjApi(or generically onimpl JjApi) so a test can swap the real client for a double. Most methods take the working directory as the first argument and return typed results (Change,Bookmark,BookmarkRef,Operation,Workspace,ChangedPath,FileDiff,AnnotationLine, …) or a structuredError. The groups: changes (status,log,describe,new_change), bookmarks (bookmarks,bookmark_create,bookmark_move, …), the operation log (op_log,op_head,op_restore,op_undo), diff/query (diff,diff_stat,evolog,file_annotate,template_query), mutations (rebase,squash_paths,split_paths,absorb,abandon), git sync (git_fetch,git_push,git_clone,git_import), and workspaces (workspace_list,workspace_root,workspace_add).Jj— the real client.Jj::newuses the job-backed runner;Jj::with_runnerinjects a fake one for tests. It is generic over theProcessRunnerseam, defaulting to the production runner.JjAt— a cwd-bound view (Jj::at) whose methods drop the leadingdir, sojj.at(dir).status()reads asjj.status(dir)— handy when one client drives one checkout.Jj::transaction— run a mutation sequence with op-log rollback: capture the current operation, run a closure, and onErrrestore the repo to it. The op log is jj’s safety net; this wraps it as a scope.Jj::workspace_rootsis a sibling inherent method — a bounded fan-out resolving many workspace roots at once.- Builder specs for the multi-option commands —
WorkspaceAdd,SquashPaths— each#[non_exhaustive], built with a constructor + chained setters, named after the flags they emit.JjFilesetwraps a repo-relative path as an exact-pathfile:"…"fileset;RevsetExpris an optional up-front-validated revset newtype for untrusted input. conflict— a typed model of jj’s native conflict markers (thediff/snapshotstyles): parse a materialized file into structured regions, re-render byte-exact, and resolve to a chosen side. (Files materialized in thegitstyle are parsed byvcs_git::conflictinstead.)capabilities— probe the installed binary’s version against this crate’s validated floor (jj ≥ 0.38); seeJjCapabilities.
There is deliberately no Jj::hardened() counterpart to vcs-git’s
untrusted-repo profile: jj has no repo-local hooks, and its config comes from
the user/repo TOML files jj itself trusts. In a colocated repo the risk
lives on the git side — git hooks fire when git commands run there, so
harden the Git client you point at it.
§Recipes
Read state — depend on the trait so the same code takes a real client or a mock:
use std::path::Path;
use vcs_jj::{Jj, JjApi};
let jj = Jj::new();
let dir = Path::new(".");
let current = jj.current_change(dir).await?; // the working-copy change `@`
let dirty = !jj.status(dir).await?.is_empty(); // any working-copy edit?Mutate inside a transaction — an Err rolls the op log back:
use std::path::Path;
use vcs_jj::Jj;
let dir = Path::new(".");
jj.transaction(dir, |tx| async move {
tx.describe("wip").await?;
tx.new_change("next").await // an Err here undoes the describe
})
.await?;§Testing
Two seams: enable the mock feature for a mockall-generated
MockJjApi (stub whole methods), or inject a
ScriptedRunner with Jj::with_runner to
exercise the real argv-building and parsing against canned output. The
cross-cutting testing patterns live in
vcs-testkit’s guide.
§Safety
Every caller value placed in a bare positional argv slot (bookmark name,
revset, operation id, merge parent, …) is refused before spawning if it is
empty or starts with - (jj would parse it as a flag); flag-value slots
(-r <revset>, -m <msg>) and the run/run_raw escape hatches are not
guarded. For eager validation at an input boundary, RevsetExpr validates
up front. Paths go through the exact-path JjFileset form.
§In-depth guide
Beyond this page, this crate ships a full how-to guide — rendered on docs.rs
from docs/. See the guide module. The conflict model is covered by
vcs-git’s conflicts guide,
which spans both backends.
Modules§
- __
mock_ Mock JjApi - __
mock_ Mock JjApi_ JjApi - blocking
- Synchronous, best-effort helpers for contexts that cannot
.await— chiefly aDropguard. They shell out throughstd::processdirectly (no async, no job-containment), so reserve them for short-lived cleanup. - conflict
- Typed model of jj’s materialized conflict markers — parse a conflicted file’s content into structured regions and write a chosen resolution back. Pure functions (no subprocess), so everything here is hermetic.
- guide
- vcs-jj — Jujutsu CLI guide
Structs§
- Annotation
Line - One line of
jj file annotateoutput: which change last touched it. - Bookmark
- A jj bookmark, parsed from
jj bookmark listoutput. - Bookmark
Ref - A bookmark from
jj bookmark list -a— local or remote-tracking. - Cancellation
Token cancellation - A token which can be used to signal a cancellation request to one or more tasks.
- Change
- A jj change, parsed from a
\t-delimited template row. - Changed
Path - One entry from
jj diff --summary: a single-letter status (M/A/D/…) and the (forward-slash-normalised) path it applies to — the new path for a rename/copy, with the original onold_path. - Diff
Stat - Aggregate line/file counts from a diff stat (
git diff --shortstat,jj diff --stat). - File
Diff - One file’s entry in a parsed git-format unified diff (
git difforjj diff --git). - Hunk
- A single
@@ … @@hunk within aFileDiff. - Jj
- The real jj client. Generic over the
ProcessRunnerso tests can inject a fake process executor;Jj::new()uses the real job-backed runner. - JjAt
- A
Jjclient with a working directory bound, so calls drop the leadingdirargument —jj.at(dir).status()isjj.status(dir). Construct one withJj::at(or, through the facade,vcs_core::Repo::jj_at). Cheap to copy: it only borrows the client and the path. - JjCapabilities
- What the installed
jjbinary supports, probed viaJjApi::capabilities. A value type — the client holds no state, so probe once and keep the result (callers cache it). - JjFileset
- An exact-path jj fileset (
file:"<path>"), so path metacharacters like(,),|,*are treated literally rather than as fileset operators. - JjVersion
- A parsed CLI version (
major.minor.patch).Ordcompares numerically, so a caller can gate a feature on a minimum version;Hashlets it key a map (e.g. a per-version capability cache). - Mock
JjApi - The jj operations this crate exposes — the interface consumers code against and mock in tests.
- Operation
- One entry of
jj op log(an operation-log row). - Process
Result - The captured result of running a process to completion.
- Revset
Expr - A pre-validated revset expression, for callers that accept revsets from
untrusted input (UIs, bots, agents) and want to fail early. Deliberately
minimal — jj’s revset grammar is too rich to validate here — it only
guarantees the expression is non-empty and cannot be parsed as a flag
(no leading
-), matching the internal guard the positional-revset methods apply anyway. The dir-taking methods stay&str; this type is optional up-front validation, not a required wrapper. - Squash
Paths - Options for
JjApi::squash_paths(jj squash --from <from> --into <into> [--use-destination-message] <filesets>). - Workspace
- A workspace from
jj workspace list(rendered withWORKSPACE_TEMPLATE). - Workspace
Add - Options for
JjApi::workspace_add(jj workspace add).
Enums§
- Change
Kind - How a file changed in a unified diff.
- Diff
Line - One line inside a
Hunk, tagged by its role. The stored text excludes the leading/+/-marker and the line terminator — a CRLF-origin diff’s trailing\ris stripped along with the\n, so reconstruct exact bytes fromFileDiff::raw, not from these lines. - Diff
Spec - What a
JjApi::diff/JjApi::diff_textcall compares. - Error
- Errors produced when launching or running a child process.
- Sparse
Mode - How a new workspace inherits sparse patterns (
jj workspace add --sparse-patterns <mode>).
Constants§
- BINARY
- Name of the underlying CLI binary this crate drives.
Traits§
- JjApi
- The jj operations this crate exposes — the interface consumers code against and mock in tests.
Functions§
- is_
transient_ fetch_ error - Whether a failed
fetch/fetch_remote_branch/remote_branch_existslooks transient (DNS, timeout, dropped connection) and is worth retrying. - parse_
diff - Parse a git-format unified diff into one
FileDiffper file. Works ongit diffandjj diff --gitoutput alike. Public so a consumer can parse diff text it obtained by other means.
Type Aliases§
- Result
- Crate result alias.