ok/jj
1
0
Fork 0
forked from mirrors/jj

cli: load revset/template aliases in order of config layers

Since we abuse TOML table syntax to define function aliases, an identical
function alias can be found more than once in the merged config. The merged
config doesn't preserve the definition order, so we need to load aliases
table per layer.
This commit is contained in:
Yuya Nishihara 2023-05-23 12:45:11 +09:00
parent 97e2ee2868
commit d6f1ab697a
4 changed files with 98 additions and 22 deletions

View file

@ -50,7 +50,7 @@ use jujutsu_lib::revset::{
RevsetIteratorExt, RevsetParseError, RevsetParseErrorKind, RevsetResolutionError,
RevsetWorkspaceContext,
};
use jujutsu_lib::settings::UserSettings;
use jujutsu_lib::settings::{ConfigResultExt as _, UserSettings};
use jujutsu_lib::transaction::Transaction;
use jujutsu_lib::tree::{Tree, TreeMergeError};
use jujutsu_lib::working_copy::{
@ -579,8 +579,8 @@ impl WorkspaceCommandHelper {
workspace: Workspace,
repo: Arc<ReadonlyRepo>,
) -> Result<Self, CommandError> {
let revset_aliases_map = load_revset_aliases(ui, &command.settings)?;
let template_aliases_map = load_template_aliases(ui, &command.settings)?;
let revset_aliases_map = load_revset_aliases(ui, &command.layered_configs)?;
let template_aliases_map = load_template_aliases(ui, &command.layered_configs)?;
// Parse commit_summary template early to report error before starting mutable
// operation.
// TODO: Parsed template can be cached if it doesn't capture repo
@ -1626,18 +1626,26 @@ fn resolve_single_op_from_store(
fn load_revset_aliases(
ui: &mut Ui,
settings: &UserSettings,
layered_configs: &LayeredConfigs,
) -> Result<RevsetAliasesMap, CommandError> {
const TABLE_KEY: &str = "revset-aliases";
let mut aliases_map = RevsetAliasesMap::new();
let table = settings.config().get_table(TABLE_KEY)?;
for (decl, value) in table.into_iter().sorted_by(|a, b| a.0.cmp(&b.0)) {
let r = value
.into_string()
.map_err(|e| e.to_string())
.and_then(|v| aliases_map.insert(&decl, v).map_err(|e| e.to_string()));
if let Err(s) = r {
writeln!(ui.warning(), r#"Failed to load "{TABLE_KEY}.{decl}": {s}"#)?;
// Load from all config layers in order. 'f(x)' in default layer should be
// overridden by 'f(a)' in user.
for (_, config) in layered_configs.sources() {
let table = if let Some(table) = config.get_table(TABLE_KEY).optional()? {
table
} else {
continue;
};
for (decl, value) in table.into_iter().sorted_by(|a, b| a.0.cmp(&b.0)) {
let r = value
.into_string()
.map_err(|e| e.to_string())
.and_then(|v| aliases_map.insert(&decl, v).map_err(|e| e.to_string()));
if let Err(s) = r {
writeln!(ui.warning(), r#"Failed to load "{TABLE_KEY}.{decl}": {s}"#)?;
}
}
}
Ok(aliases_map)
@ -1733,18 +1741,26 @@ pub fn update_working_copy(
fn load_template_aliases(
ui: &mut Ui,
settings: &UserSettings,
layered_configs: &LayeredConfigs,
) -> Result<TemplateAliasesMap, CommandError> {
const TABLE_KEY: &str = "template-aliases";
let mut aliases_map = TemplateAliasesMap::new();
let table = settings.config().get_table(TABLE_KEY)?;
for (decl, value) in table.into_iter().sorted_by(|a, b| a.0.cmp(&b.0)) {
let r = value
.into_string()
.map_err(|e| e.to_string())
.and_then(|v| aliases_map.insert(&decl, v).map_err(|e| e.to_string()));
if let Err(s) = r {
writeln!(ui.warning(), r#"Failed to load "{TABLE_KEY}.{decl}": {s}"#)?;
// Load from all config layers in order. 'f(x)' in default layer should be
// overridden by 'f(a)' in user.
for (_, config) in layered_configs.sources() {
let table = if let Some(table) = config.get_table(TABLE_KEY).optional()? {
table
} else {
continue;
};
for (decl, value) in table.into_iter().sorted_by(|a, b| a.0.cmp(&b.0)) {
let r = value
.into_string()
.map_err(|e| e.to_string())
.and_then(|v| aliases_map.insert(&decl, v).map_err(|e| e.to_string()));
if let Err(s) = r {
writeln!(ui.warning(), r#"Failed to load "{TABLE_KEY}.{decl}": {s}"#)?;
}
}
}
Ok(aliases_map)

View file

@ -117,7 +117,7 @@ impl LayeredConfigs {
.expect("loaded configs should be merged without error")
}
fn sources(&self) -> Vec<(ConfigSource, &config::Config)> {
pub fn sources(&self) -> Vec<(ConfigSource, &config::Config)> {
let config_sources = [
(ConfigSource::Default, Some(&self.default)),
(ConfigSource::Env, Some(&self.env_base)),

View file

@ -337,6 +337,36 @@ fn test_alias() {
"###);
}
#[test]
fn test_alias_override() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
test_env.add_config(
r###"
[revset-aliases]
'f(x)' = 'user'
"###,
);
// 'f(x)' should be overridden by --config-toml 'f(a)'. If aliases were sorted
// purely by name, 'f(a)' would come first.
let stderr = test_env.jj_cmd_failure(
&repo_path,
&[
"log",
"-r",
"f(_)",
"--config-toml",
"revset-aliases.'f(a)' = 'arg'",
],
);
insta::assert_snapshot!(stderr, @r###"
Error: Revision "arg" doesn't exist
"###);
}
#[test]
fn test_bad_alias_decl() {
let test_env = TestEnvironment::default();

View file

@ -808,6 +808,36 @@ fn test_templater_alias() {
"###);
}
#[test]
fn test_templater_alias_override() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
test_env.add_config(
r###"
[template-aliases]
'f(x)' = '"user"'
"###,
);
// 'f(x)' should be overridden by --config-toml 'f(a)'. If aliases were sorted
// purely by name, 'f(a)' would come first.
let stdout = test_env.jj_cmd_success(
&repo_path,
&[
"log",
"--no-graph",
"-r@",
"-T",
r#"f(_)"#,
"--config-toml",
r#"template-aliases.'f(a)' = '"arg"'"#,
],
);
insta::assert_snapshot!(stdout, @"arg");
}
#[test]
fn test_templater_bad_alias_decl() {
let test_env = TestEnvironment::default();