1717#include "shallow.h"
1818#include "trace.h"
1919#include "trace2.h"
20+ #include "dir.h"
21+ #include "hook.h"
2022
2123#define RUN_SETUP (1<<0)
2224#define RUN_SETUP_GENTLY (1<<1)
@@ -437,6 +439,67 @@ static int handle_alias(struct strvec *args)
437439 return ret ;
438440}
439441
442+ /* Runs pre/post-command hook */
443+ static struct strvec sargv = STRVEC_INIT ;
444+ static int run_post_hook = 0 ;
445+ static int exit_code = -1 ;
446+
447+ static int run_pre_command_hook (struct repository * r , const char * * argv )
448+ {
449+ char * lock ;
450+ int ret = 0 ;
451+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT ;
452+
453+ /*
454+ * Ensure the global pre/post command hook is only called for
455+ * the outer command and not when git is called recursively
456+ * or spawns multiple commands (like with the alias command)
457+ */
458+ lock = getenv ("COMMAND_HOOK_LOCK" );
459+ if (lock && !strcmp (lock , "true" ))
460+ return 0 ;
461+ setenv ("COMMAND_HOOK_LOCK" , "true" , 1 );
462+
463+ /* call the hook proc */
464+ strvec_pushv (& sargv , argv );
465+ strvec_pushv (& opt .args , sargv .v );
466+ ret = run_hooks_opt (r , "pre-command" , & opt );
467+
468+ if (!ret )
469+ run_post_hook = 1 ;
470+ return ret ;
471+ }
472+
473+ static int run_post_command_hook (struct repository * r )
474+ {
475+ char * lock ;
476+ int ret = 0 ;
477+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT ;
478+
479+ /*
480+ * Only run post_command if pre_command succeeded in this process
481+ */
482+ if (!run_post_hook )
483+ return 0 ;
484+ lock = getenv ("COMMAND_HOOK_LOCK" );
485+ if (!lock || strcmp (lock , "true" ))
486+ return 0 ;
487+
488+ strvec_pushv (& opt .args , sargv .v );
489+ strvec_pushf (& opt .args , "--exit_code=%u" , exit_code );
490+ ret = run_hooks_opt (r , "post-command" , & opt );
491+
492+ run_post_hook = 0 ;
493+ strvec_clear (& sargv );
494+ setenv ("COMMAND_HOOK_LOCK" , "false" , 1 );
495+ return ret ;
496+ }
497+
498+ static void post_command_hook_atexit (void )
499+ {
500+ run_post_command_hook (the_repository );
501+ }
502+
440503static int run_builtin (struct cmd_struct * p , int argc , const char * * argv , struct repository * repo )
441504{
442505 int status , help ;
@@ -473,16 +536,21 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct
473536 if (!help && p -> option & NEED_WORK_TREE )
474537 setup_work_tree ();
475538
539+ if (run_pre_command_hook (the_repository , argv ))
540+ die ("pre-command hook aborted command" );
541+
476542 trace_argv_printf (argv , "trace: built-in: git" );
477543 trace2_cmd_name (p -> cmd );
478544
479545 validate_cache_entries (repo -> index );
480- status = p -> fn (argc , argv , prefix , no_repo ? NULL : repo );
546+ exit_code = status = p -> fn (argc , argv , prefix , no_repo ? NULL : repo );
481547 validate_cache_entries (repo -> index );
482548
483549 if (status )
484550 return status ;
485551
552+ run_post_command_hook (the_repository );
553+
486554 /* Somebody closed stdout? */
487555 if (fstat (fileno (stdout ), & st ))
488556 return 0 ;
@@ -770,13 +838,16 @@ static void execv_dashed_external(const char **argv)
770838 */
771839 trace_argv_printf (cmd .args .v , "trace: exec:" );
772840
841+ if (run_pre_command_hook (the_repository , cmd .args .v ))
842+ die ("pre-command hook aborted command" );
843+
773844 /*
774845 * If we fail because the command is not found, it is
775846 * OK to return. Otherwise, we just pass along the status code,
776847 * or our usual generic code if we were not even able to exec
777848 * the program.
778849 */
779- status = run_command (& cmd );
850+ exit_code = status = run_command (& cmd );
780851
781852 /*
782853 * If the child process ran and we are now going to exit, emit a
@@ -787,6 +858,8 @@ static void execv_dashed_external(const char **argv)
787858 exit (status );
788859 else if (errno != ENOENT )
789860 exit (128 );
861+
862+ run_post_command_hook (the_repository );
790863}
791864
792865static int run_argv (struct strvec * args )
@@ -894,6 +967,7 @@ int cmd_main(int argc, const char **argv)
894967 }
895968
896969 trace_command_performance (argv );
970+ atexit (post_command_hook_atexit );
897971
898972 /*
899973 * "git-xxxx" is the same as "git xxxx", but we obviously:
@@ -921,10 +995,14 @@ int cmd_main(int argc, const char **argv)
921995 if (!argc ) {
922996 /* The user didn't specify a command; give them help */
923997 commit_pager_choice ();
998+ if (run_pre_command_hook (the_repository , argv ))
999+ die ("pre-command hook aborted command" );
9241000 printf (_ ("usage: %s\n\n" ), git_usage_string );
9251001 list_common_cmds_help ();
9261002 printf ("\n%s\n" , _ (git_more_info_string ));
927- exit (1 );
1003+ exit_code = 1 ;
1004+ run_post_command_hook (the_repository );
1005+ exit (exit_code );
9281006 }
9291007
9301008 if (!strcmp ("--version" , argv [0 ]) || !strcmp ("-v" , argv [0 ]))
0 commit comments