From 3ca6811418275c4ebede0993b9741e804e81094b Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Sun, 6 Apr 2025 22:21:43 +0200 Subject: [PATCH 1/5] Handle trailing slash in ref list prefix filtering Previously, `refs/heads/foo/bar` would be listed when running `repo.references()?.prefixed("refs/heads/b")`. The code identified that the last component was not a directory and started to match it as a filename prefix for all files in all recursive directories, effectively matching `refs/heads/**/b*`. This commit fixes that bug but also allows to use a trailing `/` in the prefix, allowing to filter for `refs/heads/foo/` and not get `refs/heads/foo-bar` as a result. Fixes #1934. --- gix-negotiate/tests/baseline/mod.rs | 2 +- gix-ref/src/namespace.rs | 9 +- gix-ref/src/store/file/loose/iter.rs | 38 +++---- gix-ref/src/store/file/mod.rs | 12 +- gix-ref/src/store/file/overlay_iter.rs | 76 +++++-------- ...make_packed_ref_repository_for_overlay.tar | Bin 64000 -> 70656 bytes .../make_ref_repository.tar | Bin 78848 -> 86528 bytes .../make_packed_ref_repository_for_overlay.sh | 3 + gix-ref/tests/fixtures/make_ref_repository.sh | 5 + gix-ref/tests/refs/file/store/iter.rs | 107 +++++++++++++++--- .../create_or_update/mod.rs | 2 +- gix-ref/tests/refs/file/worktree.rs | 5 +- gix-ref/tests/refs/namespace.rs | 10 +- gix/src/open/repository.rs | 14 +-- gix/src/reference/iter.rs | 28 +++-- gix/tests/gix/repository/reference.rs | 9 +- 16 files changed, 197 insertions(+), 123 deletions(-) diff --git a/gix-negotiate/tests/baseline/mod.rs b/gix-negotiate/tests/baseline/mod.rs index aefbd313f8b..e2736ae7cff 100644 --- a/gix-negotiate/tests/baseline/mod.rs +++ b/gix-negotiate/tests/baseline/mod.rs @@ -71,7 +71,7 @@ fn run() -> crate::Result { // } for tip in lookup_names(&["HEAD"]).into_iter().chain( refs.iter()? - .prefixed("refs/heads".as_ref())? + .prefixed(b"refs/heads".as_bstr().into())? .filter_map(Result::ok) .map(|r| r.target.into_id()), ) { diff --git a/gix-ref/src/namespace.rs b/gix-ref/src/namespace.rs index 1483b1c47e8..cba0b460c73 100644 --- a/gix-ref/src/namespace.rs +++ b/gix-ref/src/namespace.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::{borrow::Cow, path::Path}; use gix_object::bstr::{BStr, BString, ByteSlice, ByteVec}; @@ -18,10 +18,9 @@ impl Namespace { gix_path::from_byte_slice(&self.0) } /// Append the given `prefix` to this namespace so it becomes usable for prefixed iteration. - pub fn into_namespaced_prefix(mut self, prefix: &Path) -> PathBuf { - let prefix = gix_path::into_bstr(prefix); - self.0.push_str(prefix.as_ref()); - gix_path::to_native_path_on_windows(self.0).into_owned() + pub fn into_namespaced_prefix(mut self, prefix: &BStr) -> Cow<'_, BStr> { + self.0.push_str(prefix); + gix_path::to_unix_separators_on_windows(self.0) } pub(crate) fn into_namespaced_name(mut self, name: &FullNameRef) -> FullName { self.0.push_str(name.as_bstr()); diff --git a/gix-ref/src/store/file/loose/iter.rs b/gix-ref/src/store/file/loose/iter.rs index 0bfbbd91587..5be0bb2cddd 100644 --- a/gix-ref/src/store/file/loose/iter.rs +++ b/gix-ref/src/store/file/loose/iter.rs @@ -1,22 +1,26 @@ -use std::path::{Path, PathBuf}; +use std::{ + borrow::Cow, + path::{Path, PathBuf}, +}; use gix_features::fs::walkdir::DirEntryIter; use gix_object::bstr::ByteSlice; -use crate::{file::iter::LooseThenPacked, store_impl::file, BString, FullName}; +use crate::{file::iter::LooseThenPacked, store_impl::file, BStr, BString, FullName}; /// An iterator over all valid loose reference paths as seen from a particular base directory. pub(in crate::store_impl::file) struct SortedLoosePaths { pub(crate) base: PathBuf, - filename_prefix: Option, + /// A optional prefix to match against if the prefix is not the same as the `file_walk` path. + prefix: Option, file_walk: Option, } impl SortedLoosePaths { - pub fn at(path: &Path, base: PathBuf, filename_prefix: Option, precompose_unicode: bool) -> Self { + pub fn at(path: &Path, base: PathBuf, prefix: Option, precompose_unicode: bool) -> Self { SortedLoosePaths { base, - filename_prefix, + prefix, file_walk: path.is_dir().then(|| { // serial iteration as we expect most refs in packed-refs anyway. gix_features::fs::walkdir_sorted_new( @@ -41,23 +45,9 @@ impl Iterator for SortedLoosePaths { continue; } let full_path = entry.path().into_owned(); - if let Some((prefix, name)) = self - .filename_prefix - .as_deref() - .and_then(|prefix| full_path.file_name().map(|name| (prefix, name))) - { - match gix_path::os_str_into_bstr(name) { - Ok(name) => { - if !name.starts_with(prefix) { - continue; - } - } - Err(_) => continue, // TODO: silently skipping ill-formed UTF-8 on windows - maybe this can be better? - } - } let full_name = full_path .strip_prefix(&self.base) - .expect("prefix-stripping cannot fail as prefix is our root"); + .expect("prefix-stripping cannot fail as base is within our root"); let full_name = match gix_path::try_into_bstr(full_name) { Ok(name) => { let name = gix_path::to_unix_separators_on_windows(name); @@ -65,7 +55,11 @@ impl Iterator for SortedLoosePaths { } Err(_) => continue, // TODO: silently skipping ill-formed UTF-8 on windows here, maybe there are better ways? }; - + if let Some(prefix) = &self.prefix { + if !full_name.starts_with(prefix) { + continue; + } + } if gix_validate::reference::name_partial(full_name.as_bstr()).is_ok() { let name = FullName(full_name); return Some(Ok((full_path, name))); @@ -93,7 +87,7 @@ impl file::Store { /// Return an iterator over all loose references that start with the given `prefix`. /// /// Otherwise it's similar to [`loose_iter()`][file::Store::loose_iter()]. - pub fn loose_iter_prefixed(&self, prefix: &Path) -> std::io::Result> { + pub fn loose_iter_prefixed(&self, prefix: Cow<'_, BStr>) -> std::io::Result> { self.iter_prefixed_packed(prefix, None) } } diff --git a/gix-ref/src/store/file/mod.rs b/gix-ref/src/store/file/mod.rs index 1f8321f4354..3c7d20e8c94 100644 --- a/gix-ref/src/store/file/mod.rs +++ b/gix-ref/src/store/file/mod.rs @@ -1,9 +1,6 @@ -use std::{ - borrow::Cow, - path::{Path, PathBuf}, -}; +use std::path::PathBuf; -use crate::{bstr::BStr, store::WriteReflog, Namespace}; +use crate::{store::WriteReflog, Namespace}; /// A store for reference which uses plain files. /// @@ -92,11 +89,6 @@ pub struct Transaction<'s, 'p> { packed_refs: transaction::PackedRefs<'p>, } -pub(in crate::store_impl::file) fn path_to_name<'a>(path: impl Into>) -> Cow<'a, BStr> { - let path = gix_path::into_bstr(path.into()); - gix_path::to_unix_separators_on_windows(path) -} - /// pub mod loose; mod overlay_iter; diff --git a/gix-ref/src/store/file/overlay_iter.rs b/gix-ref/src/store/file/overlay_iter.rs index 0e39e5af05f..914035bea3d 100644 --- a/gix-ref/src/store/file/overlay_iter.rs +++ b/gix-ref/src/store/file/overlay_iter.rs @@ -7,9 +7,9 @@ use std::{ }; use crate::{ - file::{loose, loose::iter::SortedLoosePaths, path_to_name}, + file::{loose, loose::iter::SortedLoosePaths}, store_impl::{file, packed}, - BString, FullName, Namespace, Reference, + BStr, FullName, Namespace, Reference, }; /// An iterator stepping through sorted input of loose references and packed references, preferring loose refs over otherwise @@ -195,10 +195,9 @@ impl Platform<'_> { self.store.iter_packed(self.packed.as_ref().map(|b| &***b)) } - /// As [`iter(…)`][file::Store::iter()], but filters by `prefix`, i.e. "refs/heads". - /// - /// Please note that "refs/heads" or "refs\\heads" is equivalent to "refs/heads/" - pub fn prefixed(&self, prefix: &Path) -> std::io::Result> { + /// As [`iter(…)`][file::Store::iter()], but filters by `prefix`, i.e. "refs/heads/" or + /// "refs/heads/feature-". + pub fn prefixed(&self, prefix: Cow<'_, BStr>) -> std::io::Result> { self.store .iter_prefixed_packed(prefix, self.packed.as_ref().map(|b| &***b)) } @@ -242,22 +241,19 @@ pub(crate) enum IterInfo<'a> { /// The top-level directory as boundary of all references, used to create their short-names after iteration base: &'a Path, /// The original prefix - prefix: Cow<'a, Path>, - /// The remainder of the prefix that wasn't a valid path - remainder: Option, + prefix: Cow<'a, BStr>, /// If `true`, we will convert decomposed into precomposed unicode. precompose_unicode: bool, }, } impl<'a> IterInfo<'a> { - fn prefix(&self) -> Option<&Path> { + fn prefix(&self) -> Option> { match self { IterInfo::Base { .. } => None, - IterInfo::PrefixAndBase { prefix, .. } => Some(*prefix), - IterInfo::ComputedIterationRoot { prefix, .. } | IterInfo::BaseAndIterRoot { prefix, .. } => { - prefix.as_ref().into() - } + IterInfo::PrefixAndBase { prefix, .. } => Some(gix_path::into_bstr(*prefix)), + IterInfo::BaseAndIterRoot { prefix, .. } => Some(gix_path::into_bstr(prefix.clone())), + IterInfo::ComputedIterationRoot { prefix, .. } => Some(prefix.clone()), } } @@ -281,48 +277,37 @@ impl<'a> IterInfo<'a> { IterInfo::ComputedIterationRoot { iter_root, base, - prefix: _, - remainder, + prefix, precompose_unicode, - } => SortedLoosePaths::at(&iter_root, base.into(), remainder, precompose_unicode), + } => SortedLoosePaths::at(&iter_root, base.into(), Some(prefix.into_owned()), precompose_unicode), } .peekable() } - fn from_prefix(base: &'a Path, prefix: Cow<'a, Path>, precompose_unicode: bool) -> std::io::Result { - if prefix.is_absolute() { + fn from_prefix(base: &'a Path, prefix: Cow<'a, BStr>, precompose_unicode: bool) -> std::io::Result { + let prefix_path = gix_path::from_bstring(prefix.as_ref()); + if prefix_path.is_absolute() { return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, - "prefix must be a relative path, like 'refs/heads'", + "prefix must be a relative path, like 'refs/heads/'", )); } use std::path::Component::*; - if prefix.components().any(|c| matches!(c, CurDir | ParentDir)) { + if prefix_path.components().any(|c| matches!(c, CurDir | ParentDir)) { return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, "Refusing to handle prefixes with relative path components", )); } - let iter_root = base.join(prefix.as_ref()); - if iter_root.is_dir() { + let iter_root = base.join(&prefix_path); + if prefix.ends_with(b"/") { Ok(IterInfo::BaseAndIterRoot { base, iter_root, - prefix, + prefix: prefix_path.into(), precompose_unicode, }) } else { - let filename_prefix = iter_root - .file_name() - .map(ToOwned::to_owned) - .map(|p| { - gix_path::try_into_bstr(PathBuf::from(p)) - .map(std::borrow::Cow::into_owned) - .map_err(|_| { - std::io::Error::new(std::io::ErrorKind::InvalidInput, "prefix contains ill-formed UTF-8") - }) - }) - .transpose()?; let iter_root = iter_root .parent() .expect("a parent is always there unless empty") @@ -331,7 +316,6 @@ impl<'a> IterInfo<'a> { base, prefix, iter_root, - remainder: filename_prefix, precompose_unicode, }) } @@ -374,30 +358,28 @@ impl file::Store { } } - /// As [`iter(…)`][file::Store::iter()], but filters by `prefix`, i.e. "refs/heads". - /// - /// Please note that "refs/heads" or "refs\\heads" is equivalent to "refs/heads/" + /// As [`iter(…)`][file::Store::iter()], but filters by `prefix`, i.e. "refs/heads/" or + /// "refs/heads/feature-". pub fn iter_prefixed_packed<'s, 'p>( &'s self, - prefix: &Path, + prefix: Cow<'_, BStr>, packed: Option<&'p packed::Buffer>, ) -> std::io::Result> { match self.namespace.as_ref() { None => { - let git_dir_info = IterInfo::from_prefix(self.git_dir(), prefix.into(), self.precompose_unicode)?; + let git_dir_info = IterInfo::from_prefix(self.git_dir(), prefix.clone(), self.precompose_unicode)?; let common_dir_info = self .common_dir() - .map(|base| IterInfo::from_prefix(base, prefix.into(), self.precompose_unicode)) + .map(|base| IterInfo::from_prefix(base, prefix, self.precompose_unicode)) .transpose()?; self.iter_from_info(git_dir_info, common_dir_info, packed) } Some(namespace) => { - let prefix = namespace.to_owned().into_namespaced_prefix(prefix); - let git_dir_info = - IterInfo::from_prefix(self.git_dir(), prefix.clone().into(), self.precompose_unicode)?; + let prefix = namespace.to_owned().into_namespaced_prefix(prefix.as_ref()); + let git_dir_info = IterInfo::from_prefix(self.git_dir(), prefix.clone(), self.precompose_unicode)?; let common_dir_info = self .common_dir() - .map(|base| IterInfo::from_prefix(base, prefix.into(), self.precompose_unicode)) + .map(|base| IterInfo::from_prefix(base, prefix, self.precompose_unicode)) .transpose()?; self.iter_from_info(git_dir_info, common_dir_info, packed) } @@ -416,7 +398,7 @@ impl file::Store { iter_packed: match packed { Some(packed) => Some( match git_dir_info.prefix() { - Some(prefix) => packed.iter_prefixed(path_to_name(prefix).into_owned()), + Some(prefix) => packed.iter_prefixed(prefix.into_owned()), None => packed.iter(), } .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))? diff --git a/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository_for_overlay.tar b/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository_for_overlay.tar index b6b126990a780393d3cf275759e61eeacbbe64ed..9b1cdb67d27348d13539076cd422ec5f1ae2ea53 100644 GIT binary patch delta 2805 zcmb_eU1%It6wYRMHp!%$G{s7+RxXpJ-G%PY>~1z1sF*g}8k#1$t~4UJPG;`xUNV`P z&QH={bt-5<-;#asRSAgrR5CvJAgC|;sy>N`h)*g-!5^qr&~xw1Zg!K#D!7pB&%O6M z-}%mW&fIIiMz6gcyTQeL?w!MN|LA+aMScF=;h2B)2L1H@?WoTQ1A>33_E&Vo#=3%6 zC8A~4B#pEZg$r9dvCZcF$4aGqsZY$8%3{8dFBJ2A`Mg*b#Zq6sH5OO=NabYYESyjz z2O|lYk|Q@!O45~_VTIppY!Pgy(K1#I3-c3*#v*zH5eKai39+!P%P&4Uf+W+l4AUY2 z#!s|9S{P577GA@;gPbM~Hl_ecwTxAOqZokYv>x>GUT*@Fj*#UiAljJ?q+nHYHRlwK zxK1<;8M?NCVIo5XFiImtL#}B%7M6gCCFvfd zy-u8_;W|h*bX6m=VxQ)gM{pL+s1CLOkBSXk3B*X*t}J8QR$bs{ zA>DA&5R*mJB-;tagkLBkRrC~F3o$(^EXxg;D_Fx0R)YL%Kv^|3%~+>;QEsFGaY5u| z4cqNxZ44?Hpoye6a8H^RUNvZKbp;}XUo1?CfpC(Ak!yoU9vr5EZLqUUO8}P|647mE zniT@E!n0~;!@O}d08fRQPDAFTLG(0)I(T~(qVJw~tTHp3<~>^KDmFDq4(QhwXBQFs z+B7r;TQ)0Cx5EMh07+RkEM`c^VZ(4~U382UJInKJo^{z+!&V(+g}R|s8a>YssYJu4 zg`_C(LlelO@uoWrs1@!MD}}R-EV4}uCS?ADnxwP*kYl-+f1xX0*hu>WRdm}0(R*!+ zB#)UjNV3O+0Nw!GgH1CyF^n?fu|MX(OKYZz*a^Gngcs>wwulbg7K!K z%>VV`c>+y(t z8P+sX;UNhF4`u~Cn@lv#Zm&dVa1}pPj3+_e+b_3-wyH zQsHH#n?Kcn8ma-9D03MJ&niS!X&nIz)T3i2Fi~eD1tWs3U=5ys{7`49XIilxkY5;~ zW7&UlEVcB$_1a=>X1-0Mr(UkU0*T(OT-3RaC7I}Cw{zGtWetOO)NVmeqRP@-jaj3U zRxn8T)@Sux3&lU(y7tO=S=@U$DV2&-?aRqaOI$od;AV5hfBO2H(Vgdp~1GCM~``GHIuWZ__tVxb@&1hVzr%lC=J zefK-DAa*4D{+WpX>JRHI@j`K5vQxXs{*s79OOshY68|Z(_2c!{^{>bL j*Y72f(ZbY24^N5ZLb;rAb#lcYgAG24e-r%ML}&g6EqaEy delta 421 zcmZoz!P4-Bc>^oUV%AkGn_2(;X5X}fi)l0030AJn{JXxfZf2EYSK7?8=qLMT))p?7 z&HVHJGH>Rw`OmzWyM~2hGk@40*3H~W%uw;;bbwKOj?IX?v^v)R$Af^i!gqY%?{ zAs)sOaWizoO^i*=A%;(8Wc<%(YG@3!&Ct|R7idn!X2mP_87C?TPvc;0l`z2!4Rdo7 wh~DjtoQ%ATBF2__rV1L41qC^&3htRDx`w7Enm`xqXWVRXCh&>e`+! z?J9=6Js>XPfi6fzf|sqpGs4m;agl%rUV(QW5Dy3;g!V85yGY|dwv(pq#y}!cE{@On z{_p?(@Be)+{}R9a0rN2v_nDu268^rMDDLn7>sHKXZ*|1|eZTzyzwX_Q`ONt?&hLEt zeyrOjx=a>DRpXaLO|5n5C$Iho%LZKyB6A#Ni zx-x){!46q;2o|v^I#Ly&e;#R4_mjQLm6KXKO?2@Z}|lzPaPc;?#78^=PR} z#MDG7q+gz$p2fjs)zD;O*+F@l9Tpk@NJ^4n1%`wiRt%TcMJGhIvn*TZIVTxQ#F_(H zp>8;x!7sDzimH(bE+uemdmr{_yzvGDYJ~^IN|S6Qi*1vD2?PH@O)^=w-LYK4zS0yg zYNVZkin{HB=v!@zMK3UGnB*1@0(b>%4>slVm`hR89KLi3D(SeEPO1Mp=FqzkRJB7N z(tTkrK^CUyLEmRh?SZ+3c8yf~Z}A9ThVqff@taXExGMDSysK*RAb46)Ubz;UV&plH-GhIAAHFIk2_{^!|+_Bkb&yH`VD78e|{Xu?Kk(UesS^YSK#;F&;2YW7_C3U|{i>IfbdWf}7dy!oI4)P!8Bew)J_ zaJGj+1?-_z_=A2cT#(WhNvyDuc@f%VrxItQG1`ppu|%^8+g=a2O`eBnT1U#Tib|UT zx|&E;19Od_jnv06u`I(H#|k_oVc@~6fM=6Q2Oy2<(YemyBmKhwaS?&e&Se)5}p`}~f#;E>dL`F7hn(-~i9;=4X% zP-30=3C>V^nO2{DBGJ0ub3DG@b2RSvoVf!hsHYNebtE3M-TBqsPs4d>t=f--_~>ZM zNO(4yNTN2lIRyQ*y8ARNs8w-}$rlPu3p%3(Lz-dOwZktRTg6PUvOmWZ04h3W4fMp; zcK4&#{C5vsiPe(7(&JgJ3Tws+`No=qF~66)dC+HHpl7@NaQtIS@R97j8)K_Plx==9 zNLL6jl*kLhNCTlA(t7T0Y>nZNcTmXhIQa>~LL}}6>O(K0jW<$g8LeXeMgnzpZglk` z)Vk5V7u{zzy7mOWA4h*OyXXlCrh;9&)_OLGo6RvJP^Lhq4TMkv`E5P7V>kNIp4PQJ zgUCKGoZnH{v9aNh=mz3Z7m5`|vOb>v5yB@wN~6}KAdC%<2)SH-ly`OYqAyH-i~PyS LFT)RT%=Nzk&2*2Y delta 422 zcmZoz!rJhHWdkeAV%AL@i&@vPY-e>~{LQ@S2p7|GE-prvLso~XdKsfSx^Gk3x-*2Sy`fL5>wux%C<`^vnTi;IV4 zvzQRjbWuJKSL!$OV%A-3+gX(uIe517nlUmmZRay!{Li|bR}aj#VEoQHomGypp2^5? zdmA5P72`Ao#uy#W%=EncqSWNX;#37&g_5GuR4&c}AU{91Aip@ZG%qtbKLsYU+0m|o zahnUH5Yu)cFUEI_{Kf`m24*HEmPRJJK$R8S1!WlDGfw~K%Gf@=m5Wh`&BWZ?1mdUd ujP8uQj3UOCdZr2*js*odsS56yCAx;DCYqZS&)sL-t{}ts07_0&um=G2nSQ_k diff --git a/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh b/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh index 03574b865ae..c6e3a57650b 100755 --- a/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh +++ b/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh @@ -10,10 +10,13 @@ git branch A git tag -m "tag object" tag-object mkdir -p .git/refs/remotes/origin +mkdir -p .git/refs/heads/sub/dir cp .git/refs/heads/main .git/refs/remotes/origin/ echo "ref: refs/remotes/origin/main" > .git/refs/remotes/origin/HEAD +echo "ref: refs/heads/main" > .git/refs/heads-packed +echo "ref: refs/heads/main" > .git/refs/heads/sub/dir/packed git pack-refs --all --prune diff --git a/gix-ref/tests/fixtures/make_ref_repository.sh b/gix-ref/tests/fixtures/make_ref_repository.sh index 38080cddfca..c3e661cba3a 100755 --- a/gix-ref/tests/fixtures/make_ref_repository.sh +++ b/gix-ref/tests/fixtures/make_ref_repository.sh @@ -22,6 +22,11 @@ echo "ref: refs/tags/multi-link-target2" > .git/refs/heads/multi-link-target1 echo "ref: refs/remotes/origin/multi-link-target3" > .git/refs/tags/multi-link-target2 git rev-parse HEAD > .git/refs/remotes/origin/multi-link-target3 +# Regression test for issue #1934 where prefix refs/m matched refs/heads/main. +mkdir -p .git/refs/heads/sub/dir +echo "ref: refs/remotes/origin/main" > .git/refs/heads-loose +echo "ref: refs/remotes/origin/main" > .git/refs/heads/sub/dir/loose +echo "ref: refs/remotes/origin/main" > .git/refs/remotes/origin/heads echo "ref: refs/loop-b" > .git/refs/loop-a echo "ref: refs/loop-a" > .git/refs/loop-b diff --git a/gix-ref/tests/refs/file/store/iter.rs b/gix-ref/tests/refs/file/store/iter.rs index 211332cddb7..e2e2849e1ca 100644 --- a/gix-ref/tests/refs/file/store/iter.rs +++ b/gix-ref/tests/refs/file/store/iter.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use gix_object::bstr::ByteSlice; use crate::{ @@ -26,7 +28,7 @@ mod with_namespace { let ns_two = gix_ref::namespace::expand("bar")?; let namespaced_refs = store .iter()? - .prefixed(ns_two.to_path())? + .prefixed(ns_two.as_bstr().into())? .map(Result::unwrap) .map(|r: gix_ref::Reference| r.name) .collect::>(); @@ -45,7 +47,7 @@ mod with_namespace { ); assert_eq!( store - .loose_iter_prefixed(ns_two.to_path())? + .loose_iter_prefixed(ns_two.as_bstr().into())? .map(Result::unwrap) .map(|r| r.name.into_inner()) .collect::>(), @@ -90,7 +92,7 @@ mod with_namespace { assert_eq!( store .iter()? - .prefixed(ns_one.to_path())? + .prefixed(ns_one.as_bstr().into())? .map(Result::unwrap) .map(|r: gix_ref::Reference| ( r.name.as_bstr().to_owned(), @@ -262,7 +264,7 @@ fn loose_iter_with_broken_refs() -> crate::Result { let store = store()?; let mut actual: Vec<_> = store.loose_iter()?.collect(); - assert_eq!(actual.len(), 16); + assert_eq!(actual.len(), 19); actual.sort_by_key(Result::is_err); let first_error = actual .iter() @@ -271,7 +273,7 @@ fn loose_iter_with_broken_refs() -> crate::Result { .expect("there is an error"); assert_eq!( - first_error, 15, + first_error, 18, "there is exactly one invalid item, and it didn't abort the iterator most importantly" ); #[cfg(not(windows))] @@ -291,15 +293,18 @@ fn loose_iter_with_broken_refs() -> crate::Result { ref_paths, vec![ "d1", + "heads-loose", "heads/A", "heads/d1", "heads/dt1", "heads/main", "heads/multi-link-target1", + "heads/sub/dir/loose", "loop-a", "loop-b", "multi-link", "remotes/origin/HEAD", + "remotes/origin/heads", "remotes/origin/main", "remotes/origin/multi-link-target3", "tags/dt1", @@ -322,19 +327,20 @@ fn loose_iter_with_prefix_wont_allow_absolute_paths() -> crate::Result { #[cfg(windows)] let abs_path = r"c:\hello"; - match store.loose_iter_prefixed(abs_path.as_ref()) { + match store.loose_iter_prefixed(Cow::Borrowed(abs_path.as_ref())) { Ok(_) => unreachable!("absolute paths aren't allowed"), - Err(err) => assert_eq!(err.to_string(), "prefix must be a relative path, like 'refs/heads'"), + Err(err) => assert_eq!(err.to_string(), "prefix must be a relative path, like 'refs/heads/'"), } Ok(()) } #[test] fn loose_iter_with_prefix() -> crate::Result { + // Test 'refs/heads/' with slash. let store = store()?; let actual = store - .loose_iter_prefixed("refs/heads/".as_ref())? + .loose_iter_prefixed(b"refs/heads/".as_bstr().into())? .collect::, _>>() .expect("no broken ref in this subset") .into_iter() @@ -349,6 +355,39 @@ fn loose_iter_with_prefix() -> crate::Result { "refs/heads/dt1", "refs/heads/main", "refs/heads/multi-link-target1", + "refs/heads/sub/dir/loose", + ] + .into_iter() + .map(String::from) + .collect::>(), + "all paths are as expected" + ); + Ok(()) +} + +#[test] +fn loose_iter_with_partial_prefix_dir() -> crate::Result { + // Test 'refs/heads/' without slash. + let store = store()?; + + let actual = store + .loose_iter_prefixed(b"refs/heads".as_bstr().into())? + .collect::, _>>() + .expect("no broken ref in this subset") + .into_iter() + .map(|e| e.name.into_inner()) + .collect::>(); + + assert_eq!( + actual, + vec![ + "refs/heads-loose", + "refs/heads/A", + "refs/heads/d1", + "refs/heads/dt1", + "refs/heads/main", + "refs/heads/multi-link-target1", + "refs/heads/sub/dir/loose", ] .into_iter() .map(String::from) @@ -363,7 +402,7 @@ fn loose_iter_with_partial_prefix() -> crate::Result { let store = store()?; let actual = store - .loose_iter_prefixed("refs/heads/d".as_ref())? + .loose_iter_prefixed(b"refs/heads/d".as_bstr().into())? .collect::, _>>() .expect("no broken ref in this subset") .into_iter() @@ -396,9 +435,14 @@ fn overlay_iter() -> crate::Result { assert_eq!( ref_names, vec![ + ("refs/heads-packed".into(), Symbolic("refs/heads/main".try_into()?),), (b"refs/heads/A".as_bstr().to_owned(), Object(c1)), (b"refs/heads/main".into(), Object(c1)), ("refs/heads/newer-as-loose".into(), Object(c2)), + ( + "refs/heads/sub/dir/packed".into(), + Symbolic("refs/heads/main".try_into()?), + ), ( "refs/remotes/origin/HEAD".into(), Symbolic("refs/remotes/origin/main".try_into()?), @@ -505,21 +549,50 @@ fn overlay_iter_with_prefix_wont_allow_absolute_paths() -> crate::Result { #[cfg(windows)] let abs_path = r"c:\hello"; - match store.iter()?.prefixed(abs_path.as_ref()) { + match store.iter()?.prefixed(Cow::Borrowed(abs_path.as_ref())) { Ok(_) => unreachable!("absolute paths aren't allowed"), - Err(err) => assert_eq!(err.to_string(), "prefix must be a relative path, like 'refs/heads'"), + Err(err) => assert_eq!(err.to_string(), "prefix must be a relative path, like 'refs/heads/'"), } Ok(()) } #[test] fn overlay_prefixed_iter() -> crate::Result { + // Test 'refs/heads/' with slash. + use gix_ref::Target::*; + + let store = store_at("make_packed_ref_repository_for_overlay.sh")?; + let ref_names = store + .iter()? + .prefixed(b"refs/heads/".as_bstr().into())? + .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) + .collect::, _>>()?; + let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); + let c2 = hex_to_id("9902e3c3e8f0c569b4ab295ddf473e6de763e1e7"); + assert_eq!( + ref_names, + vec![ + (b"refs/heads/A".as_bstr().to_owned(), Object(c1)), + (b"refs/heads/main".into(), Object(c1)), + ("refs/heads/newer-as-loose".into(), Object(c2)), + ( + b"refs/heads/sub/dir/packed".into(), + Symbolic("refs/heads/main".try_into()?) + ), + ] + ); + Ok(()) +} + +#[test] +fn overlay_partial_prefix_dir_iter() -> crate::Result { + // Test 'refs/heads/' without slash. use gix_ref::Target::*; let store = store_at("make_packed_ref_repository_for_overlay.sh")?; let ref_names = store .iter()? - .prefixed("refs/heads".as_ref())? + .prefixed(b"refs/heads".as_bstr().into())? .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) .collect::, _>>()?; let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); @@ -527,9 +600,17 @@ fn overlay_prefixed_iter() -> crate::Result { assert_eq!( ref_names, vec![ + ( + b"refs/heads-packed".as_bstr().to_owned(), + Symbolic("refs/heads/main".try_into()?) + ), (b"refs/heads/A".as_bstr().to_owned(), Object(c1)), (b"refs/heads/main".into(), Object(c1)), ("refs/heads/newer-as-loose".into(), Object(c2)), + ( + b"refs/heads/sub/dir/packed".into(), + Symbolic("refs/heads/main".try_into()?) + ), ] ); Ok(()) @@ -542,7 +623,7 @@ fn overlay_partial_prefix_iter() -> crate::Result { let store = store_at("make_packed_ref_repository_for_overlay.sh")?; let ref_names = store .iter()? - .prefixed("refs/heads/m".as_ref())? // 'm' is partial + .prefixed(b"refs/heads/m".as_bstr().into())? // 'm' is partial .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) .collect::, _>>()?; let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); diff --git a/gix-ref/tests/refs/file/transaction/prepare_and_commit/create_or_update/mod.rs b/gix-ref/tests/refs/file/transaction/prepare_and_commit/create_or_update/mod.rs index 1ad9bf62199..3d6680f212d 100644 --- a/gix-ref/tests/refs/file/transaction/prepare_and_commit/create_or_update/mod.rs +++ b/gix-ref/tests/refs/file/transaction/prepare_and_commit/create_or_update/mod.rs @@ -843,7 +843,7 @@ fn packed_refs_creation_with_packed_refs_mode_leave_keeps_original_loose_refs() .commit(committer().to_ref())?; assert_eq!( edits.len(), - 2, + 4, "it claims to have performed all desired operations, even though some don't make it into the pack as 'side-car'" ); diff --git a/gix-ref/tests/refs/file/worktree.rs b/gix-ref/tests/refs/file/worktree.rs index 6606af9cc72..b537286021e 100644 --- a/gix-ref/tests/refs/file/worktree.rs +++ b/gix-ref/tests/refs/file/worktree.rs @@ -191,6 +191,7 @@ mod read_only { mod writable { use gix_lock::acquire::Fail; use gix_ref::{ + bstr::ByteSlice, file::{transaction::PackedRefs, Store}, transaction::{Change, LogChange, PreviousValue, RefEdit}, FullName, FullNameRef, Target, @@ -290,7 +291,7 @@ mod writable { assert_eq!( store .iter()? - .prefixed("refs/stacks/".as_ref())? + .prefixed(b"refs/stacks/".as_bstr().into())? .map(Result::unwrap) .map(|r| (r.name.to_string(), r.target.to_string())) .collect::>(), @@ -571,7 +572,7 @@ mod writable { assert_eq!( store .iter()? - .prefixed("refs/stacks/".as_ref())? + .prefixed(b"refs/stacks/".as_bstr().into())? .map(Result::unwrap) .map(|r| (r.name.to_string(), r.target.to_string())) .collect::>(), diff --git a/gix-ref/tests/refs/namespace.rs b/gix-ref/tests/refs/namespace.rs index baaa4dc95d4..70c81d1c7f7 100644 --- a/gix-ref/tests/refs/namespace.rs +++ b/gix-ref/tests/refs/namespace.rs @@ -1,12 +1,16 @@ -use std::path::Path; - #[test] fn into_namespaced_prefix() { assert_eq!( gix_ref::namespace::expand("foo") .unwrap() .into_namespaced_prefix("prefix".as_ref()), - Path::new("refs").join("namespaces").join("foo").join("prefix") + "refs/namespaces/foo/prefix".as_ref(), + ); + assert_eq!( + gix_ref::namespace::expand("foo") + .unwrap() + .into_namespaced_prefix("prefix/".as_ref()), + "refs/namespaces/foo/prefix/".as_ref(), ); } diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index 3bad04d8263..4b04909a339 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -5,6 +5,7 @@ use std::{borrow::Cow, path::PathBuf}; use super::{Error, Options}; use crate::{ + bstr::BString, config, config::{ cache::interpolate_context, @@ -348,14 +349,13 @@ impl ThreadSafeRepository { .and_then(|prefix| { let _span = gix_trace::detail!("find replacement objects"); let platform = refs.iter().ok()?; - let iter = platform.prefixed(&prefix).ok()?; - let prefix = prefix.to_str()?; + let iter = platform.prefixed(prefix.clone().into()).ok()?; let replacements = iter .filter_map(Result::ok) .filter_map(|r: gix_ref::Reference| { let target = r.target.try_id()?.to_owned(); let source = - gix_hash::ObjectId::from_hex(r.name.as_bstr().strip_prefix(prefix.as_bytes())?).ok()?; + gix_hash::ObjectId::from_hex(r.name.as_bstr().strip_prefix(prefix.as_slice())?).ok()?; Some((source, target)) }) .collect::>(); @@ -394,7 +394,7 @@ fn replacement_objects_refs_prefix( config: &gix_config::File<'static>, lenient: bool, mut filter_config_section: fn(&gix_config::file::Metadata) -> bool, -) -> Result, Error> { +) -> Result, Error> { let is_disabled = config::shared::is_replace_refs_enabled(config, lenient, filter_config_section) .map_err(config::Error::ConfigBoolean)? .unwrap_or(true); @@ -403,15 +403,15 @@ fn replacement_objects_refs_prefix( return Ok(None); } - let ref_base = gix_path::from_bstr({ + let ref_base = { let key = "gitoxide.objects.replaceRefBase"; debug_assert_eq!(gitoxide::Objects::REPLACE_REF_BASE.logical_name(), key); config .string_filter(key, &mut filter_config_section) .unwrap_or_else(|| Cow::Borrowed("refs/replace/".into())) - }) + } .into_owned(); - Ok(ref_base.into()) + Ok(Some(ref_base)) } fn check_safe_directories( diff --git a/gix/src/reference/iter.rs b/gix/src/reference/iter.rs index 60ded42fef9..5ba5ccf7c5b 100644 --- a/gix/src/reference/iter.rs +++ b/gix/src/reference/iter.rs @@ -1,8 +1,11 @@ //! #![allow(clippy::empty_docs)] -use std::path::Path; +use std::borrow::Cow; -use gix_ref::file::ReferenceExt; +use gix_ref::{ + bstr::{BStr, ByteSlice}, + file::ReferenceExt, +}; /// A platform to create iterators over references. #[must_use = "Iterators should be obtained from this iterator platform"] @@ -42,11 +45,11 @@ impl Platform<'_> { /// Return an iterator over all references that match the given `prefix`. /// - /// These are of the form `refs/heads` or `refs/remotes/origin`, and must not contain relative paths components like `.` or `..`. + /// These are of the form `refs/heads/` or `refs/remotes/origin`, and must not contain relative paths components like `.` or `..`. // TODO: Create a custom `Path` type that enforces the requirements of git naturally, this type is surprising possibly on windows // and when not using a trailing '/' to signal directories. - pub fn prefixed(&self, prefix: impl AsRef) -> Result, init::Error> { - Ok(Iter::new(self.repo, self.platform.prefixed(prefix.as_ref())?)) + pub fn prefixed(&self, prefix: Cow<'_, BStr>) -> Result, init::Error> { + Ok(Iter::new(self.repo, self.platform.prefixed(prefix)?)) } // TODO: tests @@ -54,7 +57,10 @@ impl Platform<'_> { /// /// They are all prefixed with `refs/tags`. pub fn tags(&self) -> Result, init::Error> { - Ok(Iter::new(self.repo, self.platform.prefixed("refs/tags/".as_ref())?)) + Ok(Iter::new( + self.repo, + self.platform.prefixed(b"refs/tags/".as_bstr().into())?, + )) } // TODO: tests @@ -62,7 +68,10 @@ impl Platform<'_> { /// /// They are all prefixed with `refs/heads`. pub fn local_branches(&self) -> Result, init::Error> { - Ok(Iter::new(self.repo, self.platform.prefixed("refs/heads/".as_ref())?)) + Ok(Iter::new( + self.repo, + self.platform.prefixed(b"refs/heads/".as_bstr().into())?, + )) } // TODO: tests @@ -70,7 +79,10 @@ impl Platform<'_> { /// /// They are all prefixed with `refs/remotes`. pub fn remote_branches(&self) -> Result, init::Error> { - Ok(Iter::new(self.repo, self.platform.prefixed("refs/remotes/".as_ref())?)) + Ok(Iter::new( + self.repo, + self.platform.prefixed(b"refs/remotes/".as_bstr().into())?, + )) } } diff --git a/gix/tests/gix/repository/reference.rs b/gix/tests/gix/repository/reference.rs index 94e9e8574d2..ae6f7a80b5a 100644 --- a/gix/tests/gix/repository/reference.rs +++ b/gix/tests/gix/repository/reference.rs @@ -1,5 +1,5 @@ mod set_namespace { - use gix::refs::transaction::PreviousValue; + use gix::{bstr::ByteSlice, refs::transaction::PreviousValue}; use gix_testtools::tempfile; fn easy_repo_rw() -> crate::Result<(gix::Repository, tempfile::TempDir)> { @@ -48,7 +48,7 @@ mod set_namespace { assert_eq!( repo.references()? - .prefixed("refs/tags/")? + .prefixed(b"refs/tags/".as_bstr().into())? .filter_map(Result::ok) .map(|r| r.name().as_bstr().to_owned()) .collect::>(), @@ -81,6 +81,7 @@ mod set_namespace { } mod iter_references { + use gix::bstr::ByteSlice; use crate::util::hex_to_id; @@ -124,7 +125,7 @@ mod iter_references { let repo = repo()?; assert_eq!( repo.references()? - .prefixed("refs/heads/")? + .prefixed(b"refs/heads/".as_bstr().into())? .filter_map(Result::ok) .map(|r| ( r.name().as_bstr().to_string(), @@ -155,7 +156,7 @@ mod iter_references { let repo = repo()?; assert_eq!( repo.references()? - .prefixed("refs/heads/")? + .prefixed(b"refs/heads/".as_bstr().into())? .peeled()? .filter_map(Result::ok) .map(|r| ( From 94faf5199a32a99348230c2ea4bc8c2b06319360 Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Tue, 8 Apr 2025 10:33:19 +0200 Subject: [PATCH 2/5] Rename prefix test branches --- .../make_packed_ref_repository.tar | Bin 69120 -> 73216 bytes ...make_packed_ref_repository_for_overlay.tar | Bin 70656 -> 68608 bytes .../make_ref_repository.tar | Bin 86528 -> 86528 bytes .../fixtures/make_packed_ref_repository.sh | 3 + .../make_packed_ref_repository_for_overlay.sh | 6 +- gix-ref/tests/fixtures/make_ref_repository.sh | 9 +- gix-ref/tests/refs/file/store/iter.rs | 94 ++++++++++-------- .../create_or_update/mod.rs | 4 +- gix-ref/tests/refs/packed/iter.rs | 2 +- 9 files changed, 63 insertions(+), 55 deletions(-) diff --git a/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository.tar b/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository.tar index 87d999da6baf14cf49b925aa782da9ab91baec61..4f956f6262c314d0f35d7a8668025b775f0e0bf9 100644 GIT binary patch delta 2746 zcmb_eOK%%h6wWvvH=evoMNnTtR}-tXQOBNfoCG2jkv678EsbQ1TBw9|Gjqqj&17aW zk37_(>4pW%)<8F1P%N=SLLCH~E?|QY3j{1!u<8#$uqjlOLOFNFcI~94AYm!-jPKm< zeCIpgIdd;vOJ2H}{FqOM{O7w<;l$TmGJNX$+k>I-&2TcD`0`h{`r(hkkbicU7>=C3 zKRD(RQzNUG8qzv8Xmj+XlUKeA0A^THUy zw(VH9Ljg>fY<{8b&DaiEC#HwI28kcc0+Q}ns{qF^0LyDW{jMDJC&1{4d0`Tw9os@0 z(XnrMM_9xSY8c2ejV+`$h`Iv6I7W}CXBklyn}E*97?f!6F!C%LSvr6*8f`S>+pgyj z3``ts29Wj!^%|D%A=NTeq|6w$!0*A^3gVTer8BhgwLGho6-xdC&H7{nu5oPQN4>y}|y8%!_8 zjn*M9h`ekNx09?(Km`jl!DgNGrRk7Wi`CZDAVT}bA&eLZhaG}^7eoqRGZSnp+8b#J z;JA*d>3VZfAy8EKsM`5<-lP(PXWN?2LFTkh%^ZY!^y3wX{$R&T<@tr25U^5Lh;3js zre9rJSVGZl!!k7DxKVj}9Tpn^NUEykM23VMwk)63#Uw;?^McUfSyQcb;?zJ^s2j@W z&;=o_Q-d58Gm0prCsDxS&236!Bkc@S zHeDY?@3$?&L1flA$vzJPcpYpHHkGE3m|>(vbnzlo((@gYQU7<$p?4uDbz>j0eQ_>P z7B&u`@AJ0t&|G4>wpIIY@d(<0@{zXVA4FaADfI5LPYvw|cv_ftV1t$$Jw@4$)Q7s7?8dVCFsJies~NBG*FDw8At%O zy5f;l8#a2YwniN!X%9F=?*ukd^Jn8vyqjgwK?o4mG$iTpCq)%tCKNNBWf6(M*)7Bh zxP?r7!M=4dN~up0CvId3L!0bW;!Jyt_TmQuFmqvoP!S+DK^TR#vv0DwdSbC!TUe|_ z-)EL8Cu`M8xh$w!FMp;1GgKWgk+g=yH#Dm2td4*M>aj5so2WaI;t>(85Ch(SLb|)u zH?5*4Ais8mu4VtpvCPst)~id^`BNPlef9Fq6-e}P;PHpADhT_3!$ zKAZ}NKfTL`{KfSCaLnX}TI0*faQyvy6XCE9hoq6$f8W*OlgSqUW2(j9g!9uLKG71M zNwtK7W1&!DXQn6lp_U*gQ&4`LZZ@wcs&G_lHV(>sQJxu?j*mryNv_G?3-&2fJ5EnZ zQRx!=^Bfnx{nrL}8A&&8jgEw)x4A?(_Vax{9Q}!1{dR{RV#f<;DHkH0i6UiWej0M@ zjWiVOo0{1Xsi>5?=d|?s#6--vpc4$UpGdB+vjhqwg#-O71=n9Lzz`^4qi(VF=10T3 z!wbWAxDUpXpKyB;SNEhicW-fOhxW6x#aR2#xLw>RH&~j^hbVsw@*Dp!!6n9vrCFs^ aoPJK8k$jVGhELr6g$vQ$Tk*fk=-A(R^?ES? delta 445 zcmZ9IyDtP$6vp?=+&jB_clL?RQpiw|AoIAphr^K%qoIqN4GLx7oxtnv?V8oSfhHo%{*O?~p@EYg3T^{Haid5>{PPZ6+w3K zAtJ5y)}y@t=0&=}2!{8*M3j4eL=5DK55vQs6a!OYjTP!&P9%;0XwJE7p@c>l7@{~j zxGQceQgJg7$H3S-wdpZIN+J?g#h*oFOePqQx!0>gC0X+!+O#Mkz4yr&`~(3*FFzE6 zPo?hrm0I+6wOZG-|5!J&QO4elK$AZ`J)c=HC&qK8(y1&iESb!ob#-Pgo5`6=^XZAq zWO;(UT`O=DftcXL;_v`rBh^-lp&LfCySd^-1$czQcNEsGwab{qTU&YgyA=?GN~66c fsWkLvv$LksmtJhvlDrY?&aS~x1i1OD+e-H@pIL`P diff --git a/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository_for_overlay.tar b/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository_for_overlay.tar index 9b1cdb67d27348d13539076cd422ec5f1ae2ea53..b588e583f6d88e769b00c216e13880cfd4bcc56f 100644 GIT binary patch delta 235 zcmZoz!O}2;WdqlN#at)2*b0hL(=sb2FJuat#KSTC)v85jZC1_p*khQ>w=1`vh+d6~o@Ch4c8CYF>IrRo-!rU4D+ znx4qWsG+0)w53=dRfT?WX_9_QW|4kkPI^8>-EiFrB=RCuK9^Zhl5~pqze6 zX3<1JU$Fck16E@b6C?$EPzCx0iOJciDH8?Vfa<1ZFv^LWG8h_|m>C)v85kKGFc=sZ znwXj!F&IFMzR1tWlAD;Bx7pEZKjTCN-AxC$xOl*>(S;f0htnWqBO^nc2AyfvkTNhc zG(xxC$k5mvrs*p$)8skdKW+Ne#x=R^h2ZAtM=}}tjm#}9%?-_r%*=F4^D-+oJO0_v PxH;g@1_&{E1J5M@QZHk7 diff --git a/gix-ref/tests/fixtures/generated-archives/make_ref_repository.tar b/gix-ref/tests/fixtures/generated-archives/make_ref_repository.tar index 393c27057cd04e0c8da514647b1620696bf4617c..76d6c84863f6ec8402ab5d5db3992642e99bb361 100644 GIT binary patch delta 333 zcmZoz!rHKebpzL(&0J6TSvE_)`KY;_ON#L~Gj~yHTCsiskj$)@oX8q7iHBu!28RKw zk%1Y5DNrC~GviTCMqY>l{j}7?lG38diVDsX6}Z4kpBb{47$T|ULs6MJng5OtSRIQ# zn~90B5k8go;3`dx3=NP}N~4;oUtF4`pORUmpO}-LKT*&gZ2k#-HZwzG0|o<#n=f)Q zi=yh%EiO$1`f;M5D@?tqA<*S8^(@TWS>+i2=<=DFo12(h8krgEmgZ$vY!{Sae9yRD NL5A@Gl$@yG1OVj$Yjywt delta 241 zcmZoz!rHKebpzKO&WzN=lw$qj(xi!kfs=Sxrf0Y?8nT#~F_=zHWKG%3c$}Y+9Vn@v zl36rS&=;s+a>hLaR$~(rBn3QB1^PMp`NfkJ6`Ut3u!2=SF<>Zt(pX2Clg zER*>;ykXMjCdP1SN#@N`4?bvefnB7FFpC$WSf9<<(9jgFnv;1u7Z>Ai=IyL=bhO .git/refs/remotes/origin/HEAD echo "notahexsha" > .git/refs/broken diff --git a/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh b/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh index c6e3a57650b..5c231bae009 100755 --- a/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh +++ b/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh @@ -10,13 +10,13 @@ git branch A git tag -m "tag object" tag-object mkdir -p .git/refs/remotes/origin -mkdir -p .git/refs/heads/sub/dir +mkdir -p .git/refs/prefix/feature/sub/dir cp .git/refs/heads/main .git/refs/remotes/origin/ +cp .git/refs/heads/main .git/refs/prefix/feature-suffix +cp .git/refs/heads/main .git/refs/prefix/feature/sub/dir/algo echo "ref: refs/remotes/origin/main" > .git/refs/remotes/origin/HEAD -echo "ref: refs/heads/main" > .git/refs/heads-packed -echo "ref: refs/heads/main" > .git/refs/heads/sub/dir/packed git pack-refs --all --prune diff --git a/gix-ref/tests/fixtures/make_ref_repository.sh b/gix-ref/tests/fixtures/make_ref_repository.sh index c3e661cba3a..d89948193fa 100755 --- a/gix-ref/tests/fixtures/make_ref_repository.sh +++ b/gix-ref/tests/fixtures/make_ref_repository.sh @@ -10,9 +10,12 @@ git branch d1 git branch A mkdir -p .git/refs/remotes/origin +mkdir -p .git/refs/prefix/feature/sub/dir cp .git/refs/heads/main .git/refs/remotes/origin/ cp .git/refs/heads/main .git/refs/d1 +cp .git/refs/heads/main .git/refs/prefix/feature-suffix +cp .git/refs/heads/main .git/refs/prefix/feature/sub/dir/algo echo "ref: refs/remotes/origin/main" > .git/refs/remotes/origin/HEAD echo "notahexsha" > .git/refs/broken @@ -22,12 +25,6 @@ echo "ref: refs/tags/multi-link-target2" > .git/refs/heads/multi-link-target1 echo "ref: refs/remotes/origin/multi-link-target3" > .git/refs/tags/multi-link-target2 git rev-parse HEAD > .git/refs/remotes/origin/multi-link-target3 -# Regression test for issue #1934 where prefix refs/m matched refs/heads/main. -mkdir -p .git/refs/heads/sub/dir -echo "ref: refs/remotes/origin/main" > .git/refs/heads-loose -echo "ref: refs/remotes/origin/main" > .git/refs/heads/sub/dir/loose -echo "ref: refs/remotes/origin/main" > .git/refs/remotes/origin/heads - echo "ref: refs/loop-b" > .git/refs/loop-a echo "ref: refs/loop-a" > .git/refs/loop-b diff --git a/gix-ref/tests/refs/file/store/iter.rs b/gix-ref/tests/refs/file/store/iter.rs index e2e2849e1ca..3d25a53b245 100644 --- a/gix-ref/tests/refs/file/store/iter.rs +++ b/gix-ref/tests/refs/file/store/iter.rs @@ -255,7 +255,7 @@ fn no_packed_available_thus_no_iteration_possible() -> crate::Result { #[test] fn packed_file_iter() -> crate::Result { let store = store_with_packed_refs()?; - assert_eq!(store.open_packed_buffer()?.expect("pack available").iter()?.count(), 9); + assert_eq!(store.open_packed_buffer()?.expect("pack available").iter()?.count(), 11); Ok(()) } @@ -264,7 +264,7 @@ fn loose_iter_with_broken_refs() -> crate::Result { let store = store()?; let mut actual: Vec<_> = store.loose_iter()?.collect(); - assert_eq!(actual.len(), 19); + assert_eq!(actual.len(), 18); actual.sort_by_key(Result::is_err); let first_error = actual .iter() @@ -273,7 +273,7 @@ fn loose_iter_with_broken_refs() -> crate::Result { .expect("there is an error"); assert_eq!( - first_error, 18, + first_error, 17, "there is exactly one invalid item, and it didn't abort the iterator most importantly" ); #[cfg(not(windows))] @@ -293,18 +293,17 @@ fn loose_iter_with_broken_refs() -> crate::Result { ref_paths, vec![ "d1", - "heads-loose", "heads/A", "heads/d1", "heads/dt1", "heads/main", "heads/multi-link-target1", - "heads/sub/dir/loose", "loop-a", "loop-b", "multi-link", + "prefix/feature-suffix", + "prefix/feature/sub/dir/algo", "remotes/origin/HEAD", - "remotes/origin/heads", "remotes/origin/main", "remotes/origin/multi-link-target3", "tags/dt1", @@ -355,7 +354,6 @@ fn loose_iter_with_prefix() -> crate::Result { "refs/heads/dt1", "refs/heads/main", "refs/heads/multi-link-target1", - "refs/heads/sub/dir/loose", ] .into_iter() .map(String::from) @@ -381,13 +379,11 @@ fn loose_iter_with_partial_prefix_dir() -> crate::Result { assert_eq!( actual, vec![ - "refs/heads-loose", "refs/heads/A", "refs/heads/d1", "refs/heads/dt1", "refs/heads/main", "refs/heads/multi-link-target1", - "refs/heads/sub/dir/loose", ] .into_iter() .map(String::from) @@ -435,14 +431,11 @@ fn overlay_iter() -> crate::Result { assert_eq!( ref_names, vec![ - ("refs/heads-packed".into(), Symbolic("refs/heads/main".try_into()?),), (b"refs/heads/A".as_bstr().to_owned(), Object(c1)), (b"refs/heads/main".into(), Object(c1)), ("refs/heads/newer-as-loose".into(), Object(c2)), - ( - "refs/heads/sub/dir/packed".into(), - Symbolic("refs/heads/main".try_into()?), - ), + ("refs/prefix/feature-suffix".into(), Object(c1)), + ("refs/prefix/feature/sub/dir/algo".into(), Object(c1)), ( "refs/remotes/origin/HEAD".into(), Symbolic("refs/remotes/origin/main".try_into()?), @@ -558,7 +551,6 @@ fn overlay_iter_with_prefix_wont_allow_absolute_paths() -> crate::Result { #[test] fn overlay_prefixed_iter() -> crate::Result { - // Test 'refs/heads/' with slash. use gix_ref::Target::*; let store = store_at("make_packed_ref_repository_for_overlay.sh")?; @@ -575,58 +567,74 @@ fn overlay_prefixed_iter() -> crate::Result { (b"refs/heads/A".as_bstr().to_owned(), Object(c1)), (b"refs/heads/main".into(), Object(c1)), ("refs/heads/newer-as-loose".into(), Object(c2)), - ( - b"refs/heads/sub/dir/packed".into(), - Symbolic("refs/heads/main".try_into()?) - ), ] ); Ok(()) } #[test] -fn overlay_partial_prefix_dir_iter() -> crate::Result { - // Test 'refs/heads/' without slash. +fn overlay_partial_prefix_iter() -> crate::Result { use gix_ref::Target::*; let store = store_at("make_packed_ref_repository_for_overlay.sh")?; let ref_names = store .iter()? - .prefixed(b"refs/heads".as_bstr().into())? + .prefixed(b"refs/heads/m".as_bstr().into())? // 'm' is partial .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) .collect::, _>>()?; let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); - let c2 = hex_to_id("9902e3c3e8f0c569b4ab295ddf473e6de763e1e7"); - assert_eq!( - ref_names, - vec![ - ( - b"refs/heads-packed".as_bstr().to_owned(), - Symbolic("refs/heads/main".try_into()?) - ), - (b"refs/heads/A".as_bstr().to_owned(), Object(c1)), - (b"refs/heads/main".into(), Object(c1)), - ("refs/heads/newer-as-loose".into(), Object(c2)), - ( - b"refs/heads/sub/dir/packed".into(), - Symbolic("refs/heads/main".try_into()?) - ), - ] - ); + assert_eq!(ref_names, vec![(b"refs/heads/main".as_bstr().to_owned(), Object(c1)),]); Ok(()) } #[test] -fn overlay_partial_prefix_iter() -> crate::Result { +/// The prefix `refs/d` should match `refs/d1` but not `refs/heads/d1`. +fn overlay_partial_prefix_iter_reproduce_1934() -> crate::Result { use gix_ref::Target::*; - let store = store_at("make_packed_ref_repository_for_overlay.sh")?; + let store = store_at("make_ref_repository.sh")?; + let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); + let ref_names = store .iter()? - .prefixed(b"refs/heads/m".as_bstr().into())? // 'm' is partial + .prefixed(b"refs/d".as_bstr().into())? .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) .collect::, _>>()?; + // Should not match `refs/heads/d1`. + assert_eq!(ref_names, vec![("refs/d1".into(), Object(c1)),]); + Ok(()) +} + +#[test] +fn overlay_partial_prefix_iter_when_prefix_is_dir() -> crate::Result { + // Test 'refs/prefix/' with and without trailing slash. + use gix_ref::Target::*; + + let store = store_at("make_packed_ref_repository_for_overlay.sh")?; let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); - assert_eq!(ref_names, vec![(b"refs/heads/main".as_bstr().to_owned(), Object(c1)),]); + + let ref_names = store + .iter()? + .prefixed(b"refs/prefix/feature".as_bstr().into())? + .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) + .collect::, _>>()?; + assert_eq!( + ref_names, + vec![ + ("refs/prefix/feature-suffix".into(), Object(c1)), + ("refs/prefix/feature/sub/dir/algo".into(), Object(c1)), + ] + ); + + let ref_names = store + .iter()? + .prefixed(b"refs/prefix/feature/".as_bstr().into())? + .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) + .collect::, _>>()?; + assert_eq!( + ref_names, + vec![("refs/prefix/feature/sub/dir/algo".into(), Object(c1)),] + ); + Ok(()) } diff --git a/gix-ref/tests/refs/file/transaction/prepare_and_commit/create_or_update/mod.rs b/gix-ref/tests/refs/file/transaction/prepare_and_commit/create_or_update/mod.rs index 3d6680f212d..0b177ec1354 100644 --- a/gix-ref/tests/refs/file/transaction/prepare_and_commit/create_or_update/mod.rs +++ b/gix-ref/tests/refs/file/transaction/prepare_and_commit/create_or_update/mod.rs @@ -791,7 +791,7 @@ fn packed_refs_creation_with_packed_refs_mode_prune_removes_original_loose_refs( assert_eq!( edits.len(), - 9, + 11, "there are a certain amount of loose refs that are packed" ); @@ -843,7 +843,7 @@ fn packed_refs_creation_with_packed_refs_mode_leave_keeps_original_loose_refs() .commit(committer().to_ref())?; assert_eq!( edits.len(), - 4, + 2, "it claims to have performed all desired operations, even though some don't make it into the pack as 'side-car'" ); diff --git a/gix-ref/tests/refs/packed/iter.rs b/gix-ref/tests/refs/packed/iter.rs index b22c7c7adba..78db809721a 100644 --- a/gix-ref/tests/refs/packed/iter.rs +++ b/gix-ref/tests/refs/packed/iter.rs @@ -18,7 +18,7 @@ fn packed_refs_with_header() -> crate::Result { let dir = gix_testtools::scripted_fixture_read_only_standalone("make_packed_ref_repository.sh")?; let buf = std::fs::read(dir.join(".git").join("packed-refs"))?; let iter = packed::Iter::new(&buf)?; - assert_eq!(iter.count(), 9, "it finds the right amount of items"); + assert_eq!(iter.count(), 11, "it finds the right amount of items"); Ok(()) } From 507d682c08dcda29f044068c13ce87678c1b2a5e Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Tue, 8 Apr 2025 11:10:53 +0200 Subject: [PATCH 3/5] Use Into> --- gix-negotiate/tests/baseline/mod.rs | 2 +- gix-ref/src/store/file/loose/iter.rs | 4 ++-- gix-ref/src/store/file/overlay_iter.rs | 11 ++++++----- gix-ref/tests/refs/file/store/iter.rs | 24 ++++++++++++------------ gix-ref/tests/refs/file/worktree.rs | 4 ++-- gix/src/open/repository.rs | 2 +- gix/src/reference/iter.rs | 16 +++++----------- gix/tests/gix/repository/reference.rs | 6 +++--- 8 files changed, 32 insertions(+), 37 deletions(-) diff --git a/gix-negotiate/tests/baseline/mod.rs b/gix-negotiate/tests/baseline/mod.rs index e2736ae7cff..84f71f04801 100644 --- a/gix-negotiate/tests/baseline/mod.rs +++ b/gix-negotiate/tests/baseline/mod.rs @@ -71,7 +71,7 @@ fn run() -> crate::Result { // } for tip in lookup_names(&["HEAD"]).into_iter().chain( refs.iter()? - .prefixed(b"refs/heads".as_bstr().into())? + .prefixed(b"refs/heads".as_bstr())? .filter_map(Result::ok) .map(|r| r.target.into_id()), ) { diff --git a/gix-ref/src/store/file/loose/iter.rs b/gix-ref/src/store/file/loose/iter.rs index 5be0bb2cddd..0ec3a7ddb11 100644 --- a/gix-ref/src/store/file/loose/iter.rs +++ b/gix-ref/src/store/file/loose/iter.rs @@ -87,7 +87,7 @@ impl file::Store { /// Return an iterator over all loose references that start with the given `prefix`. /// /// Otherwise it's similar to [`loose_iter()`][file::Store::loose_iter()]. - pub fn loose_iter_prefixed(&self, prefix: Cow<'_, BStr>) -> std::io::Result> { - self.iter_prefixed_packed(prefix, None) + pub fn loose_iter_prefixed<'a>(&self, prefix: impl Into>) -> std::io::Result> { + self.iter_prefixed_packed(prefix.into(), None) } } diff --git a/gix-ref/src/store/file/overlay_iter.rs b/gix-ref/src/store/file/overlay_iter.rs index 914035bea3d..c9de782d609 100644 --- a/gix-ref/src/store/file/overlay_iter.rs +++ b/gix-ref/src/store/file/overlay_iter.rs @@ -197,9 +197,9 @@ impl Platform<'_> { /// As [`iter(…)`][file::Store::iter()], but filters by `prefix`, i.e. "refs/heads/" or /// "refs/heads/feature-". - pub fn prefixed(&self, prefix: Cow<'_, BStr>) -> std::io::Result> { + pub fn prefixed<'a>(&self, prefix: impl Into>) -> std::io::Result> { self.store - .iter_prefixed_packed(prefix, self.packed.as_ref().map(|b| &***b)) + .iter_prefixed_packed(prefix.into(), self.packed.as_ref().map(|b| &***b)) } } @@ -284,8 +284,9 @@ impl<'a> IterInfo<'a> { .peekable() } - fn from_prefix(base: &'a Path, prefix: Cow<'a, BStr>, precompose_unicode: bool) -> std::io::Result { - let prefix_path = gix_path::from_bstring(prefix.as_ref()); + fn from_prefix(base: &'a Path, prefix: impl Into>, precompose_unicode: bool) -> std::io::Result { + let prefix = prefix.into(); + let prefix_path = gix_path::from_bstring(prefix.clone().into_owned()); if prefix_path.is_absolute() { return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, @@ -375,7 +376,7 @@ impl file::Store { self.iter_from_info(git_dir_info, common_dir_info, packed) } Some(namespace) => { - let prefix = namespace.to_owned().into_namespaced_prefix(prefix.as_ref()); + let prefix = namespace.to_owned().into_namespaced_prefix(&prefix); let git_dir_info = IterInfo::from_prefix(self.git_dir(), prefix.clone(), self.precompose_unicode)?; let common_dir_info = self .common_dir() diff --git a/gix-ref/tests/refs/file/store/iter.rs b/gix-ref/tests/refs/file/store/iter.rs index 3d25a53b245..2b1e24fd7b7 100644 --- a/gix-ref/tests/refs/file/store/iter.rs +++ b/gix-ref/tests/refs/file/store/iter.rs @@ -28,7 +28,7 @@ mod with_namespace { let ns_two = gix_ref::namespace::expand("bar")?; let namespaced_refs = store .iter()? - .prefixed(ns_two.as_bstr().into())? + .prefixed(ns_two.as_bstr())? .map(Result::unwrap) .map(|r: gix_ref::Reference| r.name) .collect::>(); @@ -47,7 +47,7 @@ mod with_namespace { ); assert_eq!( store - .loose_iter_prefixed(ns_two.as_bstr().into())? + .loose_iter_prefixed(ns_two.as_bstr())? .map(Result::unwrap) .map(|r| r.name.into_inner()) .collect::>(), @@ -61,7 +61,7 @@ mod with_namespace { packed .as_ref() .expect("present") - .iter_prefixed(ns_two.as_bstr().into())? + .iter_prefixed(ns_two.as_bstr().to_owned())? .map(Result::unwrap) .map(|r| r.name.to_owned().into_inner()) .collect::>(), @@ -92,7 +92,7 @@ mod with_namespace { assert_eq!( store .iter()? - .prefixed(ns_one.as_bstr().into())? + .prefixed(ns_one.as_bstr())? .map(Result::unwrap) .map(|r: gix_ref::Reference| ( r.name.as_bstr().to_owned(), @@ -339,7 +339,7 @@ fn loose_iter_with_prefix() -> crate::Result { let store = store()?; let actual = store - .loose_iter_prefixed(b"refs/heads/".as_bstr().into())? + .loose_iter_prefixed(b"refs/heads/".as_bstr())? .collect::, _>>() .expect("no broken ref in this subset") .into_iter() @@ -369,7 +369,7 @@ fn loose_iter_with_partial_prefix_dir() -> crate::Result { let store = store()?; let actual = store - .loose_iter_prefixed(b"refs/heads".as_bstr().into())? + .loose_iter_prefixed(b"refs/heads".as_bstr())? .collect::, _>>() .expect("no broken ref in this subset") .into_iter() @@ -398,7 +398,7 @@ fn loose_iter_with_partial_prefix() -> crate::Result { let store = store()?; let actual = store - .loose_iter_prefixed(b"refs/heads/d".as_bstr().into())? + .loose_iter_prefixed(b"refs/heads/d".as_bstr())? .collect::, _>>() .expect("no broken ref in this subset") .into_iter() @@ -556,7 +556,7 @@ fn overlay_prefixed_iter() -> crate::Result { let store = store_at("make_packed_ref_repository_for_overlay.sh")?; let ref_names = store .iter()? - .prefixed(b"refs/heads/".as_bstr().into())? + .prefixed(b"refs/heads/".as_bstr())? .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) .collect::, _>>()?; let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); @@ -579,7 +579,7 @@ fn overlay_partial_prefix_iter() -> crate::Result { let store = store_at("make_packed_ref_repository_for_overlay.sh")?; let ref_names = store .iter()? - .prefixed(b"refs/heads/m".as_bstr().into())? // 'm' is partial + .prefixed(b"refs/heads/m".as_bstr())? // 'm' is partial .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) .collect::, _>>()?; let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); @@ -597,7 +597,7 @@ fn overlay_partial_prefix_iter_reproduce_1934() -> crate::Result { let ref_names = store .iter()? - .prefixed(b"refs/d".as_bstr().into())? + .prefixed(b"refs/d".as_bstr())? .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) .collect::, _>>()?; // Should not match `refs/heads/d1`. @@ -615,7 +615,7 @@ fn overlay_partial_prefix_iter_when_prefix_is_dir() -> crate::Result { let ref_names = store .iter()? - .prefixed(b"refs/prefix/feature".as_bstr().into())? + .prefixed(b"refs/prefix/feature".as_bstr())? .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) .collect::, _>>()?; assert_eq!( @@ -628,7 +628,7 @@ fn overlay_partial_prefix_iter_when_prefix_is_dir() -> crate::Result { let ref_names = store .iter()? - .prefixed(b"refs/prefix/feature/".as_bstr().into())? + .prefixed(b"refs/prefix/feature/".as_bstr())? .map(|r| r.map(|r| (r.name.as_bstr().to_owned(), r.target))) .collect::, _>>()?; assert_eq!( diff --git a/gix-ref/tests/refs/file/worktree.rs b/gix-ref/tests/refs/file/worktree.rs index b537286021e..d59adb906af 100644 --- a/gix-ref/tests/refs/file/worktree.rs +++ b/gix-ref/tests/refs/file/worktree.rs @@ -291,7 +291,7 @@ mod writable { assert_eq!( store .iter()? - .prefixed(b"refs/stacks/".as_bstr().into())? + .prefixed(b"refs/stacks/".as_bstr())? .map(Result::unwrap) .map(|r| (r.name.to_string(), r.target.to_string())) .collect::>(), @@ -572,7 +572,7 @@ mod writable { assert_eq!( store .iter()? - .prefixed(b"refs/stacks/".as_bstr().into())? + .prefixed(b"refs/stacks/".as_bstr())? .map(Result::unwrap) .map(|r| (r.name.to_string(), r.target.to_string())) .collect::>(), diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index 4b04909a339..37094233c82 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -349,7 +349,7 @@ impl ThreadSafeRepository { .and_then(|prefix| { let _span = gix_trace::detail!("find replacement objects"); let platform = refs.iter().ok()?; - let iter = platform.prefixed(prefix.clone().into()).ok()?; + let iter = platform.prefixed(&prefix).ok()?; let replacements = iter .filter_map(Result::ok) .filter_map(|r: gix_ref::Reference| { diff --git a/gix/src/reference/iter.rs b/gix/src/reference/iter.rs index 5ba5ccf7c5b..576a81a785a 100644 --- a/gix/src/reference/iter.rs +++ b/gix/src/reference/iter.rs @@ -48,8 +48,8 @@ impl Platform<'_> { /// These are of the form `refs/heads/` or `refs/remotes/origin`, and must not contain relative paths components like `.` or `..`. // TODO: Create a custom `Path` type that enforces the requirements of git naturally, this type is surprising possibly on windows // and when not using a trailing '/' to signal directories. - pub fn prefixed(&self, prefix: Cow<'_, BStr>) -> Result, init::Error> { - Ok(Iter::new(self.repo, self.platform.prefixed(prefix)?)) + pub fn prefixed<'a>(&self, prefix: impl Into>) -> Result, init::Error> { + Ok(Iter::new(self.repo, self.platform.prefixed(prefix.into())?)) } // TODO: tests @@ -57,10 +57,7 @@ impl Platform<'_> { /// /// They are all prefixed with `refs/tags`. pub fn tags(&self) -> Result, init::Error> { - Ok(Iter::new( - self.repo, - self.platform.prefixed(b"refs/tags/".as_bstr().into())?, - )) + Ok(Iter::new(self.repo, self.platform.prefixed(b"refs/tags/".as_bstr())?)) } // TODO: tests @@ -68,10 +65,7 @@ impl Platform<'_> { /// /// They are all prefixed with `refs/heads`. pub fn local_branches(&self) -> Result, init::Error> { - Ok(Iter::new( - self.repo, - self.platform.prefixed(b"refs/heads/".as_bstr().into())?, - )) + Ok(Iter::new(self.repo, self.platform.prefixed(b"refs/heads/".as_bstr())?)) } // TODO: tests @@ -81,7 +75,7 @@ impl Platform<'_> { pub fn remote_branches(&self) -> Result, init::Error> { Ok(Iter::new( self.repo, - self.platform.prefixed(b"refs/remotes/".as_bstr().into())?, + self.platform.prefixed(b"refs/remotes/".as_bstr())?, )) } } diff --git a/gix/tests/gix/repository/reference.rs b/gix/tests/gix/repository/reference.rs index ae6f7a80b5a..bd5079aabfc 100644 --- a/gix/tests/gix/repository/reference.rs +++ b/gix/tests/gix/repository/reference.rs @@ -48,7 +48,7 @@ mod set_namespace { assert_eq!( repo.references()? - .prefixed(b"refs/tags/".as_bstr().into())? + .prefixed(b"refs/tags/".as_bstr())? .filter_map(Result::ok) .map(|r| r.name().as_bstr().to_owned()) .collect::>(), @@ -125,7 +125,7 @@ mod iter_references { let repo = repo()?; assert_eq!( repo.references()? - .prefixed(b"refs/heads/".as_bstr().into())? + .prefixed(b"refs/heads/".as_bstr())? .filter_map(Result::ok) .map(|r| ( r.name().as_bstr().to_string(), @@ -156,7 +156,7 @@ mod iter_references { let repo = repo()?; assert_eq!( repo.references()? - .prefixed(b"refs/heads/".as_bstr().into())? + .prefixed(b"refs/heads/".as_bstr())? .peeled()? .filter_map(Result::ok) .map(|r| ( From a138b389adef5d6e4ab48d81930f88563c54c41c Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Tue, 8 Apr 2025 12:04:29 +0200 Subject: [PATCH 4/5] Fix ci failures --- gix-ref/src/store/file/loose/iter.rs | 5 ++++- gix-ref/src/store/file/overlay_iter.rs | 6 +++++- gix/src/open/repository.rs | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/gix-ref/src/store/file/loose/iter.rs b/gix-ref/src/store/file/loose/iter.rs index 0ec3a7ddb11..99ebc2d7f4d 100644 --- a/gix-ref/src/store/file/loose/iter.rs +++ b/gix-ref/src/store/file/loose/iter.rs @@ -87,7 +87,10 @@ impl file::Store { /// Return an iterator over all loose references that start with the given `prefix`. /// /// Otherwise it's similar to [`loose_iter()`][file::Store::loose_iter()]. - pub fn loose_iter_prefixed<'a>(&self, prefix: impl Into>) -> std::io::Result> { + pub fn loose_iter_prefixed<'a>( + &self, + prefix: impl Into>, + ) -> std::io::Result> { self.iter_prefixed_packed(prefix.into(), None) } } diff --git a/gix-ref/src/store/file/overlay_iter.rs b/gix-ref/src/store/file/overlay_iter.rs index c9de782d609..0920b7bff95 100644 --- a/gix-ref/src/store/file/overlay_iter.rs +++ b/gix-ref/src/store/file/overlay_iter.rs @@ -284,7 +284,11 @@ impl<'a> IterInfo<'a> { .peekable() } - fn from_prefix(base: &'a Path, prefix: impl Into>, precompose_unicode: bool) -> std::io::Result { + fn from_prefix( + base: &'a Path, + prefix: impl Into>, + precompose_unicode: bool, + ) -> std::io::Result { let prefix = prefix.into(); let prefix_path = gix_path::from_bstring(prefix.clone().into_owned()); if prefix_path.is_absolute() { diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index 37094233c82..fef838054e1 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -349,7 +349,7 @@ impl ThreadSafeRepository { .and_then(|prefix| { let _span = gix_trace::detail!("find replacement objects"); let platform = refs.iter().ok()?; - let iter = platform.prefixed(&prefix).ok()?; + let iter = platform.prefixed(*&prefix).ok()?; let replacements = iter .filter_map(Result::ok) .filter_map(|r: gix_ref::Reference| { From 44f4f8cdf622760737cf68f6579cab7022bbb536 Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Tue, 8 Apr 2025 12:50:01 +0200 Subject: [PATCH 5/5] Fix ci failures --- gix/src/open/repository.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index fef838054e1..ac61097e5a3 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -349,7 +349,7 @@ impl ThreadSafeRepository { .and_then(|prefix| { let _span = gix_trace::detail!("find replacement objects"); let platform = refs.iter().ok()?; - let iter = platform.prefixed(*&prefix).ok()?; + let iter = platform.prefixed(prefix.clone()).ok()?; let replacements = iter .filter_map(Result::ok) .filter_map(|r: gix_ref::Reference| {