@@ -27,47 +27,50 @@ where
27
27
let varname_x86 = "ProgramFiles(x86)" ;
28
28
29
29
// Should give a 32-bit program files path on a 32-bit system. We also check this on a 64-bit
30
- // system, even though it *should* equal the process's architecture specific variable, so that
30
+ // system, even though it *should* equal the process's architecture- specific variable, so that
31
31
// we cover the case of a parent process that passes down an overly sanitized environment that
32
32
// lacks the architecture-specific variable. On a 64-bit system, because parent and child
33
- // processes' architectures can be different, Windows sets the child's ProgramFiles variable
34
- // from the ProgramW6432 or ProgramFiles(x86) variable applicable to the child's architecture.
35
- // Only if the parent does not pass that down is the passed-down ProgramFiles variable even
36
- // used. But this behavior is not well known, so that situation does sometimes happen.
33
+ // processes' architectures can be different, Windows sets the child's `ProgramFiles` variable
34
+ // from whichever of the `ProgramW6432` or `ProgramFiles(x86)` variable corresponds to the
35
+ // child's architecture. Only if the parent does not pass down the architecture-specific
36
+ // variable corresponding to the child's architecture does the child receive its `ProgramFiles`
37
+ // variable from `ProgramFiles` as passed down by the parent. But this behavior is not well
38
+ // known. So the situation where a process only passes down `ProgramFiles` sometimes happens.
37
39
let varname_current = "ProgramFiles" ;
38
40
39
- // 64-bit relative bin dir. So far, this is always mingw64, not ucrt64, clang64, or clangarm64.
40
- let suffix_64 = Path :: new ( r"Git\mingw64\bin" ) ;
41
+ // 64-bit relative bin dirs. So far, this is always `mingw64` or `clangarm64`, not `urct64` or
42
+ // `clang64`. We check `clangarm64` before `mingw64`, because in the strage case that both are
43
+ // available, we don't want to skip over a native ARM64 executable for an emulated x86_64 one.
44
+ let suffixes_64 = [ r"Git\clangarm64\bin" , r"Git\mingw64\bin" ] . as_slice ( ) ;
41
45
42
- // 32-bit relative bin dir . So far, this is always mingw32, not clang32.
43
- let suffix_32 = Path :: new ( r"Git\mingw32\bin" ) ;
46
+ // 32-bit relative bin dirs . So far, this is only ever ` mingw32` , not ` clang32` .
47
+ let suffixes_32 = [ r"Git\mingw32\bin" ] . as_slice ( ) ;
44
48
45
49
// Whichever of the 64-bit or 32-bit relative bin better matches this process's architecture.
46
50
// Unlike the system architecture, the process architecture is always known at compile time.
47
51
#[ cfg( target_pointer_width = "64" ) ]
48
- let suffix_current = suffix_64 ;
52
+ let suffixes_current = suffixes_64 ;
49
53
#[ cfg( target_pointer_width = "32" ) ]
50
- let suffix_current = suffix_32 ;
54
+ let suffixes_current = suffixes_32 ;
51
55
52
56
let rules = [
53
- ( varname_64bit, suffix_64 ) ,
54
- ( varname_x86, suffix_32 ) ,
55
- ( varname_current, suffix_current ) ,
57
+ ( varname_64bit, suffixes_64 ) ,
58
+ ( varname_x86, suffixes_32 ) ,
59
+ ( varname_current, suffixes_current ) ,
56
60
] ;
57
61
58
62
let mut locations = vec ! [ ] ;
59
63
60
- for ( name, suffix) in rules {
61
- let Some ( pf) = var_os_func ( name) else { continue } ;
62
- let pf = Path :: new ( & pf) ;
63
- if pf. is_relative ( ) {
64
- // This shouldn't happen, but if it does then don't use the path. This is mainly in
65
- // case we are accidentally invoked with the environment variable set but empty.
64
+ for ( varname, suffixes) in rules {
65
+ let Some ( program_files_dir) = var_os_func ( varname) . map ( PathBuf :: from) . filter ( |p| p. is_absolute ( ) ) else {
66
+ // The environment variable is unset or somehow not an absolute path (e.g. an empty string).
66
67
continue ;
67
- }
68
- let location = pf. join ( suffix) ;
69
- if !locations. contains ( & location) {
70
- locations. push ( location) ;
68
+ } ;
69
+ for suffix in suffixes {
70
+ let location = program_files_dir. join ( suffix) ;
71
+ if !locations. contains ( & location) {
72
+ locations. push ( location) ;
73
+ }
71
74
}
72
75
}
73
76
@@ -81,11 +84,15 @@ pub(super) const EXE_NAME: &str = "git";
81
84
82
85
/// Invoke the git executable to obtain the origin configuration, which is cached and returned.
83
86
///
84
- /// The git executable is the one found in PATH or an alternative location.
87
+ /// The git executable is the one found in ` PATH` or an alternative location.
85
88
pub ( super ) static GIT_HIGHEST_SCOPE_CONFIG_PATH : Lazy < Option < BString > > = Lazy :: new ( exe_info) ;
86
89
90
+ // There are a number of ways to refer to the null device on Windows, but they are not all equally
91
+ // well supported. Git for Windows rejects `\\.\NUL` and `\\.\nul`. On Windows 11 ARM64 (and maybe
92
+ // some others), it rejects even the legacy name `NUL`, when capitalized. But it always accepts the
93
+ // lower-case `nul`, handling it in various path checks, some of which are done case-sensitively.
87
94
#[ cfg( windows) ]
88
- const NULL_DEVICE : & str = "NUL " ;
95
+ const NULL_DEVICE : & str = "nul " ;
89
96
#[ cfg( not( windows) ) ]
90
97
const NULL_DEVICE : & str = "/dev/null" ;
91
98
@@ -130,17 +137,17 @@ fn git_cmd(executable: PathBuf) -> Command {
130
137
} else {
131
138
"/" . into ( )
132
139
} ;
133
- // Git 2.8.0 and higher support --show-origin. The -l, -z , and --name-only options were
134
- // supported even before that. In contrast, --show-scope was introduced later, in Git 2.26.0.
140
+ // Git 2.8.0 and higher support ` --show-origin` . The `-l`, `-z` , and ` --name-only` options were
141
+ // supported even before that. In contrast, ` --show-scope` was introduced later, in Git 2.26.0.
135
142
// Low versions of Git are still sometimes used, and this is sometimes reasonable because
136
143
// downstream distributions often backport security patches without adding most new features.
137
- // So for now, we forgo the convenience of --show-scope for greater backward compatibility.
144
+ // So for now, we forgo the convenience of ` --show-scope` for greater backward compatibility.
138
145
//
139
- // Separately from that, we can't use --system here, because scopes treated higher than the
146
+ // Separately from that, we can't use ` --system` here, because scopes treated higher than the
140
147
// system scope are possible. This commonly happens on macOS with Apple Git, where the config
141
148
// file under `/Library` or `/Applications` is shown as an "unknown" scope but takes precedence
142
149
// over the system scope. Although `GIT_CONFIG_NOSYSTEM` suppresses this scope along with the
143
- // system scope, passing --system selects only the system scope and omit this "unknown" scope.
150
+ // system scope, passing ` --system` selects only the system scope and not this "unknown" scope.
144
151
cmd. args ( [ "config" , "-lz" , "--show-origin" , "--name-only" ] )
145
152
. current_dir ( cwd)
146
153
. env_remove ( "GIT_CONFIG" )
@@ -161,7 +168,7 @@ fn first_file_from_config_with_origin(source: &BStr) -> Option<&BStr> {
161
168
file[ ..end_pos] . as_bstr ( ) . into ( )
162
169
}
163
170
164
- /// Try to find the file that contains git configuration coming with the git installation.
171
+ /// Try to find the file that contains Git configuration coming with the Git installation.
165
172
///
166
173
/// This returns the configuration associated with the `git` executable found in the current `PATH`
167
174
/// or an alternative location, or `None` if no `git` executable was found or there were other
0 commit comments