ref-format: Relax first component of namespaced

The [manual page](man:gitnamespaces(7)) says:

> Git supports dividing the refs of a single repository into
> multiple namespaces, each of which has its own branches,
> tags, and `HEAD`.

Here, the mention of `HEAD` implies that requiring all
namespaced to start with

    refs/namespaces/*/refs

is too strict, as there are legal namespaced refs such as

    refs/namespaces/*/HEAD

which are not captured. Relax the corresponding condition, change
documentation and add a test to avoid regression.
This commit is contained in:
Lorenz Leutgeb
2025-09-09 13:17:29 +02:00
parent 786248add3
commit 24237552ec
2 changed files with 14 additions and 2 deletions
@@ -267,9 +267,10 @@ impl Display for Qualified<'_> {
/// A [`Qualified`] ref under a git namespace.
///
/// A ref is namespaced if it starts with "refs/namespaces/", another path
/// component, and "refs/". Eg.
/// component, and "refs" or "HEAD". Eg.
///
/// refs/namespaces/xyz/refs/heads/main
/// refs/namespaces/xyz/HEAD
///
/// Note that namespaces can be nested, so the result of
/// [`Namespaced::strip_namespace`] may be convertible to a [`Namespaced`]
@@ -365,7 +366,7 @@ impl<'a> From<&'a RefStr> for Option<Namespaced<'a>> {
fn from(rs: &'a RefStr) -> Self {
let mut cs = rs.iter();
match (cs.next()?, cs.next()?, cs.next()?, cs.next()?) {
("refs", "namespaces", _, "refs") => Some(Namespaced(Cow::from(rs))),
("refs", "namespaces", _, "refs" | "HEAD") => Some(Namespaced(Cow::from(rs))),
_ => None,
}
@@ -156,6 +156,17 @@ fn namespaced() {
)
}
#[test]
fn namespaced_head() {
assert_eq!(
"refs/namespaces/foo/HEAD",
refname!("refs/namespaces/foo/HEAD")
.to_namespaced()
.unwrap()
.as_str()
)
}
#[test]
fn not_namespaced() {
assert!(name::REFS_HEADS_MAIN.to_namespaced().is_none())