From cfcc76571c180c1d6b9fd39a4f2c358aaef3d994 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Fri, 20 Oct 2023 02:46:26 +0900 Subject: [PATCH] revset: add support for glob:pattern --- CHANGELOG.md | 3 +++ Cargo.lock | 1 + docs/revsets.md | 1 + lib/Cargo.toml | 1 + lib/src/str_util.rs | 18 +++++++++++++++++- lib/tests/test_revset.rs | 4 ++++ 6 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index abd0cf3649..91fe17d0d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `jj workspace forget` can now forget multiple workspaces at once. +* `branches()`/`remote_branches()`/`author()`/`committer()`/`description()` + revsets now support glob matching. + ### Fixed bugs * Updating the working copy to a commit where a file that's currently ignored diff --git a/Cargo.lock b/Cargo.lock index 9e3b11ba38..ee9f25edff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1051,6 +1051,7 @@ dependencies = [ "esl01-renderdag", "futures 0.3.28", "git2", + "glob", "hex", "insta", "itertools 0.11.0", diff --git a/docs/revsets.md b/docs/revsets.md index 26bf34d327..368ab1a6b4 100644 --- a/docs/revsets.md +++ b/docs/revsets.md @@ -145,6 +145,7 @@ Functions that perform string matching support the following pattern syntax. * `"string"`, `substring:"string"`: Matches strings that contain `string`. * `exact:"string"`: Matches strings exactly equal to `string`. +* `glob:"pattern"`: Matches strings with Unix-style shell wildcard `pattern`. ## Aliases diff --git a/lib/Cargo.toml b/lib/Cargo.toml index cce1d098f6..95d74bb23e 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -30,6 +30,7 @@ digest = { workspace = true } futures = { workspace = true } either = { workspace = true } git2 = { workspace = true } +glob = { workspace = true } hex = { workspace = true } itertools = { workspace = true } maplit = { workspace = true } diff --git a/lib/src/str_util.rs b/lib/src/str_util.rs index 5050033b9c..e3b8476e0f 100644 --- a/lib/src/str_util.rs +++ b/lib/src/str_util.rs @@ -26,6 +26,9 @@ pub enum StringPatternParseError { /// Unknown pattern kind is specified. #[error(r#"Invalid string pattern kind "{0}""#)] InvalidKind(String), + /// Failed to parse glob pattern. + #[error(transparent)] + GlobPattern(glob::PatternError), } /// Pattern to be tested against string property like commit description or @@ -34,6 +37,8 @@ pub enum StringPatternParseError { pub enum StringPattern { /// Matches strings exactly equal to `string`. Exact(String), + /// Unix-style shell wildcard pattern. + Glob(glob::Pattern), /// Matches strings that contain `substring`. Substring(String), } @@ -44,10 +49,20 @@ impl StringPattern { StringPattern::Substring(String::new()) } + /// Parses the given string as glob pattern. + pub fn glob(src: &str) -> Result { + // TODO: might be better to do parsing and compilation separately since + // not all backends would use the compiled pattern object. + // TODO: if no meta character found, it can be mapped to Exact. + let pattern = glob::Pattern::new(src).map_err(StringPatternParseError::GlobPattern)?; + Ok(StringPattern::Glob(pattern)) + } + /// Parses the given string as pattern of the specified `kind`. pub fn from_str_kind(src: &str, kind: &str) -> Result { match kind { "exact" => Ok(StringPattern::Exact(src.to_owned())), + "glob" => StringPattern::glob(src), "substring" => Ok(StringPattern::Substring(src.to_owned())), _ => Err(StringPatternParseError::InvalidKind(kind.to_owned())), } @@ -59,7 +74,7 @@ impl StringPattern { pub fn as_exact(&self) -> Option<&str> { match self { StringPattern::Exact(literal) => Some(literal), - StringPattern::Substring(_) => None, + StringPattern::Glob(_) | StringPattern::Substring(_) => None, } } @@ -67,6 +82,7 @@ impl StringPattern { pub fn matches(&self, haystack: &str) -> bool { match self { StringPattern::Exact(literal) => haystack == literal, + StringPattern::Glob(pattern) => pattern.matches(haystack), StringPattern::Substring(needle) => haystack.contains(needle), } } diff --git a/lib/tests/test_revset.rs b/lib/tests/test_revset.rs index ae521bb200..53360bab70 100644 --- a/lib/tests/test_revset.rs +++ b/lib/tests/test_revset.rs @@ -1775,6 +1775,10 @@ fn test_evaluate_expression_branches() { resolve_commit_ids(mut_repo, "branches(exact:branch1)"), vec![commit1.id().clone()] ); + assert_eq!( + resolve_commit_ids(mut_repo, r#"branches(glob:"branch?")"#), + vec![commit2.id().clone(), commit1.id().clone()] + ); // Can silently resolve to an empty set if there's no matches assert_eq!(resolve_commit_ids(mut_repo, "branches(branch3)"), vec![]); assert_eq!(