Skip to content

Commit 5ae89cc

Browse files
committed
fix: Have gix_path::env::shell() look more carefully
It now prefers the `(git root)/bin/sh.exe` shim, falling back to the `(git root)/usr/bin/sh.exe` non-shim to support the Git for Windows SDK which doesn't have the shim. The reason to prefer the shim is that it sets environment variables, including prepending `bin` directories that provide tools one would expect to have when using it. Without this, common POSIX commands may be unavailable, or different and incompatible implementations of them may be found. In particular, if they are found in a different MSYS2 installation whose `msys-2.0.dll` is of a different version or otherwise a different build, then calling them directly may produce strange behavior. See: - https://cygwin.com/faq.html#faq.using.multiple-copies - GitoxideLabs#1862 (comment) Overall this makes things more robust than either preferring the non-shim or just doing a path search for `sh` as was done before that. But it exacerbates GitoxideLabs#1868 (as described there), so if the Git for Windows `sh.exe` shim continues to work as it currently does, then further improvements may be called for here.
1 parent adc2ce1 commit 5ae89cc

File tree

1 file changed

+30
-13
lines changed

1 file changed

+30
-13
lines changed

gix-path/src/env/mod.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,42 @@ pub fn installation_config_prefix() -> Option<&'static Path> {
3939
pub fn shell() -> &'static OsStr {
4040
static PATH: Lazy<OsString> = Lazy::new(|| {
4141
if cfg!(windows) {
42+
const MSYS_PREFIX_NAMES: &[&str] = &[
43+
"mingw64",
44+
"mingw32",
45+
"clangarm64",
46+
"clang64",
47+
"clang32",
48+
"ucrt64",
49+
"usr",
50+
];
51+
const RAW_SUFFIXES: &[&str] = &[
52+
"/bin/sh.exe", // Usually a shim, which currently we prefer, if available.
53+
"/usr/bin/sh.exe",
54+
];
55+
fn raw_join(path: &Path, raw_suffix: &str) -> OsString {
56+
let mut raw_path = OsString::from(path);
57+
raw_path.push(raw_suffix);
58+
raw_path
59+
}
4260
core_dir()
43-
.and_then(|git_core| {
44-
// Go up above something that is expected to be like mingw64/libexec/git-core.
45-
git_core.ancestors().nth(3)
61+
.filter(|core| core.is_absolute() && core.ends_with("libexec/git-core"))
62+
.and_then(|core| core.ancestors().nth(2))
63+
.filter(|prefix| {
64+
// Only use `libexec/git-core` from inside something `usr`-like, such as `mingw64`.
65+
MSYS_PREFIX_NAMES.iter().any(|name| prefix.ends_with(name))
4666
})
47-
.map(OsString::from)
48-
.map(|mut raw_path| {
49-
// Go down to where `sh.exe` usually is. To avoid breaking shell scripts that
50-
// wrongly assume the shell's own path contains no `\`, as well as to produce
67+
.and_then(|prefix| prefix.parent())
68+
.into_iter()
69+
.flat_map(|git_root| {
70+
// Enumerate the locations where `sh.exe` usually is. To avoid breaking shell
71+
// scripts that assume the shell's own path contains no `\`, and to produce
5172
// more readable messages, append literally with `/` separators. The path from
5273
// `git --exec-path` will already have all `/` separators (and no trailing `/`)
5374
// unless it was explicitly overridden to an unusual value via `GIT_EXEC_PATH`.
54-
raw_path.push("/usr/bin/sh.exe");
55-
raw_path
56-
})
57-
.filter(|raw_path| {
58-
// Check if there is something that could be a usable shell there.
59-
Path::new(raw_path).is_file()
75+
RAW_SUFFIXES.iter().map(|raw_suffix| raw_join(git_root, raw_suffix))
6076
})
77+
.find(|raw_path| Path::new(raw_path).is_file())
6178
.unwrap_or_else(|| "sh.exe".into())
6279
} else {
6380
"/bin/sh".into()

0 commit comments

Comments
 (0)