From 7d3d0fe83c9044b0bd632b7fb0a41ccec5ace3c1 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Wed, 15 Dec 2021 21:57:55 -0800 Subject: [PATCH] revsets: allow the `..` operator to be used as prefix or suffix (#46) It makes sense to omit either of the arguments of the `..` operator, even though `..x` is equivalent to `:x`. `x..`, with a implied right argument of `heads()` is more useful. --- docs/revsets.md | 5 ++++- lib/src/revset.pest | 2 ++ lib/src/revset.rs | 26 +++++++++++++++++++++----- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/docs/revsets.md b/docs/revsets.md index ffdc49c33..0400183a7 100644 --- a/docs/revsets.md +++ b/docs/revsets.md @@ -68,6 +68,9 @@ only symbols. * `x..y`: Ancestors of `y` that are not also ancestors of `x`, both inclusive. Equivalent to `:y ~ :x`. This is what `git log` calls `x..y` (i.e. the same as we call it). +* `..x`: Ancestors of `x`, including the commits in `x` itself. Equivalent to + `:x` and provided for consistency. +* `x..`: Revisions that are not ancestors of `x`. You can use parentheses to control evaluation order, such as `(x & y) | z` or `x & (y | z)`. @@ -113,7 +116,7 @@ jj log -r @- Show commits not on any remote branch: ``` -jj log -r 'remote_branches()..all()' +jj log -r 'remote_branches()..' ``` Show all ancestors of the working copy (almost like plain `git log`) diff --git a/lib/src/revset.pest b/lib/src/revset.pest index 24bdab20e..85b8f79b2 100644 --- a/lib/src/revset.pest +++ b/lib/src/revset.pest @@ -52,7 +52,9 @@ neighbors_expression = { primary ~ (parents_op | children_op)* } range_expression = { neighbors_expression ~ dag_range_op ~ neighbors_expression | neighbors_expression ~ range_op ~ neighbors_expression + | neighbors_expression ~ range_op | dag_range_op ~ neighbors_expression + | range_op ~ neighbors_expression | neighbors_expression ~ dag_range_op | neighbors_expression } diff --git a/lib/src/revset.rs b/lib/src/revset.rs index 1d0a455ab..6fd3cd2e9 100644 --- a/lib/src/revset.rs +++ b/lib/src/revset.rs @@ -406,7 +406,7 @@ fn parse_range_expression_rule( ) -> Result, RevsetParseError> { let first = pairs.next().unwrap(); match first.as_rule() { - Rule::dag_range_op => { + Rule::dag_range_op | Rule::range_op => { return Ok( parse_neighbors_expression_rule(pairs.next().unwrap().into_inner())?.ancestors(), ); @@ -431,9 +431,13 @@ fn parse_range_expression_rule( } } Rule::range_op => { - let expression2 = - parse_neighbors_expression_rule(pairs.next().unwrap().into_inner())?; - expression = expression.range(&expression2); + if let Some(heads_pair) = pairs.next() { + let heads_expression = + parse_neighbors_expression_rule(heads_pair.into_inner())?; + expression = expression.range(&heads_expression); + } else { + expression = expression.range(&RevsetExpression::heads()); + } } _ => { panic!("unxpected revset range operator rule {:?}", next.as_rule()); @@ -1334,6 +1338,13 @@ mod tests { assert_eq!(parse("@:"), Ok(checkout_symbol.descendants())); // Parse the "dag range" operator assert_eq!(parse("foo:bar"), Ok(foo_symbol.dag_range_to(&bar_symbol))); + // Parse the "range" prefix operator + assert_eq!(parse("..@"), Ok(checkout_symbol.ancestors())); + assert_eq!( + parse("@.."), + Ok(checkout_symbol.range(&RevsetExpression::heads())) + ); + assert_eq!(parse("foo..bar"), Ok(foo_symbol.range(&bar_symbol))); // Parse the "intersection" operator assert_eq!(parse("foo & bar"), Ok(foo_symbol.intersection(&bar_symbol))); // Parse the "union" operator @@ -1371,13 +1382,18 @@ mod tests { parse("foo+++"), Ok(foo_symbol.children().children().children()) ); - // Parse repeated "ancestors"/"descendants"/"dag range" operators + // Parse repeated "ancestors"/"descendants"/"dag range"/"range" operators assert_matches!(parse(":foo:"), Err(RevsetParseError::SyntaxError(_))); assert_matches!(parse("::foo"), Err(RevsetParseError::SyntaxError(_))); assert_matches!(parse("foo::"), Err(RevsetParseError::SyntaxError(_))); assert_matches!(parse("foo::bar"), Err(RevsetParseError::SyntaxError(_))); assert_matches!(parse(":foo:bar"), Err(RevsetParseError::SyntaxError(_))); assert_matches!(parse("foo:bar:"), Err(RevsetParseError::SyntaxError(_))); + assert_matches!(parse("....foo"), Err(RevsetParseError::SyntaxError(_))); + assert_matches!(parse("foo...."), Err(RevsetParseError::SyntaxError(_))); + assert_matches!(parse("foo.....bar"), Err(RevsetParseError::SyntaxError(_))); + assert_matches!(parse("..foo..bar"), Err(RevsetParseError::SyntaxError(_))); + assert_matches!(parse("foo..bar.."), Err(RevsetParseError::SyntaxError(_))); // Parse combinations of "parents"/"children" operators and the range operators. // The former bind more strongly. assert_eq!(parse("foo-+"), Ok(foo_symbol.parents().children()));