{"thread":{"id":"11893","subject":"[PATCH-v2 1/4] git-commit: support variable number of hook arguments","startedAt":"2008-02-05T10:01:44Z","lastAt":"2008-02-05T20:02:20Z","messageCount":9,"participants":["Paolo Bonzini","Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":2,"patchTotal":4},"messages":[{"id":"67501","messageId":"1202205704-10024-1-git-send-email-bonzini@gnu.org","threadId":"11893","inReplyTo":null,"subject":"[PATCH-v2 1/4] git-commit: support variable number of hook arguments","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2008-02-05T10:01:44Z","receivedAt":"2008-02-05T10:01:44Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This is a preparatory patch to allow using run_hook for the\nprepare-commit-msg hook.\n---\n builtin-commit.c |   61 +++++++++++++++++++++++++++++++-----------------------\n 1 files changed, 35 insertions(+), 26 deletions(-)\n\n\tCompared to the previous submission, this one includes the\n\trange check.\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 2ffca40..405c8b5 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -343,6 +343,40 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int\n \treturn s.commitable;\n }\n \n+static int run_hook(const char *index_file, const char *name, ...)\n+{\n+\tstruct child_process hook;\n+\tconst char *argv[10], *env[2];\n+\tchar index[PATH_MAX];\n+\tva_list args;\n+\tint i;\n+\n+\tva_start(args, name);\n+\targv[0] = git_path(\"hooks/%s\", name);\n+\ti = 0;\n+\tdo {\n+\t\tif (++i >= ARRAY_SIZE(argv))\n+\t\t\tdie (\"run_hook(): too many arguments\");\n+\t\targv[i] = va_arg(args, const char *);\n+\t} while (argv[i]);\n+\tva_end(args);\n+\n+\tsnprintf(index, sizeof(index), \"GIT_INDEX_FILE=%s\", index_file);\n+\tenv[0] = index;\n+\tenv[1] = NULL;\n+\n+\tif (access(argv[0], X_OK) < 0)\n+\t\treturn 0;\n+\n+\tmemset(&hook, 0, sizeof(hook));\n+\thook.argv = argv;\n+\thook.no_stdin = 1;\n+\thook.stdout_to_stderr = 1;\n+\thook.env = env;\n+\n+\treturn run_command(&hook);\n+}\n+\n static const char sign_off_header[] = \"Signed-off-by: \";\n \n static int prepare_log_message(const char *index_file, const char *prefix)\n@@ -677,31 +711,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \treturn commitable ? 0 : 1;\n }\n \n-static int run_hook(const char *index_file, const char *name, const char *arg)\n-{\n-\tstruct child_process hook;\n-\tconst char *argv[3], *env[2];\n-\tchar index[PATH_MAX];\n-\n-\targv[0] = git_path(\"hooks/%s\", name);\n-\targv[1] = arg;\n-\targv[2] = NULL;\n-\tsnprintf(index, sizeof(index), \"GIT_INDEX_FILE=%s\", index_file);\n-\tenv[0] = index;\n-\tenv[1] = NULL;\n-\n-\tif (access(argv[0], X_OK) < 0)\n-\t\treturn 0;\n-\n-\tmemset(&hook, 0, sizeof(hook));\n-\thook.argv = argv;\n-\thook.no_stdin = 1;\n-\thook.stdout_to_stderr = 1;\n-\thook.env = env;\n-\n-\treturn run_command(&hook);\n-}\n-\n static void print_summary(const char *prefix, const unsigned char *sha1)\n {\n \tstruct rev_info rev;\n@@ -876,7 +885,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tlaunch_editor(git_path(commit_editmsg), NULL, env);\n \t}\n \tif (!no_verify &&\n-\t    run_hook(index_file, \"commit-msg\", git_path(commit_editmsg))) {\n+\t    run_hook(index_file, \"commit-msg\", git_path(commit_editmsg), NULL)) {\n \t\trollback_index_files();\n \t\texit(1);\n \t}\n-- \n1.5.3.4.910.gc5122-dirty\n"},{"id":"67502","messageId":"1202205704-10024-2-git-send-email-bonzini@gnu.org","threadId":"11893","inReplyTo":"1202205704-10024-1-git-send-email-bonzini@gnu.org","subject":"[PATCH-v2 2/4] git-commit: set GIT_EDITOR=: if editor will not be launched","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2008-02-05T10:01:45Z","receivedAt":"2008-02-05T10:01:45Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This is a preparatory patch that provides a simple way for the future\nprepare-commit-msg hook to discover if the editor will be launched.\n---\n Documentation/hooks.txt |    4 ++++\n builtin-commit.c        |    2 ++\n 2 files changed, 6 insertions(+), 0 deletions(-)\n\n\tThis has not changed.\n\ndiff --git a/Documentation/hooks.txt b/Documentation/hooks.txt\nindex f110162..e8d80cf 100644\n--- a/Documentation/hooks.txt\n+++ b/Documentation/hooks.txt\n@@ -61,6 +61,10 @@ The default 'pre-commit' hook, when enabled, catches introduction\n of lines with trailing whitespaces and aborts the commit when\n such a line is found.\n \n+All the `git-commit` hooks are invoked with the environment\n+variable `GIT_EDITOR=:` if the command will not bring up an editor\n+to modify the commit message.\n+\n commit-msg\n ----------\n \ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 405c8b5..7d1059c 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -599,6 +599,8 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tuse_editor = 0;\n \tif (edit_flag)\n \t\tuse_editor = 1;\n+\tif (!use_editor)\n+\t\tsetenv(\"GIT_EDITOR\", \":\", 1);\n \n \tif (get_sha1(\"HEAD\", head_sha1))\n \t\tinitial_commit = 1;\n-- \n1.5.3.4.910.gc5122-dirty\n"},{"id":"67503","messageId":"1202205704-10024-3-git-send-email-bonzini@gnu.org","threadId":"11893","inReplyTo":"1202205704-10024-1-git-send-email-bonzini@gnu.org","subject":"[PATCH-v2 3/4] git-commit: Refactor creation of log message.","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2008-02-05T10:01:46Z","receivedAt":"2008-02-05T10:01:46Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This patch moves the code of run_commit, up to writing the trees, editing\nthe message and running the commit-msg hook to prepare_log_message.  It also\nrenames the latter to prepare_to_commit.\n\nThis simplifies a little the code for the next patch.\n---\n builtin-commit.c  |  171 +++++++++++++++++++++++++++--------------------------\n t/t7502-commit.sh |   28 +++++++++\n 2 files changed, 116 insertions(+), 83 deletions(-)\n\n\tWhile not strictly necessary, I moved code that invokes pre-commit\n\ttoo, because it seemed more logical.  Both for pre-commit and\n\tfor the old prepare_log_message you have to call rollback_index_files\n\tupon failure.\n\n\tI added a testcase that the editor is invoked *after* checking\n\tfor incomplete merges.\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 7d1059c..d8945ac 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -377,9 +377,17 @@ static int run_hook(const char *index_file, const char *name, ...)\n \treturn run_command(&hook);\n }\n \n+static int is_a_merge(const unsigned char *sha1)\n+{\n+\tstruct commit *commit = lookup_commit(sha1);\n+\tif (!commit || parse_commit(commit))\n+\t\tdie(\"could not parse HEAD commit\");\n+\treturn !!(commit->parents && commit->parents->next);\n+}\n+\n static const char sign_off_header[] = \"Signed-off-by: \";\n \n-static int prepare_log_message(const char *index_file, const char *prefix)\n+static int prepare_to_commit(const char *index_file, const char *prefix)\n {\n \tstruct stat statbuf;\n \tint commitable, saved_color_setting;\n@@ -387,6 +395,9 @@ static int prepare_log_message(const char *index_file, const char *prefix)\n \tchar *buffer;\n \tFILE *fp;\n \n+\tif (!no_verify && run_hook(index_file, \"pre-commit\", NULL))\n+\t\treturn 0;\n+\n \tstrbuf_init(&sb, 0);\n \tif (message.len) {\n \t\tstrbuf_addbuf(&sb, &message);\n@@ -447,13 +458,38 @@ static int prepare_log_message(const char *index_file, const char *prefix)\n \n \tstrbuf_release(&sb);\n \n-\tif (!use_editor) {\n+\tif (use_editor) {\n+\t\tif (in_merge)\n+\t\t\tfprintf(fp,\n+\t\t\t\t\"#\\n\"\n+\t\t\t\t\"# It looks like you may be committing a MERGE.\\n\"\n+\t\t\t\t\"# If this is not correct, please remove the file\\n\"\n+\t\t\t\t\"#\t%s\\n\"\n+\t\t\t\t\"# and try again.\\n\"\n+\t\t\t\t\"#\\n\",\n+\t\t\t\tgit_path(\"MERGE_HEAD\"));\n+\n+\t\tfprintf(fp,\n+\t\t\t\"\\n\"\n+\t\t\t\"# Please enter the commit message for your changes.\\n\"\n+\t\t\t\"# (Comment lines starting with '#' will \");\n+\t\tif (cleanup_mode == CLEANUP_ALL)\n+\t\t\tfprintf(fp, \"not be included)\\n\");\n+\t\telse /* CLEANUP_SPACE, that is. */\n+\t\t\tfprintf(fp, \"be kept.\\n\"\n+\t\t\t\t\"# You can remove them yourself if you want to)\\n\");\n+\t\tif (only_include_assumed)\n+\t\t\tfprintf(fp, \"# %s\\n\", only_include_assumed);\n+\n+\t\tsaved_color_setting = wt_status_use_color;\n+\t\twt_status_use_color = 0;\n+\t\tcommitable = run_status(fp, index_file, prefix, 1);\n+\t\twt_status_use_color = saved_color_setting;\n+\t} else {\n \t\tstruct rev_info rev;\n \t\tunsigned char sha1[20];\n \t\tconst char *parent = \"HEAD\";\n \n-\t\tfclose(fp);\n-\n \t\tif (!active_nr && read_cache() < 0)\n \t\t\tdie(\"Cannot read index\");\n \n@@ -461,48 +497,56 @@ static int prepare_log_message(const char *index_file, const char *prefix)\n \t\t\tparent = \"HEAD^1\";\n \n \t\tif (get_sha1(parent, sha1))\n-\t\t\treturn !!active_nr;\n+\t\t\tcommitable = !!active_nr;\n+\t\telse {\n+\t\t\tinit_revisions(&rev, \"\");\n+\t\t\trev.abbrev = 0;\n+\t\t\tsetup_revisions(0, NULL, &rev, parent);\n+\t\t\tDIFF_OPT_SET(&rev.diffopt, QUIET);\n+\t\t\tDIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);\n+\t\t\trun_diff_index(&rev, 1 /* cached */);\n+\n+\t\t\tcommitable = !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);\n+\t\t}\n+\t}\n \n-\t\tinit_revisions(&rev, \"\");\n-\t\trev.abbrev = 0;\n-\t\tsetup_revisions(0, NULL, &rev, parent);\n-\t\tDIFF_OPT_SET(&rev.diffopt, QUIET);\n-\t\tDIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);\n-\t\trun_diff_index(&rev, 1 /* cached */);\n+\tfclose(fp);\n+\n+\tif (!commitable && !in_merge && !allow_empty &&\n+\t    !(amend && is_a_merge(head_sha1))) {\n+\t\trun_status(stdout, index_file, prefix, 0);\n+\t\tunlink(commit_editmsg);\n+\t\treturn 0;\n+\t}\n \n-\t\treturn !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);\n+\t/*\n+\t * Re-read the index as pre-commit hook could have updated it,\n+\t * and write it out as a tree.  We must do this before we invoke\n+\t * the editor and after we invoke run_status above.\n+\t */\n+\tdiscard_cache();\n+\tread_cache_from(index_file);\n+\tif (!active_cache_tree)\n+\t\tactive_cache_tree = cache_tree();\n+\tif (cache_tree_update(active_cache_tree,\n+\t\t\t      active_cache, active_nr, 0, 0) < 0) {\n+\t\terror(\"Error building trees\");\n+\t\treturn 0;\n \t}\n \n-\tif (in_merge)\n-\t\tfprintf(fp,\n-\t\t\t\"#\\n\"\n-\t\t\t\"# It looks like you may be committing a MERGE.\\n\"\n-\t\t\t\"# If this is not correct, please remove the file\\n\"\n-\t\t\t\"#\t%s\\n\"\n-\t\t\t\"# and try again.\\n\"\n-\t\t\t\"#\\n\",\n-\t\t\tgit_path(\"MERGE_HEAD\"));\n-\n-\tfprintf(fp,\n-\t\t\"\\n\"\n-\t\t\"# Please enter the commit message for your changes.\\n\"\n-\t\t\"# (Comment lines starting with '#' will \");\n-\tif (cleanup_mode == CLEANUP_ALL)\n-\t\tfprintf(fp, \"not be included)\\n\");\n-\telse /* CLEANUP_SPACE, that is. */\n-\t\tfprintf(fp, \"be kept.\\n\"\n-\t\t\t\"# You can remove them yourself if you want to)\\n\");\n-\tif (only_include_assumed)\n-\t\tfprintf(fp, \"# %s\\n\", only_include_assumed);\n-\n-\tsaved_color_setting = wt_status_use_color;\n-\twt_status_use_color = 0;\n-\tcommitable = run_status(fp, index_file, prefix, 1);\n-\twt_status_use_color = saved_color_setting;\n+\tif (use_editor) {\n+\t\tchar index[PATH_MAX];\n+\t\tconst char *env[2] = { index, NULL };\n+\t\tsnprintf(index, sizeof(index), \"GIT_INDEX_FILE=%s\", index_file);\n+\t\tlaunch_editor(git_path(commit_editmsg), NULL, env);\n+\t}\n \n-\tfclose(fp);\n+\tif (!no_verify &&\n+\t    run_hook(index_file, \"commit-msg\", git_path(commit_editmsg), NULL)) {\n+\t\treturn 0;\n+\t}\n \n-\treturn commitable;\n+\treturn 1;\n }\n \n /*\n@@ -761,14 +805,6 @@ int git_commit_config(const char *k, const char *v)\n \treturn git_status_config(k, v);\n }\n \n-static int is_a_merge(const unsigned char *sha1)\n-{\n-\tstruct commit *commit = lookup_commit(sha1);\n-\tif (!commit || parse_commit(commit))\n-\t\tdie(\"could not parse HEAD commit\");\n-\treturn !!(commit->parents && commit->parents->next);\n-}\n-\n static const char commit_utf8_warn[] =\n \"Warning: commit message does not conform to UTF-8.\\n\"\n \"You may want to amend it after fixing the message, or set the config\\n\"\n@@ -800,34 +836,14 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \n \tindex_file = prepare_index(argc, argv, prefix);\n \n-\tif (!no_verify && run_hook(index_file, \"pre-commit\", NULL)) {\n-\t\trollback_index_files();\n-\t\treturn 1;\n-\t}\n-\n-\tif (!prepare_log_message(index_file, prefix) && !in_merge &&\n-\t    !allow_empty && !(amend && is_a_merge(head_sha1))) {\n-\t\trun_status(stdout, index_file, prefix, 0);\n+\t/* Set up everything for writing the commit object.  This includes\n+\t   running hooks, writing the trees, and interacting with the user.  */\n+\tif (!prepare_to_commit(index_file, prefix)) {\n \t\trollback_index_files();\n-\t\tunlink(commit_editmsg);\n \t\treturn 1;\n \t}\n \n \t/*\n-\t * Re-read the index as pre-commit hook could have updated it,\n-\t * and write it out as a tree.\n-\t */\n-\tdiscard_cache();\n-\tread_cache_from(index_file);\n-\tif (!active_cache_tree)\n-\t\tactive_cache_tree = cache_tree();\n-\tif (cache_tree_update(active_cache_tree,\n-\t\t\t      active_cache, active_nr, 0, 0) < 0) {\n-\t\trollback_index_files();\n-\t\tdie(\"Error building trees\");\n-\t}\n-\n-\t/*\n \t * The commit object\n \t */\n \tstrbuf_init(&sb, 0);\n@@ -878,19 +894,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addf(&sb, \"encoding %s\\n\", git_commit_encoding);\n \tstrbuf_addch(&sb, '\\n');\n \n-\t/* Get the commit message and validate it */\n+\t/* Finally, get the commit message */\n \theader_len = sb.len;\n-\tif (use_editor) {\n-\t\tchar index[PATH_MAX];\n-\t\tconst char *env[2] = { index, NULL };\n-\t\tsnprintf(index, sizeof(index), \"GIT_INDEX_FILE=%s\", index_file);\n-\t\tlaunch_editor(git_path(commit_editmsg), NULL, env);\n-\t}\n-\tif (!no_verify &&\n-\t    run_hook(index_file, \"commit-msg\", git_path(commit_editmsg), NULL)) {\n-\t\trollback_index_files();\n-\t\texit(1);\n-\t}\n \tif (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {\n \t\trollback_index_files();\n \t\tdie(\"could not read commit message\");\ndiff --git a/t/t7502-commit.sh b/t/t7502-commit.sh\nindex aaf497e..5ab8e23 100755\n--- a/t/t7502-commit.sh\n+++ b/t/t7502-commit.sh\n@@ -154,4 +154,33 @@ test_expect_success 'cleanup commit messages (strip,-F,-e)' '\n \n '\n \n+pwd=`pwd`\n+cat >> .git/FAKE_EDITOR << EOF\n+#! /bin/sh\n+echo editor started > \"$pwd/.git/result\"\n+exit 0\n+EOF\n+chmod +x .git/FAKE_EDITOR\n+\n+test_expect_success 'do not fire editor in the presence of conflicts' '\n+\n+\tgit clean\n+\techo f>g\n+\tgit add g\n+\tgit commit -myes\n+\tgit branch second\n+\techo master>g\n+\techo g>h\n+\tgit add g h\n+\tgit commit -mmaster\n+\tgit checkout second\n+\techo second>g\n+\tgit add g\n+\tgit commit -msecond\n+\tgit cherry-pick -n master\n+\techo \"editor not started\" > .git/result\n+\tGIT_EDITOR=`pwd`/.git/FAKE_EDITOR git commit && exit 1  # should fail\n+\ttest \"`cat .git/result`\" = \"editor not started\"\n+'\n+\n test_done\n-- \n1.5.3.4.910.gc5122-dirty\n"},{"id":"67504","messageId":"1202205704-10024-4-git-send-email-bonzini@gnu.org","threadId":"11893","inReplyTo":"1202205704-10024-1-git-send-email-bonzini@gnu.org","subject":"[PATCH-v2 4/4] git-commit: add a prepare-commit-msg hook","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2008-02-05T10:01:47Z","receivedAt":"2008-02-05T10:01:47Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"The prepare-commit-msg hook is run whenever a \"fresh\" commit message\nis prepared, just before it is shown in the editor (if it is).\nIt can modify the commit message in-place and/or abort the commit.\n\nIt takes two parameters.  The first is the source of the commit\nmessage, and can be: `message' (if a -m or -F option was\ngiven); `template' (if a -t option was given or the\nconfiguration option commit.template is set); `merge' (if the\ncommit is a merge or a .git/MERGE_MSG file exists); `squash'\n(if a .git/SQUASH_MSG file exists); or a commit SHA1 (if a\n-c, -C or --amend option was given).  The second\nparameter if the name of the file that the commit log message.\n\nThe hook is not suppressed by the --no-verify option, and the\nexit status is ignored by git.\n\nWhile the default hook just adds a Signed-off-by line at the bottom\nof the commit messsage, the hook is more intended to build a template\nfor the commit message following project standards, that the user\ncan then edit or discard altogether.\n---\n Documentation/git-commit.txt        |    4 +-\n Documentation/hooks.txt             |   32 ++++++++-\n builtin-commit.c                    |   16 ++++\n t/t7505-prepare-commit-msg-hook.sh  |  130 +++++++++++++++++++++++++++++++++++\n templates/hooks--commit-msg         |    3 +\n templates/hooks--prepare-commit-msg |   14 ++++\n 6 files changed, 196 insertions(+), 3 deletions(-)\n create mode 100755 t/t7505-prepare-commit-msg-hook.sh\n create mode 100644 templates/hooks--prepare-commit-msg\n\n\tI removed tests that involved a failing hook, since the\n\texit status is documented to be irrelevant.\n\ndiff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\nindex c3725b2..b4ae61f 100644\n--- a/Documentation/git-commit.txt\n+++ b/Documentation/git-commit.txt\n@@ -280,8 +280,8 @@ order).\n \n HOOKS\n -----\n-This command can run `commit-msg`, `pre-commit`, and\n-`post-commit` hooks.  See link:hooks.html[hooks] for more\n+This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`,\n+and `post-commit` hooks.  See link:hooks.html[hooks] for more\n information.\n \n \ndiff --git a/Documentation/hooks.txt b/Documentation/hooks.txt\nindex e8d80cf..02c8f9d 100644\n--- a/Documentation/hooks.txt\n+++ b/Documentation/hooks.txt\n@@ -55,7 +55,8 @@ This hook is invoked by `git-commit`, and can be bypassed\n with `\\--no-verify` option.  It takes no parameter, and is\n invoked before obtaining the proposed commit log message and\n making a commit.  Exiting with non-zero status from this script\n-causes the `git-commit` to abort.\n+causes the `git-commit` to abort.  This hook can also modify\n+the index.\n \n The default 'pre-commit' hook, when enabled, catches introduction\n of lines with trailing whitespaces and aborts the commit when\n@@ -65,6 +66,35 @@ All the `git-commit` hooks are invoked with the environment\n variable `GIT_EDITOR=:` if the command will not bring up an editor\n to modify the commit message.\n \n+prepare-commit-msg\n+------------------\n+\n+This hook is invoked by `git-commit` right after preparing the\n+default log message, and before the editor is started.\n+\n+It takes two parameters.  The first is the source of the commit\n+message, and can be: `message` (if a `\\-m` or `\\-F` option was\n+given); `template` (if a `\\-t` option was given or the\n+configuration option `commit.template` is set); `merge` (if the\n+commit is a merge or a `.git/MERGE_MSG` file exists); `squash`\n+(if a `.git/SQUASH_MSG` file exists); or a commit SHA1 (if a\n+`\\-c`, `\\-C` or `\\--amend` option was given).  The second\n+parameter if the name of the file that the commit log message.\n+\n+The hook is not suppressed by the `\\--no-verify` option, and\n+the exit status is ignored by `git-commit`.\n+\n+The hook is allowed to edit the message file in place, and\n+can be used to augment the default commit message with some\n+project standard information.  It can also be used for the same\n+purpose as the pre-commit message, if the verification has\n+to be skipped for automatic commits (e.g. during rebasing).\n+\n+The default 'prepare-commit-msg' hook adds a Signed-off-by line\n+(doing it with a hook is not necessarily a good idea, but doing\n+it in 'commit-msg' is worse because you are not reminded in\n+the editor).\n+\n commit-msg\n ----------\n \ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex d8945ac..3f2d3a4 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -394,6 +394,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)\n \tstruct strbuf sb;\n \tchar *buffer;\n \tFILE *fp;\n+\tconst char *hook_arg = NULL;\n \n \tif (!no_verify && run_hook(index_file, \"pre-commit\", NULL))\n \t\treturn 0;\n@@ -401,32 +402,44 @@ static int prepare_to_commit(const char *index_file, const char *prefix)\n \tstrbuf_init(&sb, 0);\n \tif (message.len) {\n \t\tstrbuf_addbuf(&sb, &message);\n+\t\thook_arg = \"message\";\n \t} else if (logfile && !strcmp(logfile, \"-\")) {\n \t\tif (isatty(0))\n \t\t\tfprintf(stderr, \"(reading log message from standard input)\\n\");\n \t\tif (strbuf_read(&sb, 0, 0) < 0)\n \t\t\tdie(\"could not read log from standard input\");\n+\t\thook_arg = \"message\";\n \t} else if (logfile) {\n \t\tif (strbuf_read_file(&sb, logfile, 0) < 0)\n \t\t\tdie(\"could not read log file '%s': %s\",\n \t\t\t    logfile, strerror(errno));\n+\t\thook_arg = \"message\";\n \t} else if (use_message) {\n \t\tbuffer = strstr(use_message_buffer, \"\\n\\n\");\n \t\tif (!buffer || buffer[2] == '\\0')\n \t\t\tdie(\"commit has empty message\");\n \t\tstrbuf_add(&sb, buffer + 2, strlen(buffer + 2));\n+\t\thook_arg = use_message;\n \t} else if (!stat(git_path(\"MERGE_MSG\"), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path(\"MERGE_MSG\"), 0) < 0)\n \t\t\tdie(\"could not read MERGE_MSG: %s\", strerror(errno));\n+\t\thook_arg = \"merge\";\n \t} else if (!stat(git_path(\"SQUASH_MSG\"), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path(\"SQUASH_MSG\"), 0) < 0)\n \t\t\tdie(\"could not read SQUASH_MSG: %s\", strerror(errno));\n+\t\thook_arg = \"squash\";\n \t} else if (template_file && !stat(template_file, &statbuf)) {\n \t\tif (strbuf_read_file(&sb, template_file, 0) < 0)\n \t\t\tdie(\"could not read %s: %s\",\n \t\t\t    template_file, strerror(errno));\n+\t\thook_arg = \"template\";\n \t}\n \n+\t/* This final case does not modify the template message, it just sets\n+\t   the argument to the prepare-commit-msg hook.  */\n+\telse if (in_merge)\n+\t\thook_arg = \"merge\";\n+\n \tfp = fopen(git_path(commit_editmsg), \"w\");\n \tif (fp == NULL)\n \t\tdie(\"could not open %s\", git_path(commit_editmsg));\n@@ -534,6 +547,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix)\n \t\treturn 0;\n \t}\n \n+\trun_hook(index_file, \"prepare-commit-msg\",\n+\t\t git_path(commit_editmsg), hook_arg, NULL);\n+\n \tif (use_editor) {\n \t\tchar index[PATH_MAX];\n \t\tconst char *env[2] = { index, NULL };\ndiff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh\nnew file mode 100755\nindex 0000000..440e197\n--- /dev/null\n+++ b/t/t7505-prepare-commit-msg-hook.sh\n@@ -0,0 +1,130 @@\n+#!/bin/sh\n+\n+test_description='prepare-commit-msg hook'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'with no hook' '\n+\n+\techo \"foo\" > file &&\n+\tgit add file &&\n+\tgit commit -m \"first\"\n+\n+'\n+\n+# set up fake editor for interactive editing\n+cat > fake-editor <<'EOF'\n+#!/bin/sh\n+exit 0\n+EOF\n+chmod +x fake-editor\n+FAKE_EDITOR=\"$(pwd)/fake-editor\"\n+export FAKE_EDITOR\n+\n+# now install hook that always succeeds and adds a message\n+HOOKDIR=\"$(git rev-parse --git-dir)/hooks\"\n+HOOK=\"$HOOKDIR/prepare-commit-msg\"\n+mkdir -p \"$HOOKDIR\"\n+cat > \"$HOOK\" <<'EOF'\n+#!/bin/sh\n+if test \"$2\" = HEAD; then\n+  set \"$1\" $(git-rev-parse HEAD)\n+fi\n+if test \"$GIT_EDITOR\" = :; then\n+  sed -e \"1s/.*/${2-default} (no editor)/\" \"$1\" > msg.tmp\n+else\n+  sed -e \"1s/.*/${2-default}/\" \"$1\" > msg.tmp\n+fi\n+mv msg.tmp \"$1\"\n+exit 0\n+EOF\n+chmod +x \"$HOOK\"\n+\n+echo dummy template > \"$(git rev-parse --git-dir)/template\"\n+\n+test_expect_success 'with hook (-m)' '\n+\n+\techo \"more\" >> file &&\n+\tgit add file &&\n+\tgit commit -m \"more\" &&\n+\ttest \"`git log -1 --pretty=format:%s`\" = \"message (no editor)\"\n+\n+'\n+\n+test_expect_success 'with hook (-m editor)' '\n+\n+\techo \"more\" >> file &&\n+\tgit add file &&\n+\tGIT_EDITOR=\"$FAKE_EDITOR\" git commit -e -m \"more more\" &&\n+\ttest \"`git log -1 --pretty=format:%s`\" = message\n+\n+'\n+\n+test_expect_success 'with hook (-t)' '\n+\n+\techo \"more\" >> file &&\n+\tgit add file &&\n+\tgit commit -t \"$(git rev-parse --git-dir)/template\" &&\n+\ttest \"`git log -1 --pretty=format:%s`\" = template\n+\n+'\n+\n+test_expect_success 'with hook (-F)' '\n+\n+\techo \"more\" >> file &&\n+\tgit add file &&\n+\t(echo more | git commit -F -) &&\n+\ttest \"`git log -1 --pretty=format:%s`\" = \"message (no editor)\"\n+\n+'\n+\n+test_expect_success 'with hook (-F editor)' '\n+\n+\techo \"more\" >> file &&\n+\tgit add file &&\n+\t(echo more more | GIT_EDITOR=\"$FAKE_EDITOR\" git commit -e -F -) &&\n+\ttest \"`git log -1 --pretty=format:%s`\" = message\n+\n+'\n+\n+test_expect_success 'with hook (-C)' '\n+\n+\thead=`git rev-parse HEAD` &&\n+\techo \"more\" >> file &&\n+\tgit add file &&\n+\tgit commit -C $head &&\n+\ttest \"`git log -1 --pretty=format:%s`\" = \"$head (no editor)\"\n+\n+'\n+\n+test_expect_success 'with hook (editor)' '\n+\n+\techo \"more more\" >> file &&\n+\tgit add file &&\n+\tGIT_EDITOR=\"$FAKE_EDITOR\" git commit &&\n+\ttest \"`git log -1 --pretty=format:%s`\" = default\n+\n+'\n+\n+test_expect_success 'with hook (--amend)' '\n+\n+\thead=`git rev-parse HEAD` &&\n+\techo \"more\" >> file &&\n+\tgit add file &&\n+\tGIT_EDITOR=\"$FAKE_EDITOR\" git commit --amend &&\n+\ttest \"`git log -1 --pretty=format:%s`\" = \"$head\"\n+\n+'\n+\n+test_expect_success 'with hook (-c)' '\n+\n+\thead=`git rev-parse HEAD` &&\n+\techo \"more\" >> file &&\n+\tgit add file &&\n+\tGIT_EDITOR=\"$FAKE_EDITOR\" git commit -c $head &&\n+\ttest \"`git log -1 --pretty=format:%s`\" = \"$head\"\n+\n+'\n+\n+\n+test_done\ndiff --git a/templates/hooks--commit-msg b/templates/hooks--commit-msg\nindex c5cdb9d..4ef86eb 100644\n--- a/templates/hooks--commit-msg\n+++ b/templates/hooks--commit-msg\n@@ -9,6 +9,9 @@\n # To enable this hook, make this file executable.\n \n # Uncomment the below to add a Signed-off-by line to the message.\n+# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n+# hook is more suited to it.\n+#\n # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n # grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n \ndiff --git a/templates/hooks--prepare-commit-msg b/templates/hooks--prepare-commit-msg\nnew file mode 100644\nindex 0000000..176283b\n--- /dev/null\n+++ b/templates/hooks--prepare-commit-msg\n@@ -0,0 +1,14 @@\n+#!/bin/sh\n+#\n+# An example hook script to prepare the commit log message.\n+# Called by git-commit with one argument, the name of the file\n+# that has the commit message.  The hook should exit with non-zero\n+# status after issuing an appropriate message if it wants to stop the\n+# commit.  The hook is allowed to edit the commit message file.\n+#\n+# To enable this hook, make this file executable.\n+\n+# This example adds a Signed-off-by line to the message, that can\n+# still be edited.\n+SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n+grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n-- \n1.5.3.4.910.gc5122-dirty\n"},{"id":"67520","messageId":"alpine.LSU.1.00.0802051500190.8543@racer.site","threadId":"11893","inReplyTo":"1202205704-10024-4-git-send-email-bonzini@gnu.org","subject":"Re: [PATCH-v2 4/4] git-commit: add a prepare-commit-msg hook","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-05T15:01:27Z","receivedAt":"2008-02-05T15:01:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 5 Feb 2008, Paolo Bonzini wrote:\n\n> The hook is not suppressed by the --no-verify option, and the\n> exit status is ignored by git.\n\nUmm.  Should the exit status really be ignored?  What _use_ would it be \nthen?\n\nCiao,\nDscho\n"},{"id":"67525","messageId":"47A87DDC.5030708@gnu.org","threadId":"11893","inReplyTo":"alpine.LSU.1.00.0802051500190.8543@racer.site","subject":"Re: [PATCH-v2 4/4] git-commit: add a prepare-commit-msg hook","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2008-02-05T15:16:44Z","receivedAt":"2008-02-05T15:16:44Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"Johannes Schindelin wrote:\n> Hi,\n> \n> On Tue, 5 Feb 2008, Paolo Bonzini wrote:\n> \n>> The hook is not suppressed by the --no-verify option, and the\n>> exit status is ignored by git.\n> \n> Umm.  Should the exit status really be ignored?  What _use_ would it be \n> then?\n\nNone; in the previous version I used an exit status of 1 to kill the \ncommit.  But since this hook, unlike others, is meant to prepare things \nfor the user, the hook itself was not suppressed by --no-verify, only \nchecking the exit status.  Junio thought it was a mess, so I now \ncompletely ignore the exit status.\n\nI just wrote the above sentence because prepare-commit-msg is the only \ngit-commit hook whose exit status is ignored.\n\nPaolo\n"},{"id":"67526","messageId":"alpine.LSU.1.00.0802051520520.8543@racer.site","threadId":"11893","inReplyTo":"47A87DDC.5030708@gnu.org","subject":"Re: [PATCH-v2 4/4] git-commit: add a prepare-commit-msg hook","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-05T15:21:45Z","receivedAt":"2008-02-05T15:21:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 5 Feb 2008, Paolo Bonzini wrote:\n\n> Johannes Schindelin wrote:\n> \n> > On Tue, 5 Feb 2008, Paolo Bonzini wrote:\n> > \n> > > The hook is not suppressed by the --no-verify option, and the exit \n> > > status is ignored by git.\n> > \n> > Umm.  Should the exit status really be ignored?  What _use_ would it \n> > be then?\n> \n> None; in the previous version I used an exit status of 1 to kill the \n> commit. But since this hook, unlike others, is meant to prepare things \n> for the user, the hook itself was not suppressed by --no-verify, only \n> checking the exit status.  Junio thought it was a mess, so I now \n> completely ignore the exit status.\n> \n> I just wrote the above sentence because prepare-commit-msg is the only \n> git-commit hook whose exit status is ignored.\n\nI might be missing something, but should this not be solved by having the \nprepare-commit-msg exit 0?  I mean, it is conceivable that such a bad \nerror occurs in the hook that the commit should be stopped then and there.\n\nCiao,\nDscho\n"},{"id":"67528","messageId":"47A88032.107@gnu.org","threadId":"11893","inReplyTo":"alpine.LSU.1.00.0802051520520.8543@racer.site","subject":"Re: [PATCH-v2 4/4] git-commit: add a prepare-commit-msg hook","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2008-02-05T15:26:42Z","receivedAt":"2008-02-05T15:26:42Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"\n> I might be missing something\n\nNo, you are not.\n\n> but should this not be solved by having the \n> prepare-commit-msg exit 0?  I mean, it is conceivable that such a bad \n> error occurs in the hook that the commit should be stopped then and there.\n\nSo you say, don't suppress the hook with --no-verify *and* exit if the \nhook returns 1, even if --no-verify is given.  That's fine by me.\n\nPaolo\n"},{"id":"67542","messageId":"7vwspj9noz.fsf@gitster.siamese.dyndns.org","threadId":"11893","inReplyTo":"47A88032.107@gnu.org","subject":"Re: [PATCH-v2 4/4] git-commit: add a prepare-commit-msg hook","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-05T20:02:20Z","receivedAt":"2008-02-05T20:02:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paolo Bonzini <bonzini@gnu.org> writes:\n\n>> I might be missing something\n>\n> No, you are not.\n>\n>> but should this not be solved by having the prepare-commit-msg exit\n>> 0?  I mean, it is conceivable that such a bad error occurs in the\n>> hook that the commit should be stopped then and there.\n>\n> So you say, don't suppress the hook with --no-verify *and* exit if the\n> hook returns 1, even if --no-verify is given.  That's fine by me.\n\nFine by me as well, and I would prefer it that way.\n"}]}