From fa3ad16bf2ac9221712683c146a76ebe8e4237be Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Mon, 7 Nov 2022 16:29:35 +0900 Subject: [PATCH] revset: add present(set) predicate that suppresses NoSuchRevision error This is copied from Mercurial. Typical use case I have in mind is "present(master) | present(main)" in stock revset. --- docs/revsets.md | 2 ++ lib/src/revset.rs | 23 +++++++++++++++++++++++ lib/tests/test_revset.rs | 13 +++++++++++++ 3 files changed, 38 insertions(+) diff --git a/docs/revsets.md b/docs/revsets.md index f6d5b2146..036f3a3a8 100644 --- a/docs/revsets.md +++ b/docs/revsets.md @@ -109,6 +109,8 @@ revsets (expressions) as arguments. * `committer(needle)`: Commits with the given string in the committer's name or email. * `file(pattern..)`: Commits modifying the paths specified by the `pattern..`. +* `present(x)`: Same as `x`, but evaluated to `none()` if any of the commits + in `x` doesn't exist (e.g. is an unknown branch name.) ## Examples diff --git a/lib/src/revset.rs b/lib/src/revset.rs index 848667d94..0abdf502a 100644 --- a/lib/src/revset.rs +++ b/lib/src/revset.rs @@ -311,6 +311,7 @@ pub enum RevsetExpression { candidates: Rc, predicate: RevsetFilterPredicate, }, + Present(Rc), Union(Rc, Rc), Intersection(Rc, Rc), Difference(Rc, Rc), @@ -782,6 +783,11 @@ fn parse_function_expression( )) } } + "present" => { + let arg = expect_one_argument(name, arguments_pair)?; + let expression = parse_expression_rule(arg.into_inner(), workspace_ctx)?; + Ok(Rc::new(RevsetExpression::Present(expression))) + } _ => Err(RevsetParseError::with_span( RevsetParseErrorKind::NoSuchFunction(name.to_owned()), name_pair.as_span(), @@ -934,6 +940,9 @@ fn transform_expression_bottom_up( candidates, predicate: predicate.clone(), }), + RevsetExpression::Present(candidates) => { + transform_rec(candidates, f).map(RevsetExpression::Present) + } RevsetExpression::Union(expression1, expression2) => { transform_rec_pair((expression1, expression2), f).map( |(expression1, expression2)| RevsetExpression::Union(expression1, expression2), @@ -1608,6 +1617,15 @@ pub fn evaluate_expression<'repo>( } } } + RevsetExpression::Present(candidates) => match candidates.evaluate(repo, workspace_ctx) { + Ok(set) => Ok(set), + Err(RevsetError::NoSuchRevision(_)) => Ok(Box::new(EagerRevset::empty())), + r @ Err( + RevsetError::AmbiguousCommitIdPrefix(_) + | RevsetError::AmbiguousChangeIdPrefix(_) + | RevsetError::StoreError(_), + ) => r, + }, RevsetExpression::Union(expression1, expression2) => { let set1 = expression1.evaluate(repo, workspace_ctx)?; let set2 = expression2.evaluate(repo, workspace_ctx)?; @@ -1995,6 +2013,11 @@ mod tests { RevsetExpression::branches().roots() ); + assert_eq!( + optimize(parse("present(branches() & all())").unwrap()), + Rc::new(RevsetExpression::Present(RevsetExpression::branches())) + ); + assert_eq!( optimize(parse("(branches() & all()) | (all() & tags())").unwrap()), RevsetExpression::branches().union(&RevsetExpression::tags()) diff --git a/lib/tests/test_revset.rs b/lib/tests/test_revset.rs index e8f6e6e59..c2ff7d454 100644 --- a/lib/tests/test_revset.rs +++ b/lib/tests/test_revset.rs @@ -125,6 +125,19 @@ fn test_resolve_symbol_commit_id() { resolve_symbol(repo_ref, "foo", None), Err(RevsetError::NoSuchRevision("foo".to_string())) ); + + // Test present() suppresses only NoSuchRevision error + assert_eq!(resolve_commit_ids(repo_ref, "present(foo)"), []); + assert_eq!( + optimize(parse("present(04)", None).unwrap()) + .evaluate(repo_ref, None) + .map(|_| ()), + Err(RevsetError::AmbiguousCommitIdPrefix("04".to_string())) + ); + assert_eq!( + resolve_commit_ids(repo_ref, "present(046)"), + vec![commits[2].id().clone()] + ); } #[test]