diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 6d0dc8de9e2fbd5066f6abf4e4eeaddeb8cd33bf..6477917dcdc485a5f2d23f16f3a5b3d7f94e9597 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -652,6 +652,7 @@ pub struct RealGitRepository { pub repository: Arc>, pub system_git_binary_path: Option, pub any_git_binary_path: PathBuf, + any_git_binary_help_output: Arc>>, executor: BackgroundExecutor, } @@ -670,6 +671,7 @@ impl RealGitRepository { system_git_binary_path, any_git_binary_path, executor, + any_git_binary_help_output: Arc::new(Mutex::new(None)), }) } @@ -680,6 +682,27 @@ impl RealGitRepository { .context("failed to read git work directory") .map(Path::to_path_buf) } + + async fn any_git_binary_help_output(&self) -> SharedString { + if let Some(output) = self.any_git_binary_help_output.lock().clone() { + return output; + } + let git_binary_path = self.any_git_binary_path.clone(); + let executor = self.executor.clone(); + let working_directory = self.working_directory(); + let output: SharedString = self + .executor + .spawn(async move { + GitBinary::new(git_binary_path, working_directory?, executor) + .run(["help", "-a"]) + .await + }) + .await + .unwrap_or_default() + .into(); + *self.any_git_binary_help_output.lock() = Some(output.clone()); + output + } } #[derive(Clone, Debug)] @@ -2290,48 +2313,50 @@ impl GitRepository for RealGitRepository { env: Arc>, ) -> BoxFuture<'_, Result<()>> { let working_directory = self.working_directory(); + let repository = self.repository.clone(); let git_binary_path = self.any_git_binary_path.clone(); let executor = self.executor.clone(); - self.executor - .spawn(async move { - let working_directory = working_directory?; - let git = GitBinary::new(git_binary_path, working_directory.clone(), executor) - .envs(HashMap::clone(&env)); - - let output = git.run(&["help", "-a"]).await?; - if !output.lines().any(|line| line.trim().starts_with("hook ")) { - log::warn!( - "git hook command not available, running the {} hook manually", - hook.as_str() - ); + let help_output = self.any_git_binary_help_output(); - let hook_abs_path = working_directory - .join(".git") - .join("hooks") - .join(hook.as_str()); - if hook_abs_path.is_file() { - let output = new_smol_command(&hook_abs_path) - .envs(env.iter()) - .current_dir(&working_directory) - .output() - .await?; - - anyhow::ensure!( - output.status.success(), - "{} hook failed:\n{}", - hook.as_str(), - String::from_utf8_lossy(&output.stderr) - ); - } + async move { + let working_directory = working_directory?; + if !help_output + .await + .lines() + .any(|line| line.trim().starts_with("hook ")) + { + let hook_abs_path = repository.lock().path().join("hooks").join(hook.as_str()); + if hook_abs_path.is_file() { + let output = self + .executor + .spawn( + new_smol_command(&hook_abs_path) + .envs(env.iter()) + .current_dir(&working_directory) + .output(), + ) + .await?; - return Ok(()); + if !output.status.success() { + return Err(GitBinaryCommandError { + stdout: String::from_utf8_lossy(&output.stdout).into_owned(), + stderr: String::from_utf8_lossy(&output.stderr).into_owned(), + status: output.status, + } + .into()); + } } - git.run(&["hook", "run", "--ignore-missing", hook.as_str()]) - .await?; - Ok(()) - }) - .boxed() + return Ok(()); + } + + let git = GitBinary::new(git_binary_path, working_directory, executor) + .envs(HashMap::clone(&env)); + git.run(&["hook", "run", "--ignore-missing", hook.as_str()]) + .await?; + Ok(()) + } + .boxed() } }