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.
|
- 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
|
Entries deleted include ones that have an address of the form `[]:port`, or
|
||||||
that have an invalid address within the `[]`.
|
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
|
## 1.8.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,25 +54,18 @@ fn matches(pattern: &RawPattern, refname: &Qualified) -> bool {
|
||||||
// that namespace, even if they are further down the hierarchy.
|
// that namespace, even if they are further down the hierarchy.
|
||||||
// Thus, the following rules are applied:
|
// 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 `**`
|
// - a `*` in between path components changes to `**`
|
||||||
let spec = match pattern.as_str().split_once(ASTERISK) {
|
match pattern.as_str().split_once(ASTERISK) {
|
||||||
None => pattern.to_string(),
|
None => pattern.as_str() == refname.as_str(),
|
||||||
// Expand `refs/tags/*` to `refs/tags/**/*`
|
Some((prefix, "")) => refname.as_str().starts_with(prefix),
|
||||||
Some((prefix, "")) => {
|
|
||||||
let mut spec = prefix.to_string();
|
|
||||||
spec.push_str("**/*");
|
|
||||||
spec
|
|
||||||
}
|
|
||||||
// Expand `refs/tags/*/v1.0` to `refs/tags/**/v1.0`
|
|
||||||
Some((prefix, suffix)) => {
|
Some((prefix, suffix)) => {
|
||||||
let mut spec = prefix.to_string();
|
let mut spec = prefix.to_string();
|
||||||
spec.push_str("**");
|
spec.push_str("**");
|
||||||
spec.push_str(suffix);
|
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.
|
/// 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((*SIGREFS_PARENT).clone().into()).is_err());
|
||||||
assert!(Pattern::new((*IDENTITY_ROOT).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