Showing the changes to git-ref/src/store (2 of 3 files). Show all
Unified · Split
| @@ -141,18 +141,30 @@ impl file::Store { · 140 unmodified lines | ||
| 141 | 141 | /// |
| 142 | 142 | /// Otherwise it's similar to [`loose_iter()`][file::Store::loose_iter()]. |
| 143 | 143 | pub fn loose_iter_prefixed(&self, prefix: impl AsRef<Path>) -> std::io::Result<Loose<'_>> { |
| 144 | − let prefix = prefix.as_ref(); | |
| 144 | + let prefix = self.validate_prefix(prefix.as_ref())?; | |
| 145 | + Ok(Loose::at_root(self, self.base.join(prefix), self.base.clone())) | |
| 146 | + } | |
| 147 | + | |
| 148 | + pub(in crate::store::file) fn refs_dir(&self) -> PathBuf { | |
| 149 | + self.base.join("refs") | |
| 150 | + } | |
| 151 | + pub(in crate::store::file) fn validate_prefix<'a>(&self, prefix: &'a Path) -> std::io::Result<&'a Path> { | |
| 145 | 152 | if prefix.is_absolute() { |
| 146 | 153 | return Err(std::io::Error::new( |
| 147 | 154 | std::io::ErrorKind::InvalidInput, |
| 148 | 155 | "prefix must be a relative path, like 'refs/heads'", |
| 149 | 156 | )); |
| 150 | 157 | } |
| 151 | − Ok(Loose::at_root(self, self.base.join(prefix), self.base.clone())) | |
| 152 | − } | |
| 153 | − | |
| 154 | − pub(in crate::store::file) fn refs_dir(&self) -> PathBuf { | |
| 155 | − self.base.join("refs") | |
| 158 | + for component in prefix.components() { | |
| 159 | + use std::path::Component::*; | |
| 160 | + if matches!(component, CurDir | ParentDir) { | |
| 161 | + return Err(std::io::Error::new( | |
| 162 | + std::io::ErrorKind::InvalidInput, | |
| 163 | + "Refusing to handle prefixes with relative path components", | |
| 164 | + )); | |
| 165 | + } | |
| 166 | + } | |
| 167 | + Ok(prefix) | |
| 156 | 168 | } |
| 157 | 169 | } |
| 158 | 170 | |
| @@ -3,7 +3,13 @@ use crate::{ · 2 unmodified lines | ||
| 3 | 3 | store::{file, packed}, |
| 4 | 4 | }; |
| 5 | 5 | use bstr::BString; |
| 6 | −use std::{cmp::Ordering, convert::TryInto, io::Read, iter::Peekable, path::PathBuf}; | |
| 6 | +use std::{ | |
| 7 | + cmp::Ordering, | |
| 8 | + convert::TryInto, | |
| 9 | + io::Read, | |
| 10 | + iter::Peekable, | |
| 11 | + path::{Path, PathBuf}, | |
| 12 | +}; | |
| 7 | 13 | |
| 8 | 14 | /// An iterator stepping through sorted input of loose references and packed references, preferring loose refs over otherwise |
| 9 | 15 | /// equivalent packed references. |
| @@ -141,6 +147,32 @@ impl file::Store { · 131 unmodified lines | ||
| 141 | 147 | buf: Vec::new(), |
| 142 | 148 | }) |
| 143 | 149 | } |
| 150 | + | |
| 151 | + /// As [`iter(…)`][file::Store::iter()], but filters by `prefix`, i.e. "refs/heads". | |
| 152 | + /// | |
| 153 | + /// Please note that "refs/heads` or "refs\\heads" is equivalent to "refs/heads/" | |
| 154 | + pub fn iter_prefixed<'p, 's>( | |
| 155 | + &'s self, | |
| 156 | + packed: &'p packed::Buffer, | |
| 157 | + prefix: impl AsRef<Path>, | |
| 158 | + ) -> std::io::Result<LooseThenPacked<'p, 's>> { | |
| 159 | + let prefix = self.validate_prefix(prefix.as_ref())?; | |
| 160 | + let packed_prefix = prefix.to_raw_bytes(); | |
| 161 | + #[cfg(windows)] | |
| 162 | + let packed_prefix = packed_prefix.replace(b"\\", b"/"); | |
| 163 | + #[cfg(not(windows))] | |
| 164 | + let packed_prefix = packed_prefix.into_owned(); | |
| 165 | + Ok(LooseThenPacked { | |
| 166 | + parent: self, | |
| 167 | + packed: packed | |
| 168 | + .iter_prefixed(packed_prefix) | |
| 169 | + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))? | |
| 170 | + .peekable(), | |
| 171 | + loose: file::loose::iter::SortedLoosePaths::at_root_with_names(self.base.join(prefix), self.base.clone()) | |
| 172 | + .peekable(), | |
| 173 | + buf: Vec::new(), | |
| 174 | + }) | |
| 175 | + } | |
| 144 | 176 | } |
| 145 | 177 | |
| 146 | 178 | mod error { |
| @@ -173,3 +205,4 @@ mod error { · 26 unmodified lines | ||
| 173 | 205 | } |
| 174 | 206 | } |
| 175 | 207 | pub use error::Error; |
| 208 | +use os_str_bytes::OsStrBytes; | |