1
1
use super :: { repository:: repo, RepoPath } ;
2
- use crate :: error:: Result ;
2
+ use crate :: error:: { self , Result } ;
3
3
use scopetime:: scope_time;
4
4
use std:: {
5
5
fs:: File ,
6
6
io:: { Read , Write } ,
7
7
path:: { Path , PathBuf } ,
8
8
process:: Command ,
9
+ str:: FromStr ,
9
10
} ;
10
11
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" ;
14
15
const HOOK_COMMIT_MSG_TEMP_FILE : & str = "COMMIT_EDITMSG" ;
15
16
16
17
struct HookPaths {
@@ -26,8 +27,30 @@ impl HookPaths {
26
27
. workdir ( )
27
28
. unwrap_or_else ( || repo. path ( ) )
28
29
. to_path_buf ( ) ;
30
+
29
31
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
+
31
54
Ok ( Self {
32
55
git : git_dir,
33
56
hook,
@@ -143,10 +166,14 @@ fn is_executable(path: &Path) -> bool {
143
166
use std:: os:: unix:: fs:: PermissionsExt ;
144
167
let metadata = match path. metadata ( ) {
145
168
Ok ( metadata) => metadata,
146
- Err ( _) => return false ,
169
+ Err ( e) => {
170
+ log:: error!( "metadata error: {}" , e) ;
171
+ return false ;
172
+ }
147
173
} ;
148
174
149
175
let permissions = metadata. permissions ( ) ;
176
+
150
177
permissions. mode ( ) & 0o111 != 0
151
178
}
152
179
@@ -181,20 +208,28 @@ mod tests {
181
208
assert_eq ! ( res, HookResult :: Ok ) ;
182
209
}
183
210
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 {
185
216
let hook = HookPaths :: new ( path, hook) . unwrap ( ) ;
186
217
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 ( ) ;
191
227
192
228
#[ cfg( not( windows) ) ]
193
229
{
194
- let hook = hook. hook . as_os_str ( ) ;
195
230
Command :: new ( "chmod" )
196
231
. arg ( "+x" )
197
- . arg ( hook )
232
+ . arg ( path )
198
233
// .current_dir(path)
199
234
. output ( )
200
235
. unwrap ( ) ;
@@ -255,6 +290,34 @@ exit 1
255
290
assert ! ( res != HookResult :: Ok ) ;
256
291
}
257
292
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
+
258
321
#[ test]
259
322
fn test_pre_commit_fail_bare ( ) {
260
323
let ( git_root, _repo) = repo_init_bare ( ) . unwrap ( ) ;
0 commit comments