Skip to content

Commit 57a6e69

Browse files
committed
more detailed tests of what's allowed and where (#450)
1 parent 9c280b2 commit 57a6e69

File tree

4 files changed

+78
-43
lines changed

4 files changed

+78
-43
lines changed

git-refspec/src/parse.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub enum Error {
1010
NegativeUnsupported,
1111
#[error("Negative specs must be object hashes")]
1212
NegativeObjectHash,
13+
#[error("Fetch destinations must be ref-names, like 'HEAD:refs/heads/branch'")]
14+
InvalidFetchDestination,
1315
#[error("Cannot push into an empty destination")]
1416
PushToEmpty,
1517
#[error("glob patterns may only involved a single '*' character, found {pattern:?}")]
@@ -98,9 +100,7 @@ pub(crate) mod function {
98100
if mode == Mode::Negative {
99101
match src {
100102
Some(spec) => {
101-
if spec.len() >= git_hash::Kind::shortest().len_in_hex()
102-
&& spec.iter().all(|b| b.is_ascii_hexdigit())
103-
{
103+
if looks_like_object_hash(spec) {
104104
return Err(Error::NegativeObjectHash);
105105
}
106106
}
@@ -115,6 +115,9 @@ pub(crate) mod function {
115115
}
116116
let (src, src_had_pattern) = validated(src, operation == Operation::Push)?;
117117
let (dst, dst_had_pattern) = validated(dst, false)?;
118+
if !dst_had_pattern && looks_like_object_hash(dst.unwrap_or_default()) {
119+
return Err(Error::InvalidFetchDestination);
120+
}
118121
if mode != Mode::Negative && src_had_pattern != dst_had_pattern {
119122
return Err(Error::PatternUnbalanced);
120123
}
@@ -126,6 +129,10 @@ pub(crate) mod function {
126129
})
127130
}
128131

132+
fn looks_like_object_hash(spec: &BStr) -> bool {
133+
spec.len() >= git_hash::Kind::shortest().len_in_hex() && spec.iter().all(|b| b.is_ascii_hexdigit())
134+
}
135+
129136
fn validated(spec: Option<&BStr>, allow_revspecs: bool) -> Result<(Option<&BStr>, bool), Error> {
130137
match spec {
131138
Some(spec) => {

git-refspec/tests/parse/fetch.rs

+56-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,59 @@
1-
use crate::parse::{assert_parse, b};
2-
use git_refspec::{Fetch, Instruction, Mode};
1+
use crate::parse::{assert_parse, b, try_parse};
2+
use git_refspec::{parse::Error, Fetch, Instruction, Mode, Operation};
3+
4+
#[test]
5+
fn revspecs_are_disallowed() {
6+
for spec in ["main~1", "^@^{}", "HEAD:main~1"] {
7+
assert!(matches!(
8+
try_parse(spec, Operation::Fetch).unwrap_err(),
9+
Error::ReferenceName(_)
10+
));
11+
}
12+
}
13+
14+
#[test]
15+
fn object_hash_as_source() {
16+
assert_parse(
17+
"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391:",
18+
Instruction::Fetch(Fetch::Only {
19+
src: b("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"),
20+
}),
21+
);
22+
}
23+
24+
#[test]
25+
fn object_hash_destination_is_invalid() {
26+
assert!(matches!(
27+
try_parse("a:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", Operation::Fetch).unwrap_err(),
28+
Error::InvalidFetchDestination
29+
));
30+
}
31+
32+
#[test]
33+
fn negative_must_not_be_empty() {
34+
assert!(matches!(
35+
try_parse("^", Operation::Fetch).unwrap_err(),
36+
Error::NegativeEmpty
37+
));
38+
}
39+
40+
#[test]
41+
fn negative_must_not_be_object_hash() {
42+
assert!(matches!(
43+
try_parse("^e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", Operation::Fetch).unwrap_err(),
44+
Error::NegativeObjectHash
45+
));
46+
}
47+
48+
#[test]
49+
fn negative_with_destination() {
50+
for spec in ["^a:b", "^a:", "^:", "^:b"] {
51+
assert!(matches!(
52+
try_parse(spec, Operation::Fetch).unwrap_err(),
53+
Error::NegativeWithDestination
54+
));
55+
}
56+
}
357

458
#[test]
559
fn exclude() {

git-refspec/tests/parse/invalid.rs

-36
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,6 @@ fn empty() {
66
assert!(matches!(try_parse("", Operation::Push).unwrap_err(), Error::Empty));
77
}
88

9-
#[test]
10-
fn negative_must_not_be_empty() {
11-
assert!(matches!(
12-
try_parse("^", Operation::Fetch).unwrap_err(),
13-
Error::NegativeEmpty
14-
));
15-
}
16-
17-
#[test]
18-
fn negative_must_not_be_object_hash() {
19-
assert!(matches!(
20-
try_parse("^e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", Operation::Fetch).unwrap_err(),
21-
Error::NegativeObjectHash
22-
));
23-
}
24-
25-
#[test]
26-
fn negative_with_destination() {
27-
for spec in ["^a:b", "^a:", "^:", "^:b"] {
28-
assert!(matches!(
29-
try_parse(spec, Operation::Fetch).unwrap_err(),
30-
Error::NegativeWithDestination
31-
));
32-
}
33-
}
34-
35-
#[test]
36-
fn negative_unsupported_when_pushing() {
37-
for spec in ["^a:b", "^a:", "^:", "^:b", "^"] {
38-
assert!(matches!(
39-
try_parse(spec, Operation::Push).unwrap_err(),
40-
Error::NegativeUnsupported
41-
));
42-
}
43-
}
44-
459
#[test]
4610
fn complex_patterns_with_more_than_one_asterisk() {
4711
for op in [Operation::Fetch, Operation::Push] {

git-refspec/tests/parse/push.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1-
use crate::parse::{assert_parse, b};
2-
use git_refspec::{Instruction, Mode, Push};
1+
use crate::parse::{assert_parse, b, try_parse};
2+
use git_refspec::{parse::Error, Instruction, Mode, Operation, Push};
3+
4+
#[test]
5+
fn negative_unsupported() {
6+
for spec in ["^a:b", "^a:", "^:", "^:b", "^"] {
7+
assert!(matches!(
8+
try_parse(spec, Operation::Push).unwrap_err(),
9+
Error::NegativeUnsupported
10+
));
11+
}
12+
}
313

414
#[test]
515
fn ampersand_is_resolved_to_head() {

0 commit comments

Comments
 (0)