From 79e0eaf4c754da47731f8c5ed3635339586b3d00 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 7 Aug 2022 09:24:58 +0800 Subject: [PATCH] negative must not be empty (#450) --- git-refspec/src/parse.rs | 12 +++- .../generated-archives/make_baseline.tar.xz | 4 +- git-refspec/tests/fixtures/make_baseline.sh | 2 + git-refspec/tests/parse/invalid.rs | 59 +++++++++++++++++++ git-refspec/tests/parse/mod.rs | 56 +----------------- 5 files changed, 73 insertions(+), 60 deletions(-) create mode 100644 git-refspec/tests/parse/invalid.rs diff --git a/git-refspec/src/parse.rs b/git-refspec/src/parse.rs index 5441bb872b..21c607321e 100644 --- a/git-refspec/src/parse.rs +++ b/git-refspec/src/parse.rs @@ -4,6 +4,8 @@ pub enum Error { Empty, #[error("Negative refspecs cannot have destinations as they exclude sources")] NegativeWithDestination, + #[error("Negative specs must not be empty")] + NegativeEmpty, #[error("Cannot push into an empty destination")] PushToEmpty, #[error("glob patterns may only involved a single '*' character, found {pattern:?}")] @@ -52,12 +54,12 @@ pub(crate) mod function { let (mut src, dst) = match spec.find_byte(b':') { Some(pos) => { - let (src, dst) = spec.split_at(pos); - let dst = &dst[1..]; if mode == Mode::Negative { return Err(Error::NegativeWithDestination); } + let (src, dst) = spec.split_at(pos); + let dst = &dst[1..]; let src = (!src.is_empty()).then(|| src.as_bstr()); let dst = (!dst.is_empty()).then(|| dst.as_bstr()); match (src, dst) { @@ -78,7 +80,7 @@ pub(crate) mod function { } None => { let src = (!spec.is_empty()).then(|| spec); - if Operation::Fetch == operation && src.is_none() { + if Operation::Fetch == operation && mode != Mode::Negative && src.is_none() { return Ok(fetch_head_only(mode)); } else { (src, None) @@ -86,6 +88,10 @@ pub(crate) mod function { } }; + if mode == Mode::Negative && src.is_none() { + return Err(Error::NegativeEmpty); + } + if let Some(spec) = src.as_mut() { if *spec == "@" { *spec = "HEAD".into(); diff --git a/git-refspec/tests/fixtures/generated-archives/make_baseline.tar.xz b/git-refspec/tests/fixtures/generated-archives/make_baseline.tar.xz index 00433aa4fd..6fbfc3d4c5 100644 --- a/git-refspec/tests/fixtures/generated-archives/make_baseline.tar.xz +++ b/git-refspec/tests/fixtures/generated-archives/make_baseline.tar.xz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ace43ca904298dc76951863d41485ae0c21ee9b88abf7d604eb6c6e7efa18ab2 -size 9352 +oid sha256:6d2478d9f90c1e68924743a45cf801b4420924751c2caf7fa9a6c14291c388cd +size 9356 diff --git a/git-refspec/tests/fixtures/make_baseline.sh b/git-refspec/tests/fixtures/make_baseline.sh index 23951021a2..f85c59e6f0 100644 --- a/git-refspec/tests/fixtures/make_baseline.sh +++ b/git-refspec/tests/fixtures/make_baseline.sh @@ -30,6 +30,8 @@ baseline fetch '^a:' baseline fetch '^a:b' baseline fetch '^:' baseline fetch '^:b' +baseline fetch '^' +baseline push '^' baseline fetch '^refs/heads/qa/*/*' baseline push '^refs/heads/qa/*/*' diff --git a/git-refspec/tests/parse/invalid.rs b/git-refspec/tests/parse/invalid.rs new file mode 100644 index 0000000000..d3f36a572d --- /dev/null +++ b/git-refspec/tests/parse/invalid.rs @@ -0,0 +1,59 @@ +use crate::parse::try_parse; +use git_refspec::{parse::Error, Operation}; + +#[test] +fn empty() { + assert!(matches!(try_parse("", Operation::Push).unwrap_err(), Error::Empty)); +} + +#[test] +fn negative_must_not_be_empty() { + for op in [Operation::Fetch, Operation::Push] { + assert!(matches!(try_parse("^", op).unwrap_err(), Error::NegativeEmpty)); + } +} + +#[test] +fn negative_with_destination() { + for op in [Operation::Fetch, Operation::Push] { + for spec in ["^a:b", "^a:", "^:", "^:b"] { + assert!(matches!( + try_parse(spec, op).unwrap_err(), + Error::NegativeWithDestination + )); + } + } +} + +#[test] +fn complex_patterns_with_more_than_one_asterisk() { + for op in [Operation::Fetch, Operation::Push] { + for spec in ["^*/*", "a/*/c/*", "a**:**b", "+:**/"] { + assert!(matches!( + try_parse(spec, op).unwrap_err(), + Error::PatternUnsupported { .. } + )); + } + } +} + +#[test] +fn both_sides_need_pattern_if_one_uses_it() { + for op in [Operation::Fetch, Operation::Push] { + for spec in ["refs/*/a", ":a/*", "+:a/*", "a*:b/c", "a:b/*"] { + assert!( + matches!(try_parse(spec, op).unwrap_err(), Error::PatternUnbalanced), + "{}", + spec + ); + } + } +} + +#[test] +fn push_to_empty() { + assert!(matches!( + try_parse("HEAD:", Operation::Push).unwrap_err(), + Error::PushToEmpty + )); +} diff --git a/git-refspec/tests/parse/mod.rs b/git-refspec/tests/parse/mod.rs index eb9af0ab12..230cbde081 100644 --- a/git-refspec/tests/parse/mod.rs +++ b/git-refspec/tests/parse/mod.rs @@ -56,62 +56,8 @@ fn baseline() { } } -mod invalid { - use crate::parse::try_parse; - use git_refspec::{parse::Error, Operation}; - - #[test] - fn empty() { - assert!(matches!(try_parse("", Operation::Push).unwrap_err(), Error::Empty)); - } - - #[test] - fn negative_with_destination() { - for op in [Operation::Fetch, Operation::Push] { - for spec in ["^a:b", "^a:", "^:", "^:b"] { - assert!(matches!( - try_parse(spec, op).unwrap_err(), - Error::NegativeWithDestination - )); - } - } - } - - #[test] - fn complex_patterns_with_more_than_one_asterisk() { - for op in [Operation::Fetch, Operation::Push] { - for spec in ["^*/*", "a/*/c/*", "a**:**b", "+:**/"] { - assert!(matches!( - try_parse(spec, op).unwrap_err(), - Error::PatternUnsupported { .. } - )); - } - } - } - - #[test] - fn both_sides_need_pattern_if_one_uses_it() { - for op in [Operation::Fetch, Operation::Push] { - for spec in ["refs/*/a", ":a/*", "+:a/*", "a*:b/c", "a:b/*"] { - assert!( - matches!(try_parse(spec, op).unwrap_err(), Error::PatternUnbalanced), - "{}", - spec - ); - } - } - } - - #[test] - fn push_to_empty() { - assert!(matches!( - try_parse("HEAD:", Operation::Push).unwrap_err(), - Error::PushToEmpty - )); - } -} - mod fetch; +mod invalid; mod push; mod util {