@@ -59,6 +59,7 @@ impl Drop for GitDaemon {
59
59
}
60
60
61
61
static SCRIPT_IDENTITY : Lazy < Mutex < BTreeMap < PathBuf , u32 > > > = Lazy :: new ( || Mutex :: new ( BTreeMap :: new ( ) ) ) ;
62
+
62
63
static EXCLUDE_LUT : Lazy < Mutex < Option < gix_worktree:: Stack > > > = Lazy :: new ( || {
63
64
let cache = ( || {
64
65
let ( repo_path, _) = gix_discover:: upwards ( Path :: new ( "." ) ) . ok ( ) ?;
@@ -86,8 +87,31 @@ static EXCLUDE_LUT: Lazy<Mutex<Option<gix_worktree::Stack>>> = Lazy::new(|| {
86
87
} ) ( ) ;
87
88
Mutex :: new ( cache)
88
89
} ) ;
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
+
89
113
/// 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" ) ) ;
91
115
92
116
/// Define how [`scripted_fixture_writable_with_args()`] uses produces the writable copy.
93
117
pub enum Creation {
@@ -116,10 +140,8 @@ pub fn should_skip_as_git_version_is_smaller_than(major: u8, minor: u8, patch: u
116
140
* GIT_VERSION < ( major, minor, patch)
117
141
}
118
142
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 ( ) ?;
123
145
git_version_from_bytes ( & output. stdout )
124
146
}
125
147
@@ -173,25 +195,14 @@ impl Drop for AutoRevertToPreviousCWD {
173
195
174
196
/// Run `git` in `working_dir` with all provided `args`.
175
197
pub 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 )
177
199
. current_dir ( working_dir)
178
200
. args ( args)
179
201
. status ( )
180
202
}
181
203
182
204
/// Spawn a git daemon process to host all repository at or below `working_dir`.
183
205
pub 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
- } ) ;
195
206
let mut ports: Vec < _ > = ( 9419u16 ..9419 + 100 ) . collect ( ) ;
196
207
fastrand:: shuffle ( & mut ports) ;
197
208
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
200
211
listener. local_addr ( ) . expect ( "listener address is available" ) . port ( )
201
212
} ;
202
213
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 ( ) ?;
208
220
209
221
let server_addr = addr_at ( free_port) ;
210
222
for time in gix_lock:: backoff:: Exponential :: default_with_random ( ) {
@@ -556,7 +568,7 @@ fn scripted_fixture_read_only_with_args_inner(
556
568
Err ( err)
557
569
if err. kind ( ) == std:: io:: ErrorKind :: PermissionDenied || err. raw_os_error ( ) == Some ( 193 ) /* windows */ =>
558
570
{
559
- cmd = std:: process:: Command :: new ( "bash" ) ;
571
+ cmd = std:: process:: Command :: new ( bash_program ( ) ) ;
560
572
configure_command ( cmd. arg ( script_absolute_path) , & args, & script_result_directory) . output ( ) ?
561
573
}
562
574
Err ( err) => return Err ( err. into ( ) ) ,
@@ -632,6 +644,22 @@ fn configure_command<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
632
644
. env ( "GIT_CONFIG_VALUE_3" , "always" )
633
645
}
634
646
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
+
635
663
fn write_failure_marker ( failure_marker : & Path ) {
636
664
std:: fs:: write ( failure_marker, [ ] ) . ok ( ) ;
637
665
}
@@ -738,7 +766,10 @@ fn populate_meta_dir(destination_dir: &Path, script_identity: u32) -> std::io::R
738
766
) ?;
739
767
std:: fs:: write (
740
768
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 ,
742
773
) ?;
743
774
Ok ( meta_dir)
744
775
}
@@ -951,7 +982,7 @@ mod tests {
951
982
let temp = tempfile:: TempDir :: new ( ) . expect ( "can create temp dir" ) ;
952
983
populate_ad_hoc_config_files ( temp. path ( ) ) ;
953
984
954
- let mut cmd = std:: process:: Command :: new ( "git" ) ;
985
+ let mut cmd = std:: process:: Command :: new ( GIT_PROGRAM ) ;
955
986
cmd. env ( "GIT_CONFIG_SYSTEM" , SCOPE_ENV_VALUE ) ;
956
987
cmd. env ( "GIT_CONFIG_GLOBAL" , SCOPE_ENV_VALUE ) ;
957
988
configure_command ( & mut cmd, [ "config" , "-l" , "--show-origin" ] , temp. path ( ) ) ;
@@ -968,4 +999,43 @@ mod tests {
968
999
assert_eq ! ( lines, Vec :: <& str >:: new( ) , "should be no config variables from files" ) ;
969
1000
assert_eq ! ( status, 0 , "reading the config should succeed" ) ;
970
1001
}
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
+ }
971
1041
}
0 commit comments