Skip to content

Commit d7c2789

Browse files
committed
handle ref-name validation mostly correctly (#450)
1 parent e8c072e commit d7c2789

File tree

4 files changed

+39
-9
lines changed

4 files changed

+39
-9
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

git-refspec/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ doctest = false
1515

1616
[dependencies]
1717
bstr = { version = "0.2.13", default-features = false, features = ["std"]}
18+
git-validate = { version = "^0.5.4", path = "../git-validate" }
1819
thiserror = "1.0.26"
20+
smallvec = "1.9.0"
1921

2022
[dev-dependencies]
2123
git-testtools = { path = "../tests/tools" }

git-refspec/src/parse.rs

+29-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub enum Error {
1010
PatternUnsupported { pattern: bstr::BString },
1111
#[error("Both sides of the specification need a pattern, like 'a/*:b/*'")]
1212
PatternUnbalanced,
13+
#[error(transparent)]
14+
Refname(#[from] git_validate::refname::Error),
1315
}
1416

1517
pub(crate) mod function {
@@ -19,6 +21,15 @@ pub(crate) mod function {
1921

2022
/// Parse `spec` for use in `operation` and return it if it is valid.
2123
pub fn parse(mut spec: &BStr, operation: Operation) -> Result<RefSpecRef<'_>, Error> {
24+
fn fetch_head_only(mode: Mode) -> RefSpecRef<'static> {
25+
RefSpecRef {
26+
mode,
27+
op: Operation::Fetch,
28+
src: Some("HEAD".into()),
29+
dst: None,
30+
}
31+
}
32+
2233
let mode = match spec.get(0) {
2334
Some(&b'^') => {
2435
spec = &spec[1..];
@@ -32,12 +43,7 @@ pub(crate) mod function {
3243
None => {
3344
return match operation {
3445
Operation::Push => Err(Error::Empty),
35-
Operation::Fetch => Ok(RefSpecRef {
36-
mode: Mode::Normal,
37-
op: operation,
38-
src: Some("HEAD".into()),
39-
dst: None,
40-
}),
46+
Operation::Fetch => Ok(fetch_head_only(Mode::Normal)),
4147
}
4248
}
4349
};
@@ -68,7 +74,14 @@ pub(crate) mod function {
6874
(Some(src), Some(dst)) => (Some(src), Some(dst)),
6975
}
7076
}
71-
None => (Some(spec), None),
77+
None => {
78+
let src = (!spec.is_empty()).then(|| spec);
79+
if Operation::Fetch == operation && src.is_none() {
80+
return Ok(fetch_head_only(mode));
81+
} else {
82+
(src, None)
83+
}
84+
}
7285
};
7386

7487
let (src, src_had_pattern) = validated(src)?;
@@ -91,6 +104,15 @@ pub(crate) mod function {
91104
if glob_count == 2 {
92105
return Err(Error::PatternUnsupported { pattern: spec.into() });
93106
}
107+
if glob_count == 1 {
108+
let mut buf = smallvec::SmallVec::<[u8; 256]>::with_capacity(spec.len());
109+
buf.extend_from_slice(&spec);
110+
let glob_pos = buf.find_byte(b'*').expect("glob present");
111+
buf[glob_pos] = b'a';
112+
git_validate::reference::name_partial(buf.as_bstr())?;
113+
} else {
114+
git_validate::reference::name_partial(spec)?;
115+
}
94116
Ok((Some(spec), glob_count == 1))
95117
}
96118
None => Ok((None, false)),

git-refspec/tests/parse/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,12 @@ mod invalid {
9393
#[test]
9494
fn both_sides_need_pattern_if_one_uses_it() {
9595
for op in [Operation::Fetch, Operation::Push] {
96-
for spec in ["/*/a", ":a/*", "+:a/*", "a*:b/c", "a:b/*"] {
97-
assert!(matches!(try_parse(spec, op).unwrap_err(), Error::PatternUnbalanced));
96+
for spec in ["refs/*/a", ":a/*", "+:a/*", "a*:b/c", "a:b/*"] {
97+
assert!(
98+
matches!(try_parse(spec, op).unwrap_err(), Error::PatternUnbalanced),
99+
"{}",
100+
spec
101+
);
98102
}
99103
}
100104
}

0 commit comments

Comments
 (0)