History

[ref] prefix iteration for all references

Sebastian Thiel committed 5 years ago

Commit 228ca001 parent aa6052a3 files · +104 −14Browse files

Unified · Split

3 files
  1. git-ref/src/store/file/loose/iter.rs +18 −6
  2. git-ref/src/store/file/overlay.rs +34 −1
  3. git-ref/tests/file/store/iter.rs +52 −7
git-ref/src/store/file/loose/iter.rs+18−6
@@ -141,18 +141,30 @@ impl file::Store { · 140 unmodified lines
141141 ///
142142 /// Otherwise it's similar to [`loose_iter()`][file::Store::loose_iter()].
143143 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> {
145152 if prefix.is_absolute() {
146153 return Err(std::io::Error::new(
147154 std::io::ErrorKind::InvalidInput,
148155 "prefix must be a relative path, like 'refs/heads'",
149156 ));
150157 }
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)
156168 }
157169 }
158170
git-ref/src/store/file/overlay.rs+34−1
@@ -3,7 +3,13 @@ use crate::{ · 2 unmodified lines
33 store::{file, packed},
44 };
55 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+};
713
814 /// An iterator stepping through sorted input of loose references and packed references, preferring loose refs over otherwise
915 /// equivalent packed references.
@@ -141,6 +147,32 @@ impl file::Store { · 131 unmodified lines
141147 buf: Vec::new(),
142148 })
143149 }
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+ }
144176 }
145177
146178 mod error {
@@ -173,3 +205,4 @@ mod error { · 26 unmodified lines
173205 }
174206 }
175207 pub use error::Error;
208+use os_str_bytes::OsStrBytes;
git-ref/tests/file/store/iter.rs+52−7
@@ -121,13 +121,12 @@ fn loose_iter_with_prefix() -> crate::Result { · 120 unmodified lines
121121 }
122122
123123 #[test]
124−fn overlay_iter() {
124+fn overlay_iter() -> crate::Result {
125125 use git_ref::mutable::Target::*;
126126
127− let store = store_at("make_packed_ref_repository_for_overlay.sh").unwrap();
127+ let store = store_at("make_packed_ref_repository_for_overlay.sh")?;
128128 let ref_names = store
129− .iter(&store.packed().unwrap().expect("packed-refs"))
130− .unwrap()
129+ .iter(&store.packed()?.expect("packed-refs"))?
131130 .map(|r| {
132131 r.map(|r| {
133132 (
@@ -137,8 +136,7 @@ fn overlay_iter() { · 3 unmodified lines
137136 )
138137 })
139138 })
140− .collect::<Result<Vec<_>, _>>()
141− .unwrap();
139+ .collect::<Result<Vec<_>, _>>()?;
142140 let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03");
143141 let c2 = hex_to_id("9902e3c3e8f0c569b4ab295ddf473e6de763e1e7");
144142 assert_eq!(
@@ -148,7 +146,7 @@ fn overlay_iter() { · 3 unmodified lines
148146 ("refs/heads/newer-as-loose".into(), Peeled(c2), false),
149147 (
150148 "refs/remotes/origin/HEAD".into(),
151− Symbolic("refs/remotes/origin/main".try_into().unwrap()),
149+ Symbolic("refs/remotes/origin/main".try_into()?),
152150 false
153151 ),
154152 ("refs/remotes/origin/main".into(), Peeled(c1), true),
@@ -159,4 +157,51 @@ fn overlay_iter() { · 4 unmodified lines
159157 )
160158 ]
161159 );
160+ Ok(())
161+}
162+
163+#[test]
164+fn overlay_iter_with_prefix_wont_allow_absolute_paths() -> crate::Result {
165+ let store = store_with_packed_refs()?;
166+ #[cfg(not(windows))]
167+ let abs_path = "/hello";
168+ #[cfg(windows)]
169+ let abs_path = "c:\\hello";
170+
171+ match store.iter_prefixed(&store.packed()?.expect("pacekd-refs"), abs_path) {
172+ Ok(_) => unreachable!("absolute paths aren't allowed"),
173+ Err(err) => assert_eq!(err.to_string(), "prefix must be a relative path, like 'refs/heads'"),
174+ }
175+ Ok(())
176+}
177+
178+#[test]
179+fn overlay_prefixed_iter() {
180+ use git_ref::mutable::Target::*;
181+
182+ let store = store_at("make_packed_ref_repository_for_overlay.sh").unwrap();
183+ let packed = store.packed().unwrap().expect("packed-refs");
184+ let ref_names = store
185+ .iter_prefixed(&packed, "refs/heads")
186+ .unwrap()
187+ .map(|r| {
188+ r.map(|r| {
189+ (
190+ r.name().expect("valid names only").into_inner(),
191+ r.target(),
192+ r.is_packed(),
193+ )
194+ })
195+ })
196+ .collect::<Result<Vec<_>, _>>()
197+ .unwrap();
198+ let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03");
199+ let c2 = hex_to_id("9902e3c3e8f0c569b4ab295ddf473e6de763e1e7");
200+ assert_eq!(
201+ ref_names,
202+ vec![
203+ (b"refs/heads/main".as_bstr().to_owned(), Peeled(c1), true),
204+ ("refs/heads/newer-as-loose".into(), Peeled(c2), false),
205+ ]
206+ );
162207 }