Skip to content

Commit

Permalink
revsets: allow the .. operator to be used as prefix or suffix (jj-v…
Browse files Browse the repository at this point in the history
…cs#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.
  • Loading branch information
martinvonz committed Dec 16, 2021
1 parent bc92b82 commit 7d3d0fe
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 6 deletions.
5 changes: 4 additions & 1 deletion docs/revsets.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)`.
Expand Down Expand Up @@ -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`)
Expand Down
2 changes: 2 additions & 0 deletions lib/src/revset.pest
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
26 changes: 21 additions & 5 deletions lib/src/revset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ fn parse_range_expression_rule(
) -> Result<Rc<RevsetExpression>, 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(),
);
Expand All @@ -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());
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()));
Expand Down

0 comments on commit 7d3d0fe

Please sign in to comment.