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)
@@ -463,6 +465,67 @@ static int handle_alias(struct strvec *args, struct string_list *expanded_aliase
463465 return ret ;
464466}
465467
468+ /* Runs pre/post-command hook */
469+ static struct strvec sargv = STRVEC_INIT ;
470+ static int run_post_hook = 0 ;
471+ static int exit_code = -1 ;
472+
473+ static int run_pre_command_hook (struct repository * r , const char * * argv )
474+ {
475+ char * lock ;
476+ int ret = 0 ;
477+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT ;
478+
479+ /*
480+ * Ensure the global pre/post command hook is only called for
481+ * the outer command and not when git is called recursively
482+ * or spawns multiple commands (like with the alias command)
483+ */
484+ lock = getenv ("COMMAND_HOOK_LOCK" );
485+ if (lock && !strcmp (lock , "true" ))
486+ return 0 ;
487+ setenv ("COMMAND_HOOK_LOCK" , "true" , 1 );
488+
489+ /* call the hook proc */
490+ strvec_pushv (& sargv , argv );
491+ strvec_pushv (& opt .args , sargv .v );
492+ ret = run_hooks_opt (r , "pre-command" , & opt );
493+
494+ if (!ret )
495+ run_post_hook = 1 ;
496+ return ret ;
497+ }
498+
499+ static int run_post_command_hook (struct repository * r )
500+ {
501+ char * lock ;
502+ int ret = 0 ;
503+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT ;
504+
505+ /*
506+ * Only run post_command if pre_command succeeded in this process
507+ */
508+ if (!run_post_hook )
509+ return 0 ;
510+ lock = getenv ("COMMAND_HOOK_LOCK" );
511+ if (!lock || strcmp (lock , "true" ))
512+ return 0 ;
513+
514+ strvec_pushv (& opt .args , sargv .v );
515+ strvec_pushf (& opt .args , "--exit_code=%u" , exit_code );
516+ ret = run_hooks_opt (r , "post-command" , & opt );
517+
518+ run_post_hook = 0 ;
519+ strvec_clear (& sargv );
520+ setenv ("COMMAND_HOOK_LOCK" , "false" , 1 );
521+ return ret ;
522+ }
523+
524+ static void post_command_hook_atexit (void )
525+ {
526+ run_post_command_hook (the_repository );
527+ }
528+
466529static int run_builtin (struct cmd_struct * p , int argc , const char * * argv , struct repository * repo )
467530{
468531 int status , help ;
@@ -499,16 +562,21 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct
499562 if (!help && p -> option & NEED_WORK_TREE )
500563 setup_work_tree ();
501564
565+ if (run_pre_command_hook (the_repository , argv ))
566+ die ("pre-command hook aborted command" );
567+
502568 trace_argv_printf (argv , "trace: built-in: git" );
503569 trace2_cmd_name (p -> cmd );
504570
505571 validate_cache_entries (repo -> index );
506- status = p -> fn (argc , argv , prefix , no_repo ? NULL : repo );
572+ exit_code = status = p -> fn (argc , argv , prefix , no_repo ? NULL : repo );
507573 validate_cache_entries (repo -> index );
508574
509575 if (status )
510576 return status ;
511577
578+ run_post_command_hook (the_repository );
579+
512580 /* Somebody closed stdout? */
513581 if (fstat (fileno (stdout ), & st ))
514582 return 0 ;
@@ -808,13 +876,16 @@ static void execv_dashed_external(const char **argv)
808876 */
809877 trace_argv_printf (cmd .args .v , "trace: exec:" );
810878
879+ if (run_pre_command_hook (the_repository , cmd .args .v ))
880+ die ("pre-command hook aborted command" );
881+
811882 /*
812883 * If we fail because the command is not found, it is
813884 * OK to return. Otherwise, we just pass along the status code,
814885 * or our usual generic code if we were not even able to exec
815886 * the program.
816887 */
817- status = run_command (& cmd );
888+ exit_code = status = run_command (& cmd );
818889
819890 /*
820891 * If the child process ran and we are now going to exit, emit a
@@ -825,6 +896,8 @@ static void execv_dashed_external(const char **argv)
825896 exit (status );
826897 else if (errno != ENOENT )
827898 exit (128 );
899+
900+ run_post_command_hook (the_repository );
828901}
829902
830903static int is_deprecated_command (const char * cmd )
@@ -930,6 +1003,7 @@ int cmd_main(int argc, const char **argv)
9301003 }
9311004
9321005 trace_command_performance (argv );
1006+ atexit (post_command_hook_atexit );
9331007
9341008 /*
9351009 * "git-xxxx" is the same as "git xxxx", but we obviously:
@@ -957,10 +1031,14 @@ int cmd_main(int argc, const char **argv)
9571031 if (!argc ) {
9581032 /* The user didn't specify a command; give them help */
9591033 commit_pager_choice ();
1034+ if (run_pre_command_hook (the_repository , argv ))
1035+ die ("pre-command hook aborted command" );
9601036 printf (_ ("usage: %s\n\n" ), git_usage_string );
9611037 list_common_cmds_help ();
9621038 printf ("\n%s\n" , _ (git_more_info_string ));
963- exit (1 );
1039+ exit_code = 1 ;
1040+ run_post_command_hook (the_repository );
1041+ exit (exit_code );
9641042 }
9651043
9661044 if (!strcmp ("--version" , argv [0 ]) || !strcmp ("-v" , argv [0 ]))
0 commit comments