55#include "run-command.h"
66#include "alias.h"
77#include "shallow.h"
8+ #include "dir.h"
9+ #include "hook.h"
810
911#define RUN_SETUP (1<<0)
1012#define RUN_SETUP_GENTLY (1<<1)
@@ -417,6 +419,67 @@ static int handle_alias(int *argcp, const char ***argv)
417419 return ret ;
418420}
419421
422+ /* Runs pre/post-command hook */
423+ static struct strvec sargv = STRVEC_INIT ;
424+ static int run_post_hook = 0 ;
425+ static int exit_code = -1 ;
426+
427+ static int run_pre_command_hook (const char * * argv )
428+ {
429+ char * lock ;
430+ int ret = 0 ;
431+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT ;
432+
433+ /*
434+ * Ensure the global pre/post command hook is only called for
435+ * the outer command and not when git is called recursively
436+ * or spawns multiple commands (like with the alias command)
437+ */
438+ lock = getenv ("COMMAND_HOOK_LOCK" );
439+ if (lock && !strcmp (lock , "true" ))
440+ return 0 ;
441+ setenv ("COMMAND_HOOK_LOCK" , "true" , 1 );
442+
443+ /* call the hook proc */
444+ strvec_pushv (& sargv , argv );
445+ strvec_pushv (& opt .args , sargv .v );
446+ ret = run_hooks_opt ("pre-command" , & opt );
447+
448+ if (!ret )
449+ run_post_hook = 1 ;
450+ return ret ;
451+ }
452+
453+ static int run_post_command_hook (void )
454+ {
455+ char * lock ;
456+ int ret = 0 ;
457+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT ;
458+
459+ /*
460+ * Only run post_command if pre_command succeeded in this process
461+ */
462+ if (!run_post_hook )
463+ return 0 ;
464+ lock = getenv ("COMMAND_HOOK_LOCK" );
465+ if (!lock || strcmp (lock , "true" ))
466+ return 0 ;
467+
468+ strvec_pushv (& opt .args , sargv .v );
469+ strvec_pushf (& opt .args , "--exit_code=%u" , exit_code );
470+ ret = run_hooks_opt ("post-command" , & opt );
471+
472+ run_post_hook = 0 ;
473+ strvec_clear (& sargv );
474+ setenv ("COMMAND_HOOK_LOCK" , "false" , 1 );
475+ return ret ;
476+ }
477+
478+ static void post_command_hook_atexit (void )
479+ {
480+ run_post_command_hook ();
481+ }
482+
420483static int run_builtin (struct cmd_struct * p , int argc , const char * * argv )
421484{
422485 int status , help ;
@@ -457,18 +520,23 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
457520 if (!help && p -> option & NEED_WORK_TREE )
458521 setup_work_tree ();
459522
523+ if (run_pre_command_hook (argv ))
524+ die ("pre-command hook aborted command" );
525+
460526 trace_argv_printf (argv , "trace: built-in: git" );
461527 trace2_cmd_name (p -> cmd );
462528 trace2_cmd_list_config ();
463529 trace2_cmd_list_env_vars ();
464530
465531 validate_cache_entries (the_repository -> index );
466- status = p -> fn (argc , argv , prefix );
532+ exit_code = status = p -> fn (argc , argv , prefix );
467533 validate_cache_entries (the_repository -> index );
468534
469535 if (status )
470536 return status ;
471537
538+ run_post_command_hook ();
539+
472540 /* Somebody closed stdout? */
473541 if (fstat (fileno (stdout ), & st ))
474542 return 0 ;
@@ -749,13 +817,16 @@ static void execv_dashed_external(const char **argv)
749817 */
750818 trace_argv_printf (cmd .args .v , "trace: exec:" );
751819
820+ if (run_pre_command_hook (cmd .args .v ))
821+ die ("pre-command hook aborted command" );
822+
752823 /*
753824 * If we fail because the command is not found, it is
754825 * OK to return. Otherwise, we just pass along the status code,
755826 * or our usual generic code if we were not even able to exec
756827 * the program.
757828 */
758- status = run_command (& cmd );
829+ exit_code = status = run_command (& cmd );
759830
760831 /*
761832 * If the child process ran and we are now going to exit, emit a
@@ -766,6 +837,8 @@ static void execv_dashed_external(const char **argv)
766837 exit (status );
767838 else if (errno != ENOENT )
768839 exit (128 );
840+
841+ run_post_command_hook ();
769842}
770843
771844static int run_argv (int * argcp , const char * * * argv )
@@ -873,6 +946,7 @@ int cmd_main(int argc, const char **argv)
873946 }
874947
875948 trace_command_performance (argv );
949+ atexit (post_command_hook_atexit );
876950
877951 /*
878952 * "git-xxxx" is the same as "git xxxx", but we obviously:
@@ -902,10 +976,14 @@ int cmd_main(int argc, const char **argv)
902976 } else {
903977 /* The user didn't specify a command; give them help */
904978 commit_pager_choice ();
979+ if (run_pre_command_hook (argv ))
980+ die ("pre-command hook aborted command" );
905981 printf (_ ("usage: %s\n\n" ), git_usage_string );
906982 list_common_cmds_help ();
907983 printf ("\n%s\n" , _ (git_more_info_string ));
908- exit (1 );
984+ exit_code = 1 ;
985+ run_post_command_hook ();
986+ exit (exit_code );
909987 }
910988 cmd = argv [0 ];
911989
0 commit comments