forked from mirrors/jj
revset: add 0-ary "::" and ".." operators as short for "all()" and "~root()"
Suppose "x::y" is the operator that defaults to "root()::visible_heads()" respectively, "::" is identical to "all()". Since we've just changed the behavior of "..y", ".." is now "root()..visible_heads()" meaning "~root()".
This commit is contained in:
parent
6b2ad23f8f
commit
b0c8e9ef62
6 changed files with 62 additions and 4 deletions
|
@ -116,6 +116,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
* Revsets gained a new function `mine()` that aliases `author(exact:"your_email")`.
|
||||
|
||||
* Added support for `::` and `..` revset operators with both left and right
|
||||
operands omitted. These expressions are equivalent to `all()` and `~root()`
|
||||
respectively.
|
||||
|
||||
* `jj log` timestamp format now accepts `.utc()` to convert a timestamp to UTC.
|
||||
|
||||
* templates now support additional string methods `.starts_with(x)`, `.ends_with(x)`
|
||||
|
|
|
@ -29,7 +29,7 @@ fn test_syntax_error() {
|
|||
1 | x &
|
||||
| ^---
|
||||
|
|
||||
= expected dag_range_pre_op, legacy_dag_range_pre_op, range_pre_op, negate_op, or primary
|
||||
= expected dag_range_pre_op, dag_range_all_op, legacy_dag_range_pre_op, range_pre_op, range_all_op, negate_op, or primary
|
||||
"###);
|
||||
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "x - y"]);
|
||||
|
@ -287,7 +287,7 @@ fn test_alias() {
|
|||
1 | whatever &
|
||||
| ^---
|
||||
|
|
||||
= expected dag_range_pre_op, legacy_dag_range_pre_op, range_pre_op, negate_op, or primary
|
||||
= expected dag_range_pre_op, dag_range_all_op, legacy_dag_range_pre_op, range_pre_op, range_all_op, negate_op, or primary
|
||||
"###);
|
||||
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "identity()"]);
|
||||
|
|
|
@ -60,11 +60,14 @@ only symbols.
|
|||
to `x: & :y`. This is what `git log` calls `--ancestry-path x..y`.
|
||||
* `::x`, `x::`, and `x::y`: New versions of for `:x`, `x:`, and `x:y` to be
|
||||
released in jj 0.9.0. We plan to delete the latter in jj 0.15+.
|
||||
* `::`: All visible commits in the repo. Equivalent to `all()`.
|
||||
* `x..y`: Ancestors of `y` that are not also ancestors of `x`. 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, but excluding
|
||||
the root commit. Equivalent to `:x ~ root()`.
|
||||
* `x..`: Revisions that are not ancestors of `x`.
|
||||
* `..`: All visible commits in the repo, but excluding the root commit.
|
||||
Equivalent to `~root()`.
|
||||
|
||||
You can use parentheses to control evaluation order, such as `(x & y) | z` or
|
||||
`x & (y | z)`.
|
||||
|
|
|
@ -33,6 +33,7 @@ compat_parents_op = { "^" }
|
|||
dag_range_op = { "::" }
|
||||
dag_range_pre_op = { "::" }
|
||||
dag_range_post_op = { "::" }
|
||||
dag_range_all_op = { "::" }
|
||||
// TODO: Drop support for these in 0.15+
|
||||
legacy_dag_range_op = { ":" }
|
||||
legacy_dag_range_pre_op = { ":" }
|
||||
|
@ -40,9 +41,11 @@ legacy_dag_range_post_op = { ":" }
|
|||
range_op = { ".." }
|
||||
range_pre_op = { ".." }
|
||||
range_post_op = { ".." }
|
||||
range_all_op = { ".." }
|
||||
range_ops = _{ dag_range_op | legacy_dag_range_op | range_op }
|
||||
range_pre_ops = _{ dag_range_pre_op | legacy_dag_range_pre_op | range_pre_op }
|
||||
range_post_ops = _{ dag_range_post_op | legacy_dag_range_post_op | range_post_op }
|
||||
range_all_ops = _{ dag_range_all_op | range_all_op }
|
||||
|
||||
negate_op = { "~" }
|
||||
union_op = { "|" }
|
||||
|
@ -81,6 +84,7 @@ range_expression = _{
|
|||
| neighbors_expression ~ range_post_ops
|
||||
| range_pre_ops ~ neighbors_expression
|
||||
| neighbors_expression
|
||||
| range_all_ops
|
||||
}
|
||||
|
||||
expression = {
|
||||
|
|
|
@ -850,7 +850,14 @@ fn parse_expression_rule(
|
|||
| Op::postfix(Rule::compat_parents_op))
|
||||
});
|
||||
PRATT
|
||||
.map_primary(|primary| parse_primary_rule(primary, state))
|
||||
.map_primary(|primary| match primary.as_rule() {
|
||||
Rule::primary => parse_primary_rule(primary, state),
|
||||
Rule::dag_range_all_op => Ok(RevsetExpression::all()),
|
||||
Rule::range_all_op => {
|
||||
Ok(RevsetExpression::root().range(&RevsetExpression::visible_heads()))
|
||||
}
|
||||
r => panic!("unexpected primary rule {r:?}"),
|
||||
})
|
||||
.map_prefix(|op, rhs| match op.as_rule() {
|
||||
Rule::negate_op => Ok(rhs?.negated()),
|
||||
Rule::dag_range_pre_op => Ok(rhs?.ancestors()),
|
||||
|
@ -2767,6 +2774,8 @@ mod tests {
|
|||
assert_eq!(parse("foo::"), Ok(foo_symbol.descendants()));
|
||||
// Parse the "dag range" operator
|
||||
assert_eq!(parse("foo::bar"), Ok(foo_symbol.dag_range_to(&bar_symbol)));
|
||||
// Parse the nullary "dag range" operator
|
||||
assert_eq!(parse("::"), Ok(RevsetExpression::all()));
|
||||
// Parse the "range" prefix operator
|
||||
assert_eq!(
|
||||
parse("..foo"),
|
||||
|
@ -2777,6 +2786,11 @@ mod tests {
|
|||
Ok(foo_symbol.range(&RevsetExpression::visible_heads()))
|
||||
);
|
||||
assert_eq!(parse("foo..bar"), Ok(foo_symbol.range(&bar_symbol)));
|
||||
// Parse the nullary "range" operator
|
||||
assert_eq!(
|
||||
parse(".."),
|
||||
Ok(RevsetExpression::root().range(&RevsetExpression::visible_heads()))
|
||||
);
|
||||
// Parse the "negate" operator
|
||||
assert_eq!(parse("~ foo"), Ok(foo_symbol.negated()));
|
||||
assert_eq!(
|
||||
|
@ -2967,6 +2981,7 @@ mod tests {
|
|||
assert_eq!(parse("x&y|z").unwrap(), parse("(x&y)|z").unwrap());
|
||||
assert_eq!(parse("x|y&z").unwrap(), parse("x|(y&z)").unwrap());
|
||||
assert_eq!(parse("x|y~z").unwrap(), parse("x|(y~z)").unwrap());
|
||||
assert_eq!(parse("::&..").unwrap(), parse("(::)&(..)").unwrap());
|
||||
// Parse repeated "ancestors"/"descendants"/"dag range"/"range" operators
|
||||
assert_eq!(parse("::foo::"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse(":::foo"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
|
@ -2977,16 +2992,21 @@ mod tests {
|
|||
assert_eq!(parse("foo::::bar"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("::foo::bar"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo::bar::"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("::::"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("....foo"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo...."), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo.....bar"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("..foo..bar"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo..bar.."), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("...."), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("::.."), Err(RevsetParseErrorKind::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()));
|
||||
assert_eq!(parse("foo-::"), Ok(foo_symbol.parents().descendants()));
|
||||
assert_eq!(parse("::foo+"), Ok(foo_symbol.children().ancestors()));
|
||||
assert_eq!(parse("::-"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("..+"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1250,6 +1250,16 @@ fn test_evaluate_expression_range(use_git: bool) {
|
|||
resolve_commit_ids(mut_repo, &format!("{}..", commit2.id().hex())),
|
||||
vec![commit4.id().clone(), commit3.id().clone()]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_commit_ids(mut_repo, ".."),
|
||||
vec![
|
||||
commit4.id().clone(),
|
||||
commit3.id().clone(),
|
||||
commit2.id().clone(),
|
||||
commit1.id().clone(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(false ; "local backend")]
|
||||
|
@ -1281,7 +1291,11 @@ fn test_evaluate_expression_dag_range(use_git: bool) {
|
|||
mut_repo,
|
||||
&format!("{}:{}", root_commit_id.hex(), commit2.id().hex())
|
||||
),
|
||||
vec![commit2.id().clone(), commit1.id().clone(), root_commit_id]
|
||||
vec![
|
||||
commit2.id().clone(),
|
||||
commit1.id().clone(),
|
||||
root_commit_id.clone(),
|
||||
]
|
||||
);
|
||||
|
||||
// Empty range
|
||||
|
@ -1344,6 +1358,19 @@ fn test_evaluate_expression_dag_range(use_git: bool) {
|
|||
commit2.id().clone(),
|
||||
]
|
||||
);
|
||||
|
||||
// Full range meaning all()
|
||||
assert_eq!(
|
||||
resolve_commit_ids(mut_repo, "::"),
|
||||
vec![
|
||||
commit5.id().clone(),
|
||||
commit4.id().clone(),
|
||||
commit3.id().clone(),
|
||||
commit2.id().clone(),
|
||||
commit1.id().clone(),
|
||||
root_commit_id.clone(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(false ; "local backend")]
|
||||
|
|
Loading…
Reference in a new issue