forked from mirrors/jj
revset: extend file() predicate to accept more than one paths
'file(a, b)' could be expressed as 'file(a) | file(b)', but the former is easier to type and can be evaluated efficiently without optimization step.
This commit is contained in:
parent
fba6741c23
commit
62511f7cad
3 changed files with 49 additions and 25 deletions
|
@ -20,8 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
* `jj git push` will search `@-` for branches to push if `@` has none.
|
||||
|
||||
* The new revset function `file(pattern)` finds commits modifying the
|
||||
paths specified by the `pattern`.
|
||||
* The new revset function `file(pattern..)` finds commits modifying the
|
||||
paths specified by the `pattern..`.
|
||||
|
||||
### Fixed bugs
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ revsets (expressions) as arguments.
|
|||
email.
|
||||
* `committer(needle)`: Commits with the given string in the committer's
|
||||
name or email.
|
||||
* `file(pattern)`: Commits modifying the paths specified by the `pattern`.
|
||||
* `file(pattern..)`: Commits modifying the paths specified by the `pattern..`.
|
||||
|
||||
|
||||
## Examples
|
||||
|
|
|
@ -211,7 +211,7 @@ pub enum RevsetFilterPredicate {
|
|||
Description(String),
|
||||
Author(String), // Matches against both name and email
|
||||
Committer(String), // Matches against both name and email
|
||||
File(RepoPath),
|
||||
File(Vec<RepoPath>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
|
@ -395,10 +395,10 @@ impl RevsetExpression {
|
|||
}
|
||||
|
||||
/// Commits in `self` modifying the paths specified by the `pattern`.
|
||||
pub fn with_file(self: &Rc<RevsetExpression>, pattern: RepoPath) -> Rc<RevsetExpression> {
|
||||
pub fn with_file(self: &Rc<RevsetExpression>, paths: Vec<RepoPath>) -> Rc<RevsetExpression> {
|
||||
Rc::new(RevsetExpression::Filter {
|
||||
candidates: self.clone(),
|
||||
predicate: RevsetFilterPredicate::File(pattern),
|
||||
predicate: RevsetFilterPredicate::File(paths),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -808,19 +808,21 @@ fn parse_function_expression(
|
|||
}
|
||||
}
|
||||
"file" => {
|
||||
if arg_count != 1 {
|
||||
if arg_count < 1 {
|
||||
return Err(RevsetParseError::InvalidFunctionArguments {
|
||||
name,
|
||||
message: "Expected 1 argument".to_string(),
|
||||
message: "Expected at least 1 argument".to_string(),
|
||||
});
|
||||
}
|
||||
if let Some(ctx) = workspace_ctx {
|
||||
let needle = parse_function_argument_to_string(
|
||||
&name,
|
||||
argument_pairs.next().unwrap().into_inner(),
|
||||
)?;
|
||||
let path = RepoPath::parse_fs_path(ctx.cwd, ctx.workspace_root, &needle)?;
|
||||
Ok(RevsetExpression::all().with_file(path))
|
||||
let paths = argument_pairs
|
||||
.map(|arg| {
|
||||
let needle = parse_function_argument_to_string(&name, arg.into_inner())?;
|
||||
let path = RepoPath::parse_fs_path(ctx.cwd, ctx.workspace_root, &needle)?;
|
||||
Ok(path)
|
||||
})
|
||||
.collect::<Result<Vec<_>, RevsetParseError>>()?;
|
||||
Ok(RevsetExpression::all().with_file(paths))
|
||||
} else {
|
||||
Err(RevsetParseError::FsPathWithoutWorkspace)
|
||||
}
|
||||
|
@ -1581,10 +1583,9 @@ pub fn evaluate_expression<'repo>(
|
|||
}),
|
||||
}))
|
||||
}
|
||||
RevsetFilterPredicate::File(pattern) => {
|
||||
RevsetFilterPredicate::File(paths) => {
|
||||
// TODO: Add support for globs and other formats
|
||||
let matcher: Box<dyn Matcher> =
|
||||
Box::new(PrefixMatcher::new(std::slice::from_ref(pattern)));
|
||||
let matcher: Box<dyn Matcher> = Box::new(PrefixMatcher::new(paths));
|
||||
Ok(filter_by_diff(repo, matcher, candidates))
|
||||
}
|
||||
}
|
||||
|
@ -1752,10 +1753,12 @@ mod tests {
|
|||
})
|
||||
);
|
||||
assert_eq!(
|
||||
foo_symbol.with_file(RepoPath::from_internal_string("pattern")),
|
||||
foo_symbol.with_file(vec![RepoPath::from_internal_string("pattern")]),
|
||||
Rc::new(RevsetExpression::Filter {
|
||||
candidates: foo_symbol.clone(),
|
||||
predicate: RevsetFilterPredicate::File(RepoPath::from_internal_string("pattern")),
|
||||
predicate: RevsetFilterPredicate::File(vec![RepoPath::from_internal_string(
|
||||
"pattern"
|
||||
)]),
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -1839,12 +1842,14 @@ mod tests {
|
|||
// Incomplete parse
|
||||
assert_matches!(parse("foo | -"), Err(RevsetParseError::SyntaxError(_)));
|
||||
// Space is allowed around infix operators and function arguments
|
||||
// TODO: test two_arg_function( arg1 , arg2 ) if any
|
||||
assert_eq!(
|
||||
parse(" description( arg1 ) ~ parents( arg1 ) ~ heads( ) "),
|
||||
parse(" description( arg1 ) ~ file( arg1 , arg2 ) ~ heads( ) "),
|
||||
Ok(RevsetExpression::all()
|
||||
.with_description("arg1".to_string())
|
||||
.minus(&RevsetExpression::symbol("arg1".to_string()).parents())
|
||||
.minus(&RevsetExpression::all().with_file(vec![
|
||||
RepoPath::from_internal_string("arg1"),
|
||||
RepoPath::from_internal_string("arg2"),
|
||||
]))
|
||||
.minus(&RevsetExpression::visible_heads()))
|
||||
);
|
||||
}
|
||||
|
@ -1918,6 +1923,19 @@ mod tests {
|
|||
parse("description(\"(foo)\")"),
|
||||
Ok(RevsetExpression::all().with_description("(foo)".to_string()))
|
||||
);
|
||||
assert!(parse("file()").is_err());
|
||||
assert_eq!(
|
||||
parse("file(foo)"),
|
||||
Ok(RevsetExpression::all().with_file(vec![RepoPath::from_internal_string("foo")]))
|
||||
);
|
||||
assert_eq!(
|
||||
parse("file(foo, bar, baz)"),
|
||||
Ok(RevsetExpression::all().with_file(vec![
|
||||
RepoPath::from_internal_string("foo"),
|
||||
RepoPath::from_internal_string("bar"),
|
||||
RepoPath::from_internal_string("baz"),
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2114,7 +2132,9 @@ mod tests {
|
|||
),
|
||||
},
|
||||
predicate: File(
|
||||
"bar",
|
||||
[
|
||||
"bar",
|
||||
],
|
||||
),
|
||||
}
|
||||
"###);
|
||||
|
@ -2129,7 +2149,9 @@ mod tests {
|
|||
),
|
||||
},
|
||||
predicate: File(
|
||||
"bar",
|
||||
[
|
||||
"bar",
|
||||
],
|
||||
),
|
||||
},
|
||||
predicate: Author(
|
||||
|
@ -2148,7 +2170,9 @@ mod tests {
|
|||
),
|
||||
),
|
||||
predicate: File(
|
||||
"bar",
|
||||
[
|
||||
"bar",
|
||||
],
|
||||
),
|
||||
}
|
||||
"###);
|
||||
|
|
Loading…
Reference in a new issue