@@ -7,9 +7,9 @@ use std::{
7
7
} ;
8
8
9
9
use crate :: {
10
- file:: { loose, loose:: iter:: SortedLoosePaths , path_to_name } ,
10
+ file:: { loose, loose:: iter:: SortedLoosePaths } ,
11
11
store_impl:: { file, packed} ,
12
- BString , FullName , Namespace , Reference ,
12
+ BStr , FullName , Namespace , Reference ,
13
13
} ;
14
14
15
15
/// An iterator stepping through sorted input of loose references and packed references, preferring loose refs over otherwise
@@ -195,12 +195,18 @@ impl Platform<'_> {
195
195
self . store . iter_packed ( self . packed . as_ref ( ) . map ( |b| & * * * b) )
196
196
}
197
197
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-".
199
200
///
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 < ' _ , ' _ > > {
202
208
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) )
204
210
}
205
211
}
206
212
@@ -228,7 +234,7 @@ pub(crate) enum IterInfo<'a> {
228
234
BaseAndIterRoot {
229
235
base : & ' a Path ,
230
236
iter_root : PathBuf ,
231
- prefix : Cow < ' a , Path > ,
237
+ prefix : PathBuf ,
232
238
precompose_unicode : bool ,
233
239
} ,
234
240
PrefixAndBase {
@@ -239,25 +245,22 @@ pub(crate) enum IterInfo<'a> {
239
245
ComputedIterationRoot {
240
246
/// The root to iterate over
241
247
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.
243
249
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 > ,
248
252
/// If `true`, we will convert decomposed into precomposed unicode.
249
253
precompose_unicode : bool ,
250
254
} ,
251
255
}
252
256
253
257
impl < ' a > IterInfo < ' a > {
254
- fn prefix ( & self ) -> Option < & Path > {
258
+ fn prefix ( & self ) -> Option < Cow < ' _ , BStr > > {
255
259
match self {
256
260
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 ( ) ) ,
261
264
}
262
265
}
263
266
@@ -281,48 +284,42 @@ impl<'a> IterInfo<'a> {
281
284
IterInfo :: ComputedIterationRoot {
282
285
iter_root,
283
286
base,
284
- prefix : _,
285
- remainder,
287
+ prefix,
286
288
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) ,
288
290
}
289
291
. peekable ( )
290
292
}
291
293
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 ( ) {
294
302
return Err ( std:: io:: Error :: new (
295
303
std:: io:: ErrorKind :: InvalidInput ,
296
- "prefix must be a relative path, like 'refs/heads'" ,
304
+ "prefix must be a relative path, like 'refs/heads/ '" ,
297
305
) ) ;
298
306
}
299
307
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 ) ) {
301
309
return Err ( std:: io:: Error :: new (
302
310
std:: io:: ErrorKind :: InvalidInput ,
303
311
"Refusing to handle prefixes with relative path components" ,
304
312
) ) ;
305
313
}
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"/" ) {
308
316
Ok ( IterInfo :: BaseAndIterRoot {
309
317
base,
310
318
iter_root,
311
- prefix,
319
+ prefix : prefix_path . into_owned ( ) ,
312
320
precompose_unicode,
313
321
} )
314
322
} 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 ( ) ?;
326
323
let iter_root = iter_root
327
324
. parent ( )
328
325
. expect ( "a parent is always there unless empty" )
@@ -331,7 +328,6 @@ impl<'a> IterInfo<'a> {
331
328
base,
332
329
prefix,
333
330
iter_root,
334
- remainder : filename_prefix,
335
331
precompose_unicode,
336
332
} )
337
333
}
@@ -374,30 +370,35 @@ impl file::Store {
374
370
}
375
371
}
376
372
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`.
378
378
///
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 > (
381
382
& ' s self ,
382
- prefix : & Path ,
383
+ prefix : impl Into < & ' a BStr > ,
383
384
packed : Option < & ' p packed:: Buffer > ,
384
385
) -> std:: io:: Result < LooseThenPacked < ' p , ' s > > {
386
+ let prefix = prefix. into ( ) ;
385
387
match self . namespace . as_ref ( ) {
386
388
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 ) ?;
388
390
let common_dir_info = self
389
391
. 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 ) )
391
393
. transpose ( ) ?;
392
394
self . iter_from_info ( git_dir_info, common_dir_info, packed)
393
395
}
394
396
Some ( namespace) => {
395
397
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 ) ?;
398
399
let common_dir_info = self
399
400
. 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 ) )
401
402
. transpose ( ) ?;
402
403
self . iter_from_info ( git_dir_info, common_dir_info, packed)
403
404
}
@@ -416,7 +417,7 @@ impl file::Store {
416
417
iter_packed : match packed {
417
418
Some ( packed) => Some (
418
419
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 ( ) ) ,
420
421
None => packed. iter ( ) ,
421
422
}
422
423
. map_err ( |err| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , err) ) ?
0 commit comments