Skip to content

Commit 5435eec

Browse files
Stephan DillyMifom
Stephan Dilly
authored andcommitted
support hookspath (gitui-org#1054)
1 parent a79cde5 commit 5435eec

File tree

6 files changed

+96
-14
lines changed

6 files changed

+96
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
![delete-tag-remote](assets/delete-tag-remote.gif)
2121

2222
### Added
23+
- support `core.hooksPath` ([#1044](https://github.com/extrawurst/gitui/issues/1044))
2324
- allow reverting a commit from the commit log ([#927](https://github.com/extrawurst/gitui/issues/927))
2425
- disable pull cmd on local-only branches ([#1047](https://github.com/extrawurst/gitui/issues/1047))
2526
- support adding annotations to tags ([#747](https://github.com/extrawurst/gitui/issues/747))

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ These are the high level goals before calling out `1.0`:
8282

8383
## 5. <a name="limitations"></a> Known Limitations <small><sup>[Top ▲](#table-of-contents)</sup></small>
8484

85-
- no support for [core.hooksPath](https://git-scm.com/docs/githooks) config (see [#1044](https://github.com/extrawurst/gitui/issues/1044))
8685
- no support for GPG signing (see [#97](https://github.com/extrawurst/gitui/issues/97))
8786

8887
Currently, this tool does not fully substitute the _git shell_, however both tools work well in tandem.

asyncgit/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ log = "0.4"
2222
openssl-sys = { version = '0.9', features = ["vendored"] }
2323
rayon-core = "1.9"
2424
scopetime = { path = "../scopetime", version = "0.1" }
25+
shellexpand = "2.1"
2526
thiserror = "1.0"
2627
unicode-truncate = "0.2.0"
2728
url = "2.2"

asyncgit/src/error.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ pub enum Error {
6161
///
6262
#[error("EasyCast error:{0}")]
6363
EasyCast(#[from] easy_cast::Error),
64+
65+
///
66+
#[error("shellexpand error:{0}")]
67+
Shell(#[from] shellexpand::LookupError<std::env::VarError>),
68+
69+
///
70+
#[error("path string error")]
71+
PathString,
6472
}
6573

6674
///

asyncgit/src/sync/hooks.rs

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
use super::{repository::repo, RepoPath};
2-
use crate::error::Result;
2+
use crate::error::{self, Result};
33
use scopetime::scope_time;
44
use std::{
55
fs::File,
66
io::{Read, Write},
77
path::{Path, PathBuf},
88
process::Command,
9+
str::FromStr,
910
};
1011

11-
const HOOK_POST_COMMIT: &str = "hooks/post-commit";
12-
const HOOK_PRE_COMMIT: &str = "hooks/pre-commit";
13-
const HOOK_COMMIT_MSG: &str = "hooks/commit-msg";
12+
const HOOK_POST_COMMIT: &str = "post-commit";
13+
const HOOK_PRE_COMMIT: &str = "pre-commit";
14+
const HOOK_COMMIT_MSG: &str = "commit-msg";
1415
const HOOK_COMMIT_MSG_TEMP_FILE: &str = "COMMIT_EDITMSG";
1516

1617
struct HookPaths {
@@ -26,8 +27,30 @@ impl HookPaths {
2627
.workdir()
2728
.unwrap_or_else(|| repo.path())
2829
.to_path_buf();
30+
2931
let git_dir = repo.path().to_path_buf();
30-
let hook = git_dir.join(hook);
32+
let hooks_path = repo
33+
.config()
34+
.and_then(|config| config.get_string("core.hooksPath"))
35+
.map_or_else(
36+
|e| {
37+
log::error!("hookspath error: {}", e);
38+
repo.path().to_path_buf().join("hooks/")
39+
},
40+
PathBuf::from,
41+
);
42+
43+
let hook = hooks_path.join(hook);
44+
45+
let hook = shellexpand::full(
46+
hook.as_os_str()
47+
.to_str()
48+
.ok_or(error::Error::PathString)?,
49+
)?;
50+
51+
let hook = PathBuf::from_str(hook.as_ref())
52+
.map_err(|_| error::Error::PathString)?;
53+
3154
Ok(Self {
3255
git: git_dir,
3356
hook,
@@ -143,10 +166,14 @@ fn is_executable(path: &Path) -> bool {
143166
use std::os::unix::fs::PermissionsExt;
144167
let metadata = match path.metadata() {
145168
Ok(metadata) => metadata,
146-
Err(_) => return false,
169+
Err(e) => {
170+
log::error!("metadata error: {}", e);
171+
return false;
172+
}
147173
};
148174

149175
let permissions = metadata.permissions();
176+
150177
permissions.mode() & 0o111 != 0
151178
}
152179

@@ -181,20 +208,28 @@ mod tests {
181208
assert_eq!(res, HookResult::Ok);
182209
}
183210

184-
fn create_hook(path: &RepoPath, hook: &str, hook_script: &[u8]) {
211+
fn create_hook(
212+
path: &RepoPath,
213+
hook: &str,
214+
hook_script: &[u8],
215+
) -> PathBuf {
185216
let hook = HookPaths::new(path, hook).unwrap();
186217

187-
File::create(&hook.hook)
188-
.unwrap()
189-
.write_all(hook_script)
190-
.unwrap();
218+
let path = hook.hook.clone();
219+
220+
create_hook_in_path(&hook.hook, hook_script);
221+
222+
path
223+
}
224+
225+
fn create_hook_in_path(path: &Path, hook_script: &[u8]) {
226+
File::create(path).unwrap().write_all(hook_script).unwrap();
191227

192228
#[cfg(not(windows))]
193229
{
194-
let hook = hook.hook.as_os_str();
195230
Command::new("chmod")
196231
.arg("+x")
197-
.arg(hook)
232+
.arg(path)
198233
// .current_dir(path)
199234
.output()
200235
.unwrap();
@@ -255,6 +290,34 @@ exit 1
255290
assert!(res != HookResult::Ok);
256291
}
257292

293+
#[test]
294+
fn test_pre_commit_fail_hookspath() {
295+
let (_td, repo) = repo_init().unwrap();
296+
let root = repo.path().parent().unwrap();
297+
let hooks = TempDir::new().unwrap();
298+
let repo_path: &RepoPath =
299+
&root.as_os_str().to_str().unwrap().into();
300+
301+
let hook = b"#!/bin/sh
302+
echo 'rejected'
303+
exit 1
304+
";
305+
306+
create_hook_in_path(&hooks.path().join("pre-commit"), hook);
307+
repo.config()
308+
.unwrap()
309+
.set_str(
310+
"core.hooksPath",
311+
hooks.path().as_os_str().to_str().unwrap(),
312+
)
313+
.unwrap();
314+
let res = hooks_pre_commit(repo_path).unwrap();
315+
assert_eq!(
316+
res,
317+
HookResult::NotOk(String::from("rejected\n"))
318+
);
319+
}
320+
258321
#[test]
259322
fn test_pre_commit_fail_bare() {
260323
let (git_root, _repo) = repo_init_bare().unwrap();

0 commit comments

Comments
 (0)