Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
radicle/git/canonical: Fix glob rewrite rule for trailing asterisk
Adrian Duke committed 3 days ago
commit 14f1d22900ccae0646c36ff698511f8ddb35c30b
parent 455138f4a7114437b5ca686991227f3531cad915
2 files changed +79 -13
modified crates/radicle/src/git/canonical/rules.rs
@@ -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.
modified crates/radicle/src/git/canonical/rules/test.rs
@@ -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")
+
    ));
+
}