In short: it picks exactly one file the same way config set does, deletes the value at that name
from the file's TOML document, and saves. It fails without writing if the name is not set in that file, or if it
names a whole [table]. It never loads the repo and never snapshots.
The command is cmd_config_unset. Its help text: "Update a
config file to unset the given option." (source). It is the
Unset variant of ConfigCommand, with the visible alias jj config u
(source, dispatch).
1. Command-line surface
| Argument | Meaning |
|---|---|
NAME (required) | A dotted config name, parsed as a TOML key (source). |
--user, --repo, --workspace, --file PATH | Which file to edit; exactly one is required. These are the same ConfigTargetArgs that config set and config edit use (source, definition). |
Shell completion for NAME runs jj config list and offers only names whose source is
user, repo or workspace config, each tagged with its level and value. If one level flag is already on the command
line, it lists that level only, including values overridden by higher layers
(source).
The global flags are on the CLI page.
2. Configuration
The loaded config decides which file is edited, exactly as for
config set: the first loaded file of the chosen level, or
that level's default path if none was loaded (source), or a
--file path checked by resolve_file_to_edit.
It only touches that file, so a value also set in another layer still applies afterwards.
3. End-to-end flow
4. Core architecture touchpoints
| Piece | Role here |
|---|---|
ConfigTargetArgs::edit_config_file | Shared with config set and config edit: turns the target flag into one ConfigFile, or fails with "No user/repo/workspace config path found to edit". |
ConfigFile::delete_value | Copies the layer if it is shared, then calls ConfigLayer::delete_value, which returns the old value, or none if nothing was there. |
ConfigUpdateError | Its WouldDeleteTable variant is the only way the deletion itself fails. |
ConfigFile::save | Serializes the edited document and writes it to the file's path. |
5. What it reads, and snapshotting
It reads only the chosen config file, through the config already loaded at startup or from disk, and never the
repo or the working copy, so there is no snapshot. --repo and --workspace look up the
repo's or workspace's config path as config set does (source). It writes only
when a value was actually removed (source).
6. Algorithm and invariants
Finding the value
ConfigLayer::delete_value splits off the last key and walks
the others from the document root. If a parent is missing, or is not a table or inline table, the result is
"nothing deleted" rather than an error (source). So a key inside an
inline table can be removed, leaving inline-table = {}
(test).
Removing it
If the leaf is a value, including an array or an inline table, it is removed and its old value returned. If it
is a non-inline [table], the call fails with WouldDeleteTable; the file is not written
(source, test). A table emptied
by removing its last key stays as an empty table header (test). Text that
belonged to the removed key goes with it: in upstream's tests the #:schema line that
config set wrote into a new repo config file is gone once its only key is unset, leaving the file
empty (test).
Nothing there
If no value was removed, the command fails with "NAME" doesn't exist before saving
(source). This includes a target file that does not exist yet, which
edit_config_file loads as an empty document, so unset never creates a config file.
7. Errors and edge cases
| Situation | Result |
|---|---|
| Name not set in that file | Error: "NAME" doesn't exist, exit 1 (test). |
| Name is a non-inline table | Error: Failed to unset NAME / Caused by: Would delete entire table NAME, exit 1, file unchanged (test). |
| Name is an inline table | Removed like any value (test). |
| No target or no name | A clap usage error, exit 2: the target group is required (source). |
8. Example output
From upstream's tests, with a user config file holding inline-table = { foo = true } and a
[non-inline-table] with foo = true (source,
last command):
$ jj config unset --user inline-table
$ jj config unset --user non-inline-table
Error: Failed to unset non-inline-table
Caused by: Would delete entire table non-inline-table
$ cat ~/.config/jj/config.toml
[non-inline-table]
foo = true
$ jj config unset --user nonexistent
Error: "nonexistent" doesn't exist