revset: do not reinterpret set&filter intersection as filter

We use a query `heads | (domain & ::heads & files(path))` in annotation process,
and it was silly that the whole expression was filtered within `all()`. We
should instead evaluate `heads | filter_within(domain & ::heads, files(path))`.

Fixes #6713
This commit is contained in:
Yuya Nishihara 2025-06-08 14:09:51 +09:00
parent c555687e8a
commit 0a9ab49dc5
3 changed files with 41 additions and 5 deletions

View file

@ -18,6 +18,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Fixed bugs
* `jj file annotate` can now process files at a hidden revision.
* `jj op log --op-diff` no longer fails at displaying "reconcile divergent
operations." [#4465](https://github.com/jj-vcs/jj/issues/4465)

View file

@ -206,8 +206,8 @@ fn test_annotate_abandoned() {
let output = work_dir.run_jj(["file", "annotate", "-rat_operation(@-, @)", "file.txt"]);
insta::assert_snapshot!(output, @r"
zzzzzzzz 1970-01-01 11:00:00 1: line1
zzzzzzzz 1970-01-01 11:00:00 2: line2
qpvuntsm test.use 2001-02-03 08:05:08 1: line1
rlvkpnrz test.use 2001-02-03 08:05:09 2: line2
[EOF]
");
}

View file

@ -1525,7 +1525,13 @@ fn internalize_filter<St: ExpressionState>(
}
fn is_filter_tree<St: ExpressionState>(expression: &RevsetExpression<St>) -> bool {
is_filter(expression) || as_filter_intersection(expression).is_some()
if let RevsetExpression::Intersection(expression1, _) = expression {
// 'f1 & f2' can't be evaluated by itself, but 's1 & f2' can be
// filtered within 's1'. 'f1 & s2' should have been reordered.
is_filter(expression1)
} else {
is_filter(expression)
}
}
// Extracts 'c & f' from intersect_down()-ed node.
@ -4103,6 +4109,33 @@ mod tests {
)
"#);
// 'merges() & foo' can be evaluated independently
insta::assert_debug_snapshot!(
optimize(parse("merges() & foo | bar").unwrap()), @r#"
Union(
Intersection(
CommitRef(Symbol("foo")),
Filter(ParentCount(2..4294967295)),
),
CommitRef(Symbol("bar")),
)
"#);
// 'merges() & foo' can be evaluated independently, but 'conflicts()'
// can't. We'll need implicit 'all() & _' anyway.
insta::assert_debug_snapshot!(
optimize(parse("merges() & foo | conflicts()").unwrap()), @r#"
AsFilter(
Union(
Intersection(
CommitRef(Symbol("foo")),
Filter(ParentCount(2..4294967295)),
),
Filter(HasConflict),
),
)
"#);
insta::assert_debug_snapshot!(
optimize(parse("(foo | committer_name(bar)) & description(baz) & qux").unwrap()), @r#"
Intersection(
@ -4120,7 +4153,8 @@ mod tests {
"#);
insta::assert_debug_snapshot!(
optimize(parse("(~present(author_name(foo) & bar) | baz) & qux").unwrap()), @r#"
optimize(parse(
"(~present(author_name(foo) & description(bar)) | baz) & qux").unwrap()), @r#"
Intersection(
CommitRef(Symbol("qux")),
AsFilter(
@ -4130,8 +4164,8 @@ mod tests {
AsFilter(
Present(
Intersection(
CommitRef(Symbol("bar")),
Filter(AuthorName(Substring("foo"))),
Filter(Description(Substring("bar"))),
),
),
),