diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b15568ab49..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' }} @@ -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 @@ -89,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 @@ -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 @@ -135,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 @@ -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 @@ -179,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 @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 012b60e5ff..70b28d1616 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 +* 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)) @@ -15,6 +16,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 +26,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** diff --git a/Cargo.lock b/Cargo.lock index f15e14f983..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", @@ -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..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" @@ -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", 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/) 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/hooks.rs b/asyncgit/src/sync/hooks.rs index 3e82cf6f8d..85b40d3c71 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() { @@ -85,7 +92,7 @@ mod tests { let hook = b"#!/bin/sh echo 'rejected' exit 1 - "; + "; git2_hooks::create_hook( &repo, @@ -120,10 +127,9 @@ mod tests { crate::sync::utils::repo_work_dir(repo_path).unwrap(); let hook = b"#!/bin/sh - echo $(pwd) + echo \"$(pwd)\" exit 1 - "; - + "; git2_hooks::create_hook( &repo, git2_hooks::HOOK_PRE_COMMIT, @@ -146,10 +152,10 @@ mod tests { let root = repo.path().parent().unwrap(); let hook = b"#!/bin/sh - echo 'msg' > $1 + echo 'msg' > \"$1\" echo 'rejected' exit 1 - "; + "; git2_hooks::create_hook( &repo, diff --git a/asyncgit/src/sync/mod.rs b/asyncgit/src/sync/mod.rs index c52a556aad..683aacd0ef 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/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/git2-hooks/src/hookspath.rs b/git2-hooks/src/hookspath.rs index 3648676ee2..ea6ce9e189 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, @@ -17,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. @@ -108,29 +108,51 @@ 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 bash_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) - .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 + .args(args) + .current_dir(&self.pwd) + .with_no_window() + .output() + }; + + let output = if cfg!(windows) { + // execute hook in shell + let command = { + let mut os_str = std::ffi::OsString::new(); + os_str.push("'"); + if let Some(hook) = hook.to_str() { + // 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 + } + os_str.push("'"); + os_str.push(" \"$@\""); + + os_str + }; + run_command( + sh_command().arg("-c").arg(command).arg(&hook), ) - .output()?; + } else { + // execute hook directly + match run_command(&mut Command::new(&hook)) { + Err(err) if err.raw_os_error() == Some(ENOEXEC) => { + run_command(sh_command().arg(&hook)) + } + result => result, + } + }?; if output.status.success() { Ok(HookResult::Ok { hook }) @@ -150,33 +172,28 @@ impl HookPaths { } } -#[cfg(unix)] -fn is_executable(path: &Path) -> bool { - use std::os::unix::fs::PermissionsExt; +fn sh_command() -> Command { + let mut command = Command::new(sh_path()); - let metadata = match path.metadata() { - Ok(metadata) => metadata, - Err(e) => { - log::error!("metadata error: {}", e); - return false; - } - }; - - let permissions = metadata.permissions(); - - permissions.mode() & 0o111 != 0 -} + 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", + ); + + // Use -l to avoid "command not found" + command.arg("-l"); + } -#[cfg(windows)] -/// windows does not consider bash 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 + command } -// Find bash.exe, and avoid finding wsl's bash.exe on Windows. -// None for non-Windows. -fn find_bash_executable() -> Option { +/// 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") @@ -190,16 +207,36 @@ 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 + "sh".into() } } -// Find default shell on Unix-like OS. -fn find_default_unix_shell() -> Option { - env::var_os("SHELL").map(PathBuf::from) +#[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; + } + }; + + let permissions = metadata.permissions(); + + permissions.mode() & 0o111 != 0 +} + +#[cfg(windows)] +/// 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 } trait CommandExt { diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs index 2a458856d7..fea3fc3bf3 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 "#; @@ -309,6 +309,41 @@ 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(); + + let HookResult::RunNotSuccessful { + code, + stdout, + stderr, + hook: h, + } = result + 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] fn test_no_hook_found() { let (_td, repo) = repo_init(); @@ -388,6 +423,8 @@ exit 1 #[test] fn test_env_containing_path() { + const PATH_EXPORT: &str = "export PATH"; + let (_td, repo) = repo_init(); let hook = b"#!/bin/sh @@ -402,9 +439,12 @@ exit 1 unreachable!() }; - assert!(stdout - .lines() - .any(|line| line.starts_with("export PATH"))); + assert!( + stdout + .lines() + .any(|line| line.starts_with(PATH_EXPORT)), + "Could not find line starting with {PATH_EXPORT:?} in: {stdout:?}" + ); } #[test] @@ -470,7 +510,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] @@ -499,9 +539,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); @@ -525,7 +565,7 @@ exit 1 let (_td, repo) = repo_init(); let hook = b"#!/bin/sh -echo 'msg' > $1 +echo 'msg' > \"$1\" exit 0 "; @@ -565,7 +605,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 +629,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 "; 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..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 @@ -490,24 +493,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/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/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 { 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