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

cli: cache immutable heads revset expression

This will help simplify warning handling in future patches. I'm going to add
deprecation warnings to revset, so Ui will be required in order to parse a user
revset expression.

revset_util::parse_immutable_expression() is inlined as it's a thin wrapper
around parse_immutable_heads_expression().
This commit is contained in:
Yuya Nishihara 2024-09-19 16:52:57 +09:00
parent 1d86e325c6
commit 7e04af1729
6 changed files with 37 additions and 49 deletions

View file

@ -595,6 +595,7 @@ pub struct WorkspaceCommandEnvironment {
template_aliases_map: TemplateAliasesMap,
path_converter: RepoPathUiConverter,
workspace_id: WorkspaceId,
immutable_heads_expression: Rc<RevsetExpression>,
short_prefixes_expression: Option<Rc<RevsetExpression>>,
}
@ -614,8 +615,10 @@ impl WorkspaceCommandEnvironment {
template_aliases_map,
path_converter,
workspace_id: workspace.workspace_id().to_owned(),
immutable_heads_expression: RevsetExpression::root(),
short_prefixes_expression: None,
};
env.immutable_heads_expression = env.load_immutable_heads_expression()?;
env.short_prefixes_expression = env.load_short_prefixes_expression()?;
Ok(env)
}
@ -663,6 +666,23 @@ impl WorkspaceCommandEnvironment {
}
}
/// User-configured expression defining the immutable set.
pub fn immutable_expression(&self) -> Rc<RevsetExpression> {
// Negated ancestors expression `~::(<heads> | root())` is slightly
// easier to optimize than negated union `~(::<heads> | root())`.
self.immutable_heads_expression.ancestors()
}
/// User-configured expression defining the heads of the immutable set.
pub fn immutable_heads_expression(&self) -> &Rc<RevsetExpression> {
&self.immutable_heads_expression
}
fn load_immutable_heads_expression(&self) -> Result<Rc<RevsetExpression>, CommandError> {
revset_util::parse_immutable_heads_expression(&self.revset_parse_context())
.map_err(|e| config_error_with_message("Invalid `revset-aliases.immutable_heads()`", e))
}
fn load_short_prefixes_expression(&self) -> Result<Option<Rc<RevsetExpression>>, CommandError> {
let revset_string = self
.settings()
@ -704,15 +724,11 @@ impl WorkspaceCommandEnvironment {
let id_prefix_context = IdPrefixContext::new(self.command.revset_extensions().clone());
let to_rewrite_revset =
RevsetExpression::commits(commits.into_iter().cloned().collect_vec());
let immutable = revset_util::parse_immutable_expression(&self.revset_parse_context())
.map_err(|e| {
config_error_with_message("Invalid `revset-aliases.immutable_heads()`", e)
})?;
let mut expression = RevsetExpressionEvaluator::new(
repo,
self.command.revset_extensions().clone(),
&id_prefix_context,
immutable,
self.immutable_expression(),
);
expression.intersect_with(&to_rewrite_revset);
@ -766,6 +782,7 @@ impl WorkspaceCommandEnvironment {
&self.workspace_id,
self.revset_parse_context(),
id_prefix_context,
self.immutable_expression(),
&self.command.data.commit_template_extensions,
)
}

View file

@ -51,7 +51,6 @@ use crate::formatter::Formatter;
use crate::git_util::get_git_repo;
use crate::git_util::with_remote_git_callbacks;
use crate::git_util::GitSidebandProgressMessageWriter;
use crate::revset_util;
use crate::ui::Ui;
/// Push to a Git remote
@ -326,9 +325,7 @@ fn validate_commits_ready_to_push(
.cloned()
.collect_vec();
let commits_to_push = RevsetExpression::commits(old_heads)
.union(&revset_util::parse_immutable_heads_expression(
&tx.base_workspace_helper().revset_parse_context(),
)?)
.union(workspace_helper.env().immutable_heads_expression())
.range(&RevsetExpression::commits(new_heads));
let config = command.settings().config();

View file

@ -24,7 +24,6 @@ use crate::cli_util::CommandHelper;
use crate::command_error::CommandError;
use crate::diff_util::get_copy_records;
use crate::diff_util::DiffFormat;
use crate::revset_util;
use crate::ui::Ui;
/// Show high-level repo status
@ -118,9 +117,7 @@ pub(crate) fn cmd_status(
.parents()
.ancestors()
.filtered(RevsetFilterPredicate::HasConflict)
.minus(&revset_util::parse_immutable_expression(
&workspace_command.revset_parse_context(),
)?),
.minus(&workspace_command.env().immutable_expression()),
)
.evaluate_to_commit_ids()?
.collect();

View file

@ -91,6 +91,7 @@ pub struct CommitTemplateLanguage<'repo> {
// are contained in RevsetParseContext for example.
revset_parse_context: RevsetParseContext<'repo>,
id_prefix_context: &'repo IdPrefixContext,
immutable_expression: Rc<RevsetExpression>,
build_fn_table: CommitTemplateBuildFnTable<'repo>,
keyword_cache: CommitKeywordCache<'repo>,
cache_extensions: ExtensionsMap,
@ -105,6 +106,7 @@ impl<'repo> CommitTemplateLanguage<'repo> {
workspace_id: &WorkspaceId,
revset_parse_context: RevsetParseContext<'repo>,
id_prefix_context: &'repo IdPrefixContext,
immutable_expression: Rc<RevsetExpression>,
extensions: &[impl AsRef<dyn CommitTemplateLanguageExtension>],
) -> Self {
let mut build_fn_table = CommitTemplateBuildFnTable::builtin();
@ -123,6 +125,7 @@ impl<'repo> CommitTemplateLanguage<'repo> {
workspace_id: workspace_id.clone(),
revset_parse_context,
id_prefix_context,
immutable_expression,
build_fn_table,
keyword_cache: CommitKeywordCache::default(),
cache_extensions,
@ -477,8 +480,12 @@ impl<'repo> CommitKeywordCache<'repo> {
language: &CommitTemplateLanguage<'repo>,
span: pest::Span<'_>,
) -> TemplateParseResult<&Rc<RevsetContainingFn<'repo>>> {
// Alternatively, a negated (i.e. visible mutable) set could be computed.
// It's usually smaller than the immutable set. The revset engine can also
// optimize "::<recent_heads>" query to use bitset-based implementation.
self.is_immutable_fn.get_or_try_init(|| {
let revset = evaluate_immutable_revset(language, span)?;
let expression = language.immutable_expression.clone();
let revset = evaluate_revset_expression(language, span, expression)?;
Ok(revset.containing_fn().into())
})
}
@ -785,21 +792,6 @@ fn evaluate_revset_expression<'repo>(
Ok(revset)
}
fn evaluate_immutable_revset<'repo>(
language: &CommitTemplateLanguage<'repo>,
span: pest::Span<'_>,
) -> Result<Box<dyn Revset + 'repo>, TemplateParseError> {
// Alternatively, a negated (i.e. visible mutable) set could be computed.
// It's usually smaller than the immutable set. The revset engine can also
// optimize "::<recent_heads>" query to use bitset-based implementation.
let expression = revset_util::parse_immutable_expression(&language.revset_parse_context)
.map_err(|err| {
TemplateParseError::expression("Failed to parse revset", span).with_source(err)
})?;
evaluate_revset_expression(language, span, expression)
}
fn evaluate_user_revset<'repo>(
language: &CommitTemplateLanguage<'repo>,
span: pest::Span<'_>,

