In short: the config is already loaded when the command runs. config list walks every layer from
the top down, flattening tables into dotted names and marking each value that a higher layer shadows. It
then filters by level, defaults and overridden values, and renders each survivor through a template.
It never loads the repo and never snapshots.
The command is cmd_config_list. Its help text: "List
variables set in config files, along with their values" (source). It is
the List variant of ConfigCommand, with the visible alias jj config l
(source, dispatch).
1. Command-line surface
| Argument | Meaning |
|---|---|
NAME (optional) | A dotted name to list. A table name lists everything under it. With no name, the whole config is listed (source, root). |
--user, --repo, --workspace | Keep only values from that layer. At most one can be given. The shared level group is required for other config commands; list makes it optional (source, group, mapping). |
--include-defaults | Also list jj's built-in defaults, which are hidden otherwise. Cannot be combined with a level flag (source). |
--include-overridden | Also list values that a higher layer shadows (source). |
-T, --template | Render each value with this template instead of templates.config_list (source). |
--config NAME=VALUE and --config-file add a command-line layer, which shows up in the
list as source cli. -R chooses whose repo and workspace config are included.
The global flags are on the CLI page.
2. Configuration
| Key | Effect |
|---|---|
templates.config_list | The default template, used when -T is not given (source). Its default is builtin_config_list (source). |
builtin_config_list | Prints format_config_item(self) and a newline, labelled overridden when shadowed (source). format_config_item prints name = value, and comments the line out with # when it is overridden (source). |
builtin_config_list_detailed | The same, followed by # <source> <path> (source). |
colors."config_list …" | Names are green, values yellow, sources blue, paths magenta, and overridden lines bright black (source). |
The template keywords are name, value, source, path and
overridden (source). value is rendered in TOML
syntax with surrounding whitespace trimmed (source). self
is the whole AnnotatedValue, so json(self) works too
(test).
3. End-to-end flow
4. Core architecture touchpoints
| Piece | Role here |
|---|---|
ConfigSource | The layer kinds, from lowest to highest precedence. They print as default, system, env, user, repo, workspace and cli; both environment layers print as env (source). |
AnnotatedValue | One listed value: dotted name, value, source, file path if any, and whether it is overridden. It is serializable, which is what json(self) prints. |
resolved_config_values | Collects the values under a name from every layer, with override marks. It does not hide disabled aliases values (source). |
CommandHelper::parse_template | Parses the template with the user's template aliases, so -T can name builtin_config_list_detailed. Parse warnings go to stderr. |
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
Collecting values
resolved_config_values walks the layers from the highest
precedence down. In each layer it looks up the name. A layer without it is skipped. If a parent of the name is
not a table, the name is recorded as shadowing everything below (source).
Tables are expanded depth first into their leaves, so test-table.y.foo is listed as its own line.
Inline tables are expanded too, because their fields can be overridden separately
(source, test). Arrays stay one value
(test).
Marking overridden values
The walk keeps the set of names already seen in upper layers. A leaf is overridden if that set holds its own name or a name beneath it, or if a parent was already shadowed. A table is shadowed if an upper layer set its name to a plain value (source). At the end, the list is reversed, so lines come out from the lowest layer to the highest (source).
Filtering
With a level flag, only values from that layer are kept. Without one, defaults are dropped unless
--include-defaults. Then overridden values are dropped unless --include-overridden
(source). The override marks are computed before filtering, as the
comment there explains (source). So --user hides a user value
that the repo config overrides, unless --include-overridden is also given
(test).
7. Errors and edge cases
| Situation | Result |
|---|---|
| Name set nowhere | Warning: No matching config key for: <name> on stderr, nothing on stdout, exit 0 (test). |
| No name, nothing left after filtering | Warning: No config to list., exit 0 (test). |
--include-defaults with --user, --repo or --workspace | A clap usage error, since the flags conflict (source). |
| Template fails to parse | The command fails before reading any values (source). |
| Same key in several layers | Only the highest one is printed. With --include-overridden, lower ones are printed first, commented out (test). |
8. Example output
From upstream's test: the user config sets test-key and test-layered-key, the repo
config sets test-layered-key again, and the test environment sets a few values through environment
variables (source):
$ jj config list -Tbuiltin_config_list_detailed --config test-cli-key=test-cli-val
test-key = "test-val" # user $TEST_ENV/config/config.toml
test-layered-key = "test-layered-val" # repo $TEST_ENV/home/.config/jj/repos/0757f5ec8418b4f0983d/config.toml
user.name = "Test User" # env
user.email = "test.user@example.com" # env
debug.commit-timestamp = "2001-02-03T04:05:11+07:00" # env
debug.randomness-seed = 5 # env
debug.operation-timestamp = "2001-02-03T04:05:11+07:00" # env
operation.hostname = "host.example.com" # env
operation.username = "test-username" # env
test-cli-key = "test-cli-val" # cli
The user's test-layered-key is missing because the repo value overrides it. Another test shows
the overridden lines (source):
$ jj config list merge-tools.vimdiff.program --include-overridden --config 'merge-tools.vimdiff.program="command-arg"'
# merge-tools.vimdiff.program = "user"
# merge-tools.vimdiff.program = "repo"
merge-tools.vimdiff.program = "command-arg"