@@ -7,9 +7,9 @@ use std::{
77} ;
88
99use crate :: {
10- file:: { loose, loose:: iter:: SortedLoosePaths , path_to_name } ,
10+ file:: { loose, loose:: iter:: SortedLoosePaths } ,
1111 store_impl:: { file, packed} ,
12- BString , FullName , Namespace , Reference ,
12+ BStr , FullName , Namespace , Reference ,
1313} ;
1414
1515/// An iterator stepping through sorted input of loose references and packed references, preferring loose refs over otherwise
@@ -195,12 +195,18 @@ impl Platform<'_> {
195195 self . store . iter_packed ( self . packed . as_ref ( ) . map ( |b| & * * * b) )
196196 }
197197
198- /// As [`iter(…)`][file::Store::iter()], but filters by `prefix`, i.e. "refs/heads".
198+ /// As [`iter(…)`](file::Store::iter()), but filters by `prefix`, i.e. "refs/heads/" or
199+ /// "refs/heads/feature-".
199200 ///
200- /// Please note that "refs/heads" or "refs\\heads" is equivalent to "refs/heads/"
201- pub fn prefixed ( & self , prefix : & Path ) -> std:: io:: Result < LooseThenPacked < ' _ , ' _ > > {
201+ /// Note that if a prefix isn't using a trailing `/`, like in `refs/heads/foo`, it will effectively
202+ /// start the traversal in the parent directory, e.g. `refs/heads/` and list everything inside that
203+ /// starts with `foo`, like `refs/heads/foo` and `refs/heads/foobar`.
204+ ///
205+ /// Prefixes are relative paths with slash-separated components.
206+ // TODO: use `RelativePath` type instead (see #1921), or a trait that helps convert into it.
207+ pub fn prefixed < ' a > ( & self , prefix : impl Into < & ' a BStr > ) -> std:: io:: Result < LooseThenPacked < ' _ , ' _ > > {
202208 self . store
203- . iter_prefixed_packed ( prefix, self . packed . as_ref ( ) . map ( |b| & * * * b) )
209+ . iter_prefixed_packed ( prefix. into ( ) , self . packed . as_ref ( ) . map ( |b| & * * * b) )
204210 }
205211}
206212
@@ -228,7 +234,7 @@ pub(crate) enum IterInfo<'a> {
228234 BaseAndIterRoot {
229235 base : & ' a Path ,
230236 iter_root : PathBuf ,
231- prefix : Cow < ' a , Path > ,
237+ prefix : PathBuf ,
232238 precompose_unicode : bool ,
233239 } ,
234240 PrefixAndBase {
@@ -239,25 +245,22 @@ pub(crate) enum IterInfo<'a> {
239245 ComputedIterationRoot {
240246 /// The root to iterate over
241247 iter_root : PathBuf ,
242- /// The top-level directory as boundary of all references, used to create their short-names after iteration
248+ /// The top-level directory as boundary of all references, used to create their short-names after iteration.
243249 base : & ' a Path ,
244- /// The original prefix
245- prefix : Cow < ' a , Path > ,
246- /// The remainder of the prefix that wasn't a valid path
247- remainder : Option < BString > ,
250+ /// The original prefix.
251+ prefix : Cow < ' a , BStr > ,
248252 /// If `true`, we will convert decomposed into precomposed unicode.
249253 precompose_unicode : bool ,
250254 } ,
251255}
252256
253257impl < ' a > IterInfo < ' a > {
254- fn prefix ( & self ) -> Option < & Path > {
258+ fn prefix ( & self ) -> Option < Cow < ' _ , BStr > > {
255259 match self {
256260 IterInfo :: Base { .. } => None ,
257- IterInfo :: PrefixAndBase { prefix, .. } => Some ( * prefix) ,
258- IterInfo :: ComputedIterationRoot { prefix, .. } | IterInfo :: BaseAndIterRoot { prefix, .. } => {
259- prefix. as_ref ( ) . into ( )
260- }
261+ IterInfo :: PrefixAndBase { prefix, .. } => Some ( gix_path:: into_bstr ( * prefix) ) ,
262+ IterInfo :: BaseAndIterRoot { prefix, .. } => Some ( gix_path:: into_bstr ( prefix. clone ( ) ) ) ,
263+ IterInfo :: ComputedIterationRoot { prefix, .. } => Some ( prefix. clone ( ) ) ,
261264 }
262265 }
263266
@@ -281,48 +284,42 @@ impl<'a> IterInfo<'a> {
281284 IterInfo :: ComputedIterationRoot {
282285 iter_root,
283286 base,
284- prefix : _,
285- remainder,
287+ prefix,
286288 precompose_unicode,
287- } => SortedLoosePaths :: at ( & iter_root, base. into ( ) , remainder , precompose_unicode) ,
289+ } => SortedLoosePaths :: at ( & iter_root, base. into ( ) , Some ( prefix . into_owned ( ) ) , precompose_unicode) ,
288290 }
289291 . peekable ( )
290292 }
291293
292- fn from_prefix ( base : & ' a Path , prefix : Cow < ' a , Path > , precompose_unicode : bool ) -> std:: io:: Result < Self > {
293- if prefix. is_absolute ( ) {
294+ fn from_prefix (
295+ base : & ' a Path ,
296+ prefix : impl Into < Cow < ' a , BStr > > ,
297+ precompose_unicode : bool ,
298+ ) -> std:: io:: Result < Self > {
299+ let prefix = prefix. into ( ) ;
300+ let prefix_path = gix_path:: from_bstr ( prefix. as_ref ( ) ) ;
301+ if prefix_path. is_absolute ( ) {
294302 return Err ( std:: io:: Error :: new (
295303 std:: io:: ErrorKind :: InvalidInput ,
296- "prefix must be a relative path, like 'refs/heads'" ,
304+ "prefix must be a relative path, like 'refs/heads/ '" ,
297305 ) ) ;
298306 }
299307 use std:: path:: Component :: * ;
300- if prefix . components ( ) . any ( |c| matches ! ( c, CurDir | ParentDir ) ) {
308+ if prefix_path . components ( ) . any ( |c| matches ! ( c, CurDir | ParentDir ) ) {
301309 return Err ( std:: io:: Error :: new (
302310 std:: io:: ErrorKind :: InvalidInput ,
303311 "Refusing to handle prefixes with relative path components" ,
304312 ) ) ;
305313 }
306- let iter_root = base. join ( prefix . as_ref ( ) ) ;
307- if iter_root . is_dir ( ) {
314+ let iter_root = base. join ( & prefix_path ) ;
315+ if prefix . ends_with ( b"/" ) {
308316 Ok ( IterInfo :: BaseAndIterRoot {
309317 base,
310318 iter_root,
311- prefix,
319+ prefix : prefix_path . into_owned ( ) ,
312320 precompose_unicode,
313321 } )
314322 } else {
315- let filename_prefix = iter_root
316- . file_name ( )
317- . map ( ToOwned :: to_owned)
318- . map ( |p| {
319- gix_path:: try_into_bstr ( PathBuf :: from ( p) )
320- . map ( std:: borrow:: Cow :: into_owned)
321- . map_err ( |_| {
322- std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidInput , "prefix contains ill-formed UTF-8" )
323- } )
324- } )
325- . transpose ( ) ?;
326323 let iter_root = iter_root
327324 . parent ( )
328325 . expect ( "a parent is always there unless empty" )
@@ -331,7 +328,6 @@ impl<'a> IterInfo<'a> {
331328 base,
332329 prefix,
333330 iter_root,
334- remainder : filename_prefix,
335331 precompose_unicode,
336332 } )
337333 }
@@ -374,30 +370,35 @@ impl file::Store {
374370 }
375371 }
376372
377- /// As [`iter(…)`][file::Store::iter()], but filters by `prefix`, i.e. "refs/heads".
373+ /// As [`iter(…)`](file::Store::iter()), but filters by `prefix`, i.e. `refs/heads/` or
374+ /// `refs/heads/feature-`.
375+ /// Note that if a prefix isn't using a trailing `/`, like in `refs/heads/foo`, it will effectively
376+ /// start the traversal in the parent directory, e.g. `refs/heads/` and list everything inside that
377+ /// starts with `foo`, like `refs/heads/foo` and `refs/heads/foobar`.
378378 ///
379- /// Please note that "refs/heads" or "refs\\heads" is equivalent to "refs/heads/"
380- pub fn iter_prefixed_packed < ' s , ' p > (
379+ /// Prefixes are relative paths with slash-separated components.
380+ // TODO: use `RelativePath` type instead (see #1921), or a trait that helps convert into it.
381+ pub fn iter_prefixed_packed < ' a , ' s , ' p > (
381382 & ' s self ,
382- prefix : & Path ,
383+ prefix : impl Into < & ' a BStr > ,
383384 packed : Option < & ' p packed:: Buffer > ,
384385 ) -> std:: io:: Result < LooseThenPacked < ' p , ' s > > {
386+ let prefix = prefix. into ( ) ;
385387 match self . namespace . as_ref ( ) {
386388 None => {
387- let git_dir_info = IterInfo :: from_prefix ( self . git_dir ( ) , prefix. into ( ) , self . precompose_unicode ) ?;
389+ let git_dir_info = IterInfo :: from_prefix ( self . git_dir ( ) , prefix, self . precompose_unicode ) ?;
388390 let common_dir_info = self
389391 . common_dir ( )
390- . map ( |base| IterInfo :: from_prefix ( base, prefix. into ( ) , self . precompose_unicode ) )
392+ . map ( |base| IterInfo :: from_prefix ( base, prefix, self . precompose_unicode ) )
391393 . transpose ( ) ?;
392394 self . iter_from_info ( git_dir_info, common_dir_info, packed)
393395 }
394396 Some ( namespace) => {
395397 let prefix = namespace. to_owned ( ) . into_namespaced_prefix ( prefix) ;
396- let git_dir_info =
397- IterInfo :: from_prefix ( self . git_dir ( ) , prefix. clone ( ) . into ( ) , self . precompose_unicode ) ?;
398+ let git_dir_info = IterInfo :: from_prefix ( self . git_dir ( ) , prefix. clone ( ) , self . precompose_unicode ) ?;
398399 let common_dir_info = self
399400 . common_dir ( )
400- . map ( |base| IterInfo :: from_prefix ( base, prefix. into ( ) , self . precompose_unicode ) )
401+ . map ( |base| IterInfo :: from_prefix ( base, prefix, self . precompose_unicode ) )
401402 . transpose ( ) ?;
402403 self . iter_from_info ( git_dir_info, common_dir_info, packed)
403404 }
@@ -416,7 +417,7 @@ impl file::Store {
416417 iter_packed : match packed {
417418 Some ( packed) => Some (
418419 match git_dir_info. prefix ( ) {
419- Some ( prefix) => packed. iter_prefixed ( path_to_name ( prefix) . into_owned ( ) ) ,
420+ Some ( prefix) => packed. iter_prefixed ( prefix. into_owned ( ) ) ,
420421 None => packed. iter ( ) ,
421422 }
422423 . map_err ( |err| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , err) ) ?
0 commit comments