@@ -9,6 +9,7 @@ use std::{
9
9
} ;
10
10
11
11
const HOOK_POST_COMMIT : & str = ".git/hooks/post-commit" ;
12
+ const HOOK_PRE_COMMIT : & str = ".git/hooks/pre-commit" ;
12
13
const HOOK_COMMIT_MSG : & str = ".git/hooks/commit-msg" ;
13
14
const HOOK_COMMIT_MSG_TEMP_FILE : & str = ".git/COMMIT_EDITMSG" ;
14
15
@@ -45,6 +46,21 @@ pub fn hooks_commit_msg(
45
46
}
46
47
}
47
48
49
+ /// this hook is documented here https://git-scm.com/docs/githooks#_pre_commit
50
+ ///
51
+ pub fn hooks_pre_commit ( repo_path : & str ) -> Result < HookResult > {
52
+ scope_time ! ( "hooks_pre_commit" ) ;
53
+
54
+ let work_dir = work_dir_as_string ( repo_path) ?;
55
+
56
+ if hook_runable ( work_dir. as_str ( ) , HOOK_PRE_COMMIT ) {
57
+ let res = run_hook ( work_dir. as_str ( ) , HOOK_PRE_COMMIT , & [ ] ) ;
58
+
59
+ Ok ( res)
60
+ } else {
61
+ Ok ( HookResult :: Ok )
62
+ }
63
+ }
48
64
///
49
65
pub fn hooks_post_commit ( repo_path : & str ) -> Result < HookResult > {
50
66
scope_time ! ( "hooks_post_commit" ) ;
@@ -94,13 +110,8 @@ fn run_hook(
94
110
hook_script : & str ,
95
111
args : & [ & str ] ,
96
112
) -> HookResult {
97
- let mut bash_args = vec ! [ hook_script. to_string( ) ] ;
98
- bash_args. extend_from_slice (
99
- & args
100
- . iter ( )
101
- . map ( |x| ( * x) . to_string ( ) )
102
- . collect :: < Vec < String > > ( ) ,
103
- ) ;
113
+ let arg_str = format ! ( "{} {}" , hook_script, args. join( " " ) ) ;
114
+ let bash_args = vec ! [ "-c" . to_string( ) , arg_str] ;
104
115
105
116
let output = Command :: new ( "bash" )
106
117
. args ( bash_args)
@@ -204,6 +215,83 @@ exit 0
204
215
assert_eq ! ( msg, String :: from( "test" ) ) ;
205
216
}
206
217
218
+ #[ test]
219
+ fn test_pre_commit_sh ( ) {
220
+ let ( _td, repo) = repo_init ( ) . unwrap ( ) ;
221
+ let root = repo. path ( ) . parent ( ) . unwrap ( ) ;
222
+ let repo_path = root. as_os_str ( ) . to_str ( ) . unwrap ( ) ;
223
+
224
+ let hook = b"#!/bin/sh
225
+ exit 0
226
+ " ;
227
+
228
+ create_hook ( root, HOOK_PRE_COMMIT , hook) ;
229
+ let res = hooks_pre_commit ( repo_path) . unwrap ( ) ;
230
+ assert_eq ! ( res, HookResult :: Ok ) ;
231
+ }
232
+
233
+ #[ test]
234
+ fn test_pre_commit_fail_sh ( ) {
235
+ let ( _td, repo) = repo_init ( ) . unwrap ( ) ;
236
+ let root = repo. path ( ) . parent ( ) . unwrap ( ) ;
237
+ let repo_path = root. as_os_str ( ) . to_str ( ) . unwrap ( ) ;
238
+
239
+ let hook = b"#!/bin/sh
240
+ echo 'rejected'
241
+ exit 1
242
+ " ;
243
+
244
+ create_hook ( root, HOOK_PRE_COMMIT , hook) ;
245
+ let res = hooks_pre_commit ( repo_path) . unwrap ( ) ;
246
+ assert ! ( res != HookResult :: Ok ) ;
247
+ }
248
+
249
+ #[ test]
250
+ fn test_pre_commit_py ( ) {
251
+ let ( _td, repo) = repo_init ( ) . unwrap ( ) ;
252
+ let root = repo. path ( ) . parent ( ) . unwrap ( ) ;
253
+ let repo_path = root. as_os_str ( ) . to_str ( ) . unwrap ( ) ;
254
+
255
+ // mirror how python pre-commmit sets itself up
256
+ #[ cfg( not( windows) ) ]
257
+ let hook = b"#!/usr/bin/env python
258
+ import sys
259
+ sys.exit(0)
260
+ " ;
261
+ #[ cfg( windows) ]
262
+ let hook = b"#!/bin/env python.exe
263
+ import sys
264
+ sys.exit(0)
265
+ " ;
266
+
267
+ create_hook ( root, HOOK_PRE_COMMIT , hook) ;
268
+ let res = hooks_pre_commit ( repo_path) . unwrap ( ) ;
269
+ assert_eq ! ( res, HookResult :: Ok ) ;
270
+ }
271
+
272
+ #[ test]
273
+ fn test_pre_commit_fail_py ( ) {
274
+ let ( _td, repo) = repo_init ( ) . unwrap ( ) ;
275
+ let root = repo. path ( ) . parent ( ) . unwrap ( ) ;
276
+ let repo_path = root. as_os_str ( ) . to_str ( ) . unwrap ( ) ;
277
+
278
+ // mirror how python pre-commmit sets itself up
279
+ #[ cfg( not( windows) ) ]
280
+ let hook = b"#!/usr/bin/env python
281
+ import sys
282
+ sys.exit(1)
283
+ " ;
284
+ #[ cfg( windows) ]
285
+ let hook = b"#!/bin/env python.exe
286
+ import sys
287
+ sys.exit(1)
288
+ " ;
289
+
290
+ create_hook ( root, HOOK_PRE_COMMIT , hook) ;
291
+ let res = hooks_pre_commit ( repo_path) . unwrap ( ) ;
292
+ assert ! ( res != HookResult :: Ok ) ;
293
+ }
294
+
207
295
#[ test]
208
296
fn test_hooks_commit_msg_reject ( ) {
209
297
let ( _td, repo) = repo_init ( ) . unwrap ( ) ;
0 commit comments