@@ -59,6 +59,7 @@ impl Drop for GitDaemon {
5959}
6060
6161static SCRIPT_IDENTITY : Lazy < Mutex < BTreeMap < PathBuf , u32 > > > = Lazy :: new ( || Mutex :: new ( BTreeMap :: new ( ) ) ) ;
62+
6263static EXCLUDE_LUT : Lazy < Mutex < Option < gix_worktree:: Stack > > > = Lazy :: new ( || {
6364 let cache = ( || {
6465 let ( repo_path, _) = gix_discover:: upwards ( Path :: new ( "." ) ) . ok ( ) ?;
@@ -86,8 +87,31 @@ static EXCLUDE_LUT: Lazy<Mutex<Option<gix_worktree::Stack>>> = Lazy::new(|| {
8687 } ) ( ) ;
8788 Mutex :: new ( cache)
8889} ) ;
90+
91+ #[ cfg( windows) ]
92+ const GIT_PROGRAM : & str = "git.exe" ;
93+ #[ cfg( not( windows) ) ]
94+ const GIT_PROGRAM : & str = "git" ;
95+
96+ static GIT_CORE_DIR : Lazy < PathBuf > = Lazy :: new ( || {
97+ let output = std:: process:: Command :: new ( GIT_PROGRAM )
98+ . arg ( "--exec-path" )
99+ . output ( )
100+ . expect ( "can execute `git --exec-path`" ) ;
101+
102+ assert ! ( output. status. success( ) , "`git --exec-path` failed" ) ;
103+
104+ output
105+ . stdout
106+ . strip_suffix ( b"\n " )
107+ . expect ( "`git --exec-path` output to be well-formed" )
108+ . to_os_str ( )
109+ . expect ( "no invalid UTF-8 in `--exec-path` except as OS allows" )
110+ . into ( )
111+ } ) ;
112+
89113/// The major, minor and patch level of the git version on the system.
90- pub static GIT_VERSION : Lazy < ( u8 , u8 , u8 ) > = Lazy :: new ( || parse_gix_version ( ) . expect ( "git version to be parsable" ) ) ;
114+ pub static GIT_VERSION : Lazy < ( u8 , u8 , u8 ) > = Lazy :: new ( || parse_git_version ( ) . expect ( "git version to be parsable" ) ) ;
91115
92116/// Define how [`scripted_fixture_writable_with_args()`] uses produces the writable copy.
93117pub enum Creation {
@@ -116,10 +140,8 @@ pub fn should_skip_as_git_version_is_smaller_than(major: u8, minor: u8, patch: u
116140 * GIT_VERSION < ( major, minor, patch)
117141}
118142
119- fn parse_gix_version ( ) -> Result < ( u8 , u8 , u8 ) > {
120- let gix_program = cfg ! ( windows) . then ( || "git.exe" ) . unwrap_or ( "git" ) ;
121- let output = std:: process:: Command :: new ( gix_program) . arg ( "--version" ) . output ( ) ?;
122-
143+ fn parse_git_version ( ) -> Result < ( u8 , u8 , u8 ) > {
144+ let output = std:: process:: Command :: new ( GIT_PROGRAM ) . arg ( "--version" ) . output ( ) ?;
123145 git_version_from_bytes ( & output. stdout )
124146}
125147
@@ -173,25 +195,14 @@ impl Drop for AutoRevertToPreviousCWD {
173195
174196/// Run `git` in `working_dir` with all provided `args`.
175197pub fn run_git ( working_dir : & Path , args : & [ & str ] ) -> std:: io:: Result < std:: process:: ExitStatus > {
176- std:: process:: Command :: new ( "git" )
198+ std:: process:: Command :: new ( GIT_PROGRAM )
177199 . current_dir ( working_dir)
178200 . args ( args)
179201 . status ( )
180202}
181203
182204/// Spawn a git daemon process to host all repository at or below `working_dir`.
183205pub fn spawn_git_daemon ( working_dir : impl AsRef < Path > ) -> std:: io:: Result < GitDaemon > {
184- static EXEC_PATH : Lazy < PathBuf > = Lazy :: new ( || {
185- let path = std:: process:: Command :: new ( "git" )
186- . arg ( "--exec-path" )
187- . stderr ( std:: process:: Stdio :: null ( ) )
188- . output ( )
189- . expect ( "can execute `git --exec-path`" )
190- . stdout ;
191- String :: from_utf8 ( path. trim ( ) . into ( ) )
192- . expect ( "no invalid UTF8 in exec-path" )
193- . into ( )
194- } ) ;
195206 let mut ports: Vec < _ > = ( 9419u16 ..9419 + 100 ) . collect ( ) ;
196207 fastrand:: shuffle ( & mut ports) ;
197208 let addr_at = |port| std:: net:: SocketAddr :: from ( ( [ 127 , 0 , 0 , 1 ] , port) ) ;
@@ -200,11 +211,12 @@ pub fn spawn_git_daemon(working_dir: impl AsRef<Path>) -> std::io::Result<GitDae
200211 listener. local_addr ( ) . expect ( "listener address is available" ) . port ( )
201212 } ;
202213
203- let child = std:: process:: Command :: new ( EXEC_PATH . join ( if cfg ! ( windows) { "git-daemon.exe" } else { "git-daemon" } ) )
204- . current_dir ( working_dir)
205- . args ( [ "--verbose" , "--base-path=." , "--export-all" , "--user-path" ] )
206- . arg ( format ! ( "--port={free_port}" ) )
207- . spawn ( ) ?;
214+ let child =
215+ std:: process:: Command :: new ( GIT_CORE_DIR . join ( if cfg ! ( windows) { "git-daemon.exe" } else { "git-daemon" } ) )
216+ . current_dir ( working_dir)
217+ . args ( [ "--verbose" , "--base-path=." , "--export-all" , "--user-path" ] )
218+ . arg ( format ! ( "--port={free_port}" ) )
219+ . spawn ( ) ?;
208220
209221 let server_addr = addr_at ( free_port) ;
210222 for time in gix_lock:: backoff:: Exponential :: default_with_random ( ) {
@@ -556,7 +568,7 @@ fn scripted_fixture_read_only_with_args_inner(
556568 Err ( err)
557569 if err. kind ( ) == std:: io:: ErrorKind :: PermissionDenied || err. raw_os_error ( ) == Some ( 193 ) /* windows */ =>
558570 {
559- cmd = std:: process:: Command :: new ( "bash" ) ;
571+ cmd = std:: process:: Command :: new ( bash_program ( ) ) ;
560572 configure_command ( cmd. arg ( script_absolute_path) , & args, & script_result_directory) . output ( ) ?
561573 }
562574 Err ( err) => return Err ( err. into ( ) ) ,
@@ -632,6 +644,22 @@ fn configure_command<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
632644 . env ( "GIT_CONFIG_VALUE_3" , "always" )
633645}
634646
647+ fn bash_program ( ) -> & ' static Path {
648+ if cfg ! ( windows) {
649+ static GIT_BASH : Lazy < Option < PathBuf > > = Lazy :: new ( || {
650+ GIT_CORE_DIR
651+ . parent ( ) ?
652+ . parent ( ) ?
653+ . parent ( )
654+ . map ( |installation_dir| installation_dir. join ( "bin" ) . join ( "bash.exe" ) )
655+ . filter ( |bash| bash. is_file ( ) )
656+ } ) ;
657+ GIT_BASH . as_deref ( ) . unwrap_or ( Path :: new ( "bash.exe" ) )
658+ } else {
659+ Path :: new ( "bash" )
660+ }
661+ }
662+
635663fn write_failure_marker ( failure_marker : & Path ) {
636664 std:: fs:: write ( failure_marker, [ ] ) . ok ( ) ;
637665}
@@ -738,7 +766,10 @@ fn populate_meta_dir(destination_dir: &Path, script_identity: u32) -> std::io::R
738766 ) ?;
739767 std:: fs:: write (
740768 meta_dir. join ( META_GIT_VERSION ) ,
741- std:: process:: Command :: new ( "git" ) . arg ( "--version" ) . output ( ) ?. stdout ,
769+ std:: process:: Command :: new ( GIT_PROGRAM )
770+ . arg ( "--version" )
771+ . output ( ) ?
772+ . stdout ,
742773 ) ?;
743774 Ok ( meta_dir)
744775}
@@ -951,7 +982,7 @@ mod tests {
951982 let temp = tempfile:: TempDir :: new ( ) . expect ( "can create temp dir" ) ;
952983 populate_ad_hoc_config_files ( temp. path ( ) ) ;
953984
954- let mut cmd = std:: process:: Command :: new ( "git" ) ;
985+ let mut cmd = std:: process:: Command :: new ( GIT_PROGRAM ) ;
955986 cmd. env ( "GIT_CONFIG_SYSTEM" , SCOPE_ENV_VALUE ) ;
956987 cmd. env ( "GIT_CONFIG_GLOBAL" , SCOPE_ENV_VALUE ) ;
957988 configure_command ( & mut cmd, [ "config" , "-l" , "--show-origin" ] , temp. path ( ) ) ;
@@ -968,4 +999,43 @@ mod tests {
968999 assert_eq ! ( lines, Vec :: <& str >:: new( ) , "should be no config variables from files" ) ;
9691000 assert_eq ! ( status, 0 , "reading the config should succeed" ) ;
9701001 }
1002+
1003+ #[ test]
1004+ #[ cfg( windows) ]
1005+ fn bash_program_ok_for_platform ( ) {
1006+ let path = bash_program ( ) ;
1007+ assert ! ( path. is_absolute( ) ) ;
1008+
1009+ let for_version = std:: process:: Command :: new ( path)
1010+ . arg ( "--version" )
1011+ . output ( )
1012+ . expect ( "can pass it `--version`" ) ;
1013+ assert ! ( for_version. status. success( ) , "passing `--version` succeeds" ) ;
1014+ let version_line = for_version
1015+ . stdout
1016+ . lines ( )
1017+ . nth ( 0 )
1018+ . expect ( "`--version` output has first line" ) ;
1019+ assert ! (
1020+ version_line. ends_with( b"-pc-msys)" ) , // On Windows, "-pc-linux-gnu)" would be WSL.
1021+ "it is an MSYS bash (such as Git Bash)"
1022+ ) ;
1023+
1024+ let for_uname_os = std:: process:: Command :: new ( path)
1025+ . args ( [ "-c" , "uname -o" ] )
1026+ . output ( )
1027+ . expect ( "can tell it to run `uname -o`" ) ;
1028+ assert ! ( for_uname_os. status. success( ) , "telling it to run `uname -o` succeeds" ) ;
1029+ assert_eq ! (
1030+ for_uname_os. stdout. trim_end( ) ,
1031+ b"Msys" ,
1032+ "it runs commands in an MSYS environment"
1033+ ) ;
1034+ }
1035+
1036+ #[ test]
1037+ #[ cfg( not( windows) ) ]
1038+ fn bash_program_ok_for_platform ( ) {
1039+ assert_eq ! ( bash_program( ) , Path :: new( "bash" ) ) ;
1040+ }
9711041}
0 commit comments