mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-01 00:50:57 +00:00
revset: add subject() predicate that matches first line of descriptions
It's generally useful, and we can get by without introducing weird special case about newline terminator. Closes #5227
This commit is contained in:
parent
3d7858df26
commit
a3636d8a83
5 changed files with 64 additions and 3 deletions
|
@ -107,6 +107,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||
* New `author_name`/`author_email`/`committer_name`/`committer_email(pattern)`
|
||||
revset functions to match either name or email field explicitly.
|
||||
|
||||
* New `subject(pattern)` revset function that matches first line of commit
|
||||
descriptions.
|
||||
|
||||
### Fixed bugs
|
||||
|
||||
* Fixed diff selection by external tools with `jj split`/`commit -i FILESETS`.
|
||||
|
|
|
@ -267,6 +267,14 @@ revsets (expressions) as arguments.
|
|||
* `description(pattern)`: Commits that have a description matching the given
|
||||
[string pattern](#string-patterns).
|
||||
|
||||
A non-empty description is usually terminated with newline character. For
|
||||
example, `description(exact:"")` matches commits without description, and
|
||||
`description(exact:"foo\n")` matches commits with description `"foo\n"`.
|
||||
|
||||
* `subject(pattern)`: Commits that have a subject matching the given [string
|
||||
pattern](#string-patterns). A subject is the first line of the description
|
||||
(without newline character.)
|
||||
|
||||
* `author(pattern)`: Commits with the author's name or email matching the given
|
||||
[string pattern](#string-patterns). Equivalent to `author_name(pattern) |
|
||||
author_email(pattern)`.
|
||||
|
|
|
@ -1161,6 +1161,14 @@ fn build_predicate_fn(
|
|||
Ok(pattern.matches(commit.description()))
|
||||
})
|
||||
}
|
||||
RevsetFilterPredicate::Subject(pattern) => {
|
||||
let pattern = pattern.clone();
|
||||
box_pure_predicate_fn(move |index, pos| {
|
||||
let entry = index.entry_by_pos(pos);
|
||||
let commit = store.get_commit(&entry.commit_id())?;
|
||||
Ok(pattern.matches(commit.description().lines().next().unwrap_or_default()))
|
||||
})
|
||||
}
|
||||
RevsetFilterPredicate::AuthorName(pattern) => {
|
||||
let pattern = pattern.clone();
|
||||
box_pure_predicate_fn(move |index, pos| {
|
||||
|
|
|
@ -156,6 +156,8 @@ pub enum RevsetFilterPredicate {
|
|||
ParentCount(Range<u32>),
|
||||
/// Commits with description matching the pattern.
|
||||
Description(StringPattern),
|
||||
/// Commits with first line of the description matching the pattern.
|
||||
Subject(StringPattern),
|
||||
/// Commits with author name matching the pattern.
|
||||
AuthorName(StringPattern),
|
||||
/// Commits with author email matching the pattern.
|
||||
|
@ -834,6 +836,12 @@ static BUILTIN_FUNCTION_MAP: Lazy<HashMap<&'static str, RevsetFunction>> = Lazy:
|
|||
RevsetFilterPredicate::Description(pattern),
|
||||
))
|
||||
});
|
||||
map.insert("subject", |diagnostics, function, _context| {
|
||||
let [arg] = function.expect_exact_arguments()?;
|
||||
let pattern = expect_string_pattern(diagnostics, arg)?;
|
||||
let predicate = RevsetFilterPredicate::Subject(pattern);
|
||||
Ok(RevsetExpression::filter(predicate))
|
||||
});
|
||||
map.insert("author", |diagnostics, function, _context| {
|
||||
let [arg] = function.expect_exact_arguments()?;
|
||||
let pattern = expect_string_pattern(diagnostics, arg)?;
|
||||
|
|
|
@ -2654,17 +2654,17 @@ fn test_evaluate_expression_description() {
|
|||
let mut_repo = tx.repo_mut();
|
||||
|
||||
let commit1 = create_random_commit(mut_repo)
|
||||
.set_description("commit 1")
|
||||
.set_description("commit 1\n")
|
||||
.write()
|
||||
.unwrap();
|
||||
let commit2 = create_random_commit(mut_repo)
|
||||
.set_parents(vec![commit1.id().clone()])
|
||||
.set_description("commit 2")
|
||||
.set_description("commit 2\n\nblah blah...\n")
|
||||
.write()
|
||||
.unwrap();
|
||||
let commit3 = create_random_commit(mut_repo)
|
||||
.set_parents(vec![commit2.id().clone()])
|
||||
.set_description("commit 3")
|
||||
.set_description("commit 3\n")
|
||||
.write()
|
||||
.unwrap();
|
||||
|
||||
|
@ -2687,6 +2687,40 @@ fn test_evaluate_expression_description() {
|
|||
resolve_commit_ids(mut_repo, "visible_heads() & description(\"commit 2\")"),
|
||||
vec![]
|
||||
);
|
||||
|
||||
// Exact match
|
||||
assert_eq!(
|
||||
resolve_commit_ids(mut_repo, r#"description(exact:"commit 1\n")"#),
|
||||
vec![commit1.id().clone()]
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_commit_ids(mut_repo, r#"description(exact:"commit 2\n")"#),
|
||||
vec![]
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_commit_ids(mut_repo, "description(exact:'')"),
|
||||
vec![mut_repo.store().root_commit_id().clone()]
|
||||
);
|
||||
|
||||
// Match subject line
|
||||
assert_eq!(
|
||||
resolve_commit_ids(mut_repo, "subject(glob:'commit ?')"),
|
||||
vec![
|
||||
commit3.id().clone(),
|
||||
commit2.id().clone(),
|
||||
commit1.id().clone(),
|
||||
]
|
||||
);
|
||||
assert_eq!(resolve_commit_ids(mut_repo, "subject('blah')"), vec![]);
|
||||
assert_eq!(
|
||||
resolve_commit_ids(mut_repo, "subject(exact:'commit 2')"),
|
||||
vec![commit2.id().clone()]
|
||||
);
|
||||
// Empty description should have empty subject line
|
||||
assert_eq!(
|
||||
resolve_commit_ids(mut_repo, "subject(exact:'')"),
|
||||
vec![mut_repo.store().root_commit_id().clone()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in a new issue