From 5411397f9a642e2268903e3a78a264f5c11afb80 Mon Sep 17 00:00:00 2001 From: hamflx Date: Thu, 2 Feb 2023 17:33:57 +0800 Subject: [PATCH] Fix commit hooks error and "no such file" error on Windows with wsl2 installed. (#1532) * use git bash instead of wsl bash * add unittest for some simple shell commands. * fix incompatible unitest with macOS --- CHANGELOG.md | 1 + asyncgit/src/sync/hooks.rs | 54 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d9f2278..ff5d7329 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * crash on branches popup in small terminal ([#1470](https://github.com/extrawurst/gitui/issues/1470)) * `edit` command duplication ([#1489](https://github.com/extrawurst/gitui/issues/1489)) * syntax errors in `key_bindings.ron` will be logged ([#1491](https://github.com/extrawurst/gitui/issues/1491)) +* commit hooks report "command not found" on Windows with wsl2 installed ([#1528](https://github.com/extrawurst/gitui/issues/1528)) ### Changed * minimum supported rust version bumped to 1.64 (thank you `clap`) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index 3a4b3758..f4588715 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -66,11 +66,15 @@ impl HookPaths { /// see pub fn run_hook(&self, args: &[&str]) -> Result { let arg_str = format!("{:?} {}", self.hook, args.join(" ")); - let bash_args = vec!["-c".to_string(), arg_str]; + // Use -l to avoid "command not found" on Windows. + let bash_args = + vec!["-l".to_string(), "-c".to_string(), arg_str]; log::trace!("run hook '{:?}' in '{:?}'", self.hook, self.pwd); - let output = Command::new("bash") + let git_bash = find_bash_executable() + .unwrap_or_else(|| PathBuf::from("bash")); + let output = Command::new(git_bash) .args(bash_args) .current_dir(&self.pwd) // This call forces Command to handle the Path environment correctly on windows, @@ -184,6 +188,29 @@ const fn is_executable(_: &Path) -> bool { true } +// Find bash.exe, and avoid finding wsl's bash.exe on Windows. +// None for non-Windows. +fn find_bash_executable() -> Option { + if cfg!(windows) { + Command::new("where.exe") + .arg("git") + .output() + .ok() + .map(|out| { + PathBuf::from(Into::::into( + String::from_utf8_lossy(&out.stdout), + )) + }) + .as_deref() + .and_then(Path::parent) + .and_then(Path::parent) + .map(|p| p.join("usr/bin/bash.exe")) + .filter(|p| p.exists()) + } else { + None + } +} + #[cfg(test)] mod tests { use super::*; @@ -257,6 +284,29 @@ exit 0 assert_eq!(msg, String::from("test")); } + #[test] + fn test_hooks_commit_msg_with_shell_command_ok() { + let (_td, repo) = repo_init().unwrap(); + let root = repo.path().parent().unwrap(); + let repo_path: &RepoPath = + &root.as_os_str().to_str().unwrap().into(); + + let hook = br#"#!/bin/sh +COMMIT_MSG="$(cat "$1")" +printf "$COMMIT_MSG" | sed 's/sth/shell_command/g' >"$1" +exit 0 + "#; + + create_hook(repo_path, HOOK_COMMIT_MSG, hook); + + let mut msg = String::from("test_sth"); + let res = hooks_commit_msg(repo_path, &mut msg).unwrap(); + + assert_eq!(res, HookResult::Ok); + + assert_eq!(msg, String::from("test_shell_command")); + } + #[test] fn test_pre_commit_sh() { let (_td, repo) = repo_init().unwrap();