View file

@ -211,15 +211,6 @@ pub fn parse_immutable_heads_expression(
Ok(heads.union(&RevsetExpression::root()))
}
/// Parses user-configured expression defining the immutable set.
pub fn parse_immutable_expression(
context: &RevsetParseContext,
) -> Result<Rc<RevsetExpression>, RevsetParseError> {
// Negated ancestors expression `~::(<heads> | root())` is slightly easier
// to optimize than negated union `~(::<heads> | root())`.
Ok(parse_immutable_heads_expression(context)?.ancestors())
}
pub(super) fn evaluate_revset_to_single_commit<'a>(
revision_str: &str,
expression: &RevsetExpressionEvaluator<'_>,

View file

@ -771,22 +771,16 @@ fn test_log_immutable() {
test_env.add_config("revset-aliases.'immutable_heads()' = 'unknown_fn()'");
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r::", "-T", template]);
insta::assert_snapshot!(stderr, @r###"
Error: Failed to parse template: Failed to parse revset
Caused by:
1: --> 5:10
|
5 | if(immutable, "[immutable]"),
| ^-------^
|
= Failed to parse revset
2: --> 1:1
insta::assert_snapshot!(stderr, @r#"
Config error: Invalid `revset-aliases.immutable_heads()`
Caused by: --> 1:1
|
1 | unknown_fn()
| ^--------^
|
= Function "unknown_fn" doesn't exist
"###);
For help, see https://martinvonz.github.io/jj/latest/config/.
"#);
test_env.add_config("revset-aliases.'immutable_heads()' = 'unknown_symbol'");
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r::", "-T", template]);