From 16a551261139a09abf2ab51dc09e2852cf25a465 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Mon, 3 Aug 2026 08:53:01 -0700 Subject: [PATCH 1/2] cli: make `RevsetExpressionEvaluator` async Revset symbol resolution will soon be async, so this needs to be async too. --- cli/src/cli_util.rs | 14 +++++++++----- cli/src/commands/abandon.rs | 3 ++- cli/src/commands/absorb.rs | 3 ++- cli/src/commands/arrange.rs | 3 ++- cli/src/commands/bisect/run.rs | 3 ++- cli/src/commands/bookmark/advance.rs | 3 ++- cli/src/commands/bookmark/list.rs | 6 +++++- cli/src/commands/bookmark/move.rs | 3 ++- cli/src/commands/describe.rs | 3 ++- cli/src/commands/diff.rs | 9 ++++++--- cli/src/commands/duplicate.rs | 3 ++- cli/src/commands/evolog.rs | 3 ++- cli/src/commands/fix.rs | 3 ++- cli/src/commands/gerrit/upload.rs | 6 ++++-- cli/src/commands/git/push.rs | 24 ++++++++++++++---------- cli/src/commands/log.rs | 4 ++-- cli/src/commands/metaedit.rs | 3 ++- cli/src/commands/parallelize.rs | 3 ++- cli/src/commands/rebase.rs | 3 ++- cli/src/commands/revert.rs | 3 ++- cli/src/commands/run.rs | 6 ++++-- cli/src/commands/show.rs | 2 +- cli/src/commands/sign.rs | 3 ++- cli/src/commands/simplify_parents.rs | 9 ++++++--- cli/src/commands/squash.rs | 3 ++- cli/src/commands/status.rs | 3 ++- cli/src/commands/tag/list.rs | 6 +++++- cli/src/commands/unsign.rs | 3 ++- cli/src/revset_util.rs | 17 ++++++++++------- 29 files changed, 102 insertions(+), 55 deletions(-) diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs index b3e925bdd47..46f9826a22e 100644 --- a/cli/src/cli_util.rs +++ b/cli/src/cli_util.rs @@ -1109,7 +1109,7 @@ impl WorkspaceCommandEnvironment { /// Resolves the effective `immutable()` expression to test against commits /// during a rewrite, taking the `--ignore-immutable` flag into account. - fn resolve_immutable_expression( + async fn resolve_immutable_expression( &self, repo: &dyn Repo, ) -> Result, CommandError> { @@ -1130,6 +1130,7 @@ impl WorkspaceCommandEnvironment { immutable_expression, ) .resolve() + .await .map_err(|e| config_error_with_message("Invalid `revset-aliases.immutable_heads()`", e)) } @@ -1811,7 +1812,7 @@ to the current parents may contain changes from multiple commits. let mut all_commits = IndexSet::new(); for revision_arg in revision_args { let expression = self.parse_revset(ui, revision_arg)?; - let mut stream = expression.evaluate_to_commit_ids()?; + let mut stream = expression.evaluate_to_commit_ids().await?; while let Some(commit_id) = stream.try_next().await? { all_commits.insert(commit_id); } @@ -2007,7 +2008,7 @@ to the current parents may contain changes from multiple commits. to_rewrite_expr: &Arc, ) -> Result<(), CommandError> { let repo = self.repo().as_ref(); - let immutable_expr = self.env.resolve_immutable_expression(repo)?; + let immutable_expr = self.env.resolve_immutable_expression(repo).await?; let Some(commit_id) = immutable_expr .intersection(to_rewrite_expr) .evaluate(repo)? @@ -2099,6 +2100,7 @@ to the current parents may contain changes from multiple commits. let immutable_expr = self .env .resolve_immutable_expression(tx.repo()) + .await .map_err(snapshot_command_error)?; let wc_immutable = !immutable_expr .intersection(&RevsetExpression::commit(wc_commit.id().clone())) @@ -2316,7 +2318,7 @@ to the current parents may contain changes from multiple commits. // failures can be ignored. snapshot_working_copy() ensures that the // working-copy commit is mutable. let maybe_new_wc_commit = if let Some(wc_commit) = &maybe_new_wc_commit - && let Ok(immutable_expr) = self.env.resolve_immutable_expression(tx.repo()) + && let Ok(immutable_expr) = self.env.resolve_immutable_expression(tx.repo()).await && !immutable_expr .intersection(&RevsetExpression::commit(wc_commit.id().clone())) .evaluate(tx.repo())? @@ -2927,7 +2929,9 @@ async fn rebase_mutable_descendants( // tx.base_repo() here because we're interested in existing immutable // commits that are still reachable. let mut num_rebased = 0; - let immutable = env.resolve_immutable_expression(tx.base_repo().as_ref())?; + let immutable = env + .resolve_immutable_expression(tx.base_repo().as_ref()) + .await?; tx.repo_mut() .rebase_descendants_with_options( &immutable, diff --git a/cli/src/commands/abandon.rs b/cli/src/commands/abandon.rs index a70f3880685..55ccafbaabd 100644 --- a/cli/src/commands/abandon.rs +++ b/cli/src/commands/abandon.rs @@ -79,7 +79,8 @@ pub(crate) async fn cmd_abandon( } else { workspace_command.parse_revset(ui, &RevisionArg::AT)? } - .resolve()?; + .resolve() + .await?; let visible_expr = target_expr.intersection(&RevsetExpression::visible_heads().ancestors()); workspace_command .check_rewritable_expr(&visible_expr) diff --git a/cli/src/commands/absorb.rs b/cli/src/commands/absorb.rs index 12cde6a5262..2a4880dd039 100644 --- a/cli/src/commands/absorb.rs +++ b/cli/src/commands/absorb.rs @@ -94,7 +94,8 @@ pub(crate) async fn cmd_absorb( let source_commit = workspace_command.resolve_single_rev(ui, &args.from).await?; let destinations = workspace_command .parse_union_revsets(ui, &args.into)? - .resolve()?; + .resolve() + .await?; let fileset_expression = workspace_command.parse_file_patterns(ui, &args.paths)?; let matcher = fileset_expression.to_matcher(); diff --git a/cli/src/commands/arrange.rs b/cli/src/commands/arrange.rs index 3958f913208..57775ce1e01 100644 --- a/cli/src/commands/arrange.rs +++ b/cli/src/commands/arrange.rs @@ -101,7 +101,8 @@ pub(crate) async fn cmd_arrange( workspace_command .parse_union_revsets(ui, &[&*args.revisions_pos, &*args.revisions_opt].concat())? } - .resolve()?; + .resolve() + .await?; workspace_command .check_rewritable_expr(&target_expression) .await?; diff --git a/cli/src/commands/bisect/run.rs b/cli/src/commands/bisect/run.rs index c727b488f75..a5a34518611 100644 --- a/cli/src/commands/bisect/run.rs +++ b/cli/src/commands/bisect/run.rs @@ -128,7 +128,8 @@ pub(crate) async fn cmd_bisect_run( let input_range = workspace_command .parse_union_revsets(ui, &args.range)? - .resolve()?; + .resolve() + .await?; let initial_repo = workspace_command.repo().clone(); diff --git a/cli/src/commands/bookmark/advance.rs b/cli/src/commands/bookmark/advance.rs index 11d66bd6c2b..f593855c5f2 100644 --- a/cli/src/commands/bookmark/advance.rs +++ b/cli/src/commands/bookmark/advance.rs @@ -139,7 +139,8 @@ pub async fn cmd_bookmark_advance( let is_source_commit = workspace_command .attach_revset_evaluator(expression) - .evaluate()? + .evaluate() + .await? .containing_fn(); let is_source_ref = async |target: &RefTarget| -> Result { Ok( diff --git a/cli/src/commands/bookmark/list.rs b/cli/src/commands/bookmark/list.rs index d16c2fcbd27..fb6d5a86931 100644 --- a/cli/src/commands/bookmark/list.rs +++ b/cli/src/commands/bookmark/list.rs @@ -145,7 +145,11 @@ pub async fn cmd_bookmark_list( // Intersects with the set of local bookmark targets to minimize the lookup // space. expression.intersect_with(&RevsetExpression::bookmarks(StringExpression::all())); - expression.evaluate_to_commit_ids()?.try_collect().await? + expression + .evaluate_to_commit_ids() + .await? + .try_collect() + .await? } else { HashSet::new() }; diff --git a/cli/src/commands/bookmark/move.rs b/cli/src/commands/bookmark/move.rs index 9e2514f55f0..31ea7bb2884 100644 --- a/cli/src/commands/bookmark/move.rs +++ b/cli/src/commands/bookmark/move.rs @@ -83,7 +83,8 @@ pub async fn cmd_bookmark_move( Some( workspace_command .parse_union_revsets(ui, &args.from)? - .evaluate()? + .evaluate() + .await? .containing_fn(), ) } else { diff --git a/cli/src/commands/describe.rs b/cli/src/commands/describe.rs index 10542d95ae7..935ce0d9d15 100644 --- a/cli/src/commands/describe.rs +++ b/cli/src/commands/describe.rs @@ -96,7 +96,8 @@ pub(crate) async fn cmd_describe( } else { workspace_command.parse_revset(ui, &RevisionArg::AT)? } - .resolve()?; + .resolve() + .await?; workspace_command .check_rewritable_expr(&target_expr) .await?; diff --git a/cli/src/commands/diff.rs b/cli/src/commands/diff.rs index 4eb161b1560..8ebf83ed0cd 100644 --- a/cli/src/commands/diff.rs +++ b/cli/src/commands/diff.rs @@ -154,7 +154,8 @@ pub(crate) async fn cmd_diff( .range(&target_expression.heads()) .minus(target_expression), ) - .evaluate_to_commit_ids()?; + .evaluate_to_commit_ids() + .await?; if let Some(commit_id) = gaps_revset.try_next().await? { return Err( user_error("Cannot diff revsets with gaps in.").hinted(format!( @@ -165,12 +166,14 @@ pub(crate) async fn cmd_diff( } let heads: Vec<_> = workspace_command .attach_revset_evaluator(target_expression.heads()) - .evaluate_to_commits()? + .evaluate_to_commits() + .await? .try_collect() .await?; let roots: Vec<_> = workspace_command .attach_revset_evaluator(target_expression.roots()) - .evaluate_to_commits()? + .evaluate_to_commits() + .await? .try_collect() .await?; diff --git a/cli/src/commands/duplicate.rs b/cli/src/commands/duplicate.rs index 96367037c82..a439fe1cf01 100644 --- a/cli/src/commands/duplicate.rs +++ b/cli/src/commands/duplicate.rs @@ -113,7 +113,8 @@ pub(crate) async fn cmd_duplicate( } else { workspace_command.parse_revset(ui, &RevisionArg::AT)? } - .evaluate_to_commit_ids()? + .evaluate_to_commit_ids() + .await? .try_collect() .await?; // in reverse topological order if to_duplicate.is_empty() { diff --git a/cli/src/commands/evolog.rs b/cli/src/commands/evolog.rs index 85b51535683..a0539c2a716 100644 --- a/cli/src/commands/evolog.rs +++ b/cli/src/commands/evolog.rs @@ -112,7 +112,8 @@ pub(crate) async fn cmd_evolog( let start_commit_ids: Vec<_> = workspace_command .parse_union_revsets(ui, &args.revisions)? - .evaluate_to_commit_ids()? + .evaluate_to_commit_ids() + .await? .try_collect() .await?; diff --git a/cli/src/commands/fix.rs b/cli/src/commands/fix.rs index 7f300b98f5c..4a86a322aba 100644 --- a/cli/src/commands/fix.rs +++ b/cli/src/commands/fix.rs @@ -204,7 +204,8 @@ pub(crate) async fn cmd_fix( } else { workspace_command.parse_union_revsets(ui, &args.source)? } - .resolve()?; + .resolve() + .await?; workspace_command .check_rewritable_expr(&target_expr) .await?; diff --git a/cli/src/commands/gerrit/upload.rs b/cli/src/commands/gerrit/upload.rs index acef52cdbaf..b44a0ac63a1 100644 --- a/cli/src/commands/gerrit/upload.rs +++ b/cli/src/commands/gerrit/upload.rs @@ -461,7 +461,8 @@ pub async fn cmd_gerrit_upload( } else { let target_expr = workspace_command .parse_union_revsets(ui, &args.revisions)? - .resolve()?; + .resolve() + .await?; workspace_command .check_rewritable_expr(&target_expr) .await?; @@ -488,7 +489,8 @@ pub async fn cmd_gerrit_upload( .immutable_expression() .range(&RevsetExpression::commits(revisions.clone())), ) - .evaluate_to_commits()? + .evaluate_to_commits() + .await? .try_collect() .await?; diff --git a/cli/src/commands/git/push.rs b/cli/src/commands/git/push.rs index 077b52becad..d438cc8d4b3 100644 --- a/cli/src/commands/git/push.rs +++ b/cli/src/commands/git/push.rs @@ -286,7 +286,7 @@ pub async fn cmd_git_push( let mut ref_updates = GitPushRefTargets::default(); if args.all { let mut commits_validator = - CommitsValidator::new(ui, tx.base_workspace_helper(), remote, args)?; + CommitsValidator::new(ui, tx.base_workspace_helper(), remote, args).await?; for (name, targets) in view.local_remote_bookmarks(remote) { let remote_symbol = name.to_remote_symbol(remote); let allow_new = true; // implied by --all @@ -317,7 +317,7 @@ pub async fn cmd_git_push( ); } else if args.tracked { let mut commits_validator = - CommitsValidator::new(ui, tx.base_workspace_helper(), remote, args)?; + CommitsValidator::new(ui, tx.base_workspace_helper(), remote, args).await?; for (name, targets) in view.local_remote_bookmarks(remote) { if !targets.remote_ref.is_tracked() { continue; @@ -355,7 +355,7 @@ pub async fn cmd_git_push( } else if args.deleted { // There shouldn't be new heads to push, but we run validation for consistency. let mut commits_validator = - CommitsValidator::new(ui, tx.base_workspace_helper(), remote, args)?; + CommitsValidator::new(ui, tx.base_workspace_helper(), remote, args).await?; for (name, targets) in view.local_remote_bookmarks(remote) { if targets.local_target.is_present() { continue; @@ -486,7 +486,7 @@ pub async fn cmd_git_push( } let mut commits_validator = - CommitsValidator::new(ui, tx.base_workspace_helper(), remote, args)?; + CommitsValidator::new(ui, tx.base_workspace_helper(), remote, args).await?; // Error out if explicitly-specified targets can't be pushed. commits_validator .validate_updates(&ref_updates) @@ -670,7 +670,7 @@ struct CommitsValidator<'repo> { } impl<'repo> CommitsValidator<'repo> { - fn new( + async fn new( ui: &Ui, workspace_helper: &'repo WorkspaceCommandHelper, remote: &RemoteName, @@ -685,13 +685,15 @@ impl<'repo> CommitsValidator<'repo> { .collect(); let immutable_heads = workspace_helper .attach_revset_evaluator(workspace_helper.env().immutable_heads_expression().clone()) - .resolve()?; + .resolve() + .await?; let private_commits = if !args.allow_private { let settings = workspace_helper.settings(); let revset_str = settings.get_string("git.private-commits")?; let is_private = workspace_helper .parse_revset(ui, &RevisionArg::from(revset_str.clone()))? - .evaluate()? + .evaluate() + .await? .containing_fn(); Some((revset_str, is_private)) } else { @@ -809,7 +811,8 @@ async fn sign_commits_before_push( let commit_ids: IndexSet = tx .base_workspace_helper() .attach_revset_evaluator(commits_to_push) - .evaluate_to_commits()? + .evaluate_to_commits() + .await? // TODO: make filter condition configurable by revset? .try_filter(|commit| { future::ready(!commit.is_signed() && sign_settings.should_sign(commit.store_commit())) @@ -1293,7 +1296,8 @@ async fn find_default_target_revisions( ); let commit_ids = workspace_command .attach_revset_evaluator(expression) - .evaluate_to_commit_ids()? + .evaluate_to_commit_ids() + .await? .peekable(); let mut commit_ids = std::pin::pin!(commit_ids); if commit_ids.as_mut().peek().await.is_none() { @@ -1319,7 +1323,7 @@ async fn find_target_revisions( &RevsetExpression::bookmarks(StringExpression::all()) .union(&RevsetExpression::tags(StringExpression::all())), ); - let commit_ids = expression.evaluate_to_commit_ids()?.peekable(); + let commit_ids = expression.evaluate_to_commit_ids().await?.peekable(); let mut commit_ids = std::pin::pin!(commit_ids); if commit_ids.as_mut().as_mut().peek().await.is_none() { writeln!( diff --git a/cli/src/commands/log.rs b/cli/src/commands/log.rs index 56a2ea42865..88768c20ec1 100644 --- a/cli/src/commands/log.rs +++ b/cli/src/commands/log.rs @@ -162,7 +162,7 @@ pub(crate) async fn cmd_log( expression }; - let revset = revset_expression.evaluate()?; + let revset = revset_expression.evaluate().await?; if args.count { let (lower, upper) = revset.count_estimate()?; @@ -224,7 +224,7 @@ pub(crate) async fn cmd_log( let mut stream: LocalBoxStream<_> = { let mut topo_order = TopoGroupedGraph::new(revset.stream_graph(), |id| id); - let mut prio_stream = prio_revset.evaluate_to_commit_ids()?; + let mut prio_stream = prio_revset.evaluate_to_commit_ids().await?; while let Some(prio) = prio_stream.try_next().await? { topo_order.prioritize_branch(prio); } diff --git a/cli/src/commands/metaedit.rs b/cli/src/commands/metaedit.rs index 3ac3b2d5ae3..04446be025e 100644 --- a/cli/src/commands/metaedit.rs +++ b/cli/src/commands/metaedit.rs @@ -145,7 +145,8 @@ pub(crate) async fn cmd_metaedit( } else { workspace_command.parse_revset(ui, &RevisionArg::AT)? } - .resolve()?; + .resolve() + .await?; workspace_command .check_rewritable_expr(&target_expr) .await?; diff --git a/cli/src/commands/parallelize.rs b/cli/src/commands/parallelize.rs index bb2969419ad..0eb390ccd98 100644 --- a/cli/src/commands/parallelize.rs +++ b/cli/src/commands/parallelize.rs @@ -78,7 +78,8 @@ pub(crate) async fn cmd_parallelize( // here with children before parents. let target_commits: Vec = workspace_command .parse_union_revsets(ui, &[&*args.revisions_pos, &*args.revisions_opt].concat())? - .evaluate_to_commits()? + .evaluate_to_commits() + .await? .try_collect() .await?; diff --git a/cli/src/commands/rebase.rs b/cli/src/commands/rebase.rs index d94c15f6c49..f631d11bbc4 100644 --- a/cli/src/commands/rebase.rs +++ b/cli/src/commands/rebase.rs @@ -436,7 +436,8 @@ async fn plan_rebase_revisions( ) -> Result { let target_expr = workspace_command .parse_union_revsets(ui, revisions)? - .resolve()?; + .resolve() + .await?; workspace_command .check_rewritable_expr(&target_expr) .await?; diff --git a/cli/src/commands/revert.rs b/cli/src/commands/revert.rs index 0aff2e132a7..d409a815c4b 100644 --- a/cli/src/commands/revert.rs +++ b/cli/src/commands/revert.rs @@ -104,7 +104,8 @@ pub(crate) async fn cmd_revert( let mut workspace_command = command.workspace_helper(ui).await?; let to_revert: Vec<_> = workspace_command .parse_union_revsets(ui, &args.revisions)? - .evaluate_to_commits()? + .evaluate_to_commits() + .await? .try_collect() .await?; // in reverse topological order if to_revert.is_empty() { diff --git a/cli/src/commands/run.rs b/cli/src/commands/run.rs index 95a4b2be37c..194aea09304 100644 --- a/cli/src/commands/run.rs +++ b/cli/src/commands/run.rs @@ -706,13 +706,15 @@ pub async fn cmd_run( let revs = workspace_command.settings().get_string("revsets.run")?; workspace_command .parse_revset(ui, &RevisionArg::from(revs))? - .evaluate_to_commits()? + .evaluate_to_commits() + .await? .try_collect() .await? } else { workspace_command .parse_union_revsets(ui, &args.revisions)? - .evaluate_to_commits()? + .evaluate_to_commits() + .await? .try_collect() .await? }; diff --git a/cli/src/commands/show.rs b/cli/src/commands/show.rs index f512aeb3a59..4ddd0ba14f2 100644 --- a/cli/src/commands/show.rs +++ b/cli/src/commands/show.rs @@ -91,7 +91,7 @@ pub(crate) async fn cmd_show( .labeled(["show", "commit"]); let diff_renderer = workspace_command.diff_renderer_for(&args.format)?; - let mut commit_stream = target_expr.evaluate_to_commits()?; + let mut commit_stream = target_expr.evaluate_to_commits().await?; if args.reversed { let commits: Vec<_> = commit_stream.try_collect().await?; commit_stream = stream::iter(commits.into_iter().rev().map(Ok)).boxed_local(); diff --git a/cli/src/commands/sign.rs b/cli/src/commands/sign.rs index 14f7b9253e6..1aa723cab85 100644 --- a/cli/src/commands/sign.rs +++ b/cli/src/commands/sign.rs @@ -82,7 +82,8 @@ pub async fn cmd_sign( } else { workspace_command.parse_union_revsets(ui, &args.revisions)? } - .resolve()?; + .resolve() + .await?; workspace_command .check_rewritable_expr(&revset_expression) diff --git a/cli/src/commands/simplify_parents.rs b/cli/src/commands/simplify_parents.rs index b8a54cd3915..4fb44122e1e 100644 --- a/cli/src/commands/simplify_parents.rs +++ b/cli/src/commands/simplify_parents.rs @@ -49,16 +49,19 @@ pub(crate) async fn cmd_simplify_parents( .get_string("revsets.simplify-parents")?; workspace_command .parse_revset(ui, &RevisionArg::from(revs))? - .resolve()? + .resolve() + .await? } else { workspace_command .parse_union_revsets(ui, &args.source)? - .resolve()? + .resolve() + .await? .descendants() .union( &workspace_command .parse_union_revsets(ui, &args.revisions)? - .resolve()?, + .resolve() + .await?, ) }; workspace_command.check_rewritable_expr(&revs).await?; diff --git a/cli/src/commands/squash.rs b/cli/src/commands/squash.rs index 4a8d0d468f1..ec492ffd9ca 100644 --- a/cli/src/commands/squash.rs +++ b/cli/src/commands/squash.rs @@ -198,7 +198,8 @@ pub(crate) async fn cmd_squash( } else { workspace_command.parse_union_revsets(ui, &args.from)? } - .evaluate_to_commits()? + .evaluate_to_commits() + .await? .try_collect() .await?; if insert_destination_commit { diff --git a/cli/src/commands/status.rs b/cli/src/commands/status.rs index 64defc3e429..917d1ce4bb1 100644 --- a/cli/src/commands/status.rs +++ b/cli/src/commands/status.rs @@ -179,7 +179,8 @@ pub(crate) async fn cmd_status( .filtered(RevsetFilterPredicate::HasConflict) .minus(&workspace_command.env().immutable_expression()), ) - .evaluate_to_commit_ids()? + .evaluate_to_commit_ids() + .await? .try_collect() .await?; diff --git a/cli/src/commands/tag/list.rs b/cli/src/commands/tag/list.rs index 9fab3efec5b..54695b882be 100644 --- a/cli/src/commands/tag/list.rs +++ b/cli/src/commands/tag/list.rs @@ -148,7 +148,11 @@ pub async fn cmd_tag_list( // Intersects with the set of local tag targets to minimize the lookup // space. expression.intersect_with(&RevsetExpression::tags(StringExpression::all())); - expression.evaluate_to_commit_ids()?.try_collect().await? + expression + .evaluate_to_commit_ids() + .await? + .try_collect() + .await? } else { HashSet::new() }; diff --git a/cli/src/commands/unsign.rs b/cli/src/commands/unsign.rs index fabf8f9770a..43d41b2ace3 100644 --- a/cli/src/commands/unsign.rs +++ b/cli/src/commands/unsign.rs @@ -52,7 +52,8 @@ pub async fn cmd_unsign( let target_expr = workspace_command .parse_union_revsets(ui, &args.revisions)? - .resolve()?; + .resolve() + .await?; workspace_command .check_rewritable_expr(&target_expr) .await?; diff --git a/cli/src/revset_util.rs b/cli/src/revset_util.rs index fbee8a6812b..af040a95407 100644 --- a/cli/src/revset_util.rs +++ b/cli/src/revset_util.rs @@ -106,7 +106,7 @@ impl<'repo> RevsetExpressionEvaluator<'repo> { } /// Resolves user symbols in the expression, returns new expression. - pub fn resolve(&self) -> Result, RevsetResolutionError> { + pub async fn resolve(&self) -> Result, RevsetResolutionError> { let symbol_resolver = default_symbol_resolver( self.repo, self.extensions.symbol_resolvers(), @@ -117,8 +117,9 @@ impl<'repo> RevsetExpressionEvaluator<'repo> { } /// Evaluates the expression. - pub fn evaluate(&self) -> Result, UserRevsetEvaluationError> { + pub async fn evaluate(&self) -> Result, UserRevsetEvaluationError> { self.resolve() + .await .map_err(UserRevsetEvaluationError::Resolution)? .evaluate(self.repo) .map_err(UserRevsetEvaluationError::Evaluation) @@ -126,25 +127,26 @@ impl<'repo> RevsetExpressionEvaluator<'repo> { /// Evaluates the expression to an iterator over commit ids. Entries are /// sorted in reverse topological order. - pub fn evaluate_to_commit_ids( + pub async fn evaluate_to_commit_ids( &self, ) -> Result< LocalBoxStream<'repo, Result>, UserRevsetEvaluationError, > { - Ok(self.evaluate()?.stream()) + Ok(self.evaluate().await?.stream()) } /// Evaluates the expression to an iterator over commit objects. Entries are /// sorted in reverse topological order. - pub fn evaluate_to_commits( + pub async fn evaluate_to_commits( &self, ) -> Result< LocalBoxStream<'repo, Result>, UserRevsetEvaluationError, > { Ok(self - .evaluate()? + .evaluate() + .await? .stream() .commits(self.repo.store()) .boxed_local()) @@ -231,7 +233,8 @@ pub(super) async fn evaluate_revset_to_single_commit<'a>( commit_summary_template: impl FnOnce() -> TemplateRenderer<'a, Commit>, ) -> Result { let commits: Vec<_> = expression - .evaluate_to_commits()? + .evaluate_to_commits() + .await? .take(6) .try_collect() .await?; From 5b294bb34f53e5774c130200c622b9d6db45ed2e Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Mon, 3 Aug 2026 07:07:03 -0700 Subject: [PATCH 2/2] revset: make symbol resolution async Symbol resolution can be customized and may involve I/O, so it should be async. --- cli/examples/custom-commit-templater/main.rs | 4 +- cli/src/cli_util.rs | 2 +- cli/src/commands/bench/revset.rs | 1 + cli/src/commands/debug/revset.rs | 4 +- cli/src/commands/operation/diff.rs | 16 +- cli/src/commit_templater.rs | 1 + cli/src/revset_util.rs | 9 +- lib/src/id_prefix.rs | 7 +- lib/src/revset.rs | 156 ++++++++++--------- lib/tests/test_id_prefix.rs | 13 +- lib/tests/test_revset.rs | 37 +++-- 11 files changed, 153 insertions(+), 97 deletions(-) diff --git a/cli/examples/custom-commit-templater/main.rs b/cli/examples/custom-commit-templater/main.rs index 9b3946e44e2..f5ae21ae80e 100644 --- a/cli/examples/custom-commit-templater/main.rs +++ b/cli/examples/custom-commit-templater/main.rs @@ -14,6 +14,7 @@ use std::sync::Arc; +use async_trait::async_trait; use futures::TryStreamExt as _; use jj_cli::cli_util::CliRunner; use jj_cli::commit_templater::CommitTemplateBuildFnTable; @@ -90,8 +91,9 @@ struct TheDigitestResolver { cache: MostDigitsInId, } +#[async_trait(?Send)] impl PartialSymbolResolver for TheDigitestResolver { - fn resolve_symbol( + async fn resolve_symbol( &self, repo: &dyn Repo, symbol: &str, diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs index 46f9826a22e..00fa619d2a4 100644 --- a/cli/src/cli_util.rs +++ b/cli/src/cli_util.rs @@ -536,7 +536,7 @@ impl CommandHelper { self.resolve_operation(ui, workspace.repo_loader(), workspace.workspace_name())?; let repo = workspace.repo_loader().load_at(&op_head).await?; if let Err(err) = - revset_util::try_resolve_trunk_alias(repo.as_ref(), &env.revset_parse_context()) + revset_util::try_resolve_trunk_alias(repo.as_ref(), &env.revset_parse_context()).await { // The fallback can be builtin_trunk() if we're willing to support // inferred trunk forever. (#7990) diff --git a/cli/src/commands/bench/revset.rs b/cli/src/commands/bench/revset.rs index ca4765fce4a..e2e0c3f27a2 100644 --- a/cli/src/commands/bench/revset.rs +++ b/cli/src/commands/bench/revset.rs @@ -96,6 +96,7 @@ fn bench_revset( SymbolResolver::new(repo, &([] as [Box; 0])); let resolved = expression .resolve_user_expression(repo, &symbol_resolver) + .block_on() .unwrap(); let revset = resolved.evaluate(repo).unwrap(); revset.stream().count().block_on() diff --git a/cli/src/commands/debug/revset.rs b/cli/src/commands/debug/revset.rs index e3760375521..b86131adda7 100644 --- a/cli/src/commands/debug/revset.rs +++ b/cli/src/commands/debug/revset.rs @@ -71,7 +71,9 @@ pub async fn cmd_debug_revset( command.revset_extensions().symbol_resolvers(), workspace_command.id_prefix_context(), ); - let mut expression = expression.resolve_user_expression(repo, &symbol_resolver)?; + let mut expression = expression + .resolve_user_expression(repo, &symbol_resolver) + .await?; writeln!(ui.stdout(), "-- Resolved:")?; writeln!(ui.stdout(), "{expression:#?}")?; writeln!(ui.stdout())?; diff --git a/cli/src/commands/operation/diff.rs b/cli/src/commands/operation/diff.rs index 143eb44ec95..9e37932cb0a 100644 --- a/cli/src/commands/operation/diff.rs +++ b/cli/src/commands/operation/diff.rs @@ -238,7 +238,7 @@ pub fn parse_op_diff_changes_in( /// Resolves the `op-diff-changes-in` expression for both the "from" and "to" /// repositories. -fn resolve_op_diff_changes_exprs( +async fn resolve_op_diff_changes_exprs( workspace_env: &WorkspaceCommandEnvironment, op_diff_changes_expr: &UserRevsetExpression, from_repo: &ReadonlyRepo, @@ -250,10 +250,12 @@ fn resolve_op_diff_changes_exprs( .symbol_resolvers(); let from_repo_symbol_resolver = SymbolResolver::new(from_repo, extensions); let to_repo_symbol_resolver = SymbolResolver::new(to_repo, extensions); - let from_op_diff_changes_expr = - op_diff_changes_expr.resolve_user_expression(from_repo, &from_repo_symbol_resolver)?; - let to_op_diff_changes_expr = - op_diff_changes_expr.resolve_user_expression(to_repo, &to_repo_symbol_resolver)?; + let from_op_diff_changes_expr = op_diff_changes_expr + .resolve_user_expression(from_repo, &from_repo_symbol_resolver) + .await?; + let to_op_diff_changes_expr = op_diff_changes_expr + .resolve_user_expression(to_repo, &to_repo_symbol_resolver) + .await?; Ok((from_op_diff_changes_expr, to_op_diff_changes_expr)) } @@ -280,7 +282,9 @@ pub async fn show_op_diff( &op_diff_changes_expr, from_repo.as_ref(), to_repo.as_ref(), - ) { + ) + .await + { Ok((from_op_diff_changes_expr, to_op_diff_changes_expr)) => { let op_commits_diff = compute_operation_commits_diff( current_repo, diff --git a/cli/src/commit_templater.rs b/cli/src/commit_templater.rs index 7060019ea39..a455a00372d 100644 --- a/cli/src/commit_templater.rs +++ b/cli/src/commit_templater.rs @@ -1439,6 +1439,7 @@ fn evaluate_revset_expression<'repo>( ); let revset = expression .resolve_user_expression(repo, &symbol_resolver) + .block_on() .map_err(|err| make_error().with_source(err))? .evaluate(repo) .map_err(|err| make_error().with_source(err))?; diff --git a/cli/src/revset_util.rs b/cli/src/revset_util.rs index af040a95407..57be575bef4 100644 --- a/cli/src/revset_util.rs +++ b/cli/src/revset_util.rs @@ -114,6 +114,7 @@ impl<'repo> RevsetExpressionEvaluator<'repo> { ); self.expression .resolve_user_expression(self.repo, &symbol_resolver) + .await } /// Evaluates the expression. @@ -209,9 +210,9 @@ pub fn parse_immutable_heads_expression( /// /// Returns `None` if the alias couldn't be parsed. Returns `Err` if the parsed /// expression had name resolution error. -pub(super) fn try_resolve_trunk_alias( +pub(super) async fn try_resolve_trunk_alias( repo: &dyn Repo, - context: &RevsetParseContext, + context: &RevsetParseContext<'_>, ) -> Result>, RevsetResolutionError> { let (_, _, revset_str, _) = context .aliases_map @@ -223,7 +224,9 @@ pub(super) fn try_resolve_trunk_alias( // Not using IdPrefixContext since trunk() revset shouldn't contain short // prefixes. let symbol_resolver = SymbolResolver::new(repo, context.extensions.symbol_resolvers()); - let resolved = expression.resolve_user_expression(repo, &symbol_resolver)?; + let resolved = expression + .resolve_user_expression(repo, &symbol_resolver) + .await?; Ok(Some(resolved)) } diff --git a/lib/src/id_prefix.rs b/lib/src/id_prefix.rs index adeeec3fe1b..a0fafe0c1c3 100644 --- a/lib/src/id_prefix.rs +++ b/lib/src/id_prefix.rs @@ -69,7 +69,8 @@ impl DisambiguationData { let symbol_resolver = SymbolResolver::new(repo, extensions); let revset = self .expression - .resolve_user_expression(repo, &symbol_resolver)? + .resolve_user_expression(repo, &symbol_resolver) + .block_on()? .evaluate(repo)?; let commit_change_ids: Vec<_> = revset.commit_change_ids().try_collect().block_on()?; @@ -158,7 +159,7 @@ impl IdPrefixIndex<'_> { } /// Resolve an unambiguous commit ID prefix. - pub fn resolve_commit_prefix( + pub async fn resolve_commit_prefix( &self, repo: &dyn Repo, prefix: &HexPrefix, @@ -219,7 +220,7 @@ impl IdPrefixIndex<'_> { } /// Resolve an unambiguous change ID prefix to the commit IDs in the revset. - pub fn resolve_change_prefix( + pub async fn resolve_change_prefix( &self, repo: &dyn Repo, prefix: &HexPrefix, diff --git a/lib/src/revset.rs b/lib/src/revset.rs index dbdd2cf9ad9..2f901984736 100644 --- a/lib/src/revset.rs +++ b/lib/src/revset.rs @@ -24,6 +24,7 @@ use std::ops::Range; use std::sync::Arc; use std::sync::LazyLock; +use async_trait::async_trait; use futures::Stream; use futures::StreamExt as _; use futures::future::LocalBoxFuture; @@ -668,12 +669,12 @@ impl> RevsetExpression { impl UserRevsetExpression { /// Resolve a user-provided expression. Symbols will be resolved using the /// provided [`SymbolResolver`]. - pub fn resolve_user_expression( + pub async fn resolve_user_expression( &self, repo: &dyn Repo, - symbol_resolver: &SymbolResolver, + symbol_resolver: &SymbolResolver<'_>, ) -> Result, RevsetResolutionError> { - resolve_symbols(repo, self, symbol_resolver) + resolve_symbols(repo, self, symbol_resolver).await } } @@ -1717,26 +1718,27 @@ fn try_transform_expression( /// /// This is similar to [`try_transform_expression()`], but is supposed to /// transform the resolution state from `InSt` to `OutSt`. +#[async_trait(?Send)] trait ExpressionStateFolder { type Error; /// Transforms the `expression`. By default, inner items are transformed /// recursively. - fn fold_expression( + async fn fold_expression( &mut self, expression: &RevsetExpression, ) -> Result>, Self::Error> { - fold_child_expression_state(self, expression) + fold_child_expression_state(self, expression).await } /// Transforms commit ref such as symbol. - fn fold_commit_ref( + async fn fold_commit_ref( &mut self, commit_ref: &InSt::CommitRef, ) -> Result>, Self::Error>; /// Transforms `at_operation(operation, candidates)` expression. - fn fold_at_operation( + async fn fold_at_operation( &mut self, operation: &InSt::Operation, candidates: &RevsetExpression, @@ -1744,7 +1746,7 @@ trait ExpressionStateFolder { } /// Transforms inner items of the `expression` by using the `folder`. -fn fold_child_expression_state( +async fn fold_child_expression_state( folder: &mut F, expression: &RevsetExpression, ) -> Result>, F::Error> @@ -1762,13 +1764,13 @@ where } RevsetExpression::Root => RevsetExpression::Root.into(), RevsetExpression::Commits(ids) => RevsetExpression::Commits(ids.clone()).into(), - RevsetExpression::CommitRef(commit_ref) => folder.fold_commit_ref(commit_ref)?, + RevsetExpression::CommitRef(commit_ref) => folder.fold_commit_ref(commit_ref).await?, RevsetExpression::Ancestors { heads, generation, parents_range, } => { - let heads = folder.fold_expression(heads)?; + let heads = folder.fold_expression(heads).await?; let generation = generation.clone(); let parents_range = parents_range.clone(); RevsetExpression::Ancestors { @@ -1779,7 +1781,7 @@ where .into() } RevsetExpression::Descendants { roots, generation } => { - let roots = folder.fold_expression(roots)?; + let roots = folder.fold_expression(roots).await?; let generation = generation.clone(); RevsetExpression::Descendants { roots, generation }.into() } @@ -1789,8 +1791,8 @@ where generation, parents_range, } => { - let roots = folder.fold_expression(roots)?; - let heads = folder.fold_expression(heads)?; + let roots = folder.fold_expression(roots).await?; + let heads = folder.fold_expression(heads).await?; let generation = generation.clone(); let parents_range = parents_range.clone(); RevsetExpression::Range { @@ -1802,17 +1804,17 @@ where .into() } RevsetExpression::DagRange { roots, heads } => { - let roots = folder.fold_expression(roots)?; - let heads = folder.fold_expression(heads)?; + let roots = folder.fold_expression(roots).await?; + let heads = folder.fold_expression(heads).await?; RevsetExpression::DagRange { roots, heads }.into() } RevsetExpression::Reachable { sources, domain } => { - let sources = folder.fold_expression(sources)?; - let domain = folder.fold_expression(domain)?; + let sources = folder.fold_expression(sources).await?; + let domain = folder.fold_expression(domain).await?; RevsetExpression::Reachable { sources, domain }.into() } RevsetExpression::Heads(heads) => { - let heads = folder.fold_expression(heads)?; + let heads = folder.fold_expression(heads).await?; RevsetExpression::Heads(heads).into() } RevsetExpression::HeadsRange { @@ -1821,10 +1823,10 @@ where parents_range, filter, } => { - let roots = folder.fold_expression(roots)?; - let heads = folder.fold_expression(heads)?; + let roots = folder.fold_expression(roots).await?; + let heads = folder.fold_expression(heads).await?; let parents_range = parents_range.clone(); - let filter = folder.fold_expression(filter)?; + let filter = folder.fold_expression(filter).await?; RevsetExpression::HeadsRange { roots, heads, @@ -1834,47 +1836,47 @@ where .into() } RevsetExpression::Roots(roots) => { - let roots = folder.fold_expression(roots)?; + let roots = folder.fold_expression(roots).await?; RevsetExpression::Roots(roots).into() } RevsetExpression::Forks => RevsetExpression::Forks.into(), RevsetExpression::ForkPoint(expression) => { - let expression = folder.fold_expression(expression)?; + let expression = folder.fold_expression(expression).await?; RevsetExpression::ForkPoint(expression).into() } RevsetExpression::MergePoint(expression) => { - let expression = folder.fold_expression(expression)?; + let expression = folder.fold_expression(expression).await?; RevsetExpression::MergePoint(expression).into() } RevsetExpression::Bisect(expression) => { - let expression = folder.fold_expression(expression)?; + let expression = folder.fold_expression(expression).await?; RevsetExpression::Bisect(expression).into() } RevsetExpression::HasSize { candidates, count } => { - let candidates = folder.fold_expression(candidates)?; + let candidates = folder.fold_expression(candidates).await?; let count = *count; RevsetExpression::HasSize { candidates, count }.into() } RevsetExpression::Latest { candidates, count } => { - let candidates = folder.fold_expression(candidates)?; + let candidates = folder.fold_expression(candidates).await?; let count = *count; RevsetExpression::Latest { candidates, count }.into() } RevsetExpression::Filter(predicate) => RevsetExpression::Filter(predicate.clone()).into(), RevsetExpression::AsFilter(candidates) => { - let candidates = folder.fold_expression(candidates)?; + let candidates = folder.fold_expression(candidates).await?; RevsetExpression::AsFilter(candidates).into() } RevsetExpression::Divergent => RevsetExpression::Divergent.into(), RevsetExpression::AtOperation { operation, candidates, - } => folder.fold_at_operation(operation, candidates)?, + } => folder.fold_at_operation(operation, candidates).await?, RevsetExpression::WithinReference { candidates, commits, } => { - let candidates = folder.fold_expression(candidates)?; + let candidates = folder.fold_expression(candidates).await?; let commits = commits.clone(); RevsetExpression::WithinReference { candidates, @@ -1886,7 +1888,7 @@ where candidates, visible_heads, } => { - let candidates = folder.fold_expression(candidates)?; + let candidates = folder.fold_expression(candidates).await?; let visible_heads = visible_heads.clone(); RevsetExpression::WithinVisibility { candidates, @@ -1895,31 +1897,31 @@ where .into() } RevsetExpression::Coalesce(expression1, expression2) => { - let expression1 = folder.fold_expression(expression1)?; - let expression2 = folder.fold_expression(expression2)?; + let expression1 = folder.fold_expression(expression1).await?; + let expression2 = folder.fold_expression(expression2).await?; RevsetExpression::Coalesce(expression1, expression2).into() } RevsetExpression::Present(candidates) => { - let candidates = folder.fold_expression(candidates)?; + let candidates = folder.fold_expression(candidates).await?; RevsetExpression::Present(candidates).into() } RevsetExpression::NotIn(complement) => { - let complement = folder.fold_expression(complement)?; + let complement = folder.fold_expression(complement).await?; RevsetExpression::NotIn(complement).into() } RevsetExpression::Union(expression1, expression2) => { - let expression1 = folder.fold_expression(expression1)?; - let expression2 = folder.fold_expression(expression2)?; + let expression1 = folder.fold_expression(expression1).await?; + let expression2 = folder.fold_expression(expression2).await?; RevsetExpression::Union(expression1, expression2).into() } RevsetExpression::Intersection(expression1, expression2) => { - let expression1 = folder.fold_expression(expression1)?; - let expression2 = folder.fold_expression(expression2)?; + let expression1 = folder.fold_expression(expression1).await?; + let expression2 = folder.fold_expression(expression2).await?; RevsetExpression::Intersection(expression1, expression2).into() } RevsetExpression::Difference(expression1, expression2) => { - let expression1 = folder.fold_expression(expression1)?; - let expression2 = folder.fold_expression(expression2)?; + let expression1 = folder.fold_expression(expression1).await?; + let expression2 = folder.fold_expression(expression2).await?; RevsetExpression::Difference(expression1, expression2).into() } }; @@ -2686,8 +2688,9 @@ fn make_no_such_symbol_error(repo: &dyn Repo, name: String) -> RevsetResolutionE /// A symbol resolver for a specific namespace of labels. /// /// Returns None if it cannot handle the symbol. +#[async_trait(?Send)] pub trait PartialSymbolResolver { - fn resolve_symbol( + async fn resolve_symbol( &self, repo: &dyn Repo, symbol: &str, @@ -2696,8 +2699,9 @@ pub trait PartialSymbolResolver { struct TagResolver; +#[async_trait(?Send)] impl PartialSymbolResolver for TagResolver { - fn resolve_symbol( + async fn resolve_symbol( &self, repo: &dyn Repo, symbol: &str, @@ -2709,8 +2713,9 @@ impl PartialSymbolResolver for TagResolver { struct BookmarkResolver; +#[async_trait(?Send)] impl PartialSymbolResolver for BookmarkResolver { - fn resolve_symbol( + async fn resolve_symbol( &self, repo: &dyn Repo, symbol: &str, @@ -2728,7 +2733,7 @@ struct CommitPrefixResolver<'a> { } impl CommitPrefixResolver<'_> { - fn try_resolve( + async fn try_resolve( &self, repo: &dyn Repo, prefix: &HexPrefix, @@ -2741,6 +2746,7 @@ impl CommitPrefixResolver<'_> { .unwrap_or(IdPrefixIndex::empty()); match index .resolve_commit_prefix(repo, prefix) + .await .map_err(|err| RevsetResolutionError::Other(err.into()))? { PrefixResolution::AmbiguousMatch => { @@ -2752,14 +2758,15 @@ impl CommitPrefixResolver<'_> { } } +#[async_trait(?Send)] impl PartialSymbolResolver for CommitPrefixResolver<'_> { - fn resolve_symbol( + async fn resolve_symbol( &self, repo: &dyn Repo, symbol: &str, ) -> Result, RevsetResolutionError> { if let Some(prefix) = HexPrefix::try_from_hex(symbol) { - self.try_resolve(repo, &prefix) + self.try_resolve(repo, &prefix).await } else { Ok(None) } @@ -2772,7 +2779,7 @@ struct ChangePrefixResolver<'a> { } impl ChangePrefixResolver<'_> { - fn try_resolve( + async fn try_resolve( &self, repo: &dyn Repo, prefix: &HexPrefix, @@ -2785,6 +2792,7 @@ impl ChangePrefixResolver<'_> { .unwrap_or(IdPrefixIndex::empty()); match index .resolve_change_prefix(repo, prefix) + .await .map_err(|err| RevsetResolutionError::Other(err.into()))? { PrefixResolution::AmbiguousMatch => Err( @@ -2796,8 +2804,9 @@ impl ChangePrefixResolver<'_> { } } +#[async_trait(?Send)] impl PartialSymbolResolver for ChangePrefixResolver<'_> { - fn resolve_symbol( + async fn resolve_symbol( &self, repo: &dyn Repo, symbol: &str, @@ -2816,7 +2825,7 @@ impl PartialSymbolResolver for ChangePrefixResolver<'_> { let Some(prefix) = HexPrefix::try_from_reverse_hex(change_id) else { return Ok(None); }; - let Some(targets) = self.try_resolve(repo, &prefix)? else { + let Some(targets) = self.try_resolve(repo, &prefix).await? else { return Ok(None); }; if let Some(offset) = offset { @@ -2899,7 +2908,7 @@ impl<'a> SymbolResolver<'a> { } /// Looks up `symbol` in the given `repo`. - pub fn resolve_symbol( + pub async fn resolve_symbol( &self, repo: &dyn Repo, symbol: &str, @@ -2909,7 +2918,7 @@ impl<'a> SymbolResolver<'a> { } for partial_resolver in self.partial_resolvers() { - if let Some(id) = partial_resolver.resolve_symbol(repo, symbol)? { + if let Some(id) = partial_resolver.resolve_symbol(repo, symbol).await? { return Ok(id); } } @@ -2918,14 +2927,14 @@ impl<'a> SymbolResolver<'a> { } } -fn resolve_commit_ref( +async fn resolve_commit_ref( repo: &dyn Repo, commit_ref: &RevsetCommitRef, - symbol_resolver: &SymbolResolver, + symbol_resolver: &SymbolResolver<'_>, ) -> Result, RevsetResolutionError> { match commit_ref { RevsetCommitRef::Symbol(symbol) => { - let commit_id = symbol_resolver.resolve_symbol(repo, symbol)?; + let commit_id = symbol_resolver.resolve_symbol(repo, symbol).await?; Ok(vec![commit_id]) } RevsetCommitRef::RemoteSymbol(symbol) => { @@ -2946,13 +2955,18 @@ fn resolve_commit_ref( RevsetCommitRef::ChangeId(prefix) => { let resolver = &symbol_resolver.change_id_resolver; Ok(resolver - .try_resolve(repo, prefix)? + .try_resolve(repo, prefix) + .await? .and_then(ResolvedChangeTargets::into_visible) .unwrap_or_else(Vec::new)) } RevsetCommitRef::CommitId(prefix) => { let resolver = &symbol_resolver.commit_id_resolver; - Ok(resolver.try_resolve(repo, prefix)?.into_iter().collect()) + Ok(resolver + .try_resolve(repo, prefix) + .await? + .into_iter() + .collect()) } RevsetCommitRef::Bookmarks(expression) => { let commit_ids = repo @@ -3032,19 +3046,22 @@ impl<'a, 'b> ExpressionSymbolResolver<'a, 'b> { } } +#[async_trait(?Send)] impl ExpressionStateFolder for ExpressionSymbolResolver<'_, '_> { type Error = RevsetResolutionError; - fn fold_expression( + async fn fold_expression( &mut self, expression: &UserRevsetExpression, ) -> Result, Self::Error> { match expression { // 'present(x)' opens new symbol resolution scope to map error to 'none()' - RevsetExpression::Present(candidates) => { - self.fold_expression(candidates).or_else(|err| match err { + RevsetExpression::Present(candidates) => self + .fold_expression(candidates) + .await + .or_else(|err| match err { RevsetResolutionError::NoSuchRevision { .. } | RevsetResolutionError::WorkspaceMissingWorkingCopy { .. } => { Ok(RevsetExpression::none()) @@ -3056,28 +3073,27 @@ impl ExpressionStateFolder | RevsetResolutionError::ConflictedRef { .. } | RevsetResolutionError::Backend(_) | RevsetResolutionError::Other(_) => Err(err), - }) - } - _ => fold_child_expression_state(self, expression), + }), + _ => fold_child_expression_state(self, expression).await, } } - fn fold_commit_ref( + async fn fold_commit_ref( &mut self, commit_ref: &RevsetCommitRef, ) -> Result, Self::Error> { - let commit_ids = resolve_commit_ref(self.repo(), commit_ref, self.symbol_resolver)?; + let commit_ids = resolve_commit_ref(self.repo(), commit_ref, self.symbol_resolver).await?; Ok(RevsetExpression::commits(commit_ids)) } - fn fold_at_operation( + async fn fold_at_operation( &mut self, operation: &String, candidates: &UserRevsetExpression, ) -> Result, Self::Error> { let repo = reload_repo_at_operation(self.repo(), operation)?; self.repo_stack.push(repo); - let candidates = self.fold_expression(candidates)?; + let candidates = self.fold_expression(candidates).await?; let visible_heads = self.repo().view().heads().iter().cloned().collect(); self.repo_stack.pop(); Ok(Arc::new(RevsetExpression::WithinVisibility { @@ -3087,13 +3103,13 @@ impl ExpressionStateFolder } } -fn resolve_symbols( +async fn resolve_symbols( repo: &dyn Repo, expression: &UserRevsetExpression, - symbol_resolver: &SymbolResolver, + symbol_resolver: &SymbolResolver<'_>, ) -> Result, RevsetResolutionError> { let mut resolver = ExpressionSymbolResolver::new(repo, symbol_resolver); - resolver.fold_expression(expression) + resolver.fold_expression(expression).await } /// Inserts implicit `all()` and `visible_heads()` nodes to the `expression`. diff --git a/lib/tests/test_id_prefix.rs b/lib/tests/test_id_prefix.rs index ed542c14334..5d9326698f3 100644 --- a/lib/tests/test_id_prefix.rs +++ b/lib/tests/test_id_prefix.rs @@ -155,7 +155,10 @@ fn test_id_prefix() -> TestResult { .unwrap() }; let resolve_commit_prefix = |index: &IdPrefixIndex, prefix: HexPrefix| { - index.resolve_commit_prefix(repo.as_ref(), &prefix).unwrap() + index + .resolve_commit_prefix(repo.as_ref(), &prefix) + .block_on() + .unwrap() }; let shortest_change_prefix_len = |index: &IdPrefixIndex, change_id| { index @@ -165,6 +168,7 @@ fn test_id_prefix() -> TestResult { let resolve_change_prefix = |index: &IdPrefixIndex, prefix: HexPrefix| { index .resolve_change_prefix(repo.as_ref(), &prefix) + .block_on() .unwrap() .filter_map(ResolvedChangeTargets::into_visible) }; @@ -325,6 +329,7 @@ fn test_id_prefix_divergent() -> TestResult { let resolve_change_prefix = |index: &IdPrefixIndex, prefix: HexPrefix| { index .resolve_change_prefix(repo.as_ref(), &prefix) + .block_on() .unwrap() .filter_map(ResolvedChangeTargets::into_visible) }; @@ -474,7 +479,10 @@ fn test_id_prefix_hidden() -> TestResult { .unwrap() }; let resolve_commit_prefix = |index: &IdPrefixIndex, prefix: HexPrefix| { - index.resolve_commit_prefix(repo.as_ref(), &prefix).unwrap() + index + .resolve_commit_prefix(repo.as_ref(), &prefix) + .block_on() + .unwrap() }; let shortest_change_prefix_len = |index: &IdPrefixIndex, change_id| { index @@ -484,6 +492,7 @@ fn test_id_prefix_hidden() -> TestResult { let resolve_change_prefix = |index: &IdPrefixIndex, prefix: HexPrefix| { index .resolve_change_prefix(repo.as_ref(), &prefix) + .block_on() .unwrap() .filter_map(ResolvedChangeTargets::into_visible) }; diff --git a/lib/tests/test_revset.rs b/lib/tests/test_revset.rs index 56019ee7650..7e466164c9e 100644 --- a/lib/tests/test_revset.rs +++ b/lib/tests/test_revset.rs @@ -111,7 +111,8 @@ fn resolve_symbol(repo: &dyn Repo, symbol: &str) -> Result, Revset assert_matches!(*expression, RevsetExpression::CommitRef(_)); let symbol_resolver = default_symbol_resolver(repo); match expression - .resolve_user_expression(repo, &symbol_resolver)? + .resolve_user_expression(repo, &symbol_resolver) + .block_on()? .as_ref() { RevsetExpression::Commits(commits) => Ok(commits.clone()), @@ -126,6 +127,7 @@ fn revset_for_commits<'index>( let symbol_resolver = default_symbol_resolver(repo); RevsetExpression::commits(commits.iter().map(|commit| commit.id().clone()).collect()) .resolve_user_expression(repo, &symbol_resolver) + .block_on() .unwrap() .evaluate(repo) .unwrap() @@ -243,7 +245,7 @@ fn test_resolve_symbol_commit_id() -> TestResult { }; assert_matches!( parse(&mut RevsetDiagnostics::new(), "present(01)", &context)? - .resolve_user_expression(repo.as_ref(), &symbol_resolver), + .resolve_user_expression(repo.as_ref(), &symbol_resolver).block_on(), Err(RevsetResolutionError::AmbiguousCommitIdPrefix(s)) if s == "01" ); assert_eq!( @@ -543,24 +545,32 @@ fn test_resolve_symbol_in_different_disambiguation_context() -> TestResult { // Sanity check let change_hex = commit2.change_id().reverse_hex(); assert_eq!( - symbol_resolver.resolve_symbol(repo2.as_ref(), &change_hex[0..1])?, + symbol_resolver + .resolve_symbol(repo2.as_ref(), &change_hex[0..1]) + .block_on()?, commit2.id().clone() ); assert_eq!( - symbol_resolver.resolve_symbol(repo2.as_ref(), &commit2.id().hex()[0..1])?, + symbol_resolver + .resolve_symbol(repo2.as_ref(), &commit2.id().hex()[0..1]) + .block_on()?, commit2.id().clone() ); // Change ID is disambiguated within repo2, then resolved in repo1. assert_eq!( - symbol_resolver.resolve_symbol(repo1.as_ref(), &change_hex[0..1])?, + symbol_resolver + .resolve_symbol(repo1.as_ref(), &change_hex[0..1]) + .block_on()?, commit1.id().clone() ); // Commit ID can be found in the disambiguation index, but doesn't exist in // repo1. assert_matches!( - symbol_resolver.resolve_symbol(repo1.as_ref(), &commit2.id().hex()[0..1]), + symbol_resolver + .resolve_symbol(repo1.as_ref(), &commit2.id().hex()[0..1]) + .block_on(), Err(RevsetResolutionError::NoSuchRevision { .. }) ); Ok(()) @@ -582,7 +592,7 @@ fn test_resolve_working_copy() -> TestResult { let symbol_resolver = default_symbol_resolver(tx.repo()); assert_matches!( RevsetExpression::working_copy(ws1.clone()) - .resolve_user_expression(tx.repo(), &symbol_resolver), + .resolve_user_expression(tx.repo(), &symbol_resolver).block_on(), Err(RevsetResolutionError::WorkspaceMissingWorkingCopy { name }) if name == "ws1" ); @@ -590,7 +600,8 @@ fn test_resolve_working_copy() -> TestResult { assert_eq!( RevsetExpression::working_copy(ws1.clone()) .present() - .resolve_user_expression(tx.repo(), &symbol_resolver)? + .resolve_user_expression(tx.repo(), &symbol_resolver) + .block_on()? .evaluate(tx.repo())? .stream() .map(Result::unwrap) @@ -609,6 +620,7 @@ fn test_resolve_working_copy() -> TestResult { let resolve = |name: WorkspaceNameBuf| -> Vec { RevsetExpression::working_copy(name) .resolve_user_expression(tx.repo(), &symbol_resolver) + .block_on() .unwrap() .evaluate(tx.repo()) .unwrap() @@ -647,6 +659,7 @@ fn test_resolve_working_copies() -> TestResult { let resolve = || -> Vec { RevsetExpression::working_copies() .resolve_user_expression(tx.repo(), &symbol_resolver) + .block_on() .unwrap() .evaluate(tx.repo()) .unwrap() @@ -1072,7 +1085,9 @@ fn try_resolve_expression( }; let expression = parse(&mut RevsetDiagnostics::new(), revset_str, &context).unwrap(); let symbol_resolver = default_symbol_resolver(repo); - expression.resolve_user_expression(repo, &symbol_resolver) + expression + .resolve_user_expression(repo, &symbol_resolver) + .block_on() } fn try_resolve_commit_ids( @@ -1126,6 +1141,7 @@ fn resolve_commit_ids_in_workspace( let symbol_resolver = default_symbol_resolver(repo); let expression = expression .resolve_user_expression(repo, &symbol_resolver) + .block_on() .unwrap(); expression .evaluate(repo) @@ -1252,7 +1268,8 @@ fn test_evaluate_expression_root_and_checkout() -> TestResult { // Shouldn't panic by unindexed commit ID let symbol_resolver = default_symbol_resolver(tx.repo()); let expression = RevsetExpression::commit(commit1.id().clone()) - .resolve_user_expression(tx.repo(), &symbol_resolver)?; + .resolve_user_expression(tx.repo(), &symbol_resolver) + .block_on()?; assert!(expression.evaluate(tx.base_repo().as_ref()).is_err()); Ok(()) }