radicle/git/canonical: Fix glob rewrite rule for trailing asterisk
Previously a rule like `refs/heads/releases-*` would be rewritten incorrectly to `refs/heads/releases-**/*` matching the 'trailing asterisk' rule. Simplify the rewrite rule so that a trailing `*` becomes a prefix matching rule. Add tests.
This commit is contained in:
parent
455138f4a7
commit
fb40bdc0ac
|
|
@ -60,6 +60,13 @@ COB type names and payload IDs remain unchanged for backwards compatibility.
|
|||
- Delete invalid node addresses, of the IPv6 type, from the SQLite address book.
|
||||
Entries deleted include ones that have an address of the form `[]:port`, or
|
||||
that have an invalid address within the `[]`.
|
||||
- Correctly handle the canonical reference rules that have a glob star suffix,
|
||||
e.g., `refs/heads/main*`, `refs/heads/main-*`, etc.
|
||||
Previously, these would be expanded into sub-component searches. For example,
|
||||
in the previous examples, the search would be expanded to
|
||||
`refs/heads/main**/*` and `refs/heads/main-**/*`.
|
||||
Now, the rule will match by prefix, e.g. `refs/heads/main-*` will match
|
||||
`refs/heads/main-a`, `refs/heads/main-b`, `refs/heads/main-deadbeef`, etc.
|
||||
|
||||
## 1.8.0
|
||||
|
||||
|
|
|
|||
|
|
@ -54,25 +54,18 @@ fn matches(pattern: &RawPattern, refname: &Qualified) -> bool {
|
|||
// that namespace, even if they are further down the hierarchy.
|
||||
// Thus, the following rules are applied:
|
||||
//
|
||||
// - a trailing `*` changes to `**/*`
|
||||
// - a trailing `*` matches anything that starts with the prefix
|
||||
// - a `*` in between path components changes to `**`
|
||||
let spec = match pattern.as_str().split_once(ASTERISK) {
|
||||
None => pattern.to_string(),
|
||||
// Expand `refs/tags/*` to `refs/tags/**/*`
|
||||
Some((prefix, "")) => {
|
||||
let mut spec = prefix.to_string();
|
||||
spec.push_str("**/*");
|
||||
spec
|
||||
}
|
||||
// Expand `refs/tags/*/v1.0` to `refs/tags/**/v1.0`
|
||||
match pattern.as_str().split_once(ASTERISK) {
|
||||
None => pattern.as_str() == refname.as_str(),
|
||||
Some((prefix, "")) => refname.as_str().starts_with(prefix),
|
||||
Some((prefix, suffix)) => {
|
||||
let mut spec = prefix.to_string();
|
||||
spec.push_str("**");
|
||||
spec.push_str(suffix);
|
||||
spec
|
||||
fast_glob::glob_match(&spec, refname.as_str())
|
||||
}
|
||||
};
|
||||
fast_glob::glob_match(&spec, refname.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// Patterns are ordered by their specificity.
|
||||
|
|
|
|||
|
|
@ -477,3 +477,76 @@ fn special_branches() {
|
|||
assert!(Pattern::new((*SIGREFS_PARENT).clone().into()).is_err());
|
||||
assert!(Pattern::new((*IDENTITY_ROOT).clone().into()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matches_expands_globs_appropriately() {
|
||||
let exact = qualified_pattern!("refs/heads/main");
|
||||
assert!(matches(&exact, &git::fmt::qualified!("refs/heads/main")));
|
||||
assert!(!matches(&exact, &git::fmt::qualified!("refs/heads/main-2")));
|
||||
assert!(!matches(&exact, &git::fmt::qualified!("refs/heads/other")));
|
||||
|
||||
let trailing_slash = qualified_pattern!("refs/heads/*");
|
||||
assert!(matches(
|
||||
&trailing_slash,
|
||||
&git::fmt::qualified!("refs/heads/main")
|
||||
));
|
||||
assert!(matches(
|
||||
&trailing_slash,
|
||||
&git::fmt::qualified!("refs/heads/feature/1")
|
||||
));
|
||||
assert!(!matches(
|
||||
&trailing_slash,
|
||||
&git::fmt::qualified!("refs/tags/main")
|
||||
));
|
||||
|
||||
let trailing_text = qualified_pattern!("refs/heads/feature-*");
|
||||
assert!(matches(
|
||||
&trailing_text,
|
||||
&git::fmt::qualified!("refs/heads/feature-")
|
||||
));
|
||||
assert!(matches(
|
||||
&trailing_text,
|
||||
&git::fmt::qualified!("refs/heads/feature-1")
|
||||
));
|
||||
assert!(matches(
|
||||
&trailing_text,
|
||||
&git::fmt::qualified!("refs/heads/feature-1/sub")
|
||||
));
|
||||
assert!(!matches(
|
||||
&trailing_text,
|
||||
&git::fmt::qualified!("refs/heads/feature")
|
||||
));
|
||||
|
||||
// Because `*` expands to `**`, it matches zero or more path components.
|
||||
let middle = qualified_pattern!("refs/heads/*/main");
|
||||
assert!(matches(
|
||||
&middle,
|
||||
&git::fmt::qualified!("refs/heads/alice/main")
|
||||
));
|
||||
assert!(matches(
|
||||
&middle,
|
||||
&git::fmt::qualified!("refs/heads/alice/bob/main")
|
||||
));
|
||||
|
||||
//
|
||||
// NOTE(Ade): In git this won't match as glob '*' isn't inclusive of zero matches like `**`
|
||||
// See: https://git.kernel.org/pub/scm/git/git.git/tree/refspec.c?h=v2.54.0&id=94f057755b7941b321fd11fec1b2e3ca5313a4e0#n298
|
||||
//
|
||||
assert!(matches(&middle, &git::fmt::qualified!("refs/heads/main")));
|
||||
|
||||
assert!(!matches(
|
||||
&middle,
|
||||
&git::fmt::qualified!("refs/heads/alice/dev")
|
||||
));
|
||||
|
||||
// HardenedBSD glob expansion issue
|
||||
let hbsd = qualified_pattern!("refs/heads/quarterly/hardened/15-stable/main*");
|
||||
assert!(matches(
|
||||
&hbsd,
|
||||
&git::fmt::qualified!("refs/heads/quarterly/hardened/15-stable/main-2026q2")
|
||||
));
|
||||
assert!(matches(
|
||||
&hbsd,
|
||||
&git::fmt::qualified!("refs/heads/quarterly/hardened/15-stable/main/2026q2")
|
||||
));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue