History

[ref] prefix iteration for all references

Sebastian Thiel committed 5 years ago

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

Showing the changes to git-ref (3 of 3 files). Show all

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
141 ///141 ///
142 /// Otherwise it's similar to [`loose_iter()`][file::Store::loose_iter()].142 /// Otherwise it's similar to [`loose_iter()`][file::Store::loose_iter()].
143 pub fn loose_iter_prefixed(&self, prefix: impl AsRef<Path>) -> std::io::Result<Loose<'_>> {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 if prefix.is_absolute() {152 if prefix.is_absolute() {
146 return Err(std::io::Error::new(153 return Err(std::io::Error::new(
147 std::io::ErrorKind::InvalidInput,154 std::io::ErrorKind::InvalidInput,
148 "prefix must be a relative path, like 'refs/heads'",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()))158 for component in prefix.components() {
152 }159 use std::path::Component::*;
153 160 if matches!(component, CurDir | ParentDir) {
154 pub(in crate::store::file) fn refs_dir(&self) -> PathBuf {161 return Err(std::io::Error::new(
155 self.base.join("refs")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
git-ref/src/store/file/overlay.rs+34−1
@@ -3,7 +3,13 @@ use crate::{ · 2 unmodified lines
3 store::{file, packed},3 store::{file, packed},
4};4};
5use bstr::BString;5use bstr::BString;
6use std::{cmp::Ordering, convert::TryInto, io::Read, iter::Peekable, path::PathBuf};6use std::{
7 cmp::Ordering,
8 convert::TryInto,
9 io::Read,
10 iter::Peekable,
11 path::{Path, PathBuf},
12};
7 13
8/// An iterator stepping through sorted input of loose references and packed references, preferring loose refs over otherwise14/// An iterator stepping through sorted input of loose references and packed references, preferring loose refs over otherwise
9/// equivalent packed references.15/// equivalent packed references.
@@ -141,6 +147,32 @@ impl file::Store { · 131 unmodified lines
141 buf: Vec::new(),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
146mod error {178mod error {
@@ -173,3 +205,4 @@ mod error { · 26 unmodified lines
173 }205 }
174}206}
175pub use error::Error;207pub use error::Error;
208use 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
121}121}
122 122
123#[test]123#[test]
124fn overlay_iter() {124fn overlay_iter() -> crate::Result {
125 use git_ref::mutable::Target::*;125 use git_ref::mutable::Target::*;
126 126
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")?;
128 let ref_names = store128 let ref_names = store
129 .iter(&store.packed().unwrap().expect("packed-refs"))129 .iter(&store.packed()?.expect("packed-refs"))?
130 .unwrap()
131 .map(|r| {130 .map(|r| {
132 r.map(|r| {131 r.map(|r| {
133 (132 (
@@ -137,8 +136,7 @@ fn overlay_iter() { · 3 unmodified lines
137 )136 )
138 })137 })
139 })138 })
140 .collect::<Result<Vec<_>, _>>()139 .collect::<Result<Vec<_>, _>>()?;
141 .unwrap();
142 let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03");140 let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03");
143 let c2 = hex_to_id("9902e3c3e8f0c569b4ab295ddf473e6de763e1e7");141 let c2 = hex_to_id("9902e3c3e8f0c569b4ab295ddf473e6de763e1e7");
144 assert_eq!(142 assert_eq!(
@@ -148,7 +146,7 @@ fn overlay_iter() { · 3 unmodified lines
148 ("refs/heads/newer-as-loose".into(), Peeled(c2), false),146 ("refs/heads/newer-as-loose".into(), Peeled(c2), false),
149 (147 (
150 "refs/remotes/origin/HEAD".into(),148 "refs/remotes/origin/HEAD".into(),
151 Symbolic("refs/remotes/origin/main".try_into().unwrap()),149 Symbolic("refs/remotes/origin/main".try_into()?),
152 false150 false
153 ),151 ),
154 ("refs/remotes/origin/main".into(), Peeled(c1), true),152 ("refs/remotes/origin/main".into(), Peeled(c1), true),
@@ -159,4 +157,51 @@ fn overlay_iter() { · 4 unmodified lines
159 )157 )
160 ]158 ]
161 );159 );
160 Ok(())
161}
162
163#[test]
164fn 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]
179fn 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 );
162}207}