mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-26 14:00:51 +00:00
cli: add convenient wrapper for user revset evaluation
Many callers of resolve_revset() and evaluate_revset() will be migrated to this wrapper. "single" and "default_single" APIs won't be replaced because they require more contexts to construct error messages. id_prefix_context() now uses bare revset::parse() to avoid dependency cycle.
This commit is contained in:
parent
75e938c6b8
commit
690270670e
9 changed files with 103 additions and 43 deletions
|
@ -53,7 +53,7 @@ use jj_lib::repo::{
|
|||
use jj_lib::repo_path::{FsPathParseError, RepoPath, RepoPathBuf};
|
||||
use jj_lib::revset::{
|
||||
Revset, RevsetAliasesMap, RevsetCommitRef, RevsetExpression, RevsetFilterPredicate,
|
||||
RevsetIteratorExt, RevsetParseContext, RevsetParseError, RevsetWorkspaceContext,
|
||||
RevsetIteratorExt, RevsetParseContext, RevsetWorkspaceContext,
|
||||
};
|
||||
use jj_lib::rewrite::restore_tree;
|
||||
use jj_lib::settings::{ConfigResultExt as _, UserSettings};
|
||||
|
@ -88,6 +88,7 @@ use crate::git_util::{
|
|||
};
|
||||
use crate::merge_tools::{DiffEditor, MergeEditor, MergeToolConfigError};
|
||||
use crate::operation_templater::OperationTemplateLanguageExtension;
|
||||
use crate::revset_util::RevsetExpressionEvaluator;
|
||||
use crate::template_builder::TemplateLanguage;
|
||||
use crate::template_parser::TemplateAliasesMap;
|
||||
use crate::templater::{PropertyPlaceholder, TemplateRenderer};
|
||||
|
@ -754,9 +755,10 @@ impl WorkspaceCommandHelper {
|
|||
|
||||
/// Resolve a revset any number of revisions (including 0).
|
||||
pub fn resolve_revset(&self, revision_str: &str) -> Result<Vec<Commit>, CommandError> {
|
||||
let revset_expression = self.parse_revset(revision_str)?;
|
||||
let revset = self.evaluate_revset(revset_expression)?;
|
||||
Ok(revset.iter().commits(self.repo().store()).try_collect()?)
|
||||
Ok(self
|
||||
.parse_revset(revision_str)?
|
||||
.evaluate_to_commits()?
|
||||
.try_collect()?)
|
||||
}
|
||||
|
||||
/// Resolve a revset any number of revisions (including 0), but require the
|
||||
|
@ -781,8 +783,7 @@ impl WorkspaceCommandHelper {
|
|||
should_hint_about_all_prefix: bool,
|
||||
) -> Result<Commit, CommandError> {
|
||||
let revset_expression = self.parse_revset(revision_str)?;
|
||||
let revset = self.evaluate_revset(revset_expression.clone())?;
|
||||
let mut iter = revset.iter().commits(self.repo().store()).fuse();
|
||||
let mut iter = revset_expression.evaluate_to_commits()?.fuse();
|
||||
match (iter.next(), iter.next()) {
|
||||
(Some(commit), None) => Ok(commit?),
|
||||
(None, _) => Err(user_error(format!(
|
||||
|
@ -821,7 +822,7 @@ impl WorkspaceCommandHelper {
|
|||
`jj abandon -r <REVISION>`.",
|
||||
);
|
||||
} else if let RevsetExpression::CommitRef(RevsetCommitRef::Symbol(branch_name)) =
|
||||
revset_expression.as_ref()
|
||||
revset_expression.expression().as_ref()
|
||||
{
|
||||
// Separate hint if there's a conflicted branch
|
||||
cmd_err.add_formatted_hint_with(|formatter| {
|
||||
|
@ -860,30 +861,41 @@ impl WorkspaceCommandHelper {
|
|||
pub fn parse_revset(
|
||||
&self,
|
||||
revision_str: &str,
|
||||
) -> Result<Rc<RevsetExpression>, RevsetParseError> {
|
||||
revset::parse(revision_str, &self.revset_parse_context())
|
||||
) -> Result<RevsetExpressionEvaluator<'_>, CommandError> {
|
||||
let expression = revset::parse(revision_str, &self.revset_parse_context())?;
|
||||
self.attach_revset_evaluator(expression)
|
||||
}
|
||||
|
||||
/// Parses the given revset expressions and concatenates them all.
|
||||
pub fn parse_union_revsets(
|
||||
&self,
|
||||
revision_args: &[RevisionArg],
|
||||
) -> Result<Rc<RevsetExpression>, RevsetParseError> {
|
||||
) -> Result<RevsetExpressionEvaluator<'_>, CommandError> {
|
||||
let context = self.revset_parse_context();
|
||||
let expressions: Vec<_> = revision_args
|
||||
.iter()
|
||||
.map(|s| revset::parse(s, &context))
|
||||
.try_collect()?;
|
||||
Ok(RevsetExpression::union_all(&expressions))
|
||||
let expression = RevsetExpression::union_all(&expressions);
|
||||
self.attach_revset_evaluator(expression)
|
||||
}
|
||||
|
||||
pub fn evaluate_revset<'repo>(
|
||||
&'repo self,
|
||||
expression: Rc<RevsetExpression>,
|
||||
) -> Result<Box<dyn Revset + 'repo>, CommandError> {
|
||||
let repo = self.repo().as_ref();
|
||||
let symbol_resolver = revset_util::default_symbol_resolver(repo, self.id_prefix_context()?);
|
||||
Ok(revset_util::evaluate(repo, &symbol_resolver, expression)?)
|
||||
Ok(self.attach_revset_evaluator(expression)?.evaluate()?)
|
||||
}
|
||||
|
||||
fn attach_revset_evaluator(
|
||||
&self,
|
||||
expression: Rc<RevsetExpression>,
|
||||
) -> Result<RevsetExpressionEvaluator<'_>, CommandError> {
|
||||
Ok(RevsetExpressionEvaluator::new(
|
||||
self.repo().as_ref(),
|
||||
self.id_prefix_context()?,
|
||||
expression,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn revset_parse_context(&self) -> RevsetParseContext {
|
||||
|
@ -908,9 +920,10 @@ impl WorkspaceCommandHelper {
|
|||
.get_string("revsets.short-prefixes")
|
||||
.unwrap_or_else(|_| self.settings.default_revset());
|
||||
if !revset_string.is_empty() {
|
||||
let disambiguation_revset = self.parse_revset(&revset_string).map_err(|err| {
|
||||
config_error_with_message("Invalid `revsets.short-prefixes`", err)
|
||||
})?;
|
||||
let disambiguation_revset =
|
||||
revset::parse(&revset_string, &self.revset_parse_context()).map_err(|err| {
|
||||
config_error_with_message("Invalid `revsets.short-prefixes`", err)
|
||||
})?;
|
||||
context = context.disambiguate_within(revset::optimize(disambiguation_revset));
|
||||
}
|
||||
Ok(context)
|
||||
|
|
|
@ -16,8 +16,6 @@ use std::io::Write;
|
|||
|
||||
use itertools::Itertools as _;
|
||||
use jj_lib::object_id::ObjectId;
|
||||
use jj_lib::repo::Repo as _;
|
||||
use jj_lib::revset::RevsetIteratorExt as _;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::cli_util::{CommandHelper, RevisionArg};
|
||||
|
@ -52,12 +50,10 @@ pub(crate) fn cmd_abandon(
|
|||
args: &AbandonArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let mut workspace_command = command.workspace_helper(ui)?;
|
||||
let to_abandon: Vec<_> = {
|
||||
let repo = workspace_command.repo();
|
||||
let expression = workspace_command.parse_union_revsets(&args.revisions)?;
|
||||
let revset = workspace_command.evaluate_revset(expression)?;
|
||||
revset.iter().commits(repo.store()).try_collect()?
|
||||
};
|
||||
let to_abandon: Vec<_> = workspace_command
|
||||
.parse_union_revsets(&args.revisions)?
|
||||
.evaluate_to_commits()?
|
||||
.try_collect()?;
|
||||
if to_abandon.is_empty() {
|
||||
writeln!(ui.stderr(), "No revisions to abandon.")?;
|
||||
return Ok(());
|
||||
|
|
|
@ -204,7 +204,7 @@ fn bench_revset<M: Measurement>(
|
|||
revset: &str,
|
||||
) -> Result<(), CommandError> {
|
||||
writeln!(ui.stderr(), "----------Testing revset: {revset}----------")?;
|
||||
let expression = revset::optimize(workspace_command.parse_revset(revset)?);
|
||||
let expression = revset::optimize(workspace_command.parse_revset(revset)?.expression().clone());
|
||||
// Time both evaluation and iteration.
|
||||
let routine = |workspace_command: &WorkspaceCommandHelper, expression: Rc<RevsetExpression>| {
|
||||
// Evaluate the expression without parsing/evaluating short-prefixes.
|
||||
|
|
|
@ -624,7 +624,10 @@ fn cmd_branch_list(
|
|||
}
|
||||
if !args.revisions.is_empty() {
|
||||
// Match against local targets only, which is consistent with "jj git push".
|
||||
let filter_expression = workspace_command.parse_union_revsets(&args.revisions)?;
|
||||
let filter_expression = workspace_command
|
||||
.parse_union_revsets(&args.revisions)?
|
||||
.expression()
|
||||
.clone();
|
||||
// Intersects with the set of local branch targets to minimize the lookup space.
|
||||
let revset_expression = RevsetExpression::branches(StringPattern::everything())
|
||||
.intersection(&filter_expression);
|
||||
|
|
|
@ -42,11 +42,10 @@ pub(crate) fn cmd_duplicate(
|
|||
args: &DuplicateArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let mut workspace_command = command.workspace_helper(ui)?;
|
||||
let to_duplicate: Vec<CommitId> = {
|
||||
let expression = workspace_command.parse_union_revsets(&args.revisions)?;
|
||||
let revset = workspace_command.evaluate_revset(expression)?;
|
||||
revset.iter().collect() // in reverse topological order
|
||||
};
|
||||
let to_duplicate: Vec<CommitId> = workspace_command
|
||||
.parse_union_revsets(&args.revisions)?
|
||||
.evaluate_to_commit_ids()?
|
||||
.collect(); // in reverse topological order
|
||||
if to_duplicate.is_empty() {
|
||||
writeln!(ui.stderr(), "No revisions to duplicate.")?;
|
||||
return Ok(());
|
||||
|
|
|
@ -1205,6 +1205,7 @@ fn find_branches_targeted_by_revisions<'a>(
|
|||
for rev_str in revisions {
|
||||
let expression = workspace_command
|
||||
.parse_revset(rev_str)?
|
||||
.expression()
|
||||
.intersection(&RevsetExpression::branches(StringPattern::everything()));
|
||||
let revset = workspace_command.evaluate_revset(expression)?;
|
||||
let mut commit_ids = revset.iter().peekable();
|
||||
|
|
|
@ -81,7 +81,9 @@ pub(crate) fn cmd_log(
|
|||
workspace_command.parse_revset(&command.settings().default_revset())?
|
||||
} else {
|
||||
workspace_command.parse_union_revsets(&args.revisions)?
|
||||
};
|
||||
}
|
||||
.expression()
|
||||
.clone();
|
||||
if !args.paths.is_empty() {
|
||||
let repo_paths: Vec<_> = args
|
||||
.paths
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
//! This file contains the internal implementation of `run`.
|
||||
|
||||
use itertools::Itertools as _;
|
||||
use jj_lib::repo::Repo as _;
|
||||
use jj_lib::revset::RevsetIteratorExt as _;
|
||||
|
||||
use crate::cli_util::{CommandHelper, RevisionArg};
|
||||
use crate::command_error::{user_error, CommandError};
|
||||
|
@ -52,12 +50,10 @@ pub struct RunArgs {
|
|||
|
||||
pub fn cmd_run(ui: &mut Ui, command: &CommandHelper, args: &RunArgs) -> Result<(), CommandError> {
|
||||
let workspace_command = command.workspace_helper(ui)?;
|
||||
let _resolved_commits: Vec<_> = {
|
||||
let repo = workspace_command.repo();
|
||||
let expression = workspace_command.parse_union_revsets(&args.revisions)?;
|
||||
let revset = workspace_command.evaluate_revset(expression)?;
|
||||
revset.iter().commits(repo.store()).try_collect()?
|
||||
};
|
||||
let _resolved_commits: Vec<_> = workspace_command
|
||||
.parse_union_revsets(&args.revisions)?
|
||||
.evaluate_to_commits()?
|
||||
.try_collect()?;
|
||||
// Jobs are resolved in this order:
|
||||
// 1. Commandline argument iff > 0.
|
||||
// 2. the amount of cores available.
|
||||
|
|
|
@ -17,12 +17,13 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use itertools::Itertools as _;
|
||||
use jj_lib::backend::CommitId;
|
||||
use jj_lib::backend::{BackendResult, CommitId};
|
||||
use jj_lib::commit::Commit;
|
||||
use jj_lib::id_prefix::IdPrefixContext;
|
||||
use jj_lib::repo::Repo;
|
||||
use jj_lib::revset::{
|
||||
self, DefaultSymbolResolver, Revset, RevsetAliasesMap, RevsetEvaluationError, RevsetExpression,
|
||||
RevsetParseContext, RevsetParseError, RevsetResolutionError,
|
||||
RevsetIteratorExt as _, RevsetParseContext, RevsetParseError, RevsetResolutionError,
|
||||
};
|
||||
use jj_lib::settings::ConfigResultExt as _;
|
||||
use thiserror::Error;
|
||||
|
@ -41,6 +42,55 @@ pub enum UserRevsetEvaluationError {
|
|||
Evaluation(RevsetEvaluationError),
|
||||
}
|
||||
|
||||
/// Wrapper around `RevsetExpression` to provide convenient methods.
|
||||
pub struct RevsetExpressionEvaluator<'repo> {
|
||||
repo: &'repo dyn Repo,
|
||||
id_prefix_context: &'repo IdPrefixContext,
|
||||
expression: Rc<RevsetExpression>,
|
||||
}
|
||||
|
||||
impl<'repo> RevsetExpressionEvaluator<'repo> {
|
||||
pub fn new(
|
||||
repo: &'repo dyn Repo,
|
||||
id_prefix_context: &'repo IdPrefixContext,
|
||||
expression: Rc<RevsetExpression>,
|
||||
) -> Self {
|
||||
RevsetExpressionEvaluator {
|
||||
repo,
|
||||
id_prefix_context,
|
||||
expression,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying expression.
|
||||
pub fn expression(&self) -> &Rc<RevsetExpression> {
|
||||
&self.expression
|
||||
}
|
||||
|
||||
/// Evaluates the expression.
|
||||
pub fn evaluate(&self) -> Result<Box<dyn Revset + 'repo>, UserRevsetEvaluationError> {
|
||||
let symbol_resolver = default_symbol_resolver(self.repo, self.id_prefix_context);
|
||||
evaluate(self.repo, &symbol_resolver, self.expression.clone())
|
||||
}
|
||||
|
||||
/// Evaluates the expression to an iterator over commit ids. Entries are
|
||||
/// sorted in reverse topological order.
|
||||
pub fn evaluate_to_commit_ids(
|
||||
&self,
|
||||
) -> Result<Box<dyn Iterator<Item = CommitId> + 'repo>, UserRevsetEvaluationError> {
|
||||
Ok(self.evaluate()?.iter())
|
||||
}
|
||||
|
||||
/// Evaluates the expression to an iterator over commit objects. Entries are
|
||||
/// sorted in reverse topological order.
|
||||
pub fn evaluate_to_commits(
|
||||
&self,
|
||||
) -> Result<impl Iterator<Item = BackendResult<Commit>> + 'repo, UserRevsetEvaluationError>
|
||||
{
|
||||
Ok(self.evaluate()?.iter().commits(self.repo.store()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_revset_aliases(
|
||||
ui: &Ui,
|
||||
layered_configs: &LayeredConfigs,
|
||||
|
|
Loading…
Reference in a new issue