jj / How jj config edit works

How jj config edit works

jj config edit --user|--repo|--workspace|--file PATH opens one config file in your editor, and reopens it until it parses or you choose to restore the previous contents.

On this page
  1. 1. Command-line surface
  2. 2. Configuration
  3. 3. End-to-end flow
  4. 4. Core architecture touchpoints
  5. 5. What it reads, and snapshotting
  6. 6. Algorithm and invariants
  7. Creating the file first
  8. The edit loop
  9. Without a terminal
  10. 7. Errors and edge cases
  11. 8. Example output

In short: it picks exactly one file the same way config set does, creates it if it does not exist, and runs ui.editor on the file itself, not a copy. Afterwards it re-parses the file as TOML. If that fails, it asks whether to keep editing; answering no writes back the contents it loaded before the editor ran. It never loads the repo and never snapshots.

The command is cmd_config_edit. Its help text: "Start an editor on a jj config file. Creates the file if it doesn't already exist regardless of what the editor does." (source). It is the Edit variant of ConfigCommand, with the visible alias jj config e (source, dispatch).

1. Command-line surface

ArgumentMeaning
--userThe first user config file (test; source).
--repoThe repo's config file, kept outside the repo under the user's config directory (source, test).
--workspaceThe current workspace's config file (source).
--file PATHA file at a location jj recognizes as config; created with missing parents if absent (source).

The command has no other arguments: ConfigEditArgs is just the shared target group, of which exactly one is required (source, group).

The global flags are on the CLI page.

2. Configuration

SettingEffect
ui.editorThe command to run (source). It is read before anything else, so a bad value fails the command before any file is created (source).
Built-in defaultnano on Unix (source) and Notepad on Windows (source).
$VISUAL, then $EDITORSet ui.editor in the base environment layer, which user, repo and workspace config override (source).
$JJ_EDITORSets ui.editor in the override layer, above every config file (source).

The editor is run with the file's path as its last argument, unless the configured command already contains a $path placeholder; a $line placeholder is also available (source).

3. End-to-end flow

jj config edit: call flow The command first reads ui.editor, then edit_config_file picks one file and loads it. If the file does not exist it is saved at once, creating it. The editor runs on the file in place. The file is then parsed again. If it parses, the command is done. If not, a warning shows the parse error and asks whether to keep editing: yes runs the editor again, no writes back the contents loaded before editing. text_editor: read ui.editor before any file is touched edit_config_file: pick one file loaded, or an empty document missing? save it now creates the file with a #:schema line editor.edit_file(path) edits the real file in place parse fails: warn and ask "keep editing?" (Yn); yes → editor again no: restore save the contents loaded before editing parses: done the edited file is kept as written

4. Core architecture touchpoints

PieceRole here
ConfigTargetArgs::edit_config_fileShared with config set and config unset: turns the target flag into one ConfigFile, or fails with "No user/repo/workspace config path found to edit".
TextEditorThe same editor wrapper other commands use, but here it opens the config file directly instead of a temporary copy. A non-zero exit from the editor is an error.
ConfigFile::load_or_emptyGives a missing file an empty document with the #:schema header, which is what the early save writes.
Ui::prompt_yes_noAsks whether to keep editing, defaulting to yes.

5. What it reads, and snapshotting

It reads the chosen config file, through the config already loaded at startup or from disk, and the edited file again after each editor run (source). It never loads the repo or the working copy, so there is no snapshot. It writes the file when it did not exist, and again only on a restore (source, restore); otherwise the only writer is the editor.

6. Algorithm and invariants

Creating the file first

If the chosen path does not exist, the loaded empty document is saved before the editor starts, so the file exists even if the editor is closed without saving. For --user and --repo, its directory is created too (test, test). The path is printed to stderr as Editing file: PATH (source).

The edit loop

After the editor exits successfully, the file is parsed with ConfigLayer::load_from_file, tagged with the file's source. Only TOML syntax is checked here, not whether the values suit jj's schema. On failure it prints "An error has been found inside the config:" and the error chain, then asks "Do you want to keep editing the file? If not, previous config will be restored." (source). Yes reopens the editor on the same, still-invalid file. No saves the ConfigFile that was loaded before the first editor run, putting back what was there (source).

Without a terminal

When the session cannot prompt, prompt_choice_with picks the default without waiting (source). The default here is yes, so with invalid contents the editor is simply run again.

7. Errors and edge cases

SituationResult
No target flagA clap usage error, exit 2 (test).
--repo outside a repoError: No repo config path found to edit, exit 1 (test).
No user config file yetThe file and its directory are created, then opened (test).
Editor cannot start or exits non-zeroAn error; the file is not re-checked or restored (source, source).
Invalid TOML, then "Y"The editor reopens; a later valid version is kept (test).
Invalid TOML, then "n"The previous contents are written back (test).

8. Example output

From upstream's test, where the editor first writes invalid config here into the repo config (source):

$ jj config edit --repo
Editing file: $TEST_ENV/home/.config/jj/repos/8e4fac809cbb3b162c95/config.toml
Warning: An error has been found inside the config:
Caused by:
1: Configuration cannot be parsed as TOML document
2: TOML parse error at line 1, column 9
  |
1 | invalid config here
  |         ^
key with no value, expected `=`

Do you want to keep editing the file? If not, previous config will be restored. (Yn): Y

The second editor run writes test="success", and afterwards:

$ jj config get test
success