diff --git a/git-refspec/src/lib.rs b/git-refspec/src/lib.rs index 3245571277..2d38f3931b 100644 --- a/git-refspec/src/lib.rs +++ b/git-refspec/src/lib.rs @@ -23,35 +23,7 @@ pub struct RefSpec { dest: Option, } -mod types; -pub use types::{Instruction, Mode, Operation}; - -mod spec { - use crate::{Instruction, Mode, RefSpec, RefSpecRef}; - - /// Access - impl RefSpecRef<'_> { - /// Return the refspec mode. - pub fn mode(&self) -> Mode { - self.mode - } +mod spec; - /// Transform the state of the refspec into an instruction making clear what to do with it. - pub fn instruction(&self) -> Instruction<'_> { - todo!() - } - } - - /// Conversion - impl RefSpecRef<'_> { - /// Convert this ref into a standalone, owned copy. - pub fn to_owned(&self) -> RefSpec { - RefSpec { - mode: self.mode, - op: self.op, - src: self.src.map(ToOwned::to_owned), - dest: self.dest.map(ToOwned::to_owned), - } - } - } -} +mod types; +pub use types::{Fetch, Instruction, Mode, Operation, Push}; diff --git a/git-refspec/src/spec.rs b/git-refspec/src/spec.rs new file mode 100644 index 0000000000..d9488d2b68 --- /dev/null +++ b/git-refspec/src/spec.rs @@ -0,0 +1,63 @@ +use crate::types::Push; +use crate::{Instruction, Mode, Operation, RefSpec, RefSpecRef}; +use bstr::BStr; + +/// Access +impl RefSpecRef<'_> { + /// Return the refspec mode. + pub fn mode(&self) -> Mode { + self.mode + } + + /// Transform the state of the refspec into an instruction making clear what to do with it. + pub fn instruction(&self) -> Instruction<'_> { + fn has_pattern(item: &BStr) -> bool { + item.contains(&b'*') + } + match (self.op, self.mode, self.src, self.dest) { + (Operation::Push, Mode::Normal | Mode::Force, Some(src), None) => Instruction::Push(Push::Single { + src, + dest: src, + allow_non_fast_forward: matches!(self.mode, Mode::Force), + }), + (Operation::Push, Mode::Normal | Mode::Force, None, None) => Instruction::Push(Push::AllMatchingBranches { + allow_non_fast_forward: matches!(self.mode, Mode::Force), + }), + (Operation::Push, Mode::Normal | Mode::Force, Some(src), Some(dest)) if has_pattern(src) => { + Instruction::Push(Push::MultipleWithGlob { + src, + dest, + allow_non_fast_forward: matches!(self.mode, Mode::Force), + }) + } + (Operation::Push, Mode::Normal | Mode::Force, Some(src), Some(dest)) => Instruction::Push(Push::Single { + src, + dest, + allow_non_fast_forward: matches!(self.mode, Mode::Force), + }), + (Operation::Push, Mode::Negative, Some(src), None) if has_pattern(src) => { + Instruction::Push(Push::ExcludeMultipleWithGlob { src }) + } + (Operation::Push, Mode::Negative, Some(src), None) => Instruction::Push(Push::ExcludeSingle { src }), + (op, mode, src, dest) => { + unreachable!( + "BUG: instructions with {:?} {:?} {:?} {:?} are not possible", + op, mode, src, dest + ) + } + } + } +} + +/// Conversion +impl RefSpecRef<'_> { + /// Convert this ref into a standalone, owned copy. + pub fn to_owned(&self) -> RefSpec { + RefSpec { + mode: self.mode, + op: self.op, + src: self.src.map(ToOwned::to_owned), + dest: self.dest.map(ToOwned::to_owned), + } + } +} diff --git a/git-refspec/src/types.rs b/git-refspec/src/types.rs index f15ec9240f..ac129f75cf 100644 --- a/git-refspec/src/types.rs +++ b/git-refspec/src/types.rs @@ -20,17 +20,17 @@ pub enum Operation { Fetch, } +#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)] pub enum Instruction<'a> { Push(Push<'a>), Fetch(Fetch<'a>), } +#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)] pub enum Push<'a> { - /// Push a single ref knowing only one ref name. - SingleMatching { - /// The name of the ref to push from `src` to `dest`. - src_and_dest: &'a BStr, - /// If true, allow non-fast-forward updates of `dest`. + /// Push all local branches to the matching destination on the remote, which has to exist to be updated. + AllMatchingBranches { + /// If true, allow non-fast-forward updates of the matched destination branch. allow_non_fast_forward: bool, }, /// Exclude a single ref. @@ -63,6 +63,7 @@ pub enum Push<'a> { }, } +#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)] pub enum Fetch<'a> { Only { /// The ref name to fetch on the remote side, without updating the local side. @@ -96,3 +97,12 @@ pub enum Fetch<'a> { allow_non_fast_forward: bool, }, } + +impl Instruction<'_> { + pub fn operation(&self) -> Operation { + match self { + Instruction::Push(_) => Operation::Push, + Instruction::Fetch(_) => Operation::Fetch, + } + } +} diff --git a/git-refspec/tests/parse/mod.rs b/git-refspec/tests/parse/mod.rs index b5b3bb1172..4948708e33 100644 --- a/git-refspec/tests/parse/mod.rs +++ b/git-refspec/tests/parse/mod.rs @@ -72,26 +72,35 @@ mod invalid { mod push { use crate::parse::assert_parse; - use git_refspec::{Mode, Operation}; + use git_refspec::{Instruction, Push}; #[test] #[ignore] fn colon_alone_is_for_pushing_matching_refs() { - assert_parse(":", Operation::Push, None, None, Mode::Normal); + assert_parse( + ":", + Instruction::Push(Push::AllMatchingBranches { + allow_non_fast_forward: false, + }), + ); } } } mod util { - use git_refspec::{Mode, Operation, RefSpecRef}; + use git_refspec::{Instruction, Operation, RefSpecRef}; + + // 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, op: Operation, _src: Option<&str>, _dest: Option<&str>, mode: Mode) { - let spec = try_parse(spec, op).expect("no error"); - assert_eq!(spec.mode(), mode); + pub fn assert_parse(spec: &str, expected: Instruction<'_>) { + let spec = try_parse(spec, expected.operation()).expect("no error"); + assert_eq!(spec.instruction(), expected) } } pub use util::*;