Configuration
Configuration is dot-notation keys over JSON, merged from up to four layers. Later layers win:
| Priority | Layer | Persisted? |
|---|---|---|
| 1 (lowest) | .jj/config.json (global) |
yes |
| 2 | config.load({ override }) |
no — session only |
| 3 | .jj/workspace-config.json |
yes |
| 4 (highest) | config.load({ workspace }) |
no — session only |
The API
Section titled “The API”await jj.config.set({ name: 'user.name', value: 'Alice' }); // persists to .jj/config.jsonawait jj.config.get({ name: 'user.email' }); // merged view; null if unsetawait jj.config.list(); // whole merged objectawait jj.config.load({ override, workspace }); // reload + apply layersname takes dot paths (user.email, custom.nested.value); values can be
anything JSON. (key is accepted as a legacy alias for name; set also
takes a scope of 'user' | 'repo' | 'global'.)
Programmatic layers
Section titled “Programmatic layers”No file I/O — merged in memory over whatever the files say:
await jj.config.load({ override: { ui: { theme: 'light' } } });
// workspace outranks override:await jj.config.load({ override: { ui: { theme: 'light' } }, workspace: { ui: { theme: 'high-contrast' } }, // ← this wins});Three patterns this exists for:
// Tests — configure without touching the filesystem
// Browser — config from user input, no IndexedDB writesawait repo.config.load({ workspace: { user: { email: emailInput.value } } });
// Environment switchesconst cfg = process.env.NODE_ENV === 'production' ? prodCfg : devCfg;await repo.config.load({ workspace: cfg });Workspace config files
Section titled “Workspace config files”Drop overrides in .jj/workspace-config.json (layer 3) for per-checkout
settings that should persist — e.g. a work email in a work clone:
Call config.load() after writing it (or on your next session) to apply.
Deep-merge semantics
Section titled “Deep-merge semantics”Objects merge recursively; overriding one nested key keeps its siblings:
await jj.config.set({ name: 'user.name', value: 'Alice' });
await jj.config.get({ name: 'user.name' }); // 'Alice' (kept)Resetting
Section titled “Resetting”Programmatic layers are session-only. A bare load() drops them and
returns you to what the files say:
await jj.config.load({ workspace: { temp: { flag: true } } });await jj.config.load(); // temp.flag gone; files rule againTo reset user identity wholesale: jj.userConfig.init({ userName, userEmail }) + jj.userConfig.save().
What lives in config
Section titled “What lives in config”The schema is open. The keys the library itself reads are user.name /
user.email (commit authorship); everything else — ui.*, merge.*, your
own namespaces — is yours to define and read back. This is library
configuration: real jj’s config-file discovery (/etc/jj, TOML files,
conditional scopes) is out of scope here.
Everything above works identically in the browser; file-backed layers just live in IndexedDB via LightningFS.