mirror of
https://github.com/extrawurst/gitui.git
synced 2024-12-26 18:43:37 +03:00
support hookspath (#1054)
This commit is contained in:
parent
449dc43a5f
commit
435de9cda3
@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
![delete-tag-remote](assets/delete-tag-remote.gif)
|
||||
|
||||
### Added
|
||||
- support `core.hooksPath` ([#1044](https://github.com/extrawurst/gitui/issues/1044))
|
||||
- allow reverting a commit from the commit log ([#927](https://github.com/extrawurst/gitui/issues/927))
|
||||
- disable pull cmd on local-only branches ([#1047](https://github.com/extrawurst/gitui/issues/1047))
|
||||
- support adding annotations to tags ([#747](https://github.com/extrawurst/gitui/issues/747))
|
||||
|
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -75,6 +75,7 @@ dependencies = [
|
||||
"rayon-core",
|
||||
"scopetime",
|
||||
"serial_test",
|
||||
"shellexpand",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"unicode-truncate",
|
||||
@ -1219,6 +1220,15 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
|
||||
|
||||
[[package]]
|
||||
name = "shellexpand"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.13"
|
||||
|
@ -82,7 +82,6 @@ These are the high level goals before calling out `1.0`:
|
||||
|
||||
## 5. <a name="limitations"></a> Known Limitations <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
- no support for [core.hooksPath](https://git-scm.com/docs/githooks) config (see [#1044](https://github.com/extrawurst/gitui/issues/1044))
|
||||
- no support for GPG signing (see [#97](https://github.com/extrawurst/gitui/issues/97))
|
||||
|
||||
Currently, this tool does not fully substitute the _git shell_, however both tools work well in tandem.
|
||||
|
@ -22,6 +22,7 @@ log = "0.4"
|
||||
openssl-sys = { version = '0.9', features = ["vendored"] }
|
||||
rayon-core = "1.9"
|
||||
scopetime = { path = "../scopetime", version = "0.1" }
|
||||
shellexpand = "2.1"
|
||||
thiserror = "1.0"
|
||||
unicode-truncate = "0.2.0"
|
||||
url = "2.2"
|
||||
|
@ -61,6 +61,14 @@ pub enum Error {
|
||||
///
|
||||
#[error("EasyCast error:{0}")]
|
||||
EasyCast(#[from] easy_cast::Error),
|
||||
|
||||
///
|
||||
#[error("shellexpand error:{0}")]
|
||||
Shell(#[from] shellexpand::LookupError<std::env::VarError>),
|
||||
|
||||
///
|
||||
#[error("path string error")]
|
||||
PathString,
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -1,16 +1,17 @@
|
||||
use super::{repository::repo, RepoPath};
|
||||
use crate::error::Result;
|
||||
use crate::error::{self, Result};
|
||||
use scopetime::scope_time;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
const HOOK_POST_COMMIT: &str = "hooks/post-commit";
|
||||
const HOOK_PRE_COMMIT: &str = "hooks/pre-commit";
|
||||
const HOOK_COMMIT_MSG: &str = "hooks/commit-msg";
|
||||
const HOOK_POST_COMMIT: &str = "post-commit";
|
||||
const HOOK_PRE_COMMIT: &str = "pre-commit";
|
||||
const HOOK_COMMIT_MSG: &str = "commit-msg";
|
||||
const HOOK_COMMIT_MSG_TEMP_FILE: &str = "COMMIT_EDITMSG";
|
||||
|
||||
struct HookPaths {
|
||||
@ -26,8 +27,30 @@ impl HookPaths {
|
||||
.workdir()
|
||||
.unwrap_or_else(|| repo.path())
|
||||
.to_path_buf();
|
||||
|
||||
let git_dir = repo.path().to_path_buf();
|
||||
let hook = git_dir.join(hook);
|
||||
let hooks_path = repo
|
||||
.config()
|
||||
.and_then(|config| config.get_string("core.hooksPath"))
|
||||
.map_or_else(
|
||||
|e| {
|
||||
log::error!("hookspath error: {}", e);
|
||||
repo.path().to_path_buf().join("hooks/")
|
||||
},
|
||||
PathBuf::from,
|
||||
);
|
||||
|
||||
let hook = hooks_path.join(hook);
|
||||
|
||||
let hook = shellexpand::full(
|
||||
hook.as_os_str()
|
||||
.to_str()
|
||||
.ok_or(error::Error::PathString)?,
|
||||
)?;
|
||||
|
||||
let hook = PathBuf::from_str(hook.as_ref())
|
||||
.map_err(|_| error::Error::PathString)?;
|
||||
|
||||
Ok(Self {
|
||||
git: git_dir,
|
||||
hook,
|
||||
@ -143,10 +166,14 @@ fn is_executable(path: &Path) -> bool {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let metadata = match path.metadata() {
|
||||
Ok(metadata) => metadata,
|
||||
Err(_) => return false,
|
||||
Err(e) => {
|
||||
log::error!("metadata error: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let permissions = metadata.permissions();
|
||||
|
||||
permissions.mode() & 0o111 != 0
|
||||
}
|
||||
|
||||
@ -181,20 +208,28 @@ mod tests {
|
||||
assert_eq!(res, HookResult::Ok);
|
||||
}
|
||||
|
||||
fn create_hook(path: &RepoPath, hook: &str, hook_script: &[u8]) {
|
||||
fn create_hook(
|
||||
path: &RepoPath,
|
||||
hook: &str,
|
||||
hook_script: &[u8],
|
||||
) -> PathBuf {
|
||||
let hook = HookPaths::new(path, hook).unwrap();
|
||||
|
||||
File::create(&hook.hook)
|
||||
.unwrap()
|
||||
.write_all(hook_script)
|
||||
.unwrap();
|
||||
let path = hook.hook.clone();
|
||||
|
||||
create_hook_in_path(&hook.hook, hook_script);
|
||||
|
||||
path
|
||||
}
|
||||
|
||||
fn create_hook_in_path(path: &Path, hook_script: &[u8]) {
|
||||
File::create(path).unwrap().write_all(hook_script).unwrap();
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let hook = hook.hook.as_os_str();
|
||||
Command::new("chmod")
|
||||
.arg("+x")
|
||||
.arg(hook)
|
||||
.arg(path)
|
||||
// .current_dir(path)
|
||||
.output()
|
||||
.unwrap();
|
||||
@ -255,6 +290,34 @@ exit 1
|
||||
assert!(res != HookResult::Ok);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pre_commit_fail_hookspath() {
|
||||
let (_td, repo) = repo_init().unwrap();
|
||||
let root = repo.path().parent().unwrap();
|
||||
let hooks = TempDir::new().unwrap();
|
||||
let repo_path: &RepoPath =
|
||||
&root.as_os_str().to_str().unwrap().into();
|
||||
|
||||
let hook = b"#!/bin/sh
|
||||
echo 'rejected'
|
||||
exit 1
|
||||
";
|
||||
|
||||
create_hook_in_path(&hooks.path().join("pre-commit"), hook);
|
||||
repo.config()
|
||||
.unwrap()
|
||||
.set_str(
|
||||
"core.hooksPath",
|
||||
hooks.path().as_os_str().to_str().unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let res = hooks_pre_commit(repo_path).unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
HookResult::NotOk(String::from("rejected\n"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pre_commit_fail_bare() {
|
||||
let (git_root, _repo) = repo_init_bare().unwrap();
|
||||
|
Loading…
Reference in New Issue
Block a user