···2020* `jj config list` now uses multi-line strings and single-quoted strings in the
2121 output when appropriate.
22222323+* `jj config get`/`list`/`set` now parse `name` argument as [TOML
2424+ key](https://toml.io/en/v1.0.0#keys). Quote meta characters as needed.
2525+ Example: `jj config get "revset-aliases.'trunk()'"`
2626+2327### Deprecations
24282529- Attempting to alias a built-in command now gives a warning, rather than being silently ignored.
+6-6
cli/src/commands/config.rs
···116116#[command(verbatim_doc_comment)]
117117pub(crate) struct ConfigGetArgs {
118118 #[arg(required = true)]
119119- name: String,
119119+ name: ConfigNamePathBuf,
120120}
121121122122/// Update config file to set the given option to a given value.
123123#[derive(clap::Args, Clone, Debug)]
124124pub(crate) struct ConfigSetArgs {
125125 #[arg(required = true)]
126126- name: String,
126126+ name: ConfigNamePathBuf,
127127 #[arg(required = true)]
128128 value: String,
129129 #[command(flatten)]
···277277 command: &CommandHelper,
278278 args: &ConfigGetArgs,
279279) -> Result<(), CommandError> {
280280- let value = command
281281- .settings()
282282- .config()
283283- .get_string(&args.name)
280280+ let value = args
281281+ .name
282282+ .lookup_value(command.settings().config())
283283+ .and_then(|value| value.into_string())
284284 .map_err(|err| match err {
285285 config::ConfigError::Type {
286286 origin,
+9-5
cli/src/config.rs
···1717use std::path::{Path, PathBuf};
1818use std::process::Command;
1919use std::str::FromStr;
2020-use std::{env, fmt};
2020+use std::{env, fmt, slice};
21212222use config::Source;
2323use itertools::Itertools;
···5252 /// Returns true if the path is empty (i.e. pointing to the root table.)
5353 pub fn is_root(&self) -> bool {
5454 self.0.is_empty()
5555+ }
5656+5757+ /// Returns iterator of path components (or keys.)
5858+ pub fn components(&self) -> slice::Iter<'_, toml_edit::Key> {
5959+ self.0.iter()
5560 }
56615762 /// Appends the given `key` component.
···525530}
526531527532pub fn write_config_value_to_file(
528528- key: &str,
533533+ key: &ConfigNamePathBuf,
529534 value_str: &str,
530535 path: &Path,
531536) -> Result<(), CommandError> {
···555560 _ => toml_edit::value(value_str),
556561 };
557562 let mut target_table = doc.as_table_mut();
558558- let mut key_parts_iter = key.split('.');
559559- // Note: split guarantees at least one item.
560560- let last_key_part = key_parts_iter.next_back().unwrap();
563563+ let mut key_parts_iter = key.components();
564564+ let last_key_part = key_parts_iter.next_back().expect("key must not be empty");
561565 for key_part in key_parts_iter {
562566 target_table = target_table
563567 .entry(key_part)
+34-3
cli/tests/test_config_command.rs
···420420}
421421422422#[test]
423423-fn test_config_set_missing_opts() {
423423+fn test_config_set_bad_opts() {
424424 let test_env = TestEnvironment::default();
425425 let stderr = test_env.jj_cmd_cli_error(test_env.env_root(), &["config", "set"]);
426426 insta::assert_snapshot!(stderr, @r###"
···433433434434 For more information, try '--help'.
435435 "###);
436436+437437+ let stderr =
438438+ test_env.jj_cmd_cli_error(test_env.env_root(), &["config", "set", "--user", "", "x"]);
439439+ insta::assert_snapshot!(stderr, @r###"
440440+ error: invalid value '' for '<NAME>': TOML parse error at line 1, column 1
441441+ |
442442+ 1 |
443443+ | ^
444444+ invalid key
445445+446446+447447+ For more information, try '--help'.
448448+ "###);
436449}
437450438451#[test]
···452465 &repo_path,
453466 &["config", "set", "--user", "test-table.foo", "true"],
454467 );
468468+ test_env.jj_cmd_ok(
469469+ &repo_path,
470470+ &["config", "set", "--user", "test-table.'bar()'", "0"],
471471+ );
455472456473 // Ensure test-key successfully written to user config.
457474 let user_config_toml = std::fs::read_to_string(&user_config_path)
···461478462479 [test-table]
463480 foo = true
481481+ "bar()" = 0
464482 "###);
465483}
466484···741759 insta::assert_snapshot!(stdout, @r###"
742760 'b c'.e.'f[]'=2
743761 "###);
762762+ let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "get", "'b c'.e.'f[]'"]);
763763+ insta::assert_snapshot!(stdout, @r###"
764764+ 2
765765+ "###);
744766745767 // Not a table
746768 let (stdout, stderr) =
···748770 insta::assert_snapshot!(stdout, @"");
749771 insta::assert_snapshot!(stderr, @r###"
750772 Warning: No matching config key for a.'b()'.x
773773+ "###);
774774+ let stderr = test_env.jj_cmd_failure(test_env.env_root(), &["config", "get", "a.'b()'.x"]);
775775+ insta::assert_snapshot!(stderr, @r###"
776776+ Config error: configuration property "a.'b()'.x" not found
777777+ For help, see https://github.com/martinvonz/jj/blob/main/docs/config.md.
751778 "###);
752779753780 // "-" and "_" are valid TOML keys
···765792 insta::assert_snapshot!(stdout, @r###"
766793 '.'=5
767794 "###);
768768- let stderr = test_env.jj_cmd_cli_error(test_env.env_root(), &["config", "list", "."]);
795795+ let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "get", "'.'"]);
796796+ insta::assert_snapshot!(stdout, @r###"
797797+ 5
798798+ "###);
799799+ let stderr = test_env.jj_cmd_cli_error(test_env.env_root(), &["config", "get", "."]);
769800 insta::assert_snapshot!(stderr, @r###"
770770- error: invalid value '.' for '[NAME]': TOML parse error at line 1, column 1
801801+ error: invalid value '.' for '<NAME>': TOML parse error at line 1, column 1
771802 |
772803 1 | .
773804 | ^
+1-1
docs/contributing.md
···115115hidden from the default `jj log` output) by running the following in your repo:
116116117117```shell
118118-jj config set --repo "revset-aliases.immutable_heads()" 'remote_branches(exact:"main") | remote_branches(exact:"gh-pages")'
118118+jj config set --repo "revset-aliases.'immutable_heads()'" 'remote_branches(exact:"main") | remote_branches(exact:"gh-pages")'
119119```
120120121121### Summary