From 3f6ee524d7fd99146f98964aefc21be72de374c4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Sun, 22 Dec 2024 10:13:17 +0100 Subject: [PATCH 1/2] feat: add `io_err` module with classifiers. This makes it easier to portably identify IO errors, especially while the MSRV is not Rustc v1.83 which stabilizes the error kind variants. --- gix-fs/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gix-fs/src/lib.rs b/gix-fs/src/lib.rs index 187097573cb..9f77d8e8614 100644 --- a/gix-fs/src/lib.rs +++ b/gix-fs/src/lib.rs @@ -83,6 +83,18 @@ pub fn is_executable(metadata: &std::fs::Metadata) -> bool { (metadata.mode() & 0o100) != 0 } +/// Classifiers for IO-errors. +pub mod io_err { + use std::io::ErrorKind; + + /// Return `true` if `err` indicates that the entry doesn't exist on disk. `raw` is used as well + /// for additional checks while the variants are outside the MSRV. + pub fn is_not_found(err: ErrorKind, raw_err: Option<i32>) -> bool { + // TODO: use variant once MSRV is 1.83 + err == ErrorKind::NotFound || raw_err == Some(20) + } +} + #[cfg(not(unix))] /// Returns whether a a file has the executable permission set. pub fn is_executable(_metadata: &std::fs::Metadata) -> bool { From f3b76d0f52da8b4ea0f4d03490fc0c18fd7c3a96 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel <sebastian.thiel@icloud.com> Date: Sun, 22 Dec 2024 09:36:14 +0100 Subject: [PATCH 2/2] fix: Replacing a directory with non-directory will not cause an error anymore (#1735). --- gix-status/src/index_as_worktree/function.rs | 12 ++++++--- .../generated-archives/status_many.tar | Bin 144384 -> 205312 bytes gix-status/tests/fixtures/status_many.sh | 13 +++++++++ gix-status/tests/status/index_as_worktree.rs | 25 ++++++++++++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/gix-status/src/index_as_worktree/function.rs b/gix-status/src/index_as_worktree/function.rs index 5dbf16715ac..8fb300abdb5 100644 --- a/gix-status/src/index_as_worktree/function.rs +++ b/gix-status/src/index_as_worktree/function.rs @@ -356,8 +356,10 @@ impl<'index> State<'_, 'index> { { let worktree_path = match self.path_stack.verified_path(gix_path::from_bstr(rela_path).as_ref()) { Ok(path) => path, - Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(Some(Change::Removed.into())), - Err(err) => return Err(Error::Io(err)), + Err(err) if gix_fs::io_err::is_not_found(err.kind(), err.raw_os_error()) => { + return Ok(Some(Change::Removed.into())) + } + Err(err) => return Err(err.into()), }; self.symlink_metadata_calls.fetch_add(1, Ordering::Relaxed); let metadata = match gix_index::fs::Metadata::from_path_no_follow(worktree_path) { @@ -379,7 +381,9 @@ impl<'index> State<'_, 'index> { } } Ok(metadata) => metadata, - Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(Some(Change::Removed.into())), + Err(err) if gix_fs::io_err::is_not_found(err.kind(), err.raw_os_error()) => { + return Ok(Some(Change::Removed.into())) + } Err(err) => { return Err(err.into()); } @@ -539,7 +543,7 @@ where // conversion to bstr can never fail because symlinks are only used // on unix (by git) so no reason to use the try version here let symlink_path = - gix_path::to_unix_separators_on_windows(gix_path::into_bstr(std::fs::read_link(self.path)?)); + gix_path::to_unix_separators_on_windows(gix_path::into_bstr(std::fs::read_link(self.path).unwrap())); self.buf.extend_from_slice(&symlink_path); self.worktree_bytes.fetch_add(self.buf.len() as u64, Ordering::Relaxed); Stream { diff --git a/gix-status/tests/fixtures/generated-archives/status_many.tar b/gix-status/tests/fixtures/generated-archives/status_many.tar index 8e656859d8e5d1a1f98718e26d0255db17efa0cf..2f9698d313302c48bd939b74be637ac5ec7dccdf 100644 GIT binary patch delta 5860 zcmdT|dsGzH8Q*W%U0{{h0xP(R8wxBDmYvtmu#KrPK2i|cn)bw`i81?R@dW}_O-Z7j z#MY!yVN&+Av57fAFeeE?#yVAFPlCp9jIl&58bhfu28|lqLyQunZtpBRJAw@BY0v37 z{b%pqx!-T@_xRoK`|hl}5VP*!#I{sSNKJ$|tR>R`>XyvOm|Dop01eh+?gF)7?uI`2 zURiIFu1UM0EHr{jl?aY)CseBSO)3?9t5T^_IK0CWlxzMO7L-F{NbavA<kp4c7JvA8 zP!5eDxlcyOb#-J1-?#p=KLzF7tuKe<jv=|P!~3QkC{J0Kuy@wmXWWC%n+1d2WiQr# zz02}6=a{s#p$GDWywd=fkbf3#K#REtw1Sz0XiO+@LLg4CtOK>4%Wk6T+ti$|$tw8w z!q#ZPTnIWfm+c*X>_v}jp&?4J&=92-@Erg#Ty~DQDEMZ;2bva32^d-|_k-GRX@iH; zY?O+?X@<Z_oTP9Sj*}!yq7muj=HcasHT>f3uSM0Fv<-zoaM`uPZ>on6QU%LA7}T_w zO``8RP#A>#PPh@>Vm^q1W6lR{i#b<ZItMXWi}f;^TQLm%42k~+06mKlr+vswYdrRi zM#vmQu@$WSFre|9vtUs=Nf4uyBUqZHhLsZ_8y<!H*z&R!6;&RWquOJ-*Hlxr%A<>| zMDvQ}l@-;VRV&I}6>e#TpY`M#*qwsqU_wqBHUx-A7$#(9VZG2`fC8alGF**n$uwbz zTp8ku9=?g<Q!&7JLyQJ%H11XN#`pUFe@WJU(1h`(BR8PYcu30|r$l3Z#4V=IG!gNG z_m%94^COnA(tOGOBtE<8S~PFW)T#Mw?@*K<(JHj;_t9YG{TVMt`4P)#2~)p6n$OM| z()bb2!jdJTB=dZ#AMq~y_WV8Qb#2m|^ZkD67-Yn4G&=?v+iUMK_!0lYFT;6FqY)`- zd`p8YY`+oRXlzwC8V%Y;qaHmy8iV-}1#Oa+-TO3t#K!RavN#`-*ZaizfIL6D5B!L( zVflV>y-!~666f*q`ar9OH=K(@oA%2>J-mi9#+Q#pYa2z9{~fJgf^<IpCdwX@F&K(% zHlTlocnE-yIuYd*-k`o1wL1sXV}d>(y8;OmYiCJ{Vknyhosl&{(lPi4_-~rAU!~Eb zGXg=gEIq6p2M}nOj>V@?_5zzJcUEQPGLPwj@)`?aqwf}y)X)pbRi4Ua4wuK`F0Znz zF0b)fyyYB`LDiWuf{81}s-*~qQQ3wwOfo?7ZSu=J{<|_@_HeW~Uksrp*@%EH8E`6N z5;;0^Fa#S)YoZb94!7InEl1jyuP{}wa*En|Ts0L{Yeg&0L+Th-h+1{ao9K*_2^_yI zL)i#1jSd_4z7DcAVo8X9qWU+(W6IORrQr{AMwF0HNs4#eXbj3ZwnIp6?g+V1NlJLM zVZSIRjv=|VBjma|(%E=qlNH%!UE!&&@widxQw87s^VY%O_()G%2s?8WHdxLQGW(AQ z_dsJvZZ(p-wVYAj1uC0~(0!4&v(2!1`J7jsRj)6K-#G6>XU9Ega}|Zwj%*G*_2#r^ z4{Y7o{P%9_*1s`(Y73B3D#C>8ew6;iJF$-}p1x~nOZ}vk)jMB*_3WjS`>S@Z%+vpo zK0ajr>B)P({N0Im`!GH0Oo>WiIW31J2G_86K(Dhhq8-tTj!3T_8PG+jZDd41uMFt4 zYMBvdD3@hP+QX6_hsW(?TrLOWU>UEIrb)(L$hv9V?shP4mSjaXo>F8ZDQ)pvAKZ~n zP6@#IN^lKhBP6Dl1|}-`cAbn_f;<(12(TUkit*ShW10vx5jqe8?1O+-X`ezFlZk66 zHoTD?0+<r0Rt9<Hjd&<ihR_luSi!V7E2+oBG-cEd3B~#9aScj$Qo4R)9Fu0qpd3O? zhx39tpjI-dM1uHzsA_6Sk_qor3V{?Q*!>cW^W}@qV<h!vkK?>H8Kh8r37bTaTRK4{ z9qOgm`F-_Bb<|acwBX?>WzFYFD9(r98bLzYM<yAfLv9W0I6*ml9Wq2r5U7caRDU{U z1WFK8<nw-50{DF-;2NBWT;q;N5YDHYl5WFqtGp;_g!%#=gl$lNIQMSTou&?ML7Uz9 zeUoCL<7PeiXQfacjHDQ9%R;>T-zt_bD;7-jk;76TBhnDR!dj;*lO<P%=!30CsGKJ& zL(a(%1FoSNJR-j?%Lv3TJTh6XAiz|yQRjT8<bxuhzAb=dkJ+wR3(CrCtQF2BsI#fI zvTR+_%pe@?hYq%U0E)+Gz(!-OcUG_JlG1`(w4!EybPI<1O~mk^F9n!@Veri6_jMh3 z;+%WW=Upo(ZZBeEe?5@*sr!ZJx{FI@1$s9;m~iyVxTu7jzZ|FO`(D`h(A@j-Nn`iM z4L?~~aP8TQ<W+O$n;!Ty*}LnlD{mZrd;O-IzQ-S%R@~;!YB&>h>4c}CbCbS(XTuMh zn-`teE#CHncRQ)l>kBroedTE4)CHBZmY%wlYg;n)WJT%pq0{AWKGS+#%PwqwaQn1* z>p$0Sc`m`TqyDqU7GI1z`d)Dl|61EaibWX1!i_3&EE}1TY6J)c@(QjY<y@`7lnFK@ z(*?C3DCf!HD~cFCm&;n{@p@Ue!%27xX%}V33mJ#a?R7Jx*N(g06yvnBE|Meg^kDdO zD17v&4w|GGJd|n$_zETfH{s%ptd9<=Vp5);&!r7zC3jcPJG6B9cgJTIz1z0ti}vrf zJRYUm6f^yd*uK4_=)apXuP>m}7Oq@@aVpn<?2WUC%#pKs%!nYN?+UW}#=d^&<RD!Y zgA~4=O&BLgeX?326(Xy3LAL!EDj8BgCi+*I3h^Us!6V;{5@lNkQ7H>I6iMTlGANsJ zXt<$}J(d}fa5Q2Py79$H=>}cBWO(FsLwl@*oxvSm+-|emyrjcPdtHTIJ4vuC!BS4b z!PvbX(&LoUjecqxGrD+^6e+bYqCcY{k8$8Mj#I>f&iR&tqEg2i=K3d>ODDZ!|K;kX zt$ADauDnq6<A2Uk%`VgHMaq5*cal~;R5-1YQMbk-vY8&rN;;i{6SWG39>z`6b}xgN z=3s1Y+*8QVjEiMR7fFT96gfhN!}k@WK<ZP0r3*h2ro7U%`|3^GzhYN^{EO}j>D=@a zMd|}*tOn7HO^PS4UyiXz*T{FEt1#Pd)xmHP>P5%G7ETD?jzicic^_04ZvUufBc-eP zfBI2RsgFsKj*8!>LoF%AO62E3`NM;ZnTT#xa>sxWDPu9;pRb3)I+Gztc6G)id{p8j aY~i}cgC-gTy&A57U^)h8Lcd&_y7=FOE^dAR delta 5910 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 K;QhUf+x`u%v3cwO diff --git a/gix-status/tests/fixtures/status_many.sh b/gix-status/tests/fixtures/status_many.sh index 9a11f3f4f8d..e7a67475920 100755 --- a/gix-status/tests/fixtures/status_many.sh +++ b/gix-status/tests/fixtures/status_many.sh @@ -39,3 +39,16 @@ cp -R changed-and-untracked changed-and-untracked-and-renamed echo change >> content-with-rewrite ) + +cp -R changed-and-untracked replace-dir-with-file +(cd replace-dir-with-file + git checkout executable + rm untracked dir/untracked + + mkdir dir/sub + touch dir/sub/nested + git add dir && git commit -m "add file in sub-directory" + + rm -Rf dir/ + touch dir +) diff --git a/gix-status/tests/status/index_as_worktree.rs b/gix-status/tests/status/index_as_worktree.rs index 27a3a272c0b..dc4b357902e 100644 --- a/gix-status/tests/status/index_as_worktree.rs +++ b/gix-status/tests/status/index_as_worktree.rs @@ -243,6 +243,31 @@ fn removed() { ); } +#[test] +fn replace_dir_with_file() { + let out = fixture_filtered_detailed( + "status_many", + "replace-dir-with-file", + &[], + &[ + (BStr::new(b"dir/content"), 0, status_removed()), + (BStr::new(b"dir/content2"), 1, status_removed()), + (BStr::new(b"dir/sub/nested"), 2, status_removed()), + ], + |_| {}, + false, + ); + assert_eq!( + out, + Outcome { + entries_to_process: 5, + entries_processed: 5, + symlink_metadata_calls: if cfg!(windows) { 5 } else { 4 }, + ..Default::default() + } + ); +} + #[test] fn subomdule_nochange() { assert_eq!(