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 ;
@@ -775,13 +843,16 @@ static void execv_dashed_external(const char **argv)
775843	 */ 
776844	trace_argv_printf (cmd .args .v , "trace: exec:" );
777845
846+ 	if  (run_pre_command_hook (the_repository , cmd .args .v ))
847+ 		die ("pre-command hook aborted command" );
848+ 
778849	/* 
779850	 * If we fail because the command is not found, it is 
780851	 * OK to return. Otherwise, we just pass along the status code, 
781852	 * or our usual generic code if we were not even able to exec 
782853	 * the program. 
783854	 */ 
784- 	status  =  run_command (& cmd );
855+ 	exit_code   =   status  =  run_command (& cmd );
785856
786857	/* 
787858	 * If the child process ran and we are now going to exit, emit a 
@@ -792,6 +863,8 @@ static void execv_dashed_external(const char **argv)
792863		exit (status );
793864	else  if  (errno  !=  ENOENT )
794865		exit (128 );
866+ 
867+ 	run_post_command_hook (the_repository );
795868}
796869
797870static  int  run_argv (struct  strvec  * args )
@@ -899,6 +972,7 @@ int cmd_main(int argc, const char **argv)
899972	}
900973
901974	trace_command_performance (argv );
975+ 	atexit (post_command_hook_atexit );
902976
903977	/* 
904978	 * "git-xxxx" is the same as "git xxxx", but we obviously: 
@@ -926,10 +1000,14 @@ int cmd_main(int argc, const char **argv)
9261000	if  (!argc ) {
9271001		/* The user didn't specify a command; give them help */ 
9281002		commit_pager_choice ();
1003+ 		if  (run_pre_command_hook (the_repository , argv ))
1004+ 			die ("pre-command hook aborted command" );
9291005		printf (_ ("usage: %s\n\n" ), git_usage_string );
9301006		list_common_cmds_help ();
9311007		printf ("\n%s\n" , _ (git_more_info_string ));
932- 		exit (1 );
1008+ 		exit_code  =  1 ;
1009+ 		run_post_command_hook (the_repository );
1010+ 		exit (exit_code );
9331011	}
9341012
9351013	if  (!strcmp ("--version" , argv [0 ]) ||  !strcmp ("-v" , argv [0 ]))
0 commit comments