diff --git a/git-refspec/src/parse.rs b/git-refspec/src/parse.rs index b736abfa2d..e8b8557566 100644 --- a/git-refspec/src/parse.rs +++ b/git-refspec/src/parse.rs @@ -36,10 +36,13 @@ pub(crate) mod function { let src = (!src.is_empty()).then(|| src.as_bstr()); let dst = (!dst.is_empty()).then(|| dst.as_bstr()); match (src, dst) { - (None, None) => {} + (None, None) => (None, None), // match all + (None, Some(dst)) => match operation { + Operation::Push => (None, Some(dst)), + Operation::Fetch => (Some("HEAD".into()), Some(dst)), + }, _ => todo!("src or dst handling"), - }; - (src, dst) + } } None => todo!("no colon"), }; diff --git a/git-refspec/src/spec.rs b/git-refspec/src/spec.rs index 161f0fd919..4215a7cd12 100644 --- a/git-refspec/src/spec.rs +++ b/git-refspec/src/spec.rs @@ -1,5 +1,5 @@ use crate::types::Push; -use crate::{Instruction, Mode, Operation, RefSpec, RefSpecRef}; +use crate::{Fetch, Instruction, Mode, Operation, RefSpec, RefSpecRef}; use bstr::BStr; /// Access @@ -15,14 +15,29 @@ impl RefSpecRef<'_> { item.contains(&b'*') } match (self.op, self.mode, self.src, self.dst) { + (Operation::Fetch, Mode::Normal | Mode::Force, Some(src), Some(dst)) => { + Instruction::Fetch(Fetch::AndUpdateSingle { + src, + dst, + allow_non_fast_forward: matches!(self.mode, Mode::Force), + }) + } (Operation::Push, Mode::Normal | Mode::Force, Some(src), None) => Instruction::Push(Push::Single { src, dst: src, allow_non_fast_forward: matches!(self.mode, Mode::Force), }), + (Operation::Push, Mode::Normal | Mode::Force, None, Some(dst)) => { + Instruction::Push(Push::Delete { ref_or_pattern: dst }) + } (Operation::Push, Mode::Normal | Mode::Force, None, None) => Instruction::Push(Push::AllMatchingBranches { allow_non_fast_forward: matches!(self.mode, Mode::Force), }), + (Operation::Fetch, Mode::Normal | Mode::Force, None, None) => { + Instruction::Fetch(Fetch::AllMatchingBranches { + allow_non_fast_forward: matches!(self.mode, Mode::Force), + }) + } (Operation::Push, Mode::Normal | Mode::Force, Some(src), Some(dst)) if has_pattern(src) => { Instruction::Push(Push::MultipleWithGlob { src, diff --git a/git-refspec/src/types.rs b/git-refspec/src/types.rs index 6ca621aacf..9588cafbc6 100644 --- a/git-refspec/src/types.rs +++ b/git-refspec/src/types.rs @@ -70,6 +70,11 @@ pub enum Push<'a> { #[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)] pub enum Fetch<'a> { + /// TODO: figure out what this actually does - it's valid for sure and only fetches HEAD -> FETCH_HEAD apparently + AllMatchingBranches { + /// Unclear what this does, but it's allowed + allow_non_fast_forward: bool, + }, Only { /// The ref name to fetch on the remote side, without updating the local side. src: &'a BStr, 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 6ec8641a7a..73c87d7d28 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:588dd4f20c618e60aff39c9cd47dd1300c83c39572f0c1db2c751ccbaca5709c -size 9328 +oid sha256:515045d1eade39e1aeee1f171216ac389b8adc54af541b2ff49bf1b1ab68eae2 +size 9340 diff --git a/git-refspec/tests/fixtures/make_baseline.sh b/git-refspec/tests/fixtures/make_baseline.sh index 68d28e37e0..a87d40cbf1 100644 --- a/git-refspec/tests/fixtures/make_baseline.sh +++ b/git-refspec/tests/fixtures/make_baseline.sh @@ -84,6 +84,12 @@ baseline fetch 'HEAD:' baseline push ':refs/remotes/frotz/deleteme' baseline fetch ':refs/remotes/frotz/HEAD-to-me' +baseline push ':a' +baseline push '+:a' + +baseline fetch ':a' +baseline fetch '+:a' + baseline fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' baseline push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' diff --git a/git-refspec/tests/parse/mod.rs b/git-refspec/tests/parse/mod.rs index 35ae4b3d54..1cb3f76f72 100644 --- a/git-refspec/tests/parse/mod.rs +++ b/git-refspec/tests/parse/mod.rs @@ -31,7 +31,10 @@ fn baseline() { match res { Ok(res) => match (res.is_ok(), err_code == 0) { (true, true) | (false, false) => {} - _ => mismatch += 1, + _ => { + eprintln!("{res:?} {err_code}"); + mismatch += 1; + } }, Err(_) => { panics += 1; @@ -40,8 +43,11 @@ fn baseline() { } if panics != 0 || mismatch != 0 { panic!( - "Out of {} baseline entries, got {} mismatches and {} panics", - count, mismatch, panics + "Out of {} baseline entries, got {} right, ({} mismatches and {} panics)", + count, + count - (mismatch + panics), + mismatch, + panics ); } } @@ -68,11 +74,51 @@ mod invalid { } } - mod fetch {} + mod fetch { + use crate::parse::{assert_parse, b}; + use git_refspec::{Fetch, Instruction}; + + #[test] + fn empty_lhs_colon_rhs_fetches_head_to_destination() { + assert_parse( + ":a", + Instruction::Fetch(Fetch::AndUpdateSingle { + src: b("HEAD"), + dst: b("a"), + allow_non_fast_forward: false, + }), + ); + + assert_parse( + "+:a", + Instruction::Fetch(Fetch::AndUpdateSingle { + src: b("HEAD"), + dst: b("a"), + allow_non_fast_forward: true, + }), + ); + } + + #[test] + fn colon_alone_is_for_fetching_into_fetchhead() { + assert_parse( + ":", + Instruction::Fetch(Fetch::AllMatchingBranches { + allow_non_fast_forward: false, + }), + ); + assert_parse( + "+:", + Instruction::Fetch(Fetch::AllMatchingBranches { + allow_non_fast_forward: true, + }), + ); + } + } mod push { - use crate::parse::assert_parse; - use git_refspec::{Instruction, Push}; + use crate::parse::{assert_parse, b}; + use git_refspec::{Instruction, Mode, Push}; #[test] fn colon_alone_is_for_pushing_matching_refs() { @@ -89,23 +135,35 @@ mod invalid { }), ); } + + #[test] + fn delete() { + assert_parse(":a", Instruction::Push(Push::Delete { ref_or_pattern: b("a") })); + let spec = assert_parse("+:a", Instruction::Push(Push::Delete { ref_or_pattern: b("a") })); + assert_eq!( + spec.mode(), + Mode::Force, + "force is set, even though it has no effect in the actual instruction" + ); + } } } mod util { use git_refspec::{Instruction, Operation, RefSpecRef}; - // pub fn b(input: &str) -> &bstr::BStr { - // input.into() - // } + pub fn b(input: &str) -> &bstr::BStr { + input.into() + } pub fn try_parse(spec: &str, op: Operation) -> Result, git_refspec::parse::Error> { git_refspec::parse(spec.into(), op) } - pub fn assert_parse(spec: &str, expected: Instruction<'_>) { + pub fn assert_parse<'a>(spec: &'a str, expected: Instruction<'_>) -> RefSpecRef<'a> { let spec = try_parse(spec, expected.operation()).expect("no error"); - assert_eq!(spec.instruction(), expected) + assert_eq!(spec.instruction(), expected); + spec } } pub use util::*;