1
- use super :: { repository:: repo, utils :: work_dir , RepoPath } ;
2
- use crate :: error:: { Error , Result } ;
1
+ use super :: { repository:: repo, RepoPath } ;
2
+ use crate :: error:: Result ;
3
3
use scopetime:: scope_time;
4
4
use std:: {
5
5
fs:: File ,
6
6
io:: { Read , Write } ,
7
- path:: Path ,
7
+ path:: { Path , PathBuf } ,
8
8
process:: Command ,
9
9
} ;
10
10
11
- const HOOK_POST_COMMIT : & str = ".git/ hooks/post-commit" ;
12
- const HOOK_PRE_COMMIT : & str = ".git/ hooks/pre-commit" ;
13
- const HOOK_COMMIT_MSG : & str = ".git/ hooks/commit-msg" ;
14
- const HOOK_COMMIT_MSG_TEMP_FILE : & str = ".git/ COMMIT_EDITMSG" ;
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" ;
14
+ const HOOK_COMMIT_MSG_TEMP_FILE : & str = "COMMIT_EDITMSG" ;
15
15
16
16
/// this hook is documented here <https://git-scm.com/docs/githooks#_commit_msg>
17
17
/// we use the same convention as other git clients to create a temp file containing
18
- /// the commit message at `.git/COMMIT_EDITMSG` and pass it's relative path as the only
18
+ /// the commit message at `< .git|hooksPath> /COMMIT_EDITMSG` and pass it's relative path as the only
19
19
/// parameter to the hook script.
20
20
pub fn hooks_commit_msg (
21
21
repo_path : & RepoPath ,
22
22
msg : & mut String ,
23
23
) -> Result < HookResult > {
24
24
scope_time ! ( "hooks_commit_msg" ) ;
25
25
26
- let work_dir = work_dir_as_string ( repo_path) ?;
26
+ let git_dir = git_dir_as_string ( repo_path) ?;
27
+ let git_dir = git_dir. as_path ( ) ;
27
28
28
- if hook_runable ( work_dir. as_str ( ) , HOOK_COMMIT_MSG ) {
29
- let temp_file = Path :: new ( work_dir. as_str ( ) )
30
- . join ( HOOK_COMMIT_MSG_TEMP_FILE ) ;
29
+ if hook_runable ( git_dir, HOOK_COMMIT_MSG ) {
30
+ let temp_file = git_dir. join ( HOOK_COMMIT_MSG_TEMP_FILE ) ;
31
31
File :: create ( & temp_file) ?. write_all ( msg. as_bytes ( ) ) ?;
32
32
33
33
let res = run_hook (
34
- work_dir . as_str ( ) ,
34
+ git_dir ,
35
35
HOOK_COMMIT_MSG ,
36
36
& [ HOOK_COMMIT_MSG_TEMP_FILE ] ,
37
37
) ?;
@@ -51,10 +51,11 @@ pub fn hooks_commit_msg(
51
51
pub fn hooks_pre_commit ( repo_path : & RepoPath ) -> Result < HookResult > {
52
52
scope_time ! ( "hooks_pre_commit" ) ;
53
53
54
- let work_dir = work_dir_as_string ( repo_path) ?;
54
+ let git_dir = git_dir_as_string ( repo_path) ?;
55
+ let git_dir = git_dir. as_path ( ) ;
55
56
56
- if hook_runable ( work_dir . as_str ( ) , HOOK_PRE_COMMIT ) {
57
- Ok ( run_hook ( work_dir . as_str ( ) , HOOK_PRE_COMMIT , & [ ] ) ?)
57
+ if hook_runable ( git_dir , HOOK_PRE_COMMIT ) {
58
+ Ok ( run_hook ( git_dir , HOOK_PRE_COMMIT , & [ ] ) ?)
58
59
} else {
59
60
Ok ( HookResult :: Ok )
60
61
}
@@ -63,32 +64,23 @@ pub fn hooks_pre_commit(repo_path: &RepoPath) -> Result<HookResult> {
63
64
pub fn hooks_post_commit ( repo_path : & RepoPath ) -> Result < HookResult > {
64
65
scope_time ! ( "hooks_post_commit" ) ;
65
66
66
- let work_dir = work_dir_as_string ( repo_path) ?;
67
- let work_dir_str = work_dir . as_str ( ) ;
67
+ let git_dir = git_dir_as_string ( repo_path) ?;
68
+ let git_dir = git_dir . as_path ( ) ;
68
69
69
- if hook_runable ( work_dir_str , HOOK_POST_COMMIT ) {
70
- Ok ( run_hook ( work_dir_str , HOOK_POST_COMMIT , & [ ] ) ?)
70
+ if hook_runable ( git_dir , HOOK_POST_COMMIT ) {
71
+ Ok ( run_hook ( git_dir , HOOK_POST_COMMIT , & [ ] ) ?)
71
72
} else {
72
73
Ok ( HookResult :: Ok )
73
74
}
74
75
}
75
76
76
- fn work_dir_as_string ( repo_path : & RepoPath ) -> Result < String > {
77
+ fn git_dir_as_string ( repo_path : & RepoPath ) -> Result < PathBuf > {
77
78
let repo = repo ( repo_path) ?;
78
- work_dir ( & repo) ?
79
- . to_str ( )
80
- . map ( std:: string:: ToString :: to_string)
81
- . ok_or_else ( || {
82
- Error :: Generic (
83
- "workdir contains invalid utf8" . to_string ( ) ,
84
- )
85
- } )
79
+ Ok ( repo. path ( ) . to_path_buf ( ) )
86
80
}
87
81
88
- fn hook_runable ( path : & str , hook : & str ) -> bool {
89
- let path = Path :: new ( path) ;
82
+ fn hook_runable ( path : & Path , hook : & str ) -> bool {
90
83
let path = path. join ( hook) ;
91
-
92
84
path. exists ( ) && is_executable ( & path)
93
85
}
94
86
@@ -104,7 +96,7 @@ pub enum HookResult {
104
96
/// this function calls hook scripts based on conventions documented here
105
97
/// see <https://git-scm.com/docs/githooks>
106
98
fn run_hook (
107
- path : & str ,
99
+ path : & Path ,
108
100
hook_script : & str ,
109
101
args : & [ & str ] ,
110
102
) -> Result < HookResult > {
@@ -155,8 +147,10 @@ const fn is_executable(_: &Path) -> bool {
155
147
156
148
#[ cfg( test) ]
157
149
mod tests {
150
+ use tempfile:: TempDir ;
151
+
158
152
use super :: * ;
159
- use crate :: sync:: tests:: repo_init;
153
+ use crate :: sync:: tests:: { repo_init, repo_init_bare } ;
160
154
use std:: fs:: { self , File } ;
161
155
162
156
#[ test]
@@ -177,6 +171,13 @@ mod tests {
177
171
}
178
172
179
173
fn create_hook ( path : & Path , hook_path : & str , hook_script : & [ u8 ] ) {
174
+ let path = git_dir_as_string (
175
+ & path. as_os_str ( ) . to_str ( ) . unwrap ( ) . into ( ) ,
176
+ )
177
+ . unwrap ( ) ;
178
+ let path = path. as_path ( ) ;
179
+ dbg ! ( & path) ;
180
+
180
181
File :: create ( & path. join ( hook_path) )
181
182
. unwrap ( )
182
183
. write_all ( hook_script)
@@ -246,6 +247,26 @@ exit 1
246
247
assert ! ( res != HookResult :: Ok ) ;
247
248
}
248
249
250
+ #[ test]
251
+ fn test_pre_commit_fail_bare ( ) {
252
+ let ( git_root, _repo) = repo_init_bare ( ) . unwrap ( ) ;
253
+ let workdir = TempDir :: new ( ) . unwrap ( ) ;
254
+ let git_root = git_root. into_path ( ) ;
255
+ let repo_path = & RepoPath :: Workdir {
256
+ gitdir : git_root. to_path_buf ( ) ,
257
+ workdir : workdir. into_path ( ) ,
258
+ } ;
259
+
260
+ let hook = b"#!/bin/sh
261
+ echo 'rejected'
262
+ exit 1
263
+ " ;
264
+
265
+ create_hook ( git_root. as_path ( ) , "hooks/pre-commit" , hook) ;
266
+ let res = hooks_pre_commit ( repo_path) . unwrap ( ) ;
267
+ assert ! ( res != HookResult :: Ok ) ;
268
+ }
269
+
249
270
#[ test]
250
271
fn test_pre_commit_py ( ) {
251
272
let ( _td, repo) = repo_init ( ) . unwrap ( ) ;
0 commit comments