{"thread":{"id":"5852","subject":"[PATCH] [RFC] Make git-update-ref invoke the update and post-update hooks","startedAt":"2006-10-08T00:08:08Z","lastAt":"2006-10-08T05:00:32Z","messageCount":2,"participants":["Petr Baudis","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"28401","messageId":"20061008000808.1128.36962.stgit@machine.or.cz","threadId":"5852","inReplyTo":null,"subject":"[PATCH] [RFC] Make git-update-ref invoke the update and post-update hooks","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-10-08T00:08:08Z","receivedAt":"2006-10-08T00:08:08Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Someone raised a concern that the update and post-update hooks are not\ninvoked at fetch time in the similar way as they are invoked at push\ntime, and the idea sort of makes sense. But this patch goes further - it\nmakes Git invoke those hooks each time a ref is updated in a repository\nusing the git-update-ref command, which I believe makes a lot of sense as\nwell - the behaviour is consistent with the current pushing behaviour\nand you suddenly finally get a hook where you can properly notify even\nabout fast-forwards etc.\n\nSigned-off-by: Petr Baudis <pasky@suse.cz>\n---\n\n Documentation/git-update-ref.txt |    3 ++\n Documentation/hooks.txt          |   12 ++++--\n Makefile                         |    2 +\n builtin-update-ref.c             |   12 ++++++\n hooks.c                          |   68 ++++++++++++++++++++++++++++++++++++\n hooks.h                          |   15 ++++++++\n receive-pack.c                   |   71 +-------------------------------------\n 7 files changed, 106 insertions(+), 77 deletions(-)\n\ndiff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt\nindex 71bcb79..bd20d05 100644\n--- a/Documentation/git-update-ref.txt\n+++ b/Documentation/git-update-ref.txt\n@@ -51,6 +51,9 @@ for reading but not for writing (so we'l\n ref symlink to some other tree, if you have copied a whole\n archive by creating a symlink tree).\n \n+Just before updating the reference, the update hook is invoked.\n+After updating the reference, the post-update hook is invoked.\n+\n With `-d` flag, it deletes the named <ref> after verifying it\n still contains <oldvalue>.\n \ndiff --git a/Documentation/hooks.txt b/Documentation/hooks.txt\nindex 517f49b..53fa270 100644\n--- a/Documentation/hooks.txt\n+++ b/Documentation/hooks.txt\n@@ -99,8 +99,9 @@ update\n This hook is invoked by `git-receive-pack` on the remote repository,\n which happens when a `git push` is done on a local repository.\n Just before updating the ref on the remote repository, the update hook\n-is invoked.  Its exit status determines the success or failure of\n-the ref update.\n+is invoked.  It is also invoked on the local repository anytime a ref\n+value is updated (that includes mainly fetches, merges, commits).\n+Its exit status determines the success or failure of the ref update.\n \n The hook executes once for each ref to be updated, and takes\n three parameters:\n@@ -135,9 +136,10 @@ post-update\n -----------\n \n This hook is invoked by `git-receive-pack` on the remote repository,\n-which happens when a `git push` is done on a local repository.\n-It executes on the remote repository once after all the refs have\n-been updated.\n+which happens when a `git push` is done on a local repository\n+It is also invoked on the local repository anytime a ref value is\n+updated (that includes mainly fetches, merges, commits).  It executes\n+on the repository once after all the refs have been updated.\n \n It takes a variable number of parameters, each of which is the\n name of ref that was actually updated.\ndiff --git a/Makefile b/Makefile\nindex 2962c2e..8e58585 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -263,7 +263,7 @@ LIB_OBJS = \\\n \tfetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \\\n \twrite_or_die.o trace.o list-objects.o grep.o \\\n \talloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \\\n-\tcolor.o wt-status.o archive-zip.o archive-tar.o\n+\tcolor.o wt-status.o archive-zip.o archive-tar.o hooks.o\n \n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\ndiff --git a/builtin-update-ref.c b/builtin-update-ref.c\nindex b34e598..546cbb1 100644\n--- a/builtin-update-ref.c\n+++ b/builtin-update-ref.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"refs.h\"\n #include \"builtin.h\"\n+#include \"hooks.h\"\n \n static const char git_update_ref_usage[] =\n \"git-update-ref [-m <reason>] (-d <refname> <value> | <refname> <value> [<oldval>])\";\n@@ -11,6 +12,7 @@ int cmd_update_ref(int argc, const char \n \tstruct ref_lock *lock;\n \tunsigned char sha1[20], oldsha1[20];\n \tint i, delete;\n+\tstruct command *cmd;\n \n \tdelete = 0;\n \tsetup_ident();\n@@ -63,9 +65,17 @@ int cmd_update_ref(int argc, const char \n \tlock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL);\n \tif (!lock)\n \t\treturn 1;\n+\tif (run_update_hook(refname, oldval, value)) {\n+\t\tunlock_ref(lock);\n+\t\treturn 1;\n+\t}\n \tif (write_ref_sha1(lock, sha1, msg) < 0)\n \t\treturn 1;\n-\n \t/* write_ref_sha1 always unlocks the ref, no need to do it explicitly */\n+\n+\tcmd = xmalloc(sizeof(struct command) + strlen(refname) + 1);\n+\tstrcpy(cmd->ref_name, refname);\n+\trun_update_post_hook(cmd);\n+\n \treturn 0;\n }\ndiff --git a/hooks.c b/hooks.c\nnew file mode 100644\nindex 0000000..efade2b\n--- /dev/null\n+++ b/hooks.c\n@@ -0,0 +1,68 @@\n+#include \"cache.h\"\n+#include \"run-command.h\"\n+#include \"hooks.h\"\n+\n+\n+static char update_hook[] = \"hooks/update\";\n+\n+int run_update_hook(const char *refname, const char *old_hex, const char *new_hex)\n+{\n+\tchar *update_hook_p = git_path(\"%s\", update_hook);\n+\tint code;\n+\n+\tif (access(update_hook_p, X_OK) < 0)\n+\t\treturn 0;\n+\tcode = run_command(update_hook_p, refname, old_hex, new_hex, NULL);\n+\tswitch (code) {\n+\tcase 0:\n+\t\treturn 0;\n+\tcase -ERR_RUN_COMMAND_FORK:\n+\t\treturn error(\"hook fork failed\");\n+\tcase -ERR_RUN_COMMAND_EXEC:\n+\t\treturn error(\"hook execute failed\");\n+\tcase -ERR_RUN_COMMAND_WAITPID:\n+\t\treturn error(\"waitpid failed\");\n+\tcase -ERR_RUN_COMMAND_WAITPID_WRONG_PID:\n+\t\treturn error(\"waitpid is confused\");\n+\tcase -ERR_RUN_COMMAND_WAITPID_SIGNAL:\n+\t\treturn error(\"%s died of signal\", update_hook);\n+\tcase -ERR_RUN_COMMAND_WAITPID_NOEXIT:\n+\t\treturn error(\"%s died strangely\", update_hook);\n+\tdefault:\n+\t\terror(\"%s exited with error code %d\", update_hook, -code);\n+\t\treturn -code;\n+\t}\n+}\n+\n+\n+static char update_post_hook[] = \"hooks/post-update\";\n+\n+void run_update_post_hook(struct command *cmd)\n+{\n+\tchar *update_post_hook_p = git_path(\"%s\", update_post_hook);\n+\tstruct command *cmd_p;\n+\tint argc;\n+\tconst char **argv;\n+\n+\tif (access(update_post_hook_p, X_OK) < 0)\n+\t\treturn;\n+\tfor (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {\n+\t\tif (cmd_p->error_string)\n+\t\t\tcontinue;\n+\t\targc++;\n+\t}\n+\targv = xmalloc(sizeof(*argv) * (1 + argc));\n+\targv[0] = update_post_hook_p;\n+\n+\tfor (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {\n+\t\tchar *p;\n+\t\tif (cmd_p->error_string)\n+\t\t\tcontinue;\n+\t\tp = xmalloc(strlen(cmd_p->ref_name) + 1);\n+\t\tstrcpy(p, cmd_p->ref_name);\n+\t\targv[argc] = p;\n+\t\targc++;\n+\t}\n+\targv[argc] = NULL;\n+\trun_command_v_opt(argc, argv, RUN_COMMAND_NO_STDIO);\n+}\ndiff --git a/hooks.h b/hooks.h\nnew file mode 100644\nindex 0000000..8ddeca3\n--- /dev/null\n+++ b/hooks.h\n@@ -0,0 +1,15 @@\n+#ifndef HOOKS_H\n+#define HOOKS_H\n+\n+int run_update_hook(const char *refname, const char *old_hex, const char *new_hex);\n+\n+struct command {\n+\tstruct command *next;\n+\tconst char *error_string;\n+\tunsigned char old_sha1[20];\n+\tunsigned char new_sha1[20];\n+\tchar ref_name[FLEX_ARRAY]; /* more */\n+};\n+void run_update_post_hook(struct command *cmd);\n+\n+#endif\ndiff --git a/receive-pack.c b/receive-pack.c\nindex 1fcf3a9..9d5ece6 100644\n--- a/receive-pack.c\n+++ b/receive-pack.c\n@@ -4,6 +4,7 @@ #include \"pkt-line.h\"\n #include \"run-command.h\"\n #include \"commit.h\"\n #include \"object.h\"\n+#include \"hooks.h\"\n \n static const char receive_pack_usage[] = \"git-receive-pack <git-dir>\";\n \n@@ -33,47 +34,8 @@ static void write_head_info(void)\n \n }\n \n-struct command {\n-\tstruct command *next;\n-\tconst char *error_string;\n-\tunsigned char old_sha1[20];\n-\tunsigned char new_sha1[20];\n-\tchar ref_name[FLEX_ARRAY]; /* more */\n-};\n-\n static struct command *commands;\n \n-static char update_hook[] = \"hooks/update\";\n-\n-static int run_update_hook(const char *refname,\n-\t\t\t   char *old_hex, char *new_hex)\n-{\n-\tint code;\n-\n-\tif (access(update_hook, X_OK) < 0)\n-\t\treturn 0;\n-\tcode = run_command(update_hook, refname, old_hex, new_hex, NULL);\n-\tswitch (code) {\n-\tcase 0:\n-\t\treturn 0;\n-\tcase -ERR_RUN_COMMAND_FORK:\n-\t\treturn error(\"hook fork failed\");\n-\tcase -ERR_RUN_COMMAND_EXEC:\n-\t\treturn error(\"hook execute failed\");\n-\tcase -ERR_RUN_COMMAND_WAITPID:\n-\t\treturn error(\"waitpid failed\");\n-\tcase -ERR_RUN_COMMAND_WAITPID_WRONG_PID:\n-\t\treturn error(\"waitpid is confused\");\n-\tcase -ERR_RUN_COMMAND_WAITPID_SIGNAL:\n-\t\treturn error(\"%s died of signal\", update_hook);\n-\tcase -ERR_RUN_COMMAND_WAITPID_NOEXIT:\n-\t\treturn error(\"%s died strangely\", update_hook);\n-\tdefault:\n-\t\terror(\"%s exited with error code %d\", update_hook, -code);\n-\t\treturn -code;\n-\t}\n-}\n-\n static int update(struct command *cmd)\n {\n \tconst char *name = cmd->ref_name;\n@@ -127,37 +89,6 @@ static int update(struct command *cmd)\n \treturn 0;\n }\n \n-static char update_post_hook[] = \"hooks/post-update\";\n-\n-static void run_update_post_hook(struct command *cmd)\n-{\n-\tstruct command *cmd_p;\n-\tint argc;\n-\tconst char **argv;\n-\n-\tif (access(update_post_hook, X_OK) < 0)\n-\t\treturn;\n-\tfor (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {\n-\t\tif (cmd_p->error_string)\n-\t\t\tcontinue;\n-\t\targc++;\n-\t}\n-\targv = xmalloc(sizeof(*argv) * (1 + argc));\n-\targv[0] = update_post_hook;\n-\n-\tfor (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {\n-\t\tchar *p;\n-\t\tif (cmd_p->error_string)\n-\t\t\tcontinue;\n-\t\tp = xmalloc(strlen(cmd_p->ref_name) + 1);\n-\t\tstrcpy(p, cmd_p->ref_name);\n-\t\targv[argc] = p;\n-\t\targc++;\n-\t}\n-\targv[argc] = NULL;\n-\trun_command_v_opt(argc, argv, RUN_COMMAND_NO_STDIO);\n-}\n-\n /*\n  * This gets called after(if) we've successfully\n  * unpacked the data payload.\n"},{"id":"28405","messageId":"7v64evpfkf.fsf@assigned-by-dhcp.cox.net","threadId":"5852","inReplyTo":"20061008000808.1128.36962.stgit@machine.or.cz","subject":"Re: [PATCH] [RFC] Make git-update-ref invoke the update and post-update hooks","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-08T05:00:32Z","receivedAt":"2006-10-08T05:00:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Petr Baudis <pasky@suse.cz> writes:\n\n> Someone raised a concern that the update and post-update hooks are not\n> invoked at fetch time in the similar way as they are invoked at push\n> time, and the idea sort of makes sense. But this patch goes further - it\n> makes Git invoke those hooks each time a ref is updated in a repository\n> using the git-update-ref command, which I believe makes a lot of sense as\n> well - the behaviour is consistent with the current pushing behaviour\n> and you suddenly finally get a hook where you can properly notify even\n> about fast-forwards etc.\n\nIn principle I do not have problem with this approach per-se,\nbut I wonder if we were to do this we might want to make\nreceive-pack.c::update() and cmd_update_ref() call the same\nunderlying function, and make that underlying function implement\nthis \"ask the hook if updating is ok\" dance.  It might even make\nsense to have update-ref honor deny_non_fast_forwards for that\nmatter (I am mildly doubtful of this last point, though).\n"}]}