From 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:41:41 +0200 Subject: Merging upstream version 1.70.0+dfsg2. Signed-off-by: Daniel Baumann --- vendor/gix-refspec/src/spec.rs | 257 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 vendor/gix-refspec/src/spec.rs (limited to 'vendor/gix-refspec/src/spec.rs') diff --git a/vendor/gix-refspec/src/spec.rs b/vendor/gix-refspec/src/spec.rs new file mode 100644 index 000000000..9361ae682 --- /dev/null +++ b/vendor/gix-refspec/src/spec.rs @@ -0,0 +1,257 @@ +use bstr::{BStr, BString, ByteSlice}; + +use crate::{ + instruction::{Fetch, Push}, + parse::Operation, + types::Mode, + Instruction, RefSpec, RefSpecRef, +}; + +/// Conversion. Use the [RefSpecRef][RefSpec::to_ref()] type for more usage options. +impl RefSpec { + /// Return ourselves as reference type. + pub fn to_ref(&self) -> RefSpecRef<'_> { + RefSpecRef { + mode: self.mode, + op: self.op, + src: self.src.as_ref().map(|b| b.as_ref()), + dst: self.dst.as_ref().map(|b| b.as_ref()), + } + } + + /// Return true if the spec stats with a `+` and thus forces setting the reference. + pub fn allow_non_fast_forward(&self) -> bool { + matches!(self.mode, Mode::Force) + } +} + +mod impls { + use std::{ + cmp::Ordering, + hash::{Hash, Hasher}, + }; + + use crate::{RefSpec, RefSpecRef}; + + impl From> for RefSpec { + fn from(v: RefSpecRef<'_>) -> Self { + v.to_owned() + } + } + + impl Hash for RefSpec { + fn hash(&self, state: &mut H) { + self.to_ref().hash(state) + } + } + + impl Hash for RefSpecRef<'_> { + fn hash(&self, state: &mut H) { + self.instruction().hash(state) + } + } + + impl PartialEq for RefSpec { + fn eq(&self, other: &Self) -> bool { + self.to_ref().eq(&other.to_ref()) + } + } + + impl PartialEq for RefSpecRef<'_> { + fn eq(&self, other: &Self) -> bool { + self.instruction().eq(&other.instruction()) + } + } + + impl PartialOrd for RefSpecRef<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + self.instruction().partial_cmp(&other.instruction()) + } + } + + impl PartialOrd for RefSpec { + fn partial_cmp(&self, other: &Self) -> Option { + self.to_ref().partial_cmp(&other.to_ref()) + } + } + + impl Ord for RefSpecRef<'_> { + fn cmp(&self, other: &Self) -> Ordering { + self.instruction().cmp(&other.instruction()) + } + } + + impl Ord for RefSpec { + fn cmp(&self, other: &Self) -> Ordering { + self.to_ref().cmp(&other.to_ref()) + } + } +} + +/// Access +impl<'a> RefSpecRef<'a> { + /// Return the left-hand side of the spec, typically the source. + /// It takes many different forms so don't rely on this being a ref name. + /// + /// It's not present in case of deletions. + pub fn source(&self) -> Option<&BStr> { + self.src + } + + /// Return the right-hand side of the spec, typically the destination. + /// It takes many different forms so don't rely on this being a ref name. + /// + /// It's not present in case of source-only specs. + pub fn destination(&self) -> Option<&BStr> { + self.dst + } + + /// Always returns the remote side, whose actual side in the refspec depends on how it was parsed. + pub fn remote(&self) -> Option<&BStr> { + match self.op { + Operation::Push => self.dst, + Operation::Fetch => self.src, + } + } + + /// Always returns the local side, whose actual side in the refspec depends on how it was parsed. + pub fn local(&self) -> Option<&BStr> { + match self.op { + Operation::Push => self.src, + Operation::Fetch => self.dst, + } + } + + /// Derive the prefix from the [`source`][Self::source()] side of this spec if this is a fetch spec, + /// or the [`destination`][Self::destination()] side if it is a push spec, if it is possible to do so without ambiguity. + /// + /// This means it starts with `refs/`. Note that it won't contain more than two components, like `refs/heads/` + pub fn prefix(&self) -> Option<&BStr> { + if self.mode == Mode::Negative { + return None; + } + let source = match self.op { + Operation::Fetch => self.source(), + Operation::Push => self.destination(), + }?; + if source == "HEAD" { + return source.into(); + } + let suffix = source.strip_prefix(b"refs/")?; + let slash_pos = suffix.find_byte(b'/')?; + let prefix = source[..="refs/".len() + slash_pos].as_bstr(); + (!prefix.contains(&b'*')).then_some(prefix) + } + + /// As opposed to [`prefix()`][Self::prefix], if the latter is `None` it will expand to all possible prefixes and place them in `out`. + /// + /// Note that only the `source` side is considered. + pub fn expand_prefixes(&self, out: &mut Vec) { + match self.prefix() { + Some(prefix) => out.push(prefix.into()), + None => { + let source = match match self.op { + Operation::Fetch => self.source(), + Operation::Push => self.destination(), + } { + Some(source) => source, + None => return, + }; + if let Some(rest) = source.strip_prefix(b"refs/") { + if !rest.contains(&b'/') { + out.push(source.into()); + } + return; + } else if gix_hash::ObjectId::from_hex(source).is_ok() { + return; + } + expand_partial_name(source, |expanded| { + out.push(expanded.into()); + None::<()> + }); + } + } + } + + /// Transform the state of the refspec into an instruction making clear what to do with it. + pub fn instruction(&self) -> Instruction<'a> { + match self.op { + Operation::Fetch => match (self.mode, self.src, self.dst) { + (Mode::Normal | Mode::Force, Some(src), None) => Instruction::Fetch(Fetch::Only { src }), + (Mode::Normal | Mode::Force, Some(src), Some(dst)) => Instruction::Fetch(Fetch::AndUpdate { + src, + dst, + allow_non_fast_forward: matches!(self.mode, Mode::Force), + }), + (Mode::Negative, Some(src), None) => Instruction::Fetch(Fetch::Exclude { src }), + (mode, src, dest) => { + unreachable!( + "BUG: fetch instructions with {:?} {:?} {:?} are not possible", + mode, src, dest + ) + } + }, + Operation::Push => match (self.mode, self.src, self.dst) { + (Mode::Normal | Mode::Force, Some(src), None) => Instruction::Push(Push::Matching { + src, + dst: src, + allow_non_fast_forward: matches!(self.mode, Mode::Force), + }), + (Mode::Normal | Mode::Force, None, Some(dst)) => { + Instruction::Push(Push::Delete { ref_or_pattern: dst }) + } + (Mode::Normal | Mode::Force, None, None) => Instruction::Push(Push::AllMatchingBranches { + allow_non_fast_forward: matches!(self.mode, Mode::Force), + }), + (Mode::Normal | Mode::Force, Some(src), Some(dst)) => Instruction::Push(Push::Matching { + src, + dst, + allow_non_fast_forward: matches!(self.mode, Mode::Force), + }), + (mode, src, dest) => { + unreachable!( + "BUG: push instructions with {:?} {:?} {:?} are not possible", + 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), + dst: self.dst.map(ToOwned::to_owned), + } + } +} + +pub(crate) fn expand_partial_name(name: &BStr, mut cb: impl FnMut(&BStr) -> Option) -> Option { + use bstr::ByteVec; + let mut buf = BString::from(Vec::with_capacity(128)); + for (base, append_head) in [ + ("", false), + ("refs/", false), + ("refs/tags/", false), + ("refs/heads/", false), + ("refs/remotes/", false), + ("refs/remotes/", true), + ] { + buf.clear(); + buf.push_str(base); + buf.push_str(name); + if append_head { + buf.push_str("/HEAD"); + } + if let Some(res) = cb(buf.as_ref()) { + return Some(res); + } + } + None +} -- cgit v1.2.3