mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-24 20:54:15 +00:00
Add a "config" command with "get" and "list" subcommands
Partially fixes #531.
This commit is contained in:
parent
9bf5f1c430
commit
e824c491bf
4 changed files with 235 additions and 2 deletions
|
@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
* `jj git push` now accepts multiple `--branch`/`--change` arguments
|
||||
|
||||
* `jj config list` command prints values from config (with other subcommands
|
||||
coming soon).
|
||||
|
||||
### Fixed bugs
|
||||
|
||||
* When sharing the working copy with a Git repo, we used to forget to export
|
||||
|
@ -39,6 +42,7 @@ Thanks to the people who made this release happen!
|
|||
* Danny Hooper (hooper@google.com)
|
||||
* Yuya Nishihara (@yuja)
|
||||
* Ilya Grigoriev (@ilyagr)
|
||||
* David Barnett (@dbarnett)
|
||||
|
||||
## [0.6.1] - 2022-12-05
|
||||
|
||||
|
|
|
@ -1234,6 +1234,49 @@ pub fn write_commit_summary(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_config_entry(
|
||||
ui: &mut Ui,
|
||||
path: &str,
|
||||
value: config::Value,
|
||||
) -> Result<(), CommandError> {
|
||||
match value.kind {
|
||||
// Handle table values specially to render each child nicely on its own line.
|
||||
config::ValueKind::Table(table) => {
|
||||
// TODO: Remove sorting when config crate maintains deterministic ordering.
|
||||
for (key, table_val) in table.into_iter().sorted_by_key(|(k, _)| k.to_owned()) {
|
||||
let key_path = match path {
|
||||
"" => key,
|
||||
_ => format!("{path}.{key}"),
|
||||
};
|
||||
write_config_entry(ui, key_path.as_str(), table_val)?;
|
||||
}
|
||||
}
|
||||
_ => writeln!(ui, "{path}={}", serialize_config_value(value))?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Use a proper TOML library to serialize instead.
|
||||
fn serialize_config_value(value: config::Value) -> String {
|
||||
match value.kind {
|
||||
config::ValueKind::Table(table) => format!(
|
||||
"{{{}}}",
|
||||
// TODO: Remove sorting when config crate maintains deterministic ordering.
|
||||
table
|
||||
.into_iter()
|
||||
.sorted_by_key(|(k, _)| k.to_owned())
|
||||
.map(|(k, v)| format!("{k}={}", serialize_config_value(v)))
|
||||
.join(", ")
|
||||
),
|
||||
config::ValueKind::Array(vals) => format!(
|
||||
"[{}]",
|
||||
vals.into_iter().map(serialize_config_value).join(", ")
|
||||
),
|
||||
config::ValueKind::String(val) => format!("{val:?}"),
|
||||
_ => value.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn short_commit_description(commit: &Commit) -> String {
|
||||
let first_line = commit.description().split('\n').next().unwrap();
|
||||
format!("{} ({})", short_commit_hash(commit.id()), first_line)
|
||||
|
|
|
@ -25,6 +25,7 @@ use std::{fs, io};
|
|||
use chrono::{FixedOffset, LocalResult, TimeZone, Utc};
|
||||
use clap::builder::NonEmptyStringValueParser;
|
||||
use clap::{ArgGroup, ArgMatches, CommandFactory, FromArgMatches, Subcommand};
|
||||
use config::Source;
|
||||
use itertools::Itertools;
|
||||
use jujutsu_lib::backend::{CommitId, Timestamp, TreeValue};
|
||||
use jujutsu_lib::commit::Commit;
|
||||
|
@ -52,8 +53,8 @@ use pest::Parser;
|
|||
use crate::cli_util::{
|
||||
self, check_stale_working_copy, print_checkout_stats, print_failed_git_export,
|
||||
resolve_base_revs, short_commit_description, short_commit_hash, user_error,
|
||||
user_error_with_hint, write_commit_summary, Args, CommandError, CommandHelper, DescriptionArg,
|
||||
RevisionArg, WorkspaceCommandHelper,
|
||||
user_error_with_hint, write_commit_summary, write_config_entry, Args, CommandError,
|
||||
CommandHelper, DescriptionArg, RevisionArg, WorkspaceCommandHelper,
|
||||
};
|
||||
use crate::config::FullCommandArgs;
|
||||
use crate::diff_util::{self, DiffFormat, DiffFormatArgs};
|
||||
|
@ -68,6 +69,8 @@ use crate::ui::Ui;
|
|||
enum Commands {
|
||||
Version(VersionArgs),
|
||||
Init(InitArgs),
|
||||
#[command(subcommand)]
|
||||
Config(ConfigSubcommand),
|
||||
Checkout(CheckoutArgs),
|
||||
Untrack(UntrackArgs),
|
||||
Files(FilesArgs),
|
||||
|
@ -141,6 +144,31 @@ struct InitArgs {
|
|||
git_repo: Option<String>,
|
||||
}
|
||||
|
||||
/// Get config options
|
||||
///
|
||||
/// Operates on jj configuration, which comes from the config file and
|
||||
/// environment variables. Uses the config file at ~/.jjconfig.toml or
|
||||
/// $XDG_CONFIG_HOME/jj/config.toml, unless overridden with the JJ_CONFIG
|
||||
/// environment variable.
|
||||
///
|
||||
/// For supported config options and more details about jj config, see
|
||||
/// https://github.com/martinvonz/jj/blob/main/docs/config.md.
|
||||
///
|
||||
/// Note: Currently only supports getting config options, but support for
|
||||
/// setting options and editing config files is also planned (see
|
||||
/// https://github.com/martinvonz/jj/issues/531).
|
||||
#[derive(clap::Subcommand, Clone, Debug)]
|
||||
enum ConfigSubcommand {
|
||||
/// List variables set in config file, along with their values.
|
||||
#[command(visible_alias("l"))]
|
||||
List {
|
||||
/// An optional name of a specific config option to look up.
|
||||
#[arg(value_parser=NonEmptyStringValueParser::new())]
|
||||
name: Option<String>,
|
||||
// TODO: Support --show-origin once mehcode/config-rs#319 is done.
|
||||
},
|
||||
}
|
||||
|
||||
/// Create a new, empty change and edit it in the working copy
|
||||
///
|
||||
/// For more information, see
|
||||
|
@ -1171,6 +1199,35 @@ Set `ui.allow-init-native` to allow initializing a repo with the native backend.
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_config(
|
||||
ui: &mut Ui,
|
||||
_command: &CommandHelper,
|
||||
subcommand: &ConfigSubcommand,
|
||||
) -> Result<(), CommandError> {
|
||||
ui.request_pager();
|
||||
match subcommand {
|
||||
ConfigSubcommand::List { name } => {
|
||||
let raw_values = match name {
|
||||
Some(name) => {
|
||||
ui.settings()
|
||||
.config()
|
||||
.get::<config::Value>(name)
|
||||
.map_err(|e| match e {
|
||||
config::ConfigError::NotFound { .. } => {
|
||||
user_error("key not found in config")
|
||||
}
|
||||
_ => e.into(),
|
||||
})?
|
||||
}
|
||||
None => ui.settings().config().collect()?.into(),
|
||||
};
|
||||
write_config_entry(ui, name.as_deref().unwrap_or(""), raw_values)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_checkout(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
|
@ -4296,6 +4353,7 @@ pub fn run_command(
|
|||
match &derived_subcommands {
|
||||
Commands::Version(sub_args) => cmd_version(ui, command_helper, sub_args),
|
||||
Commands::Init(sub_args) => cmd_init(ui, command_helper, sub_args),
|
||||
Commands::Config(sub_args) => cmd_config(ui, command_helper, sub_args),
|
||||
Commands::Checkout(sub_args) => cmd_checkout(ui, command_helper, sub_args),
|
||||
Commands::Untrack(sub_args) => cmd_untrack(ui, command_helper, sub_args),
|
||||
Commands::Files(sub_args) => cmd_files(ui, command_helper, sub_args),
|
||||
|
|
128
tests/test_config_command.rs
Normal file
128
tests/test_config_command.rs
Normal file
|
@ -0,0 +1,128 @@
|
|||
// Copyright 2022 The Jujutsu Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use itertools::Itertools;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::common::TestEnvironment;
|
||||
|
||||
pub mod common;
|
||||
|
||||
#[test]
|
||||
fn test_config_list_single() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.add_config(
|
||||
r###"
|
||||
[test-table]
|
||||
somekey = "some value"
|
||||
"###
|
||||
.as_bytes(),
|
||||
);
|
||||
|
||||
let stdout = test_env.jj_cmd_success(
|
||||
test_env.env_root(),
|
||||
&["config", "list", "test-table.somekey"],
|
||||
);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
test-table.somekey="some value"
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_list_table() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.add_config(
|
||||
r###"
|
||||
[test-table]
|
||||
x = true
|
||||
y.foo = "abc"
|
||||
y.bar = 123
|
||||
"###
|
||||
.as_bytes(),
|
||||
);
|
||||
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "list", "test-table"]);
|
||||
insta::assert_snapshot!(
|
||||
stdout,
|
||||
@r###"
|
||||
test-table.x=true
|
||||
test-table.y.bar=123
|
||||
test-table.y.foo="abc"
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_list_array() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.add_config(
|
||||
r###"
|
||||
test-array = [1, "b", 3.4]
|
||||
"###
|
||||
.as_bytes(),
|
||||
);
|
||||
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "list", "test-array"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
test-array=[1, "b", 3.4]
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_list_inline_table() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.add_config(
|
||||
r###"
|
||||
[[test-table]]
|
||||
x = 1
|
||||
[[test-table]]
|
||||
y = ["z"]
|
||||
"###
|
||||
.as_bytes(),
|
||||
);
|
||||
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "list", "test-table"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
test-table=[{x=1}, {y=["z"]}]
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_list_all() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.add_config(
|
||||
r###"
|
||||
test-val = [1, 2, 3]
|
||||
[test-table]
|
||||
x = true
|
||||
y.foo = "abc"
|
||||
y.bar = 123
|
||||
"###
|
||||
.as_bytes(),
|
||||
);
|
||||
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "list"]);
|
||||
insta::assert_snapshot!(
|
||||
find_stdout_lines(r"(test-val|test-table\b[^=]*)", &stdout),
|
||||
@r###"
|
||||
test-table.x=true
|
||||
test-table.y.bar=123
|
||||
test-table.y.foo="abc"
|
||||
test-val=[1, 2, 3]
|
||||
"###);
|
||||
}
|
||||
|
||||
fn find_stdout_lines(keyname_pattern: &str, stdout: &str) -> String {
|
||||
let key_line_re = Regex::new(&format!(r"(?m)^{}=.*$", keyname_pattern)).unwrap();
|
||||
key_line_re
|
||||
.find_iter(stdout)
|
||||
.map(|m| m.as_str())
|
||||
.collect_vec()
|
||||
.join("\n")
|
||||
}
|
Loading…
Reference in a new issue