In short: the config was already loaded and resolved before the command ran. config get
looks the name up through every layer, from the highest precedence down, merging tables along the way.
It prints strings raw and everything else as TOML. It never loads the repo and never snapshots.
The command is cmd_config_get. Its help text: "Get the value of
a given config option", followed by an example contrasting it with jj config list
(source). It is the Get variant of ConfigCommand,
with the visible alias jj config g (source,
dispatch).
1. Command-line surface
| Argument | Meaning |
|---|---|
NAME (required) | A dotted config name such as user.name or table (source). It is parsed as a TOML key, so a component with dots or spaces can be quoted (source). Shell completion offers the leaf keys of jj's config schema (source). |
There are no level flags: unlike set or path, it never asks which file, only what the
merged result is. --config NAME=VALUE and --config-file are layers like any other,
so jj --config user.name=x config get user.name prints x. -R chooses
whose repo and workspace config are included.
The global flags are on the CLI page.
2. Configuration
The command reads configuration instead of being shaped by it. Any key can be asked for, including
the built-in defaults, which are the lowest layer: in an empty config, config get still returns
their values (test). The output format is fixed; there is no template.
3. End-to-end flow
4. Core architecture touchpoints
| Piece | Role here |
|---|---|
ConfigSource | The layer kinds, in precedence order from lowest to highest: defaults, system, base environment variables, user, repo, workspace, override environment variables, command-line arguments. |
ConfigEnv::resolve_config | Turns the raw layers into the StackedConfig the command reads, resolving conditional scopes against the home directory, repo path, workspace path, command name, hostname and environment. |
UserSettings::get_value | What the command calls; it delegates to StackedConfig::get_value. |
From<ConfigGetError> | Makes a lookup failure a config error, which is printed as Config error: … with a pointer to the config docs, exit 1 (source). |
5. What it reads, and snapshotting
It never snapshots and never loads the repo. Everything it reads was loaded before dispatch: the system and user
config files, then the repo and workspace config of the workspace containing the current directory
(source), replaced by those of -R's workspace if given
(source). Repo config is read only if its file exists
(source). It writes nothing.
6. Algorithm and invariants
Looking a name up
get_merged_item walks the layers from the highest precedence
down. A layer with no value at that name is skipped. A non-table value found before any table is the answer at
once. Tables are collected; a non-table found after a table, or a layer where a parent of the name is not a table,
shadows everything below and stops the walk. The collected tables are then merged from the bottom up, each upper
table's fields overriding the lower's recursively (source). If nothing
was found, the result is NotFound (source).
Printing it
A string is printed as its bare value, without quotes. Integers, floats, booleans, datetimes, arrays and inline
tables are printed in TOML syntax with surrounding whitespace removed (source).
So a merged table comes out as one inline table. Because strings lose their quotes, false and
"false" print the same; upstream's own test notes this (source).
7. Errors and edge cases
| Situation | Result |
|---|---|
| Name set nowhere | Config error: Value not found for <name> plus the docs pointer, exit 1 (test). |
| Name is a table set in several layers | Printed as one merged inline table, upper layers winning field by field (test). |
| Same scalar set in two files | The higher-precedence one wins (test). |
| No argument | A clap usage error: NAME is required (source). |
| Run outside any workspace | Works; only the default, system, user, environment and command-line layers apply (test, run from the test root). |
8. Example output
From upstream's test, with two user config files: the first sets table.string,
table.int, table.list and table.overridden = "foo", and the second sets
table.overridden = "bar" (source):
$ jj config get table.string
some value 1
$ jj config get table.int
123
$ jj config get table.list
["list", "value"]
$ jj config get table
{ string = "some value 1", int = 123, list = ["list", "value"], overridden = "bar" }
$ jj config get table.overridden
bar
$ jj config get nonexistent
Config error: Value not found for nonexistent
For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.