From 2d90eb5c56b0e072d6e7dfa5db14396715bf9e53 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 15 Jan 2025 18:00:00 +0000 Subject: [PATCH 01/55] use /bin/sh for git-hooks --- git2-hooks/src/hookspath.rs | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 3648676ee2..e709486e03 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -3,7 +3,6 @@ use git2::Repository; use crate::{error::Result, HookResult, HooksError}; use std::{ - env, path::{Path, PathBuf}, process::Command, str::FromStr, @@ -111,16 +110,13 @@ impl HookPaths { let arg_str = format!("{:?} {}", hook, args.join(" ")); // Use -l to avoid "command not found" on Windows. - let bash_args = + let shell_args = vec!["-l".to_string(), "-c".to_string(), arg_str]; log::trace!("run hook '{:?}' in '{:?}'", hook, self.pwd); - let git_shell = find_bash_executable() - .or_else(find_default_unix_shell) - .unwrap_or_else(|| "bash".into()); - let output = Command::new(git_shell) - .args(bash_args) + let output = Command::new(shell()) + .args(shell_args) .with_no_window() .current_dir(&self.pwd) // This call forces Command to handle the Path environment correctly on windows, @@ -168,15 +164,20 @@ fn is_executable(path: &Path) -> bool { } #[cfg(windows)] -/// windows does not consider bash scripts to be executable so we consider everything +/// windows does not consider shell scripts to be executable so we consider everything /// to be executable (which is not far from the truth for windows platform.) 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 { +/// Return the shell that Git would use, the shell to execute commands from. +/// +/// On Windows, this is the full path to `sh.exe` bundled with it, and on +/// Unix it's `/bin/sh` as posix compatible shell. +/// If the bundled shell on Windows cannot be found, `sh` is returned as the name of a shell +/// as it could possibly be found in `PATH`. +/// Note that the returned path might not be a path on disk. +pub fn shell() -> PathBuf { if cfg!(windows) { Command::new("where.exe") .arg("git") @@ -190,18 +191,14 @@ fn find_bash_executable() -> Option { .as_deref() .and_then(Path::parent) .and_then(Path::parent) - .map(|p| p.join("usr/bin/bash.exe")) + .map(|p| p.join("usr/bin/sh.exe")) .filter(|p| p.exists()) + .unwrap_or_else(|| "sh".into()) } else { - None + "/bin/sh".into() } } -// Find default shell on Unix-like OS. -fn find_default_unix_shell() -> Option { - env::var_os("SHELL").map(PathBuf::from) -} - trait CommandExt { /// The process is a console application that is being run without a /// console window. Therefore, the console handle for the application is From 6a4b9d6db35745d80b8844aa0f796294ceba85fd Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 15 Jan 2025 19:00:00 +0000 Subject: [PATCH 02/55] update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05823362cf..f52cd1c76d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +* use /bin/sh for git-hooks [[@Joshix](https://github.com/Joshix-1)] ([#2483](https://github.com/extrawurst/gitui/pull/2483)) ## [0.27.0] - 2024-01-14 From 8571406ff609843ee4c9761da9686bd7e6063ddb Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 15 Jan 2025 20:00:00 +0000 Subject: [PATCH 03/55] fix escape bugs in hooks --- asyncgit/src/sync/hooks.rs | 13 ++++++++++--- asyncgit/src/sync/mod.rs | 10 +++++++++- git2-hooks/src/hookspath.rs | 24 ++++++++++++++++++------ git2-hooks/src/lib.rs | 10 +++++----- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index 3e82cf6f8d..3c01ee91b2 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -74,8 +74,15 @@ pub fn hooks_prepare_commit_msg( #[cfg(test)] mod tests { + use git2::Repository; + use tempfile::TempDir; + use super::*; - use crate::sync::tests::repo_init; + use crate::sync::tests::repo_init_with_prefix; + + fn repo_init() -> Result<(TempDir, Repository)> { + repo_init_with_prefix("gitui $# ") + } #[test] fn test_post_commit_hook_reject_in_subfolder() { @@ -120,7 +127,7 @@ mod tests { crate::sync::utils::repo_work_dir(repo_path).unwrap(); let hook = b"#!/bin/sh - echo $(pwd) + echo \"$(pwd)\" exit 1 "; @@ -146,7 +153,7 @@ mod tests { let root = repo.path().parent().unwrap(); let hook = b"#!/bin/sh - echo 'msg' > $1 + echo 'msg' > \"$1\" echo 'rejected' exit 1 "; diff --git a/asyncgit/src/sync/mod.rs b/asyncgit/src/sync/mod.rs index 3c4b106c3e..feb69fba10 100644 --- a/asyncgit/src/sync/mod.rs +++ b/asyncgit/src/sync/mod.rs @@ -144,11 +144,19 @@ pub mod tests { /// pub fn repo_init() -> Result<(TempDir, Repository)> { + repo_init_with_prefix("gitui") + } + + /// + #[inline] + pub fn repo_init_with_prefix( + prefix: &'static str, + ) -> Result<(TempDir, Repository)> { init_log(); sandbox_config_files(); - let td = TempDir::new()?; + let td = TempDir::with_prefix(prefix)?; let repo = Repository::init(td.path())?; { let mut config = repo.config()?; diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index e709486e03..f6cde98c31 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -3,6 +3,7 @@ use git2::Repository; use crate::{error::Result, HookResult, HooksError}; use std::{ + ffi::{OsStr, OsString}, path::{Path, PathBuf}, process::Command, str::FromStr, @@ -107,12 +108,23 @@ impl HookPaths { /// see pub fn run_hook(&self, args: &[&str]) -> Result { let hook = self.hook.clone(); - - let arg_str = format!("{:?} {}", hook, args.join(" ")); - // Use -l to avoid "command not found" on Windows. - let shell_args = - vec!["-l".to_string(), "-c".to_string(), arg_str]; - + let command = { + let mut os_str = OsString::new(); + os_str.push("'"); + os_str.push(hook.as_os_str()); // TODO: this doesn't work if `hook` contains single-quotes + os_str.push("'"); + os_str.push(" \"$@\""); + os_str + }; + let shell_args = { + let mut shell_args = vec![ + OsStr::new("-c"), + command.as_os_str(), + hook.as_os_str(), + ]; + shell_args.extend(args.iter().map(OsStr::new)); + shell_args + }; log::trace!("run hook '{:?}' in '{:?}'", hook, self.pwd); let output = Command::new(shell()) diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs index 2a458856d7..d8d5f796e4 100644 --- a/git2-hooks/src/lib.rs +++ b/git2-hooks/src/lib.rs @@ -282,7 +282,7 @@ exit 0 let hook = br#"#!/bin/sh COMMIT_MSG="$(cat "$1")" -printf "$COMMIT_MSG" | sed 's/sth/shell_command/g' >"$1" +printf "$COMMIT_MSG" | sed 's/sth/shell_command/g' > "$1" exit 0 "#; @@ -499,7 +499,7 @@ sys.exit(1) let (_td, repo) = repo_init(); let hook = b"#!/bin/sh -echo 'msg' > $1 +echo 'msg' > \"$1\" echo 'rejected' exit 1 "; @@ -525,7 +525,7 @@ exit 1 let (_td, repo) = repo_init(); let hook = b"#!/bin/sh -echo 'msg' > $1 +echo 'msg' > \"$1\" exit 0 "; @@ -565,7 +565,7 @@ exit 0 let (_td, repo) = repo_init(); let hook = b"#!/bin/sh -echo msg:$2 > $1 +echo \"msg:$2\" > \"$1\" exit 0 "; @@ -589,7 +589,7 @@ exit 0 let (_td, repo) = repo_init(); let hook = b"#!/bin/sh -echo $2,$3 > $1 +echo \"$2,$3\" > \"$1\" echo 'rejected' exit 2 "; From cba51424ac0f40033bfde675a6e4b27da363f82a Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 15 Jan 2025 21:00:00 +0000 Subject: [PATCH 04/55] simplify logic for running hooks on non-windows platforms --- git2-hooks/src/hookspath.rs | 118 ++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index f6cde98c31..b1aa4469c2 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -3,7 +3,7 @@ use git2::Repository; use crate::{error::Result, HookResult, HooksError}; use std::{ - ffi::{OsStr, OsString}, + ffi::OsStr, path::{Path, PathBuf}, process::Command, str::FromStr, @@ -108,37 +108,45 @@ impl HookPaths { /// see pub fn run_hook(&self, args: &[&str]) -> Result { let hook = self.hook.clone(); - let command = { - let mut os_str = OsString::new(); - os_str.push("'"); - os_str.push(hook.as_os_str()); // TODO: this doesn't work if `hook` contains single-quotes - os_str.push("'"); - os_str.push(" \"$@\""); - os_str - }; - let shell_args = { - let mut shell_args = vec![ - OsStr::new("-c"), - command.as_os_str(), - hook.as_os_str(), - ]; - shell_args.extend(args.iter().map(OsStr::new)); - shell_args - }; log::trace!("run hook '{:?}' in '{:?}'", hook, self.pwd); - let output = Command::new(shell()) - .args(shell_args) - .with_no_window() - .current_dir(&self.pwd) - // This call forces Command to handle the Path environment correctly on windows, - // the specific env set here does not matter - // see https://github.com/rust-lang/rust/issues/37519 - .env( - "DUMMY_ENV_TO_FIX_WINDOWS_CMD_RUNS", - "FixPathHandlingOnWindows", + let run_command = |command: &mut Command| { + command.current_dir(&self.pwd).with_no_window().output() + }; + let output = if cfg!(windows) { + run_command(Command::new(&hook).args(args)) + } else { + let command = { + let mut os_str = std::ffi::OsString::new(); + os_str.push("'"); + os_str.push(hook.as_os_str()); // TODO: this doesn't work if `hook` contains single-quotes + os_str.push("'"); + os_str.push(" \"$@\""); + os_str + }; + let shell_args = { + let mut shell_args = vec![ + OsStr::new("-l"), // Use -l to avoid "command not found" + OsStr::new("-c"), + command.as_os_str(), + hook.as_os_str(), + ]; + shell_args.extend(args.iter().map(OsStr::new)); + shell_args + }; + + run_command( + Command::new(sh_on_windows()) + .args(shell_args) + // This call forces Command to handle the Path environment correctly on windows, + // the specific env set here does not matter + // see https://github.com/rust-lang/rust/issues/37519 + .env( + "DUMMY_ENV_TO_FIX_WINDOWS_CMD_RUNS", + "FixPathHandlingOnWindows", + ), ) - .output()?; + }?; if output.status.success() { Ok(HookResult::Ok { hook }) @@ -158,6 +166,29 @@ impl HookPaths { } } +/// Get the path to the sh.exe bundled with Git for Windows +fn sh_on_windows() -> PathBuf { + 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/sh.exe")) + .filter(|p| p.exists()) + .unwrap_or_else(|| "sh".into()) + } else { + "sh".into() + } +} + #[cfg(unix)] fn is_executable(path: &Path) -> bool { use std::os::unix::fs::PermissionsExt; @@ -182,35 +213,6 @@ const fn is_executable(_: &Path) -> bool { true } -/// Return the shell that Git would use, the shell to execute commands from. -/// -/// On Windows, this is the full path to `sh.exe` bundled with it, and on -/// Unix it's `/bin/sh` as posix compatible shell. -/// If the bundled shell on Windows cannot be found, `sh` is returned as the name of a shell -/// as it could possibly be found in `PATH`. -/// Note that the returned path might not be a path on disk. -pub fn shell() -> PathBuf { - 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/sh.exe")) - .filter(|p| p.exists()) - .unwrap_or_else(|| "sh".into()) - } else { - "/bin/sh".into() - } -} - trait CommandExt { /// The process is a console application that is being run without a /// console window. Therefore, the console handle for the application is From 7f70d526a5aedbca51a7372e910956bdb76749e7 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 15 Jan 2025 21:00:00 +0000 Subject: [PATCH 05/55] invert if check --- git2-hooks/src/hookspath.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index b1aa4469c2..8c8cf86d17 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -114,8 +114,6 @@ impl HookPaths { command.current_dir(&self.pwd).with_no_window().output() }; let output = if cfg!(windows) { - run_command(Command::new(&hook).args(args)) - } else { let command = { let mut os_str = std::ffi::OsString::new(); os_str.push("'"); @@ -146,6 +144,8 @@ impl HookPaths { "FixPathHandlingOnWindows", ), ) + } else { + run_command(Command::new(&hook).args(args)) }?; if output.status.success() { From bd61ad8047c2e81a212010e3b0d0b642bf22c480 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 15 Jan 2025 21:00:00 +0000 Subject: [PATCH 06/55] debug_assert --- git2-hooks/src/hookspath.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 8c8cf86d17..57446eeeb0 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -185,6 +185,7 @@ fn sh_on_windows() -> PathBuf { .filter(|p| p.exists()) .unwrap_or_else(|| "sh".into()) } else { + debug_assert!(false, "should only be called on windows"); "sh".into() } } From c257d02507d120c6bc74efc03f6ec550e32f318d Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 15 Jan 2025 21:00:00 +0000 Subject: [PATCH 07/55] update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f52cd1c76d..b1d82b0b8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased -* use /bin/sh for git-hooks [[@Joshix](https://github.com/Joshix-1)] ([#2483](https://github.com/extrawurst/gitui/pull/2483)) +* execute git-hooks without shell (on *nix) [[@Joshix](https://github.com/Joshix-1)] ([#2483](https://github.com/extrawurst/gitui/pull/2483)) ## [0.27.0] - 2024-01-14 From da71107e460357c642a3bac4ce2629fb6262cc98 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 18:11:46 +0100 Subject: [PATCH 08/55] create build_shell_script_execution_command --- git2-hooks/src/hookspath.rs | 61 ++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 57446eeeb0..0c0df5289e 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -114,28 +114,8 @@ impl HookPaths { command.current_dir(&self.pwd).with_no_window().output() }; let output = if cfg!(windows) { - let command = { - let mut os_str = std::ffi::OsString::new(); - os_str.push("'"); - os_str.push(hook.as_os_str()); // TODO: this doesn't work if `hook` contains single-quotes - os_str.push("'"); - os_str.push(" \"$@\""); - os_str - }; - let shell_args = { - let mut shell_args = vec![ - OsStr::new("-l"), // Use -l to avoid "command not found" - OsStr::new("-c"), - command.as_os_str(), - hook.as_os_str(), - ]; - shell_args.extend(args.iter().map(OsStr::new)); - shell_args - }; - run_command( - Command::new(sh_on_windows()) - .args(shell_args) + build_shell_script_execution_command(&hook, args) // This call forces Command to handle the Path environment correctly on windows, // the specific env set here does not matter // see https://github.com/rust-lang/rust/issues/37519 @@ -145,7 +125,10 @@ impl HookPaths { ), ) } else { - run_command(Command::new(&hook).args(args)) + run_command(&mut build_shell_script_execution_command( + &hook, args, + )) + //run_command(Command::new(&hook).args(args)) }?; if output.status.success() { @@ -166,8 +149,37 @@ impl HookPaths { } } -/// Get the path to the sh.exe bundled with Git for Windows -fn sh_on_windows() -> PathBuf { +fn build_shell_script_execution_command( + script: &Path, + args: &[&str], +) -> Command { + let command = { + let mut os_str = std::ffi::OsString::new(); + os_str.push("'"); + os_str.push(script.as_os_str()); // TODO: this doesn't work if `script` contains single-quotes + os_str.push("'"); + os_str.push(" \"$@\""); + os_str + }; + let shell_args = { + let mut shell_args = vec![ + OsStr::new("-l"), // Use -l to avoid "command not found" + OsStr::new("-c"), + command.as_os_str(), + script.as_os_str(), + ]; + shell_args.extend(args.iter().map(OsStr::new)); + shell_args + }; + + let mut command = Command::new(sh_path()); + command.args(shell_args); + command +} + +/// Get the path to the sh executable. +/// On Windows get the sh.exe bundled with Git for Windows +pub fn sh_path() -> PathBuf { if cfg!(windows) { Command::new("where.exe") .arg("git") @@ -185,7 +197,6 @@ fn sh_on_windows() -> PathBuf { .filter(|p| p.exists()) .unwrap_or_else(|| "sh".into()) } else { - debug_assert!(false, "should only be called on windows"); "sh".into() } } From 2b6d2955d3bfd3f76511dd6336a8d1ec85af967f Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 17:00:00 +0000 Subject: [PATCH 09/55] small refactorings --- git2-hooks/src/hookspath.rs | 63 +++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 0c0df5289e..3130f81ed8 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -110,26 +110,21 @@ impl HookPaths { let hook = self.hook.clone(); log::trace!("run hook '{:?}' in '{:?}'", hook, self.pwd); - let run_command = |command: &mut Command| { - command.current_dir(&self.pwd).with_no_window().output() - }; - let output = if cfg!(windows) { - run_command( - build_shell_script_execution_command(&hook, args) - // This call forces Command to handle the Path environment correctly on windows, - // the specific env set here does not matter - // see https://github.com/rust-lang/rust/issues/37519 - .env( - "DUMMY_ENV_TO_FIX_WINDOWS_CMD_RUNS", - "FixPathHandlingOnWindows", - ), - ) + let mut command = if cfg!(windows) { + build_shell_script_execution_command(&hook, args) } else { - run_command(&mut build_shell_script_execution_command( - &hook, args, - )) + eprintln!("linux"); + + let c = build_shell_script_execution_command(&hook, args); //run_command(Command::new(&hook).args(args)) - }?; + eprintln!("{:?}", c.get_args()); + c + }; + + let output = command + .current_dir(&self.pwd) + .with_no_window() + .output()?; if output.status.success() { Ok(HookResult::Ok { hook }) @@ -153,7 +148,7 @@ fn build_shell_script_execution_command( script: &Path, args: &[&str], ) -> Command { - let command = { + let command_string = { let mut os_str = std::ffi::OsString::new(); os_str.push("'"); os_str.push(script.as_os_str()); // TODO: this doesn't work if `script` contains single-quotes @@ -161,19 +156,27 @@ fn build_shell_script_execution_command( os_str.push(" \"$@\""); os_str }; - let shell_args = { - let mut shell_args = vec![ - OsStr::new("-l"), // Use -l to avoid "command not found" - OsStr::new("-c"), - command.as_os_str(), - script.as_os_str(), - ]; - shell_args.extend(args.iter().map(OsStr::new)); - shell_args - }; let mut command = Command::new(sh_path()); - command.args(shell_args); + + if cfg!(windows) { + // Use -l to avoid "command not found" + command.arg(OsStr::new("-l")); + } + command.args([OsStr::new("-c"), command_string.as_os_str()]); + + command.arg(script.as_os_str()); + command.args(args.iter().map(OsStr::new)); + + if cfg!(windows) { + // This call forces Command to handle the Path environment correctly on windows, + // the specific env set here does not matter + // see https://github.com/rust-lang/rust/issues/37519 + command.env( + "DUMMY_ENV_TO_FIX_WINDOWS_CMD_RUNS", + "FixPathHandlingOnWindows", + ); + } command } From 816e17528d39e5fa3451ce319e1b0b39b9792a85 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 17:00:00 +0000 Subject: [PATCH 10/55] simplify build_shell_script_execution_command --- git2-hooks/src/hookspath.rs | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 3130f81ed8..2b4a7c40d4 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -3,7 +3,6 @@ use git2::Repository; use crate::{error::Result, HookResult, HooksError}; use std::{ - ffi::OsStr, path::{Path, PathBuf}, process::Command, str::FromStr, @@ -148,26 +147,8 @@ fn build_shell_script_execution_command( script: &Path, args: &[&str], ) -> Command { - let command_string = { - let mut os_str = std::ffi::OsString::new(); - os_str.push("'"); - os_str.push(script.as_os_str()); // TODO: this doesn't work if `script` contains single-quotes - os_str.push("'"); - os_str.push(" \"$@\""); - os_str - }; - let mut command = Command::new(sh_path()); - if cfg!(windows) { - // Use -l to avoid "command not found" - command.arg(OsStr::new("-l")); - } - command.args([OsStr::new("-c"), command_string.as_os_str()]); - - command.arg(script.as_os_str()); - command.args(args.iter().map(OsStr::new)); - if cfg!(windows) { // This call forces Command to handle the Path environment correctly on windows, // the specific env set here does not matter @@ -176,7 +157,14 @@ fn build_shell_script_execution_command( "DUMMY_ENV_TO_FIX_WINDOWS_CMD_RUNS", "FixPathHandlingOnWindows", ); + // Use -l to avoid "command not found" + command.arg("-l"); } + command + .arg(script) // the script to execute + .arg(script) // argv[0] + .args(args); // argv[1..] + command } From ce9fc0ea7561c2b9065e951974facbe05514f1cf Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 19:00:20 +0100 Subject: [PATCH 11/55] add line --- git2-hooks/src/hookspath.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 2b4a7c40d4..f61098f5c0 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -157,6 +157,7 @@ fn build_shell_script_execution_command( "DUMMY_ENV_TO_FIX_WINDOWS_CMD_RUNS", "FixPathHandlingOnWindows", ); + // Use -l to avoid "command not found" command.arg("-l"); } From 04a31eb8519c7d72911068aed530a7842ae1f884 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 19:16:28 +0100 Subject: [PATCH 12/55] execute hoooks directly and fallback to using sh --- git2-hooks/src/hookspath.rs | 38 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index f61098f5c0..96c7581343 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -109,21 +109,27 @@ impl HookPaths { let hook = self.hook.clone(); log::trace!("run hook '{:?}' in '{:?}'", hook, self.pwd); - let mut command = if cfg!(windows) { - build_shell_script_execution_command(&hook, args) - } else { - eprintln!("linux"); - - let c = build_shell_script_execution_command(&hook, args); - //run_command(Command::new(&hook).args(args)) - eprintln!("{:?}", c.get_args()); - c + let run_command = |mut command: Command| { + command.current_dir(&self.pwd).with_no_window().output() }; - let output = command - .current_dir(&self.pwd) - .with_no_window() - .output()?; + let output = if cfg!(windows) { + let command = + build_shell_script_execution_command(&hook, args); + run_command(command) + } else { + let mut command = Command::new(&hook); + command.args(args); + match run_command(command) { + Err(err) if err.raw_os_error() == Some(8) => { + run_command(build_shell_script_execution_command( + &hook, args, + )) + } + + result => result, + } + }?; if output.status.success() { Ok(HookResult::Ok { hook }) @@ -161,10 +167,8 @@ fn build_shell_script_execution_command( // Use -l to avoid "command not found" command.arg("-l"); } - command - .arg(script) // the script to execute - .arg(script) // argv[0] - .args(args); // argv[1..] + + command.arg(script).args(args); command } From 8ba997d34e59fcc88d0cd6f5c7d22eb9af269cf9 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 18:00:00 +0000 Subject: [PATCH 13/55] rename function --- git2-hooks/src/hookspath.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 96c7581343..ad32435da6 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -114,17 +114,14 @@ impl HookPaths { }; let output = if cfg!(windows) { - let command = - build_shell_script_execution_command(&hook, args); - run_command(command) + run_command(sh_command(&hook, args)) } else { let mut command = Command::new(&hook); command.args(args); match run_command(command) { Err(err) if err.raw_os_error() == Some(8) => { - run_command(build_shell_script_execution_command( - &hook, args, - )) + // if execution failed with ENOEXEC execute with /bin/sh + run_command(sh_command(&hook, args)) } result => result, @@ -149,10 +146,7 @@ impl HookPaths { } } -fn build_shell_script_execution_command( - script: &Path, - args: &[&str], -) -> Command { +fn sh_command(script: &Path, args: &[&str]) -> Command { let mut command = Command::new(sh_path()); if cfg!(windows) { From 4f65b8148926b0a5793f2ca446be608bfd2e953c Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 19:20:23 +0100 Subject: [PATCH 14/55] update commend --- git2-hooks/src/hookspath.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index ad32435da6..606768ae8e 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -120,7 +120,7 @@ impl HookPaths { command.args(args); match run_command(command) { Err(err) if err.raw_os_error() == Some(8) => { - // if execution failed with ENOEXEC execute with /bin/sh + // if error is ENOEXEC execute with sh run_command(sh_command(&hook, args)) } From 58afb49eed0ceae7bf7a910e81d368f2c4352531 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 18:00:00 +0000 Subject: [PATCH 15/55] sh_command --- git2-hooks/src/hookspath.rs | 46 ++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 606768ae8e..45625d1590 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -109,24 +109,15 @@ impl HookPaths { let hook = self.hook.clone(); log::trace!("run hook '{:?}' in '{:?}'", hook, self.pwd); - let run_command = |mut command: Command| { - command.current_dir(&self.pwd).with_no_window().output() - }; - - let output = if cfg!(windows) { - run_command(sh_command(&hook, args)) + let mut command = if cfg!(windows) { + sh_command(&hook, args) } else { let mut command = Command::new(&hook); command.args(args); - match run_command(command) { - Err(err) if err.raw_os_error() == Some(8) => { - // if error is ENOEXEC execute with sh - run_command(sh_command(&hook, args)) - } + command + }; - result => result, - } - }?; + let output = command.current_dir(&self.pwd).output()?; if output.status.success() { Ok(HookResult::Ok { hook }) @@ -160,6 +151,8 @@ fn sh_command(script: &Path, args: &[&str]) -> Command { // Use -l to avoid "command not found" command.arg("-l"); + + command.with_no_window(); } command.arg(script).args(args); @@ -192,20 +185,21 @@ pub fn sh_path() -> PathBuf { } #[cfg(unix)] -fn is_executable(path: &Path) -> bool { - use std::os::unix::fs::PermissionsExt; - - let metadata = match path.metadata() { - Ok(metadata) => metadata, - Err(e) => { - log::error!("metadata error: {}", e); - return false; - } - }; +const fn is_executable(path: &Path) -> bool { + // use std::os::unix::fs::PermissionsExt; - let permissions = metadata.permissions(); + // let metadata = match path.metadata() { + // Ok(metadata) => metadata, + // Err(e) => { + // log::error!("metadata error: {}", e); + // return false; + // } + // }; - permissions.mode() & 0o111 != 0 + // let permissions = metadata.permissions(); + + // permissions.mode() & 0o111 != 0 + true } #[cfg(windows)] From a6e71d3129357ce144febbf2c9b9084f45027c8a Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 19:58:30 +0100 Subject: [PATCH 16/55] refactor --- git2-hooks/src/hookspath.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 45625d1590..72c26e545b 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -110,14 +110,18 @@ impl HookPaths { log::trace!("run hook '{:?}' in '{:?}'", hook, self.pwd); let mut command = if cfg!(windows) { - sh_command(&hook, args) + // execute hook with sh + sh_command(&hook) } else { - let mut command = Command::new(&hook); - command.args(args); - command + // execute hook directly + Command::new(&hook) }; - let output = command.current_dir(&self.pwd).output()?; + let output = command + .args(args) + .current_dir(&self.pwd) + .with_no_window() + .output()?; if output.status.success() { Ok(HookResult::Ok { hook }) @@ -137,7 +141,7 @@ impl HookPaths { } } -fn sh_command(script: &Path, args: &[&str]) -> Command { +fn sh_command(script: &Path) -> Command { let mut command = Command::new(sh_path()); if cfg!(windows) { @@ -151,11 +155,9 @@ fn sh_command(script: &Path, args: &[&str]) -> Command { // Use -l to avoid "command not found" command.arg("-l"); - - command.with_no_window(); } - command.arg(script).args(args); + command.arg(script); command } From dbd8feecdea8a139c5e03e919b01fe675d54e13c Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:02:12 +0100 Subject: [PATCH 17/55] fix is_executable --- git2-hooks/src/hookspath.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 72c26e545b..e013a74546 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -187,21 +187,20 @@ pub fn sh_path() -> PathBuf { } #[cfg(unix)] -const fn is_executable(path: &Path) -> bool { - // use std::os::unix::fs::PermissionsExt; - - // let metadata = match path.metadata() { - // Ok(metadata) => metadata, - // Err(e) => { - // log::error!("metadata error: {}", e); - // return false; - // } - // }; +fn is_executable(path: &Path) -> bool { + use std::os::unix::fs::PermissionsExt; + + let metadata = match path.metadata() { + Ok(metadata) => metadata, + Err(e) => { + log::error!("metadata error: {}", e); + return false; + } + }; - // let permissions = metadata.permissions(); + let permissions = metadata.permissions(); - // permissions.mode() & 0o111 != 0 - true + permissions.mode() & 0o111 != 0 } #[cfg(windows)] From 7c8b6bc277116d98e1445d9cb5a8571f90c309fd Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:03:27 +0100 Subject: [PATCH 18/55] debug print --- git2-hooks/src/hookspath.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index e013a74546..d8f4769079 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -124,6 +124,7 @@ impl HookPaths { .output()?; if output.status.success() { + eprintln!("{}", String::from_utf8_lossy(&output.stdout)); Ok(HookResult::Ok { hook }) } else { let stderr = From 1370a14afb03a2e6c61e7353440eba62bbe3cdef Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:07:44 +0100 Subject: [PATCH 19/55] handle ENOEXEC --- git2-hooks/src/hookspath.rs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index d8f4769079..dd2f3c098a 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -109,22 +109,28 @@ impl HookPaths { let hook = self.hook.clone(); log::trace!("run hook '{:?}' in '{:?}'", hook, self.pwd); - let mut command = if cfg!(windows) { + let run_command = |mut command: Command| { + command + .args(args) + .current_dir(&self.pwd) + .with_no_window() + .output() + }; + + let output = if cfg!(windows) { // execute hook with sh - sh_command(&hook) + run_command(sh_command(&hook)) } else { // execute hook directly - Command::new(&hook) - }; - - let output = command - .args(args) - .current_dir(&self.pwd) - .with_no_window() - .output()?; + match run_command(Command::new(&hook)) { + Err(err) if err.raw_os_error() == Some(8) => { + run_command(sh_command(&hook)) + } + result => result, + } + }?; if output.status.success() { - eprintln!("{}", String::from_utf8_lossy(&output.stdout)); Ok(HookResult::Ok { hook }) } else { let stderr = From 65c59970949aa15aa14ee07351adca281fb3575a Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:08:29 +0100 Subject: [PATCH 20/55] add debug print --- git2-hooks/src/hookspath.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index dd2f3c098a..1f5e5bd2ee 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -124,6 +124,7 @@ impl HookPaths { // execute hook directly match run_command(Command::new(&hook)) { Err(err) if err.raw_os_error() == Some(8) => { + eprintln!("GOT ENOEXEC: {err}"); run_command(sh_command(&hook)) } result => result, From 102e7fdb05935d6c3be93baa55028a66fa576f84 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 19:00:00 +0000 Subject: [PATCH 21/55] remove debug print --- git2-hooks/src/hookspath.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 1f5e5bd2ee..dd2f3c098a 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -124,7 +124,6 @@ impl HookPaths { // execute hook directly match run_command(Command::new(&hook)) { Err(err) if err.raw_os_error() == Some(8) => { - eprintln!("GOT ENOEXEC: {err}"); run_command(sh_command(&hook)) } result => result, From b79e5ce2c7bd12f972cd06037e307c7910b16796 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 19:00:00 +0000 Subject: [PATCH 22/55] name const --- git2-hooks/src/hookspath.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index dd2f3c098a..f46abac734 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -16,6 +16,7 @@ pub struct HookPaths { const CONFIG_HOOKS_PATH: &str = "core.hooksPath"; const DEFAULT_HOOKS_PATH: &str = "hooks"; +const ENOEXEC: i32 = 8; impl HookPaths { /// `core.hooksPath` always takes precedence. @@ -123,7 +124,7 @@ impl HookPaths { } else { // execute hook directly match run_command(Command::new(&hook)) { - Err(err) if err.raw_os_error() == Some(8) => { + Err(err) if err.raw_os_error() == Some(ENOEXEC) => { run_command(sh_command(&hook)) } result => result, From 177d9d8aead3d0834f9b48bcfc81bb5802e2ffc9 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 19:00:00 +0000 Subject: [PATCH 23/55] update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 375b73d51d..caf473ce95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased -* execute git-hooks without shell (on *nix) [[@Joshix](https://github.com/Joshix-1)] ([#2483](https://github.com/extrawurst/gitui/pull/2483)) +* execute git-hooks directly if possible (on *nix) else use sh instead of bash (without reading SHELL variable) [[@Joshix](https://github.com/Joshix-1)] ([#2483](https://github.com/extrawurst/gitui/pull/2483)) ### Added * support loading custom syntax highlighting themes from a file [[@acuteenvy](https://github.com/acuteenvy)] ([#2565](https://github.com/gitui-org/gitui/pull/2565)) From e5e562ef898ae1ae843ebbe04045153dc6778446 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 19:00:00 +0000 Subject: [PATCH 24/55] add new test --- git2-hooks/src/lib.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs index d8d5f796e4..e942fea531 100644 --- a/git2-hooks/src/lib.rs +++ b/git2-hooks/src/lib.rs @@ -309,6 +309,39 @@ exit 0 assert!(res.is_ok()); } + #[test] + fn test_hook_with_missing_shebang() { + const TEXT: &str = "Hello, world!"; + + let (_td, repo) = repo_init(); + + let hook = b"echo \"$@\"\nexit 42"; + + create_hook(&repo, HOOK_PRE_COMMIT, hook); + + let hook = + HookPaths::new(&repo, None, HOOK_PRE_COMMIT).unwrap(); + + assert!(hook.found()); + + let result = hook.run_hook(&[TEXT]).unwrap(); + + if let HookResult::RunNotSuccessful { + code, + stdout, + stderr, + hook: h, + } = result + { + assert_eq!(code, Some(42)); + assert_eq!(stdout.as_str().trim_end(), TEXT); + assert!(stderr.is_empty()); + assert_eq!(h, hook.hook); + } else { + panic!("run_hook should've failed"); + } + } + #[test] fn test_no_hook_found() { let (_td, repo) = repo_init(); From f397799f8f40151e3a40ad8ada974c1a225fe8da Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 19:00:00 +0000 Subject: [PATCH 25/55] deindent --- asyncgit/src/sync/hooks.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index 3c01ee91b2..396908bcfc 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -90,9 +90,9 @@ mod tests { let root = repo.path().parent().unwrap(); let hook = b"#!/bin/sh - echo 'rejected' - exit 1 - "; +echo 'rejected' +exit 1 +"; git2_hooks::create_hook( &repo, @@ -127,10 +127,9 @@ mod tests { crate::sync::utils::repo_work_dir(repo_path).unwrap(); let hook = b"#!/bin/sh - echo \"$(pwd)\" - exit 1 - "; - +echo \"$(pwd)\" +exit 1 +"; git2_hooks::create_hook( &repo, git2_hooks::HOOK_PRE_COMMIT, @@ -153,10 +152,9 @@ mod tests { let root = repo.path().parent().unwrap(); let hook = b"#!/bin/sh - echo 'msg' > \"$1\" - echo 'rejected' - exit 1 - "; +echo 'msg' > \"$1\" +echo 'rejected' +exit 1"; git2_hooks::create_hook( &repo, From 26b88714992f62d2f7124c11d63836357c204025 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:00:00 +0000 Subject: [PATCH 26/55] set different env var https://superuser.com/questions/1641219/git-bash-with-windows-terminal-prints-weird-characters-instead-of-newline --- git2-hooks/src/hookspath.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index f46abac734..9ca27d2a35 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -156,10 +156,7 @@ fn sh_command(script: &Path) -> Command { // This call forces Command to handle the Path environment correctly on windows, // the specific env set here does not matter // see https://github.com/rust-lang/rust/issues/37519 - command.env( - "DUMMY_ENV_TO_FIX_WINDOWS_CMD_RUNS", - "FixPathHandlingOnWindows", - ); + command.env("TERM", "cygwin"); // Use -l to avoid "command not found" command.arg("-l"); From 5327f4a4a1e51271582f59a4db715a46a55bda3d Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:00:00 +0000 Subject: [PATCH 27/55] Revert "set different env var" This reverts commit 26b88714992f62d2f7124c11d63836357c204025. --- git2-hooks/src/hookspath.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 9ca27d2a35..f46abac734 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -156,7 +156,10 @@ fn sh_command(script: &Path) -> Command { // This call forces Command to handle the Path environment correctly on windows, // the specific env set here does not matter // see https://github.com/rust-lang/rust/issues/37519 - command.env("TERM", "cygwin"); + command.env( + "DUMMY_ENV_TO_FIX_WINDOWS_CMD_RUNS", + "FixPathHandlingOnWindows", + ); // Use -l to avoid "command not found" command.arg("-l"); From 11da04a7b9dc2045c9128541e326fefa185de690 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:00:00 +0000 Subject: [PATCH 28/55] remove ansi clear from output --- git2-hooks/src/hookspath.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index f46abac734..ed2b0d8c80 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -134,11 +134,22 @@ impl HookPaths { if output.status.success() { Ok(HookResult::Ok { hook }) } else { - let stderr = + let mut stderr = String::from_utf8_lossy(&output.stderr).to_string(); - let stdout = + let mut stdout = String::from_utf8_lossy(&output.stdout).to_string(); + if cfg!(windows) { + const ANSI_CLEAR: &str = "\x1B[H\x1B[J"; + + if let Some(value) = stdout.strip_suffix(ANSI_CLEAR) { + stderr.truncate(value.len()); + } + if let Some(value) = stdout.strip_suffix(ANSI_CLEAR) { + stdout.truncate(value.len()); + } + } + Ok(HookResult::RunNotSuccessful { code: output.status.code(), stdout, From cfdb19609397105e67dbfeb5b6040d7aa9759476 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:00:00 +0000 Subject: [PATCH 29/55] fix typo --- git2-hooks/src/hookspath.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index ed2b0d8c80..84fe6fbf5c 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -142,7 +142,7 @@ impl HookPaths { if cfg!(windows) { const ANSI_CLEAR: &str = "\x1B[H\x1B[J"; - if let Some(value) = stdout.strip_suffix(ANSI_CLEAR) { + if let Some(value) = stderr.strip_suffix(ANSI_CLEAR) { stderr.truncate(value.len()); } if let Some(value) = stdout.strip_suffix(ANSI_CLEAR) { From 08dce84fa351aafe4be05bdeb81a1cb91d193964 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:00:00 +0000 Subject: [PATCH 30/55] move ansi escape hack to impl From for HookResult --- asyncgit/src/sync/hooks.rs | 12 ++++++++++-- git2-hooks/src/hookspath.rs | 15 ++------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index 396908bcfc..a3818c8be4 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -19,9 +19,17 @@ impl From for HookResult { | git2_hooks::HookResult::NoHookFound => Self::Ok, git2_hooks::HookResult::RunNotSuccessful { stdout, - stderr, + mut stderr, .. - } => Self::NotOk(format!("{stdout}{stderr}")), + } => { + const ANSI_CLEAR: &str = "\x1B[H\x1B[J"; + + if stderr == ANSI_CLEAR { + stderr.clear(); + } + + Self::NotOk(format!("{stdout}{stderr}")) + } } } } diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 84fe6fbf5c..f46abac734 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -134,22 +134,11 @@ impl HookPaths { if output.status.success() { Ok(HookResult::Ok { hook }) } else { - let mut stderr = + let stderr = String::from_utf8_lossy(&output.stderr).to_string(); - let mut stdout = + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); - if cfg!(windows) { - const ANSI_CLEAR: &str = "\x1B[H\x1B[J"; - - if let Some(value) = stderr.strip_suffix(ANSI_CLEAR) { - stderr.truncate(value.len()); - } - if let Some(value) = stdout.strip_suffix(ANSI_CLEAR) { - stdout.truncate(value.len()); - } - } - Ok(HookResult::RunNotSuccessful { code: output.status.code(), stdout, From b37817db75f133127a8b99f40b493591bb544401 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:00:00 +0000 Subject: [PATCH 31/55] fix ansi clear --- asyncgit/src/sync/hooks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index a3818c8be4..4889ca35e3 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -22,7 +22,7 @@ impl From for HookResult { mut stderr, .. } => { - const ANSI_CLEAR: &str = "\x1B[H\x1B[J"; + const ANSI_CLEAR: &str = "\x1B[H\x1B[2J\x1B[3J"; if stderr == ANSI_CLEAR { stderr.clear(); From a7fe9cb9869baf51e9cbaafee9e5eefcbd27be8d Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:00:00 +0000 Subject: [PATCH 32/55] try strip different --- asyncgit/src/sync/hooks.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index 4889ca35e3..4046ffd947 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -19,16 +19,20 @@ impl From for HookResult { | git2_hooks::HookResult::NoHookFound => Self::Ok, git2_hooks::HookResult::RunNotSuccessful { stdout, - mut stderr, + stderr, .. } => { const ANSI_CLEAR: &str = "\x1B[H\x1B[2J\x1B[3J"; - if stderr == ANSI_CLEAR { - stderr.clear(); + let mut combined = format!("{stdout}{stderr}"); + + if let Some(trimmed) = + combined.strip_suffix(ANSI_CLEAR) + { + combined.truncate(trimmed.len()); } - Self::NotOk(format!("{stdout}{stderr}")) + Self::NotOk(combined) } } } From a4e2f54ec9540b7aa717fcb90410a8144f224220 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:00:00 +0000 Subject: [PATCH 33/55] more trimming --- asyncgit/src/sync/hooks.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index 4046ffd947..ae32b1b4aa 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -24,7 +24,15 @@ impl From for HookResult { } => { const ANSI_CLEAR: &str = "\x1B[H\x1B[2J\x1B[3J"; - let mut combined = format!("{stdout}{stderr}"); + let mut combined = stdout; + + if let Some(trimmed) = + combined.strip_suffix(ANSI_CLEAR) + { + combined.truncate(trimmed.len()); + } + + combined.push_str(&stderr); if let Some(trimmed) = combined.strip_suffix(ANSI_CLEAR) From d25efdc2d25d08beb86b4e820362375a560da231 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 20:00:00 +0000 Subject: [PATCH 34/55] Revert "more trimming" This reverts commit a4e2f54ec9540b7aa717fcb90410a8144f224220. --- asyncgit/src/sync/hooks.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index ae32b1b4aa..4046ffd947 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -24,15 +24,7 @@ impl From for HookResult { } => { const ANSI_CLEAR: &str = "\x1B[H\x1B[2J\x1B[3J"; - let mut combined = stdout; - - if let Some(trimmed) = - combined.strip_suffix(ANSI_CLEAR) - { - combined.truncate(trimmed.len()); - } - - combined.push_str(&stderr); + let mut combined = format!("{stdout}{stderr}"); if let Some(trimmed) = combined.strip_suffix(ANSI_CLEAR) From caf83c8008994fb1156cadad2bd8b7ffc19bee42 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 21:00:00 +0000 Subject: [PATCH 35/55] weird hack only on windows --- asyncgit/src/sync/hooks.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index 4046ffd947..6f0b4dbec2 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -22,14 +22,16 @@ impl From for HookResult { stderr, .. } => { - const ANSI_CLEAR: &str = "\x1B[H\x1B[2J\x1B[3J"; - let mut combined = format!("{stdout}{stderr}"); - if let Some(trimmed) = - combined.strip_suffix(ANSI_CLEAR) - { - combined.truncate(trimmed.len()); + if cfg!(windows) { + const ANSI_CLEAR: &str = "\x1B[H\x1B[2J\x1B[3J"; + + if let Some(trimmed) = + combined.strip_suffix(ANSI_CLEAR) + { + combined.truncate(trimmed.len()); + } } Self::NotOk(combined) From f91aa105a667640eae4c79b0b298be48d4672344 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 21:00:00 +0000 Subject: [PATCH 36/55] revert stuff --- asyncgit/src/sync/hooks.rs | 19 ++++++++++--------- git2-hooks/src/lib.rs | 6 +++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index 6f0b4dbec2..2067861cc5 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -104,9 +104,9 @@ mod tests { let root = repo.path().parent().unwrap(); let hook = b"#!/bin/sh -echo 'rejected' -exit 1 -"; + echo 'rejected' + exit 1 + "; git2_hooks::create_hook( &repo, @@ -141,9 +141,9 @@ exit 1 crate::sync::utils::repo_work_dir(repo_path).unwrap(); let hook = b"#!/bin/sh -echo \"$(pwd)\" -exit 1 -"; + echo \"$(pwd)\" + exit 1 + "; git2_hooks::create_hook( &repo, git2_hooks::HOOK_PRE_COMMIT, @@ -166,9 +166,10 @@ exit 1 let root = repo.path().parent().unwrap(); let hook = b"#!/bin/sh -echo 'msg' > \"$1\" -echo 'rejected' -exit 1"; + echo 'msg' > \"$1\" + echo 'rejected' + exit 1 + "; git2_hooks::create_hook( &repo, diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs index e942fea531..66538d9a5c 100644 --- a/git2-hooks/src/lib.rs +++ b/git2-hooks/src/lib.rs @@ -532,9 +532,9 @@ sys.exit(1) let (_td, repo) = repo_init(); let hook = b"#!/bin/sh -echo 'msg' > \"$1\" -echo 'rejected' -exit 1 + echo 'msg' > \"$1\" + echo 'rejected' + exit 1 "; create_hook(&repo, HOOK_COMMIT_MSG, hook); From adeb50dbd61f865273b85e03f34784b242346e23 Mon Sep 17 00:00:00 2001 From: Joshix Date: Tue, 25 Mar 2025 21:00:00 +0000 Subject: [PATCH 37/55] trim_ascii_end --- git2-hooks/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs index 66538d9a5c..9a67e29e9f 100644 --- a/git2-hooks/src/lib.rs +++ b/git2-hooks/src/lib.rs @@ -334,9 +334,9 @@ exit 0 } = result { assert_eq!(code, Some(42)); - assert_eq!(stdout.as_str().trim_end(), TEXT); - assert!(stderr.is_empty()); assert_eq!(h, hook.hook); + assert_eq!(stdout.as_str().trim_ascii_end(), TEXT); + assert!(stderr.is_empty()); } else { panic!("run_hook should've failed"); } From 369ea6a140438fdbaf29ff31f3fea8195603b5e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 07:23:23 +0100 Subject: [PATCH 38/55] Bump struct-patch from 0.8.7 to 0.9.0 (#2583) Bumps [struct-patch](https://github.com/yanganto/struct-patch) from 0.8.7 to 0.9.0. - [Release notes](https://github.com/yanganto/struct-patch/releases) - [Commits](https://github.com/yanganto/struct-patch/compare/v0.8.7...v0.9.0) --- updated-dependencies: - dependency-name: struct-patch dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f15e14f983..f02a5a1817 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3320,18 +3320,18 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "struct-patch" -version = "0.8.7" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde1b55ce4b9efe4b5c302dea2d0f1297a522963024e160a587a2670c24f3f04" +checksum = "198180af96bd27ff18f84da0d689ff51e24dde06e9d95badb0d2c01bb915ffd0" dependencies = [ "struct-patch-derive", ] [[package]] name = "struct-patch-derive" -version = "0.8.7" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac94fea04bf721f57ed7f421e64d3a04858e15708d00e8aa814cad7507427503" +checksum = "81c0fa19d1fb0c92c763f74904b40b10b4d0740b80e3b0c6bfe52fcbcab2f24c" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 75bb1cd9c6..67424121c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ scopetime = { path = "./scopetime", version = "0.1" } serde = "1.0" shellexpand = "3.1" simplelog = { version = "0.12", default-features = false } -struct-patch = "0.8" +struct-patch = "0.9" syntect = { version = "5.2", default-features = false, features = [ "parsing", "default-syntaxes", From c5c1fe99e0aadb49ebb180cf4726cfce3f3eb0a6 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 26 Mar 2025 17:00:00 +0000 Subject: [PATCH 39/55] format assert --- git2-hooks/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs index 9a67e29e9f..a9739ecfe3 100644 --- a/git2-hooks/src/lib.rs +++ b/git2-hooks/src/lib.rs @@ -335,7 +335,14 @@ exit 0 { assert_eq!(code, Some(42)); assert_eq!(h, hook.hook); - assert_eq!(stdout.as_str().trim_ascii_end(), TEXT); + assert_eq!( + stdout.as_str().trim_ascii_end(), + TEXT, + "{:?} != {TEXT:?} | {:?} != {:?}", + stdout.as_str().trim_ascii_end(), + stdout.as_str().trim_ascii_end().as_bytes(), + TEXT.as_bytes() + ); assert!(stderr.is_empty()); } else { panic!("run_hook should've failed"); From c0a8996a954ea30cf4d0c1a933c4cf95d983552b Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 26 Mar 2025 17:00:00 +0000 Subject: [PATCH 40/55] move truncation --- asyncgit/src/sync/hooks.rs | 16 +--------------- git2-hooks/src/hookspath.rs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index 2067861cc5..384d222968 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -21,21 +21,7 @@ impl From for HookResult { stdout, stderr, .. - } => { - let mut combined = format!("{stdout}{stderr}"); - - if cfg!(windows) { - const ANSI_CLEAR: &str = "\x1B[H\x1B[2J\x1B[3J"; - - if let Some(trimmed) = - combined.strip_suffix(ANSI_CLEAR) - { - combined.truncate(trimmed.len()); - } - } - - Self::NotOk(combined) - } + } => Self::NotOk(format!("{stdout}{stderr}")), } } } diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index f46abac734..fd1ed215df 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -134,11 +134,23 @@ impl HookPaths { if output.status.success() { Ok(HookResult::Ok { hook }) } else { - let stderr = + let mut stderr = String::from_utf8_lossy(&output.stderr).to_string(); - let stdout = + let mut stdout = String::from_utf8_lossy(&output.stdout).to_string(); + if cfg!(windows) { + const ANSI_CLEAR: &str = "\x1B[H\x1B[2J\x1B[3J"; + + for text in [&mut stderr, &mut stdout] { + if let Some(trimmed) = + text.strip_suffix(ANSI_CLEAR) + { + text.truncate(trimmed.len()); + } + } + } + Ok(HookResult::RunNotSuccessful { code: output.status.code(), stdout, From 3e4b3c21b926a28f67d9a0b92c3bfc0819cd2534 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 26 Mar 2025 17:00:00 +0000 Subject: [PATCH 41/55] debug --- git2-hooks/src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs index a9739ecfe3..28d64b2d62 100644 --- a/git2-hooks/src/lib.rs +++ b/git2-hooks/src/lib.rs @@ -442,9 +442,12 @@ exit 1 unreachable!() }; - assert!(stdout - .lines() - .any(|line| line.starts_with("export PATH"))); + assert!( + stdout + .lines() + .any(|line| line.starts_with("export PATH")), + "{stdout:?}" + ); } #[test] @@ -510,7 +513,7 @@ sys.exit(0) create_hook(&repo, HOOK_PRE_COMMIT, hook); let res = hooks_pre_commit(&repo, None).unwrap(); - assert!(res.is_ok()); + assert!(res.is_ok(), "{res:?}"); } #[test] From db60ac3c92d75e722ad0c98af4e24ea15c720ec8 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 26 Mar 2025 17:00:00 +0000 Subject: [PATCH 42/55] fix test --- git2-hooks/src/lib.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs index 28d64b2d62..a13fea5bf7 100644 --- a/git2-hooks/src/lib.rs +++ b/git2-hooks/src/lib.rs @@ -428,6 +428,12 @@ exit 1 #[test] fn test_env_containing_path() { + const PATH_EXPORT: &str = if cfg!(windows) { + "declare -x PATH=" + } else { + "export PATH" + }; + let (_td, repo) = repo_init(); let hook = b"#!/bin/sh @@ -445,8 +451,8 @@ exit 1 assert!( stdout .lines() - .any(|line| line.starts_with("export PATH")), - "{stdout:?}" + .any(|line| line.starts_with(PATH_EXPORT)), + "Could not find line starting with {PATH_EXPORT:?} in: {stdout:?}" ); } From 5a1dfbda57b89ac8734cd6c1c4c8ea26e161fe19 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 26 Mar 2025 18:00:00 +0000 Subject: [PATCH 43/55] fix windows --- git2-hooks/src/hookspath.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index fd1ed215df..7e4dc2906d 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -110,7 +110,7 @@ impl HookPaths { let hook = self.hook.clone(); log::trace!("run hook '{:?}' in '{:?}'", hook, self.pwd); - let run_command = |mut command: Command| { + let run_command = |command: &mut Command| { command .args(args) .current_dir(&self.pwd) @@ -119,13 +119,26 @@ impl HookPaths { }; let output = if cfg!(windows) { - // execute hook with sh - run_command(sh_command(&hook)) + // execute hook in shell + let command = { + let mut os_str = std::ffi::OsString::new(); + os_str.push("'"); + if let Some(hook) = hook.to_str() { + os_str.push(hook.replace('\'', "\\'")); + } else { + os_str.push(hook.as_os_str()); // TODO: this doesn't work if `hook` contains single-quotes + } + os_str.push("'"); + os_str.push(" \"$@\""); + + os_str + }; + run_command(sh_command().arg(command)) } else { // execute hook directly - match run_command(Command::new(&hook)) { + match run_command(&mut Command::new(&hook)) { Err(err) if err.raw_os_error() == Some(ENOEXEC) => { - run_command(sh_command(&hook)) + run_command(sh_command().arg(&hook)) } result => result, } @@ -161,7 +174,7 @@ impl HookPaths { } } -fn sh_command(script: &Path) -> Command { +fn sh_command() -> Command { let mut command = Command::new(sh_path()); if cfg!(windows) { @@ -177,8 +190,6 @@ fn sh_command(script: &Path) -> Command { command.arg("-l"); } - command.arg(script); - command } From 94b9dc7d56224207191c557c04d9fe64cb150095 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 26 Mar 2025 18:00:00 +0000 Subject: [PATCH 44/55] add missing argument --- git2-hooks/src/hookspath.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 7e4dc2906d..f04940b46a 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -133,7 +133,7 @@ impl HookPaths { os_str }; - run_command(sh_command().arg(command)) + run_command(sh_command().arg("-c").arg(command)) } else { // execute hook directly match run_command(&mut Command::new(&hook)) { From f9ff366bcc828d9a8e807f8318d0157380acf491 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 26 Mar 2025 18:00:00 +0000 Subject: [PATCH 45/55] add argument --- git2-hooks/src/hookspath.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index f04940b46a..740f009a64 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -133,7 +133,9 @@ impl HookPaths { os_str }; - run_command(sh_command().arg("-c").arg(command)) + run_command( + sh_command().arg("-c").arg(command).arg(&hook), + ) } else { // execute hook directly match run_command(&mut Command::new(&hook)) { From 38a34840c02e9290ea8173c273feb1e20d242124 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 26 Mar 2025 19:00:00 +0000 Subject: [PATCH 46/55] remove hacks --- git2-hooks/src/hookspath.rs | 16 ++-------------- git2-hooks/src/lib.rs | 6 +----- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 740f009a64..c88c08188a 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -149,23 +149,11 @@ impl HookPaths { if output.status.success() { Ok(HookResult::Ok { hook }) } else { - let mut stderr = + let stderr = String::from_utf8_lossy(&output.stderr).to_string(); - let mut stdout = + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); - if cfg!(windows) { - const ANSI_CLEAR: &str = "\x1B[H\x1B[2J\x1B[3J"; - - for text in [&mut stderr, &mut stdout] { - if let Some(trimmed) = - text.strip_suffix(ANSI_CLEAR) - { - text.truncate(trimmed.len()); - } - } - } - Ok(HookResult::RunNotSuccessful { code: output.status.code(), stdout, diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs index a13fea5bf7..022aa4622e 100644 --- a/git2-hooks/src/lib.rs +++ b/git2-hooks/src/lib.rs @@ -428,11 +428,7 @@ exit 1 #[test] fn test_env_containing_path() { - const PATH_EXPORT: &str = if cfg!(windows) { - "declare -x PATH=" - } else { - "export PATH" - }; + const PATH_EXPORT: &str = "export PATH"; let (_td, repo) = repo_init(); From 7b4903b97500aee394a778ed45bc2c9babadb2e0 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 26 Mar 2025 19:00:00 +0000 Subject: [PATCH 47/55] correctly escape single-quote if utf8 --- asyncgit/src/sync/hooks.rs | 2 +- git2-hooks/src/hookspath.rs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/asyncgit/src/sync/hooks.rs b/asyncgit/src/sync/hooks.rs index 384d222968..85b40d3c71 100644 --- a/asyncgit/src/sync/hooks.rs +++ b/asyncgit/src/sync/hooks.rs @@ -81,7 +81,7 @@ mod tests { use crate::sync::tests::repo_init_with_prefix; fn repo_init() -> Result<(TempDir, Repository)> { - repo_init_with_prefix("gitui $# ") + repo_init_with_prefix("gitui $# ' ") } #[test] diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index c88c08188a..ea6ce9e189 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -124,7 +124,15 @@ impl HookPaths { let mut os_str = std::ffi::OsString::new(); os_str.push("'"); if let Some(hook) = hook.to_str() { - os_str.push(hook.replace('\'', "\\'")); + // SEE: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_02 + // Enclosing characters in single-quotes ( '' ) shall preserve the literal value of each character within the single-quotes. + // A single-quote cannot occur within single-quotes. + const REPLACEMENT: &str = concat!( + "'", // closing single-quote + "\\'", // one escaped single-quote (outside of single-quotes) + "'", // new single-quote + ); + os_str.push(hook.replace('\'', REPLACEMENT)); } else { os_str.push(hook.as_os_str()); // TODO: this doesn't work if `hook` contains single-quotes } From 0b3d2c0d000f5051c60528a8b5fa9855ab8e2460 Mon Sep 17 00:00:00 2001 From: Joshix Date: Wed, 26 Mar 2025 19:00:00 +0000 Subject: [PATCH 48/55] less indentation --- git2-hooks/src/lib.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs index 022aa4622e..fea3fc3bf3 100644 --- a/git2-hooks/src/lib.rs +++ b/git2-hooks/src/lib.rs @@ -326,27 +326,22 @@ exit 0 let result = hook.run_hook(&[TEXT]).unwrap(); - if let HookResult::RunNotSuccessful { + let HookResult::RunNotSuccessful { code, stdout, stderr, hook: h, } = result - { - assert_eq!(code, Some(42)); - assert_eq!(h, hook.hook); - assert_eq!( - stdout.as_str().trim_ascii_end(), - TEXT, - "{:?} != {TEXT:?} | {:?} != {:?}", - stdout.as_str().trim_ascii_end(), - stdout.as_str().trim_ascii_end().as_bytes(), - TEXT.as_bytes() - ); - assert!(stderr.is_empty()); - } else { - panic!("run_hook should've failed"); - } + else { + unreachable!("run_hook should've failed"); + }; + + let stdout = stdout.as_str().trim_ascii_end(); + + assert_eq!(code, Some(42)); + assert_eq!(h, hook.hook); + assert_eq!(stdout, TEXT, "{:?} != {TEXT:?}", stdout); + assert!(stderr.is_empty()); } #[test] From 156381155e12d89b39e657f07c8dac557271d24f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 13:22:09 +0800 Subject: [PATCH 49/55] Bump clap from 4.5.32 to 4.5.34 (#2585) Bumps [clap](https://github.com/clap-rs/clap) from 4.5.32 to 4.5.34. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.32...clap_complete-v4.5.34) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f02a5a1817..68ef85cd0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,18 +432,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" dependencies = [ "anstream", "anstyle", From a26afc80872c75c3c5597984150e292c874e40e5 Mon Sep 17 00:00:00 2001 From: Naseschwarz Date: Tue, 25 Mar 2025 08:41:30 +0100 Subject: [PATCH 50/55] ci: Actually use toolchain in nightly/MSRV runs Somehow at some point, additional rust toolchains became available in CI. Thus, it's no longer sufficient to install the matrix toolchain, but actually override it for the build directory. --- .github/workflows/ci.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b15568ab49..2d6df98d39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,9 @@ jobs: toolchain: ${{ matrix.rust }} components: clippy + - name: Override rust toolchain + run: rustup override set ${{ matrix.rust }} + - name: Rustup Show run: rustup show @@ -112,6 +115,12 @@ jobs: - name: Manually install target run: rustup target add x86_64-unknown-linux-musl + - name: Override rust toolchain + run: rustup override set ${{ matrix.rust }} + + - name: Rustup Show + run: rustup show + - name: Setup MUSL run: | sudo apt-get -qq install musl-tools @@ -151,6 +160,10 @@ jobs: uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} + + - name: Override rust toolchain + run: rustup override set ${{ matrix.rust }} + - name: Setup ARM toolchain run: | rustup target add aarch64-unknown-linux-gnu @@ -166,6 +179,9 @@ jobs: echo "$GITHUB_WORKSPACE/gcc-arm-8.2-2018.08-x86_64-aarch64-linux-gnu/bin" >> $GITHUB_PATH echo "$GITHUB_WORKSPACE/gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf/bin" >> $GITHUB_PATH + - name: Rustup Show + run: rustup show + - name: Build Debug run: | make build-linux-arm-debug @@ -195,9 +211,16 @@ jobs: uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} + + - name: Override rust toolchain + run: rustup override set ${{ matrix.rust }} + - name: Setup target run: rustup target add x86_64-apple-darwin + - name: Rustup Show + run: rustup show + - name: Build Debug run: | make build-apple-x86-debug From 32807fd0343bacb80a1136dd64cfb471d18879d4 Mon Sep 17 00:00:00 2001 From: Naseschwarz Date: Thu, 27 Mar 2025 10:55:36 +0100 Subject: [PATCH 51/55] Upgrade MSRV to 1.81 --- .github/workflows/ci.yml | 8 ++++---- Cargo.toml | 2 +- README.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d6df98d39..e2f4431227 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - rust: [nightly, stable, '1.70'] + rust: [nightly, stable, '1.81'] runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.rust == 'nightly' }} @@ -92,7 +92,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, stable, '1.70'] + rust: [nightly, stable, '1.81'] continue-on-error: ${{ matrix.rust == 'nightly' }} steps: - uses: actions/checkout@v4 @@ -144,7 +144,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, stable, '1.70'] + rust: [nightly, stable, '1.81'] continue-on-error: ${{ matrix.rust == 'nightly' }} steps: - uses: actions/checkout@v4 @@ -195,7 +195,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, stable, '1.70'] + rust: [nightly, stable, '1.81'] continue-on-error: ${{ matrix.rust == 'nightly' }} steps: - uses: actions/checkout@v4 diff --git a/Cargo.toml b/Cargo.toml index 67424121c3..14e50f9dbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.27.0" authors = ["extrawurst "] description = "blazing fast terminal-ui for git" edition = "2021" -rust-version = "1.70" +rust-version = "1.81" exclude = [".github/*", ".vscode/*", "assets/*"] homepage = "https://github.com/gitui-org/gitui" repository = "https://github.com/gitui-org/gitui" diff --git a/README.md b/README.md index 4b8b90e7d6..dd4270d6ed 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ see [NIGHTLIES.md](./NIGHTLIES.md) ### Requirements -- Minimum supported `rust`/`cargo` version: `1.70` +- Minimum supported `rust`/`cargo` version: `1.81` - See [Install Rust](https://www.rust-lang.org/tools/install) - To build openssl dependency (see https://docs.rs/openssl/latest/openssl/) From fce84ae488891fd249581ad0c1b5d7f55a3ac745 Mon Sep 17 00:00:00 2001 From: Naseschwarz Date: Tue, 25 Mar 2025 16:09:39 +0100 Subject: [PATCH 52/55] Temporarily allow debug formatting of hook path This will be fixed in https://github.com/gitui-org/gitui/pull/2483 --- git2-hooks/src/hookspath.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 3648676ee2..4bdc862633 100644 --- a/git2-hooks/src/hookspath.rs +++ b/git2-hooks/src/hookspath.rs @@ -109,6 +109,7 @@ impl HookPaths { pub fn run_hook(&self, args: &[&str]) -> Result { let hook = self.hook.clone(); + #[allow(clippy::unnecessary_debug_formatting)] let arg_str = format!("{:?} {}", hook, args.join(" ")); // Use -l to avoid "command not found" on Windows. let bash_args = From eea202b041cde6e3ac7657c573e934c1eb3ae656 Mon Sep 17 00:00:00 2001 From: Naseschwarz Date: Thu, 27 Mar 2025 09:08:37 +0100 Subject: [PATCH 53/55] Fix clippy remarks --- asyncgit/src/sync/branch/mod.rs | 8 ++--- asyncgit/src/sync/remotes/push.rs | 9 ++++-- asyncgit/src/sync/stash.rs | 2 +- asyncgit/src/sync/utils.rs | 4 +-- filetreelist/src/item.rs | 2 +- filetreelist/src/treeitems_iter.rs | 2 +- src/app.rs | 8 ++--- src/args.rs | 2 +- src/components/changes.rs | 2 +- src/components/commitlist.rs | 48 +++++++++++++++++++----------- src/components/textinput.rs | 2 +- src/components/utils/filetree.rs | 2 +- src/popups/commit.rs | 5 ++-- src/popups/create_remote.rs | 2 +- src/popups/file_revlog.rs | 2 +- src/popups/options.rs | 4 +-- src/tabs/stashing.rs | 2 +- src/tabs/stashlist.rs | 2 +- src/tabs/status.rs | 2 +- src/ui/scrollbar.rs | 4 +-- 20 files changed, 64 insertions(+), 50 deletions(-) diff --git a/asyncgit/src/sync/branch/mod.rs b/asyncgit/src/sync/branch/mod.rs index 93185d6f45..81ec2ec586 100644 --- a/asyncgit/src/sync/branch/mod.rs +++ b/asyncgit/src/sync/branch/mod.rs @@ -271,7 +271,7 @@ pub fn config_is_pull_rebase(repo_path: &RepoPath) -> Result { let value = rebase.value().map(String::from).unwrap_or_default(); return Ok(value == "true"); - }; + } Ok(false) } @@ -701,7 +701,7 @@ mod tests_branches { &root.as_os_str().to_str().unwrap().into(); let upstream_merge_res = - get_branch_upstream_merge(&repo_path, "master"); + get_branch_upstream_merge(repo_path, "master"); assert!( upstream_merge_res.is_ok_and(|v| v.as_ref().is_none()) ); @@ -721,12 +721,12 @@ mod tests_branches { config .set_str( &format!("branch.{branch_name}.merge"), - &upstrem_merge, + upstrem_merge, ) .expect("fail set branch merge config"); let upstream_merge_res = - get_branch_upstream_merge(&repo_path, &branch_name); + get_branch_upstream_merge(repo_path, branch_name); assert!(upstream_merge_res .as_ref() .is_ok_and(|v| v.as_ref().is_some())); diff --git a/asyncgit/src/sync/remotes/push.rs b/asyncgit/src/sync/remotes/push.rs index b28b8a22d3..00cf534ba0 100644 --- a/asyncgit/src/sync/remotes/push.rs +++ b/asyncgit/src/sync/remotes/push.rs @@ -1,3 +1,5 @@ +use std::fmt::Write; + use crate::{ error::{Error, Result}, progress::ProgressPercent, @@ -182,7 +184,8 @@ pub fn push_raw( if let Ok(Some(branch_upstream_merge)) = get_branch_upstream_merge(repo_path, branch) { - push_ref.push_str(&format!(":{branch_upstream_merge}")); + write!(&mut push_ref, ":{branch_upstream_merge}") + .map_err(|e| Error::Generic(e.to_string()))?; } } @@ -289,7 +292,7 @@ mod tests { // Attempt force push, // should work as it forces the push through - assert!(!push_branch( + assert!(push_branch( &tmp_other_repo_dir.path().to_str().unwrap().into(), "origin", "master", @@ -298,7 +301,7 @@ mod tests { None, None, ) - .is_err()); + .is_ok()); } #[test] diff --git a/asyncgit/src/sync/stash.rs b/asyncgit/src/sync/stash.rs index c496168116..8a65f92feb 100644 --- a/asyncgit/src/sync/stash.rs +++ b/asyncgit/src/sync/stash.rs @@ -144,7 +144,7 @@ mod tests { let repo_path: &RepoPath = &root.as_os_str().to_str().unwrap().into(); - assert!(!stash_save(repo_path, None, true, false).is_ok()); + assert!(stash_save(repo_path, None, true, false).is_err()); assert!(get_stashes(repo_path).unwrap().is_empty()); } diff --git a/asyncgit/src/sync/utils.rs b/asyncgit/src/sync/utils.rs index 8e767bbfd4..ebae31beb0 100644 --- a/asyncgit/src/sync/utils.rs +++ b/asyncgit/src/sync/utils.rs @@ -242,7 +242,7 @@ mod tests { let root = repo.path().parent().unwrap(); let repo_path = root.as_os_str().to_str().unwrap(); - assert!(!stage_add_file(&repo_path.into(), file_path).is_ok()); + assert!(stage_add_file(&repo_path.into(), file_path).is_err()); } #[test] @@ -440,7 +440,7 @@ mod tests { let repo_path: &RepoPath = &root.as_os_str().to_str().unwrap().into(); - assert!(!get_head(repo_path).is_ok()); + assert!(get_head(repo_path).is_err()); Ok(()) } diff --git a/filetreelist/src/item.rs b/filetreelist/src/item.rs index f664031b14..b06c275df2 100644 --- a/filetreelist/src/item.rs +++ b/filetreelist/src/item.rs @@ -55,7 +55,7 @@ impl TreeItemInfo { Path::new( self.full_path .components() - .last() + .next_back() .and_then(|c| c.as_os_str().to_str()) .unwrap_or_default(), ) diff --git a/filetreelist/src/treeitems_iter.rs b/filetreelist/src/treeitems_iter.rs index 4feeb3e8ca..fddf35a1e9 100644 --- a/filetreelist/src/treeitems_iter.rs +++ b/filetreelist/src/treeitems_iter.rs @@ -35,7 +35,7 @@ impl<'a> Iterator for TreeItemsIterator<'a> { *i += 1; } else { self.increments = Some(0); - }; + } loop { if !init { diff --git a/src/app.rs b/src/app.rs index 45037f048f..68d2987c6f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -275,7 +275,7 @@ impl App { 3 => self.stashing_tab.draw(f, chunks_main[1])?, 4 => self.stashlist_tab.draw(f, chunks_main[1])?, _ => bail!("unknown tab"), - }; + } } self.draw_popups(f)?; @@ -905,7 +905,7 @@ impl App { InternalEvent::CommitSearch(options) => { self.revlog.search(options); } - }; + } Ok(flags) } @@ -997,7 +997,7 @@ impl App { undo_last_commit(&self.repo.borrow()) ); } - }; + } flags.insert(NeedsUpdate::ALL); @@ -1019,7 +1019,7 @@ impl App { )); self.tags_popup.update_tags()?; - }; + } Ok(()) } diff --git a/src/args.rs b/src/args.rs index 8ef3aeb42f..6c09317314 100644 --- a/src/args.rs +++ b/src/args.rs @@ -139,7 +139,7 @@ fn setup_logging(path_override: Option) -> Result<()> { path }; - println!("Logging enabled. Log written to: {path:?}"); + println!("Logging enabled. Log written to: {}", path.display()); WriteLogger::init( LevelFilter::Trace, diff --git a/src/components/changes.rs b/src/components/changes.rs index 7b0bc4270b..48883d20e8 100644 --- a/src/components/changes.rs +++ b/src/components/changes.rs @@ -92,7 +92,7 @@ impl ChangesComponent { &self.repo.borrow(), path, )?, - }; + } } else { let config = self.options.borrow().status_show_untracked(); diff --git a/src/components/commitlist.rs b/src/components/commitlist.rs index 52e4e7be9d..197331e24e 100644 --- a/src/components/commitlist.rs +++ b/src/components/commitlist.rs @@ -490,24 +490,36 @@ impl CommitList { txt.push(splitter.clone()); } - let style_hash = normal - .then(|| theme.commit_hash(selected)) - .unwrap_or_else(|| theme.commit_unhighlighted()); - let style_time = normal - .then(|| theme.commit_time(selected)) - .unwrap_or_else(|| theme.commit_unhighlighted()); - let style_author = normal - .then(|| theme.commit_author(selected)) - .unwrap_or_else(|| theme.commit_unhighlighted()); - let style_tags = normal - .then(|| theme.tags(selected)) - .unwrap_or_else(|| theme.commit_unhighlighted()); - let style_branches = normal - .then(|| theme.branch(selected, true)) - .unwrap_or_else(|| theme.commit_unhighlighted()); - let style_msg = normal - .then(|| theme.text(true, selected)) - .unwrap_or_else(|| theme.commit_unhighlighted()); + let style_hash = if normal { + theme.commit_hash(selected) + } else { + theme.commit_unhighlighted() + }; + let style_time = if normal { + theme.commit_time(selected) + } else { + theme.commit_unhighlighted() + }; + let style_author = if normal { + theme.commit_author(selected) + } else { + theme.commit_unhighlighted() + }; + let style_tags = if normal { + theme.tags(selected) + } else { + theme.commit_unhighlighted() + }; + let style_branches = if normal { + theme.branch(selected, true) + } else { + theme.commit_unhighlighted() + }; + let style_msg = if normal { + theme.text(true, selected) + } else { + theme.commit_unhighlighted() + }; // commit hash txt.push(Span::styled(Cow::from(&*e.hash_short), style_hash)); diff --git a/src/components/textinput.rs b/src/components/textinput.rs index e6586b8355..e67d19eac9 100644 --- a/src/components/textinput.rs +++ b/src/components/textinput.rs @@ -176,7 +176,7 @@ impl TextInputComponent { ) .title(self.title.clone()), ); - }; + } text_area }); } diff --git a/src/components/utils/filetree.rs b/src/components/utils/filetree.rs index 8b298dbb93..a0b507fa82 100644 --- a/src/components/utils/filetree.rs +++ b/src/components/utils/filetree.rs @@ -90,7 +90,7 @@ impl FileTreeItem { match path .components() - .last() + .next_back() .map(std::path::Component::as_os_str) .map(OsStr::to_string_lossy) .map(String::from) diff --git a/src/popups/commit.rs b/src/popups/commit.rs index 4dcfc3241c..8609667c18 100644 --- a/src/popups/commit.rs +++ b/src/popups/commit.rs @@ -470,9 +470,8 @@ impl CommitPopup { let mut msg = msg.to_owned(); if let (Some(user), Some(mail)) = (user, mail) { - msg.push_str(&format!( - "\n\nSigned-off-by: {user} <{mail}>" - )); + use std::fmt::Write; + write!(msg, "\n\nSigned-off-by: {user} <{mail}>")?; } Ok(msg) diff --git a/src/popups/create_remote.rs b/src/popups/create_remote.rs index ac64934da5..8e464931e8 100644 --- a/src/popups/create_remote.rs +++ b/src/popups/create_remote.rs @@ -209,6 +209,6 @@ impl CreateRemotePopup { self.hide(); } - }; + } } } diff --git a/src/popups/file_revlog.rs b/src/popups/file_revlog.rs index c946323b5d..6bbbffbe8a 100644 --- a/src/popups/file_revlog.rs +++ b/src/popups/file_revlog.rs @@ -521,7 +521,7 @@ impl Component for FileRevlogPopup { InspectCommitOpen::new(commit_id), ), )); - }; + } } else if key_match(key, self.key_config.keys.blame) { if let Some(open_request) = self.open_request.clone() diff --git a/src/popups/options.rs b/src/popups/options.rs index 0b06131b48..e74e8bdc0b 100644 --- a/src/popups/options.rs +++ b/src/popups/options.rs @@ -207,7 +207,7 @@ impl OptionsPopup { .borrow_mut() .diff_hunk_lines_change(true); } - }; + } } else { match self.selection { AppOption::StatusShowUntracked => { @@ -246,7 +246,7 @@ impl OptionsPopup { .borrow_mut() .diff_hunk_lines_change(false); } - }; + } } self.queue diff --git a/src/tabs/stashing.rs b/src/tabs/stashing.rs index e5564563aa..5b2fe4b496 100644 --- a/src/tabs/stashing.rs +++ b/src/tabs/stashing.rs @@ -245,7 +245,7 @@ impl Component for Stashing { } else { Ok(EventState::NotConsumed) }; - }; + } } Ok(EventState::NotConsumed) diff --git a/src/tabs/stashlist.rs b/src/tabs/stashlist.rs index c8dceb2f63..1a97a0ac38 100644 --- a/src/tabs/stashlist.rs +++ b/src/tabs/stashlist.rs @@ -102,7 +102,7 @@ impl StashList { Action::StashDrop(ids) => self.drop(repo, ids)?, Action::StashPop(id) => self.pop(repo, *id)?, _ => (), - }; + } Ok(()) } diff --git a/src/tabs/status.rs b/src/tabs/status.rs index 034ffe39e6..40cf210786 100644 --- a/src/tabs/status.rs +++ b/src/tabs/status.rs @@ -348,7 +348,7 @@ impl Status { self.diff.focus(true); } - }; + } self.update_diff()?; diff --git a/src/ui/scrollbar.rs b/src/ui/scrollbar.rs index 6ae7ca3ee2..a0a9b3df80 100644 --- a/src/ui/scrollbar.rs +++ b/src/ui/scrollbar.rs @@ -49,7 +49,7 @@ impl Scrollbar { let right = area.right().saturating_sub(1); if right <= area.left() { return; - }; + } let (bar_top, bar_height) = { let scrollbar_area = area.inner(Margin { @@ -86,7 +86,7 @@ impl Scrollbar { let bottom = area.bottom().saturating_sub(1); if bottom <= area.top() { return; - }; + } let (bar_left, bar_width) = { let scrollbar_area = area.inner(Margin { From e09fe3aeb3d90179c5868ae66245d8b238053160 Mon Sep 17 00:00:00 2001 From: Naseschwarz Date: Thu, 27 Mar 2025 09:08:52 +0100 Subject: [PATCH 54/55] Disable clippy::missing_const_for_fn if impossible --- src/components/commitlist.rs | 3 +++ src/keys/key_config.rs | 10 ++++++++++ src/popups/blame_file.rs | 4 ++++ src/ui/syntax_text.rs | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/src/components/commitlist.rs b/src/components/commitlist.rs index 197331e24e..3cad2bbe96 100644 --- a/src/components/commitlist.rs +++ b/src/components/commitlist.rs @@ -114,6 +114,9 @@ impl CommitList { self.marked.len() } + // Clippy wants this to be const in nightly, which is not possible. + // Disable check to make clippy pass: + #[allow(clippy::missing_const_for_fn)] /// pub fn marked(&self) -> &[(usize, CommitId)] { &self.marked diff --git a/src/keys/key_config.rs b/src/keys/key_config.rs index 9cd4eb73f2..9646107db5 100644 --- a/src/keys/key_config.rs +++ b/src/keys/key_config.rs @@ -40,6 +40,11 @@ impl KeyConfig { Ok(Self { keys, symbols }) } + // Clippy wants this to be const in nightly + // This can't really be const, as deref into &str is not const, even + // in nightly. + // Disable clippy warning to build on nightly. + #[allow(clippy::missing_const_for_fn)] fn get_key_symbol(&self, k: KeyCode) -> &str { match k { KeyCode::Enter => &self.symbols.enter, @@ -106,6 +111,11 @@ impl KeyConfig { } } + // Clippy wants this to be const in nightly + // This can't really be const, as deref into &str is not const, even + // in nightly. + // Disable clippy warning to build on nightly. + #[allow(clippy::missing_const_for_fn)] fn get_modifier_hint(&self, modifier: KeyModifiers) -> &str { match modifier { KeyModifiers::CONTROL => &self.symbols.control, diff --git a/src/popups/blame_file.rs b/src/popups/blame_file.rs index 273ca5ace3..b48c4292a6 100644 --- a/src/popups/blame_file.rs +++ b/src/popups/blame_file.rs @@ -41,6 +41,10 @@ struct SyntaxFileBlame { } impl SyntaxFileBlame { + // Clippy wants this to be const in nightly. + // This can't really be const, as deref into Path is not const. + // Disable clippy warning to build on nightly. + #[allow(clippy::missing_const_for_fn)] fn path(&self) -> &str { &self.file_blame.path } diff --git a/src/ui/syntax_text.rs b/src/ui/syntax_text.rs index 8d758f20cc..cb36b29ac3 100644 --- a/src/ui/syntax_text.rs +++ b/src/ui/syntax_text.rs @@ -167,6 +167,10 @@ impl SyntaxText { }) } + // Clippy wants this to be const in nightly. + // This can't really be const, as deref into Path is not const. + // Disable clippy warning to build on nightly. + #[allow(clippy::missing_const_for_fn)] /// pub fn path(&self) -> &Path { &self.path From 32e8358f9a9d030b3362b289081b7f65857e58f7 Mon Sep 17 00:00:00 2001 From: Naseschwarz Date: Thu, 27 Mar 2025 11:08:26 +0100 Subject: [PATCH 55/55] Add changelog entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 012b60e5ff..99bb565a13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * add `use_selection_fg` to theme file to allow customizing selection foreground color [[@Upsylonbare](https://github.com/Upsylonbare)] ([#2515](https://github.com/gitui-org/gitui/pull/2515)) ### Changed +* increase MSRV from 1.70 to 1.81 [[@naseschwarz](https://github.com/naseschwarz)] ([#2094](https://github.com/gitui-org/gitui/issues/2094)) * improve syntax highlighting file detection [[@acuteenvy](https://github.com/acuteenvy)] ([#2524](https://github.com/extrawurst/gitui/pull/2524)) * Updated project links to point to `gitui-org` instead of `extrawurst` [[@vasleymus](https://github.com/vasleymus)] ([#2538](https://github.com/gitui-org/gitui/pull/2538)) * After commit: jump back to unstaged area [[@tommady](https://github.com/tommady)] ([#2476](https://github.com/extrawurst/gitui/issues/2476)) @@ -24,6 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * set the terminal title to `gitui ({repo_path})` [[@acuteenvy](https://github.com/acuteenvy)] ([#2462](https://github.com/gitui-org/gitui/issues/2462)) * respect `.mailmap` [[@acuteenvy](https://github.com/acuteenvy)] ([#2406](https://github.com/gitui-org/gitui/issues/2406)) +### Fixed +* build on nightly [[@naseschwarz](https://github.com/naseschwarz)] ([#2094](https://github.com/gitui-org/gitui/issues/2094)) + ## [0.27.0] - 2024-01-14 **new: manage remotes**