mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-27 23:06:45 +00:00
revset: substitute '~(::x)' to 'x..'
Suppose we have an alias 'immutable()' = '::immutable_heads()', user can express (visible) mutable set as '~immutable()'. 'immutable_heads()..' can terminate early, but a generic difference 'all() & ~immutable()' can't.
This commit is contained in:
parent
9207314173
commit
50363419fb
2 changed files with 98 additions and 1 deletions
|
@ -15,6 +15,7 @@ v2.39.0..v2.40.0
|
||||||
v2.39.0::v2.40.0
|
v2.39.0::v2.40.0
|
||||||
# Mostly recent history
|
# Mostly recent history
|
||||||
v2.40.0-..
|
v2.40.0-..
|
||||||
|
~(::v2.40.0)
|
||||||
# Tags and branches
|
# Tags and branches
|
||||||
tags()
|
tags()
|
||||||
branches()
|
branches()
|
||||||
|
|
|
@ -1865,6 +1865,23 @@ fn fold_difference(expression: &Rc<RevsetExpression>) -> TransformedExpression {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transforms remaining negated ancestors `~(::h)` to range `h..`.
|
||||||
|
///
|
||||||
|
/// Since this rule inserts redundant `visible_heads()`, negative intersections
|
||||||
|
/// should have been transformed.
|
||||||
|
fn fold_not_in_ancestors(expression: &Rc<RevsetExpression>) -> TransformedExpression {
|
||||||
|
transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
|
||||||
|
RevsetExpression::NotIn(complement)
|
||||||
|
if matches!(complement.as_ref(), RevsetExpression::Ancestors { .. }) =>
|
||||||
|
{
|
||||||
|
// ~(::heads) -> heads..
|
||||||
|
// ~(::heads-) -> ~ancestors(heads, 1..) -> heads-..
|
||||||
|
to_difference_range(&RevsetExpression::visible_heads().ancestors(), complement)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Transforms binary difference to more primitive negative intersection.
|
/// Transforms binary difference to more primitive negative intersection.
|
||||||
///
|
///
|
||||||
/// For example, `all() ~ e` will become `all() & ~e`, which can be simplified
|
/// For example, `all() ~ e` will become `all() & ~e`, which can be simplified
|
||||||
|
@ -1953,7 +1970,8 @@ pub fn optimize(expression: Rc<RevsetExpression>) -> Rc<RevsetExpression> {
|
||||||
let expression = fold_redundant_expression(&expression).unwrap_or(expression);
|
let expression = fold_redundant_expression(&expression).unwrap_or(expression);
|
||||||
let expression = fold_generation(&expression).unwrap_or(expression);
|
let expression = fold_generation(&expression).unwrap_or(expression);
|
||||||
let expression = internalize_filter(&expression).unwrap_or(expression);
|
let expression = internalize_filter(&expression).unwrap_or(expression);
|
||||||
fold_difference(&expression).unwrap_or(expression)
|
let expression = fold_difference(&expression).unwrap_or(expression);
|
||||||
|
fold_not_in_ancestors(&expression).unwrap_or(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: find better place to host this function (or add compile-time revset
|
// TODO: find better place to host this function (or add compile-time revset
|
||||||
|
@ -3777,6 +3795,84 @@ mod tests {
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_optimize_not_in_ancestors() {
|
||||||
|
// '~(::foo)' is equivalent to 'foo..'.
|
||||||
|
insta::assert_debug_snapshot!(optimize(parse("~(::foo)").unwrap()), @r###"
|
||||||
|
Range {
|
||||||
|
roots: CommitRef(
|
||||||
|
Symbol(
|
||||||
|
"foo",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
heads: CommitRef(
|
||||||
|
VisibleHeads,
|
||||||
|
),
|
||||||
|
generation: 0..18446744073709551615,
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// '~(::foo-)' is equivalent to 'foo-..'.
|
||||||
|
insta::assert_debug_snapshot!(optimize(parse("~(::foo-)").unwrap()), @r###"
|
||||||
|
Range {
|
||||||
|
roots: Ancestors {
|
||||||
|
heads: CommitRef(
|
||||||
|
Symbol(
|
||||||
|
"foo",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
generation: 1..2,
|
||||||
|
},
|
||||||
|
heads: CommitRef(
|
||||||
|
VisibleHeads,
|
||||||
|
),
|
||||||
|
generation: 0..18446744073709551615,
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
insta::assert_debug_snapshot!(optimize(parse("~(::foo--)").unwrap()), @r###"
|
||||||
|
Range {
|
||||||
|
roots: Ancestors {
|
||||||
|
heads: CommitRef(
|
||||||
|
Symbol(
|
||||||
|
"foo",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
generation: 2..3,
|
||||||
|
},
|
||||||
|
heads: CommitRef(
|
||||||
|
VisibleHeads,
|
||||||
|
),
|
||||||
|
generation: 0..18446744073709551615,
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Bounded ancestors shouldn't be substituted.
|
||||||
|
insta::assert_debug_snapshot!(optimize(parse("~ancestors(foo, 1)").unwrap()), @r###"
|
||||||
|
NotIn(
|
||||||
|
Ancestors {
|
||||||
|
heads: CommitRef(
|
||||||
|
Symbol(
|
||||||
|
"foo",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
generation: 0..1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
"###);
|
||||||
|
insta::assert_debug_snapshot!(optimize(parse("~ancestors(foo-, 1)").unwrap()), @r###"
|
||||||
|
NotIn(
|
||||||
|
Ancestors {
|
||||||
|
heads: CommitRef(
|
||||||
|
Symbol(
|
||||||
|
"foo",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
generation: 1..2,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_optimize_filter_difference() {
|
fn test_optimize_filter_difference() {
|
||||||
// '~empty()' -> '~~file(*)' -> 'file(*)'
|
// '~empty()' -> '~~file(*)' -> 'file(*)'
|
||||||
|
|
Loading…
Reference in a new issue