{"thread":{"id":"46827","subject":"[RFC PATCH 1/8] commit: move empty message checks to libgit","startedAt":"2017-09-25T10:11:19Z","lastAt":"2018-01-11T20:22:04Z","messageCount":120,"participants":["Phillip Wood","Junio C Hamano","Johannes Schindelin","Ramsay Jones","Stefan Beller","Kaartic Sivaraam","phillip.wood@talktalk.net","Jonathan Nieder"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"328832","messageId":"20170925101041.18344-2-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[RFC PATCH 1/8] commit: move empty message checks to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-09-25T10:10:34Z","receivedAt":"2017-09-25T10:11:19Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/commit.c | 70 +++-----------------------------------------------------\n sequencer.c      | 60 ++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      | 10 ++++++++\n 3 files changed, 73 insertions(+), 67 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex b3b04f5dd3a94d1661e877c5019cc56ac46854ef..0b8c1ef6f57cfed328d12255e6834adb4bda4137 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -128,12 +128,7 @@ static char *sign_commit;\n  * if editor is used, and only the whitespaces if the message\n  * is specified explicitly.\n  */\n-static enum {\n-\tCLEANUP_SPACE,\n-\tCLEANUP_NONE,\n-\tCLEANUP_SCISSORS,\n-\tCLEANUP_ALL\n-} cleanup_mode;\n+static enum cleanup_mode cleanup_mode;\n static const char *cleanup_arg;\n \n static enum commit_whence whence;\n@@ -978,65 +973,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \treturn 1;\n }\n \n-static int rest_is_empty(struct strbuf *sb, int start)\n-{\n-\tint i, eol;\n-\tconst char *nl;\n-\n-\t/* Check if the rest is just whitespace and Signed-off-by's. */\n-\tfor (i = start; i < sb->len; i++) {\n-\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n-\t\tif (nl)\n-\t\t\teol = nl - sb->buf;\n-\t\telse\n-\t\t\teol = sb->len;\n-\n-\t\tif (strlen(sign_off_header) <= eol - i &&\n-\t\t    starts_with(sb->buf + i, sign_off_header)) {\n-\t\t\ti = eol;\n-\t\t\tcontinue;\n-\t\t}\n-\t\twhile (i < eol)\n-\t\t\tif (!isspace(sb->buf[i++]))\n-\t\t\t\treturn 0;\n-\t}\n-\n-\treturn 1;\n-}\n-\n-/*\n- * Find out if the message in the strbuf contains only whitespace and\n- * Signed-off-by lines.\n- */\n-static int message_is_empty(struct strbuf *sb)\n-{\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\treturn rest_is_empty(sb, 0);\n-}\n-\n-/*\n- * See if the user edited the message in the editor or left what\n- * was in the template intact\n- */\n-static int template_untouched(struct strbuf *sb)\n-{\n-\tstruct strbuf tmpl = STRBUF_INIT;\n-\tconst char *start;\n-\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\n-\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n-\t\treturn 0;\n-\n-\tstrbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);\n-\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n-\t\tstart = sb->buf;\n-\tstrbuf_release(&tmpl);\n-\treturn rest_is_empty(sb, start - sb->buf);\n-}\n-\n static const char *find_author_by_nickname(const char *name)\n {\n \tstruct rev_info revs;\n@@ -1744,12 +1680,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (cleanup_mode != CLEANUP_NONE)\n \t\tstrbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);\n \n-\tif (message_is_empty(&sb) && !allow_empty_message) {\n+\tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit due to empty commit message.\\n\"));\n \t\texit(1);\n \t}\n-\tif (template_untouched(&sb) && !allow_empty_message) {\n+\tif (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit; you did not edit the message.\\n\"));\n \t\texit(1);\ndiff --git a/sequencer.c b/sequencer.c\nindex fcceabb80f4261006cdd65bc0ec95ac54ea42e7c..319208afb3de36c97b6c62d4ecf6e641245e7a54 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -690,6 +690,66 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static int rest_is_empty(const struct strbuf *sb, int start)\n+{\n+\tint i, eol;\n+\tconst char *nl;\n+\n+\t/* Check if the rest is just whitespace and Signed-off-by's. */\n+\tfor (i = start; i < sb->len; i++) {\n+\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n+\t\tif (nl)\n+\t\t\teol = nl - sb->buf;\n+\t\telse\n+\t\t\teol = sb->len;\n+\n+\t\tif (strlen(sign_off_header) <= eol - i &&\n+\t\t    starts_with(sb->buf + i, sign_off_header)) {\n+\t\t\ti = eol;\n+\t\t\tcontinue;\n+\t\t}\n+\t\twhile (i < eol)\n+\t\t\tif (!isspace(sb->buf[i++]))\n+\t\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+/*\n+ * Find out if the message in the strbuf contains only whitespace and\n+ * Signed-off-by lines.\n+ */\n+int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode)\n+{\n+\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\treturn rest_is_empty(sb, 0);\n+}\n+\n+/*\n+ * See if the user edited the message in the editor or left what\n+ * was in the template intact\n+ */\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum cleanup_mode cleanup_mode)\n+{\n+\tstruct strbuf tmpl = STRBUF_INIT;\n+\tconst char *start;\n+\n+\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\n+\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n+\t\treturn 0;\n+\n+\tstrbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);\n+\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n+\t\tstart = sb->buf;\n+\tstrbuf_release(&tmpl);\n+\treturn rest_is_empty(sb, start - sb->buf);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex f885b68395f4bff1ded96c0ab84ed87d164f0c7d..dd071cfcd82d165bd23726814b74cbf3384e1a17 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -50,4 +50,14 @@ extern const char sign_off_header[];\n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n \n+enum cleanup_mode {\n+\tCLEANUP_SPACE,\n+\tCLEANUP_NONE,\n+\tCLEANUP_SCISSORS,\n+\tCLEANUP_ALL\n+};\n+\n+int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum cleanup_mode cleanup_mode);\n #endif\n-- \n2.14.1\n\n"},{"id":"328833","messageId":"20170925101041.18344-5-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[RFC PATCH 4/8] commit: move post-rewrite code to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-09-25T10:10:37Z","receivedAt":"2017-09-25T10:11:22Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/commit.c | 42 +-----------------------------------------\n sequencer.c      | 46 ++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |  2 ++\n 3 files changed, 49 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 497778ba2c02afdd4a337969a27ca781e8389040..9d621098823d196643180226491e43c806154c13 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -31,9 +31,7 @@\n #include \"gpg-interface.h\"\n #include \"column.h\"\n #include \"sequencer.h\"\n-#include \"notes-utils.h\"\n #include \"mailmap.h\"\n-#include \"sigchain.h\"\n \n static const char * const builtin_commit_usage[] = {\n \tN_(\"git commit [<options>] [--] <pathspec>...\"),\n@@ -1465,37 +1463,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \treturn git_status_config(k, v, s);\n }\n \n-static int run_rewrite_hook(const struct object_id *oldoid,\n-\t\t\t    const struct object_id *newoid)\n-{\n-\tstruct child_process proc = CHILD_PROCESS_INIT;\n-\tconst char *argv[3];\n-\tint code;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\targv[0] = find_hook(\"post-rewrite\");\n-\tif (!argv[0])\n-\t\treturn 0;\n-\n-\targv[1] = \"amend\";\n-\targv[2] = NULL;\n-\n-\tproc.argv = argv;\n-\tproc.in = -1;\n-\tproc.stdout_to_stderr = 1;\n-\n-\tcode = start_command(&proc);\n-\tif (code)\n-\t\treturn code;\n-\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n-\tsigchain_push(SIGPIPE, SIG_IGN);\n-\twrite_in_full(proc.in, sb.buf, sb.len);\n-\tclose(proc.in);\n-\tstrbuf_release(&sb);\n-\tsigchain_pop(SIGPIPE);\n-\treturn finish_command(&proc);\n-}\n-\n int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)\n {\n \tstruct argv_array hook_env = ARGV_ARRAY_INIT;\n@@ -1725,14 +1692,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \trerere(0);\n \trun_commit_hook(use_editor, get_index_file(), \"post-commit\", NULL);\n \tif (amend && !no_post_rewrite) {\n-\t\tstruct notes_rewrite_cfg *cfg;\n-\t\tcfg = init_copy_notes_for_rewrite(\"amend\");\n-\t\tif (cfg) {\n-\t\t\t/* we are amending, so current_head is not NULL */\n-\t\t\tcopy_note_for_rewrite(cfg, &current_head->object.oid, &oid);\n-\t\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n-\t\t}\n-\t\trun_rewrite_hook(&current_head->object.oid, &oid);\n+\t\tcommit_post_rewrite(current_head, &oid);\n \t}\n \tif (!quiet)\n \t\tprint_summary(prefix, &oid, !current_head);\ndiff --git a/sequencer.c b/sequencer.c\nindex 1795a4df2a0021b2419d941c6083e49cd6647314..81bd0810df6bcf2e078abc220bb8984345bf467f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -20,6 +20,8 @@\n #include \"trailer.h\"\n #include \"log-tree.h\"\n #include \"wt-status.h\"\n+#include \"notes-utils.h\"\n+#include \"sigchain.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -786,6 +788,50 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,\n \treturn ret;\n }\n \n+static int run_rewrite_hook(const struct object_id *oldoid,\n+\t\t\t    const struct object_id *newoid)\n+{\n+\tstruct child_process proc = CHILD_PROCESS_INIT;\n+\tconst char *argv[3];\n+\tint code;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\targv[0] = find_hook(\"post-rewrite\");\n+\tif (!argv[0])\n+\t\treturn 0;\n+\n+\targv[1] = \"amend\";\n+\targv[2] = NULL;\n+\n+\tproc.argv = argv;\n+\tproc.in = -1;\n+\tproc.stdout_to_stderr = 1;\n+\n+\tcode = start_command(&proc);\n+\tif (code)\n+\t\treturn code;\n+\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n+\tsigchain_push(SIGPIPE, SIG_IGN);\n+\twrite_in_full(proc.in, sb.buf, sb.len);\n+\tclose(proc.in);\n+\tstrbuf_release(&sb);\n+\tsigchain_pop(SIGPIPE);\n+\treturn finish_command(&proc);\n+}\n+\n+void commit_post_rewrite(const struct commit *old_head,\n+\t\t\t const struct object_id *new_head)\n+{\n+\tstruct notes_rewrite_cfg *cfg;\n+\tcfg = init_copy_notes_for_rewrite(\"amend\");\n+\tif (cfg) {\n+\t\t/* we are amending, so current_head is not NULL */\n+\t\tcopy_note_for_rewrite(cfg, &old_head->object.oid, new_head);\n+\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n+\t}\n+\trun_rewrite_hook(&old_head->object.oid, new_head);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 87edf40e5274d59f48d5af57678100ea220d2c8a..45def684ad751d0b8dc62b6cfdfb819ddf183c89 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -62,4 +62,6 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n \t\t       enum cleanup_mode cleanup_mode);\n int update_head(const struct commit *old_head, const struct object_id *new_head,\n \t\tconst struct strbuf *msg, struct strbuf *err);\n+void commit_post_rewrite(const struct commit *current_head,\n+\t\t\t const struct object_id *new_head);\n #endif\n-- \n2.14.1\n\n"},{"id":"328834","messageId":"20170925101041.18344-1-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":null,"subject":"[RFC PATCH 0/8] sequencer: dont't fork git commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-09-25T10:10:33Z","receivedAt":"2017-09-25T10:11:27Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nThese patches teach the sequencer to create commits without forking\ngit commit when the commit message does not need to be edited. This\nspeeds up cherry picking 10 commits by 26% and picking 10 commits with\nrebase --continue by 44%. The first few patches move bits of\nbuiltin/commit.c to sequencer.c. The last two patches actually\nimplement creating commits in sequencer.c.\n\nPhillip Wood (8):\n  commit: move empty message checks to libgit\n  commit: move code to update HEAD to libgit\n  sequencer: refactor update_head()\n  commit: move post-rewrite code to libgit\n  commit: move print_commit_summary() to libgit\n  sequencer: simplify adding Signed-off-by: trailer\n  sequencer: load commit related config\n  sequencer: try to commit without forking 'git commit'\n\n builtin/commit.c         | 269 ++--------------------------\n builtin/rebase--helper.c |  13 +-\n builtin/revert.c         |  15 +-\n sequencer.c              | 447 ++++++++++++++++++++++++++++++++++++++++++++++-\n sequencer.h              |  20 +++\n 5 files changed, 503 insertions(+), 261 deletions(-)\n\n-- \n2.14.1\n\n"},{"id":"328835","messageId":"20170925101041.18344-3-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[RFC PATCH 2/8] commit: move code to update HEAD to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-09-25T10:10:35Z","receivedAt":"2017-09-25T10:11:32Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/commit.c | 31 +++++++------------------------\n sequencer.c      | 39 ++++++++++++++++++++++++++++++++++++++-\n sequencer.h      |  2 ++\n 3 files changed, 47 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0b8c1ef6f57cfed328d12255e6834adb4bda4137..497778ba2c02afdd4a337969a27ca781e8389040 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1578,13 +1578,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf author_ident = STRBUF_INIT;\n \tconst char *index_file, *reflog_msg;\n-\tchar *nl;\n \tstruct object_id oid;\n \tstruct commit_list *parents = NULL;\n \tstruct stat statbuf;\n \tstruct commit *current_head = NULL;\n \tstruct commit_extra_header *extra = NULL;\n-\tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n@@ -1625,10 +1623,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \treflog_msg = getenv(\"GIT_REFLOG_ACTION\");\n \tif (!current_head) {\n \t\tif (!reflog_msg)\n-\t\t\treflog_msg = \"commit (initial)\";\n+\t\t\tsetenv (\"GIT_REFLOG_ACTION\", \"commit (initial)\", 1);\n \t} else if (amend) {\n \t\tif (!reflog_msg)\n-\t\t\treflog_msg = \"commit (amend)\";\n+\t\t\tsetenv(\"GIT_REFLOG_ACTION\", \"commit (amend)\", 1);\n \t\tparents = copy_commit_list(current_head->parents);\n \t} else if (whence == FROM_MERGE) {\n \t\tstruct strbuf m = STRBUF_INIT;\n@@ -1637,7 +1635,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tstruct commit_list **pptr = &parents;\n \n \t\tif (!reflog_msg)\n-\t\t\treflog_msg = \"commit (merge)\";\n+\t\t\tsetenv(\"GIT_REFLOG_ACTION\", \"commit (merge)\", 1);\n \t\tpptr = commit_list_append(current_head, pptr);\n \t\tfp = xfopen(git_path_merge_head(), \"r\");\n \t\twhile (strbuf_getline_lf(&m, fp) != EOF) {\n@@ -1660,9 +1658,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\t\tparents = reduce_heads(parents);\n \t} else {\n \t\tif (!reflog_msg)\n-\t\t\treflog_msg = (whence == FROM_CHERRY_PICK)\n-\t\t\t\t\t? \"commit (cherry-pick)\"\n-\t\t\t\t\t: \"commit\";\n+\t\t\tsetenv(\"GIT_REFLOG_ACTION\", (whence == FROM_CHERRY_PICK)\n+\t\t\t\t\t\t? \"commit (cherry-pick)\"\n+\t\t\t\t\t\t: \"commit\", 1);\n \t\tcommit_list_insert(current_head, &parents);\n \t}\n \n@@ -1707,25 +1705,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&author_ident);\n \tfree_commit_extra_headers(extra);\n \n-\tnl = strchr(sb.buf, '\\n');\n-\tif (nl)\n-\t\tstrbuf_setlen(&sb, nl + 1 - sb.buf);\n-\telse\n-\t\tstrbuf_addch(&sb, '\\n');\n-\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n-\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n-\n-\ttransaction = ref_transaction_begin(&err);\n-\tif (!transaction ||\n-\t    ref_transaction_update(transaction, \"HEAD\", oid.hash,\n-\t\t\t\t   current_head\n-\t\t\t\t   ? current_head->object.oid.hash : null_sha1,\n-\t\t\t\t   0, sb.buf, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n+\tif (update_head (current_head, &oid, &sb, &err)) {\n \t\trollback_index_files();\n \t\tdie(\"%s\", err.buf);\n \t}\n-\tref_transaction_free(transaction);\n \n \tunlink(git_path_cherry_pick_head());\n \tunlink(git_path_revert_head());\ndiff --git a/sequencer.c b/sequencer.c\nindex 319208afb3de36c97b6c62d4ecf6e641245e7a54..917ad4a16216b30adb2c2c9650217926d8db8ba7 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1,10 +1,10 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"lockfile.h\"\n-#include \"sequencer.h\"\n #include \"dir.h\"\n #include \"object.h\"\n #include \"commit.h\"\n+#include \"sequencer.h\"\n #include \"tag.h\"\n #include \"run-command.h\"\n #include \"exec_cmd.h\"\n@@ -750,6 +750,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n \treturn rest_is_empty(sb, start - sb->buf);\n }\n \n+int update_head(const struct commit *old_head, const struct object_id *new_head,\n+\t\tconst struct strbuf *msg, struct strbuf *err)\n+{\n+\tstruct ref_transaction *transaction;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *nl, *reflog_msg;\n+\tint ret = 0;\n+\n+\treflog_msg = getenv(\"GIT_REFLOG_ACTION\");\n+\tif (!reflog_msg)\n+\t\treflog_msg=\"\";\n+\n+\tnl = strchr(msg->buf, '\\n');\n+\tif (nl) {\n+\t\tstrbuf_add(&sb, msg->buf, nl + 1 - msg->buf);\n+\t} else {\n+\t\tstrbuf_addbuf(&sb, msg);\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t}\n+\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n+\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n+\n+\ttransaction = ref_transaction_begin(err);\n+\tif (!transaction ||\n+\t    ref_transaction_update(transaction, \"HEAD\", new_head->hash,\n+\t\t\t\t   old_head\n+\t\t\t\t   ? old_head->object.oid.hash : null_sha1,\n+\t\t\t\t   0, sb.buf, err) ||\n+\t    ref_transaction_commit(transaction, err)) {\n+\t\tret = -1;\n+\t}\n+\tref_transaction_free(transaction);\n+\tstrbuf_release(&sb);\n+\n+\treturn ret;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex dd071cfcd82d165bd23726814b74cbf3384e1a17..87edf40e5274d59f48d5af57678100ea220d2c8a 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -60,4 +60,6 @@ enum cleanup_mode {\n int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n \t\t       enum cleanup_mode cleanup_mode);\n+int update_head(const struct commit *old_head, const struct object_id *new_head,\n+\t\tconst struct strbuf *msg, struct strbuf *err);\n #endif\n-- \n2.14.1\n\n"},{"id":"328836","messageId":"20170925101041.18344-4-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[RFC PATCH 3/8] sequencer: refactor update_head()","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-09-25T10:10:36Z","receivedAt":"2017-09-25T10:11:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nThe previous commit was a mechanical translation of the code from\nbuiltin/commit.c. Now that it uses its own strbuf for the reflog\nmessage it can be simplified slightly.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n sequencer.c | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 917ad4a16216b30adb2c2c9650217926d8db8ba7..1795a4df2a0021b2419d941c6083e49cd6647314 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -759,8 +759,9 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,\n \tint ret = 0;\n \n \treflog_msg = getenv(\"GIT_REFLOG_ACTION\");\n-\tif (!reflog_msg)\n-\t\treflog_msg=\"\";\n+\tif (reflog_msg)\n+\t\tstrbuf_addstr(&sb, reflog_msg);\n+\tstrbuf_addstr(&sb, \": \");\n \n \tnl = strchr(msg->buf, '\\n');\n \tif (nl) {\n@@ -769,8 +770,6 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,\n \t\tstrbuf_addbuf(&sb, msg);\n \t\tstrbuf_addch(&sb, '\\n');\n \t}\n-\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n-\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n \n \ttransaction = ref_transaction_begin(err);\n \tif (!transaction ||\n-- \n2.14.1\n\n"},{"id":"328837","messageId":"20170925101041.18344-6-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[RFC PATCH 5/8] commit: move print_commit_summary() to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-09-25T10:10:38Z","receivedAt":"2017-09-25T10:11:36Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/commit.c | 126 ++++---------------------------------------------------\n sequencer.c      | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |   5 +++\n 3 files changed, 129 insertions(+), 118 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 9d621098823d196643180226491e43c806154c13..fac392216c4dc4d4a2b9fbee1d9ee14da0a14209 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {\n \tNULL\n };\n \n-static const char implicit_ident_advice_noconfig[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly. Run the\\n\"\n-\"following command and follow the instructions in your editor to edit\\n\"\n-\"your configuration file:\\n\"\n-\"\\n\"\n-\"    git config --global --edit\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n-static const char implicit_ident_advice_config[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly:\\n\"\n-\"\\n\"\n-\"    git config --global user.name \\\"Your Name\\\"\\n\"\n-\"    git config --global user.email you@example.com\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n static const char empty_amend_advice[] =\n N_(\"You asked to amend the most recent commit, but doing so would make\\n\"\n \"it empty. You can repeat your command with --allow-empty, or you can\\n\"\n@@ -1343,97 +1318,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n-static const char *implicit_ident_advice(void)\n-{\n-\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n-\tchar *xdg_config = xdg_config_home(\"config\");\n-\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n-\n-\tfree(user_config);\n-\tfree(xdg_config);\n-\n-\tif (config_exists)\n-\t\treturn _(implicit_ident_advice_config);\n-\telse\n-\t\treturn _(implicit_ident_advice_noconfig);\n-\n-}\n-\n-static void print_summary(const char *prefix, const struct object_id *oid,\n-\t\t\t  int initial_commit)\n-{\n-\tstruct rev_info rev;\n-\tstruct commit *commit;\n-\tstruct strbuf format = STRBUF_INIT;\n-\tstruct object_id junk_oid;\n-\tconst char *head;\n-\tstruct pretty_print_context pctx = {0};\n-\tstruct strbuf author_ident = STRBUF_INIT;\n-\tstruct strbuf committer_ident = STRBUF_INIT;\n-\n-\tcommit = lookup_commit(oid);\n-\tif (!commit)\n-\t\tdie(_(\"couldn't look up newly created commit\"));\n-\tif (parse_commit(commit))\n-\t\tdie(_(\"could not parse newly created commit\"));\n-\n-\tstrbuf_addstr(&format, \"format:%h] %s\");\n-\n-\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n-\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n-\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n-\t\tstrbuf_addstr(&format, \"\\n Author: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n-\t}\n-\tif (author_date_is_interesting()) {\n-\t\tstruct strbuf date = STRBUF_INIT;\n-\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n-\t\tstrbuf_addstr(&format, \"\\n Date: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &date);\n-\t\tstrbuf_release(&date);\n-\t}\n-\tif (!committer_ident_sufficiently_given()) {\n-\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n-\t\tif (advice_implicit_identity) {\n-\t\t\tstrbuf_addch(&format, '\\n');\n-\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n-\t\t}\n-\t}\n-\tstrbuf_release(&author_ident);\n-\tstrbuf_release(&committer_ident);\n-\n-\tinit_revisions(&rev, prefix);\n-\tsetup_revisions(0, NULL, &rev, NULL);\n-\n-\trev.diff = 1;\n-\trev.diffopt.output_format =\n-\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n-\n-\trev.verbose_header = 1;\n-\trev.show_root_diff = 1;\n-\tget_commit_format(format.buf, &rev);\n-\trev.always_show_header = 0;\n-\trev.diffopt.detect_rename = 1;\n-\trev.diffopt.break_opt = 0;\n-\tdiff_setup_done(&rev.diffopt);\n-\n-\thead = resolve_ref_unsafe(\"HEAD\", 0, junk_oid.hash, NULL);\n-\tif (!strcmp(head, \"HEAD\"))\n-\t\thead = _(\"detached HEAD\");\n-\telse\n-\t\tskip_prefix(head, \"refs/heads/\", &head);\n-\tprintf(\"[%s%s \", head, initial_commit ? _(\" (root-commit)\") : \"\");\n-\n-\tif (!log_tree_commit(&rev, commit)) {\n-\t\trev.always_show_header = 1;\n-\t\trev.use_terminator = 1;\n-\t\tlog_tree_commit(&rev, commit);\n-\t}\n-\n-\tstrbuf_release(&format);\n-}\n-\n static int git_commit_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n@@ -1694,9 +1578,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (amend && !no_post_rewrite) {\n \t\tcommit_post_rewrite(current_head, &oid);\n \t}\n-\tif (!quiet)\n-\t\tprint_summary(prefix, &oid, !current_head);\n+\tif (!quiet) {\n+\t\tint flags = 0;\n \n+\t\tif (!current_head)\n+\t\t\tflags|= SUMMARY_INITIAL_COMMIT;\n+\t\tif (author_date_is_interesting())\n+\t\t\tflags |= SUMMARY_SHOW_AUTHOR_DATE;\n+\t\tprint_commit_summary(prefix, &oid, flags);\n+\t}\n \tstrbuf_release(&err);\n \treturn 0;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex 81bd0810df6bcf2e078abc220bb8984345bf467f..65031353f01d75624951222c2de280c427e26ac5 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -832,6 +832,122 @@ void commit_post_rewrite(const struct commit *old_head,\n \trun_rewrite_hook(&old_head->object.oid, new_head);\n }\n \n+static const char implicit_ident_advice_noconfig[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly. Run the\\n\"\n+\"following command and follow the instructions in your editor to edit\\n\"\n+\"your configuration file:\\n\"\n+\"\\n\"\n+\"    git config --global --edit\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char implicit_ident_advice_config[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly:\\n\"\n+\"\\n\"\n+\"    git config --global user.name \\\"Your Name\\\"\\n\"\n+\"    git config --global user.email you@example.com\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char *implicit_ident_advice(void)\n+{\n+\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n+\tchar *xdg_config = xdg_config_home(\"config\");\n+\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n+\n+\tfree(user_config);\n+\tfree(xdg_config);\n+\n+\tif (config_exists)\n+\t\treturn _(implicit_ident_advice_config);\n+\telse\n+\t\treturn _(implicit_ident_advice_noconfig);\n+\n+}\n+\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  int flags)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf format = STRBUF_INIT;\n+\tstruct object_id junk_oid;\n+\tconst char *head;\n+\tstruct pretty_print_context pctx = {0};\n+\tstruct strbuf author_ident = STRBUF_INIT;\n+\tstruct strbuf committer_ident = STRBUF_INIT;\n+\n+\tcommit = lookup_commit(oid);\n+\tif (!commit)\n+\t\tdie(_(\"couldn't look up newly created commit\"));\n+\tif (parse_commit(commit))\n+\t\tdie(_(\"could not parse newly created commit\"));\n+\n+\tstrbuf_addstr(&format, \"format:%h] %s\");\n+\n+\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n+\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n+\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n+\t\tstrbuf_addstr(&format, \"\\n Author: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n+\t}\n+\tif (flags & SUMMARY_SHOW_AUTHOR_DATE) {\n+\t\tstruct strbuf date = STRBUF_INIT;\n+\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n+\t\tstrbuf_addstr(&format, \"\\n Date: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &date);\n+\t\tstrbuf_release(&date);\n+\t}\n+\tif (!committer_ident_sufficiently_given()) {\n+\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n+\t\tif (advice_implicit_identity) {\n+\t\t\tstrbuf_addch(&format, '\\n');\n+\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n+\t\t}\n+\t}\n+\tstrbuf_release(&author_ident);\n+\tstrbuf_release(&committer_ident);\n+\n+\tinit_revisions(&rev, prefix);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.diff = 1;\n+\trev.diffopt.output_format =\n+\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n+\n+\trev.verbose_header = 1;\n+\trev.show_root_diff = 1;\n+\tget_commit_format(format.buf, &rev);\n+\trev.always_show_header = 0;\n+\trev.diffopt.detect_rename = 1;\n+\trev.diffopt.break_opt = 0;\n+\tdiff_setup_done(&rev.diffopt);\n+\n+\thead = resolve_ref_unsafe(\"HEAD\", 0, junk_oid.hash, NULL);\n+\tif (!strcmp(head, \"HEAD\"))\n+\t\thead = _(\"detached HEAD\");\n+\telse\n+\t\tskip_prefix(head, \"refs/heads/\", &head);\n+\tprintf(\"[%s%s \", head, (flags & SUMMARY_INITIAL_COMMIT) ? _(\" (root-commit)\") : \"\");\n+\n+\tif (!log_tree_commit(&rev, commit)) {\n+\t\trev.always_show_header = 1;\n+\t\trev.use_terminator = 1;\n+\t\tlog_tree_commit(&rev, commit);\n+\t}\n+\n+\tstrbuf_release(&format);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 45def684ad751d0b8dc62b6cfdfb819ddf183c89..d491f5cae2e6152859d114a08bbd7c935d1e80a6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -64,4 +64,9 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,\n \t\tconst struct strbuf *msg, struct strbuf *err);\n void commit_post_rewrite(const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n+\n+#define SUMMARY_INITIAL_COMMIT   (1 << 0)\n+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  int flags);\n #endif\n-- \n2.14.1\n\n"},{"id":"328838","messageId":"20170925101041.18344-7-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[RFC PATCH 6/8] sequencer: simplify adding Signed-off-by: trailer","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-09-25T10:10:39Z","receivedAt":"2017-09-25T10:11:38Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd the Signed-off-by: trailer in one place rather than adding it to\nthe message when doing a recursive merge and then again when\ncommitting. This means that if there are conflicts when merging with a\nstrategy other than 'recursive' the Signed-off-by: trailer will be\nadded if the user commits the resolution themselves without passing\n'--signoff' to 'git commit'. It also simplifies the in-process commit\nthat is about to be added to the sequencer.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n sequencer.c | 8 +++-----\n 1 file changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 65031353f01d75624951222c2de280c427e26ac5..e9060e3ca50777687c578ff09c62cd901efcfb0e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -476,9 +476,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \t\t\t_(action_name(opts)));\n \trollback_lock_file(&index_lock);\n \n-\tif (opts->signoff)\n-\t\tappend_signoff(msgbuf, 0, 0);\n-\n \tif (!clean)\n \t\tappend_conflicts_hint(msgbuf);\n \n@@ -656,8 +653,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\targv_array_push(&cmd.args, \"--amend\");\n \tif (opts->gpg_sign)\n \t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n-\tif (opts->signoff)\n-\t\targv_array_push(&cmd.args, \"-s\");\n \tif (defmsg)\n \t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n \tif ((flags & CLEANUP_MSG))\n@@ -1339,6 +1334,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n+\tif (opts->signoff)\n+\t\tappend_signoff(&msgbuf, 0, 0);\n+\n \tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n \t\tres = -1;\n \telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n-- \n2.14.1\n\n"},{"id":"328839","messageId":"20170925101041.18344-8-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[RFC PATCH 7/8] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-09-25T10:10:40Z","receivedAt":"2017-09-25T10:11:40Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nLoad default values for message cleanup and gpg signing of commits in\npreparation for committing without forking 'git commit'.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/rebase--helper.c | 13 ++++++++++++-\n builtin/revert.c         | 15 +++++++++++++--\n sequencer.c              | 30 ++++++++++++++++++++++++++++++\n sequencer.h              |  1 +\n 4 files changed, 56 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c\nindex c82b4dce6838fa039e9c2cfc769c7cd17a8ef562..3a99f9f8f1396ee5a040c7c4e9367688fe04c9a4 100644\n--- a/builtin/rebase--helper.c\n+++ b/builtin/rebase--helper.c\n@@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n \tNULL\n };\n \n+static int git_rebase_helper_config (const char *k, const char *v, void *cb)\n+{\n+\tint status;\n+\n+\tstatus = git_sequencer_config (k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_default_config(k, v, NULL);\n+}\n+\n int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts = REPLAY_OPTS_INIT;\n@@ -24,7 +35,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_rebase_helper_config, NULL);\n \n \topts.action = REPLAY_INTERACTIVE_REBASE;\n \topts.allow_ff = 1;\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex b9d927eb09c9ed87c84681df1396f4e6d9b13c97..2883f9eb71be1c2b205fd0f1d962748438f4c4b8 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n+static int git_revert_config (const char *k, const char *v, void *cb)\n+{\n+\tint status;\n+\n+\tstatus = git_sequencer_config (k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_default_config(k, v, NULL);\n+}\n+\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n@@ -208,7 +219,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tif (isatty(0))\n \t\topts.edit = 1;\n \topts.action = REPLAY_REVERT;\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_revert_config, NULL);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"revert failed\"));\n@@ -221,7 +232,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \topts.action = REPLAY_PICK;\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_revert_config, NULL);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"cherry-pick failed\"));\ndiff --git a/sequencer.c b/sequencer.c\nindex e9060e3ca50777687c578ff09c62cd901efcfb0e..230bdb8535a422b1263429e5894e3f8b1733e844 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -687,6 +687,36 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static enum cleanup_mode default_msg_cleanup = CLEANUP_NONE;\n+static char *default_gpg_sign;\n+\n+int git_sequencer_config(const char *k, const char *v, void *cb)\n+{\n+\tif (!strcmp(k, \"commit.cleanup\")) {\n+\t\tint status;\n+\t\tconst char *s;\n+\n+\t\tstatus = git_config_string(&s, k, v);\n+\t\tif (!strcmp(s, \"verbatim\"))\n+\t\t\tdefault_msg_cleanup = CLEANUP_NONE;\n+\t\telse if (!strcmp(s, \"whitespace\"))\n+\t\t\tdefault_msg_cleanup = CLEANUP_SPACE;\n+\t\telse if (!strcmp(s, \"strip\"))\n+\t\t\tdefault_msg_cleanup = CLEANUP_ALL;\n+\t\telse if (!strcmp(s, \"scissors\"))\n+\t\t\tdefault_msg_cleanup = CLEANUP_NONE;\n+\n+\t\treturn status;\n+\t}\n+\n+\tif (!strcmp(k, \"commit.gpgsign\")) {\n+\t\tdefault_gpg_sign = git_config_bool(k, v) ? \"\" : NULL;\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_gpg_config(k, v, NULL);\n+}\n+\n static int rest_is_empty(const struct strbuf *sb, int start)\n {\n \tint i, eol;\ndiff --git a/sequencer.h b/sequencer.h\nindex d491f5cae2e6152859d114a08bbd7c935d1e80a6..2759ca2f7d1aba1268c88dc8bd94eb3e593897fe 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -49,6 +49,7 @@ extern const char sign_off_header[];\n \n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n+int git_sequencer_config(const char *k, const char *v, void *cb);\n \n enum cleanup_mode {\n \tCLEANUP_SPACE,\n-- \n2.14.1\n\n"},{"id":"328840","messageId":"20170925101041.18344-9-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[RFC PATCH 8/8] sequencer: try to commit without forking 'git commit'","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-09-25T10:10:41Z","receivedAt":"2017-09-25T10:11:42Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nIf the commit message does not need to be edited then create the\ncommit without forking 'git commit'. Taking the best time of ten runs\nwith a warm cache this reduces the time taken to cherry-pick 10\ncommits by 26% (from 319ms to 235ms), and the time taken by 'git\nrebase --continue' to pick 10 commits by 44% (from 376ms to 211ms) on\nmy computer running linux. The greater saving for rebase is because it\nlonger wastes time creating the commit summary just to throw it away.\n\nEven when not forking 'git commit' the commit message is written to a\nfile and CHERRY_PICK_HEAD/REVERT_HEAD are created unnecessarily. This\ncould be eliminated in future. I hacked up a version that does not\nwrite these files and just passed an strbuf (with the wrong message\nfor fixup and squash commands) to do_commit() but I couldn't measure\nany significant time difference when running cherry-pick or rebase. I\nthink eliminating the writes properly for rebase would require a bit\nof effort as the code would need to be restructured.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nI wonder if this should reparse the author it gets from the existing\ncommit rather than just reusing it.\n\n sequencer.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 147 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 230bdb8535a422b1263429e5894e3f8b1733e844..ebba53583727e947ed767d6181b14254e23fc146 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -591,6 +591,18 @@ static int read_env_script(struct argv_array *env)\n \treturn 0;\n }\n \n+static char *get_author(const char* message)\n+{\n+\tsize_t len;\n+\tconst char *a;\n+\n+\ta = find_commit_header(message, \"author\", &len);\n+\tif (a)\n+\t\treturn xmemdupz(a, len);\n+\n+\treturn NULL;\n+}\n+\n static const char staged_changes_advice[] =\n N_(\"you have staged changes in your working tree\\n\"\n \"If these changes are meant to be squashed into the previous commit, run:\\n\"\n@@ -973,6 +985,130 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n \tstrbuf_release(&format);\n }\n \n+static int try_to_commit(struct strbuf *msgbuf, const char *author,\n+\t\t\t struct replay_opts *opts, unsigned int flags)\n+{\n+\tstruct object_id tree;\n+\tstruct object_id oid;\n+\tstruct commit *current_head;\n+\tstruct commit_list *parents = NULL;\n+\tstruct commit_extra_header *extra = NULL;\n+\tstruct strbuf err = STRBUF_INIT;\n+\tstruct strbuf amend_msg = STRBUF_INIT;\n+\tstruct strbuf *msg;\n+\tchar *amend_author = NULL;\n+\tconst char *gpg_sign;\n+\tenum cleanup_mode cleanup;\n+\tint res = 0;\n+\n+\tmsg = msgbuf;\n+\tif (flags & EDIT_MSG || flags & VERIFY_MSG)\n+\t\treturn 1;\n+\n+\tif (get_oid(\"HEAD\", &oid)) {\n+\t\tcurrent_head = NULL;\n+\t} else {\n+\t\tcurrent_head = lookup_commit_or_die(&oid, \"HEAD\");\n+\t\tif (parse_commit(current_head))\n+\t\t\treturn error(_(\"could not parse HEAD commit\"));\n+\t}\n+\n+\tif (flags & AMEND_MSG) {\n+\t\tconst char *body;\n+\t\tconst char *exclude_gpgsig[2] = { \"gpgsig\", NULL };\n+\t\tconst char *out_enc = get_commit_output_encoding();\n+\t\tconst char *message = logmsg_reencode(current_head, NULL,\n+\t\t\t\t\t\t      out_enc);\n+\n+\t\tmsg = &amend_msg;\n+\t\tif (find_commit_subject(message, &body))\n+\t\t\tstrbuf_addstr(&amend_msg, body);\n+\t\tauthor = amend_author = get_author (message);\n+\t\tunuse_commit_buffer(current_head, message);\n+\t\tif (!author) {\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t\t\tgoto out;\n+\t\t}\n+\t\tparents = copy_commit_list(current_head->parents);\n+\t\textra = read_commit_extra_headers(current_head, exclude_gpgsig);\n+\t} else if (current_head) {\n+\t\tcommit_list_insert(current_head, &parents);\n+\t}\n+\n+\tcleanup = (flags & CLEANUP_MSG) ? CLEANUP_ALL : default_msg_cleanup;\n+\tif (cleanup != CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msg, cleanup == CLEANUP_ALL);\n+\tif (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {\n+\t\tres = 1;\n+\t\tgoto out;\n+\t}\n+\n+\tgpg_sign = (opts->gpg_sign) ? opts->gpg_sign : default_gpg_sign;\n+\n+\tif (write_cache_as_tree(tree.hash, 0, NULL)) {\n+\t\tres = error(_(\"git write-tree failed to write a tree\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?\n+\t\t\t\t\t      &current_head->tree->object.oid :\n+\t\t\t\t\t      &empty_tree_oid, &tree)) {\n+\t\tres = 1;\n+\t\tgoto out;\n+\t}\n+\n+\tres = commit_tree_extended(msg->buf, msg->len, tree.hash, parents,\n+\t\t\t\t   oid.hash, author, gpg_sign, extra);\n+\tif (res) {\n+\t\tres = error(_(\"failed to write commit object\"));\n+\t\tgoto out;\n+\t}\n+\n+\tres = update_head(current_head, &oid, msg, &err);\n+\tif (res) {\n+\t\terror(\"%s\", err.buf);\n+\t\tgoto out;\n+\t}\n+\n+\tif (flags & AMEND_MSG)\n+\t\tcommit_post_rewrite(current_head, &oid);\n+\n+\tif (!is_rebase_i(opts))\n+\t\tprint_commit_summary(NULL, &oid, SUMMARY_SHOW_AUTHOR_DATE);\n+out:\n+\tfree_commit_extra_headers(extra);\n+\tstrbuf_release(&err);\n+\tstrbuf_release(&amend_msg);\n+\tif (amend_author)\n+\t\tfree(amend_author);\n+\n+\treturn res;\n+}\n+\n+static int do_commit(const char *defmsg, const char* author,\n+\t\t     struct replay_opts *opts, unsigned int flags)\n+{\n+\tint res;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tif (defmsg && strbuf_read_file(&sb, defmsg, 2048) < 0)\n+\t\treturn error_errno(_(\"unable to read commit message from '%s'\"),\n+\t\t\t\t   defmsg);\n+\tres = try_to_commit(&sb, author, opts, flags);\n+\tstrbuf_release(&sb);\n+\n+\tif (res == 0) {\n+\t\tunlink(git_path_cherry_pick_head());\n+\t\tunlink(git_path_revert_head());\n+\t\tunlink(git_path_merge_msg());\n+\t\treturn res;\n+\t} else if (res == 1) {\n+\t\treturn run_git_commit(defmsg, opts, flags);\n+\t}\n+\n+\treturn res;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\n@@ -1224,6 +1360,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \tstruct object_id head;\n \tstruct commit *base, *next, *parent;\n \tconst char *base_label, *next_label;\n+\tchar *author = NULL;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n \tint res, unborn = 0, allow;\n@@ -1339,6 +1476,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t\tstrbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));\n \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n \t\t}\n+\t\tif (!is_fixup (command))\n+\t\t\tauthor = get_author(msg.message);\n \t}\n \n \tif (command == TODO_REWORD)\n@@ -1424,9 +1563,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t} else if (allow)\n \t\tflags |= ALLOW_EMPTY;\n-\tif (!opts->no_commit)\n+\tif (!opts->no_commit) {\n fast_forward_edit:\n-\t\tres = run_git_commit(msg_file, opts, flags);\n+\t\tif (author || command == TODO_REVERT || (flags & AMEND_MSG))\n+\t\t\tres = do_commit(msg_file, author, opts, flags);\n+\t\telse\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t}\n \n \tif (!res && final_fixup) {\n \t\tunlink(rebase_path_fixup_msg());\n@@ -1435,6 +1578,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \n leave:\n \tfree_message(commit, &msg);\n+\tif (author)\n+\t\tfree(author);\n \tupdate_abort_safety_file();\n \n \treturn res;\n-- \n2.14.1\n\n"},{"id":"329962","messageId":"xmqqo9pjth92.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20170925101041.18344-3-phillip.wood@talktalk.net","subject":"Re: [RFC PATCH 2/8] commit: move code to update HEAD to libgit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-07T09:54:49Z","receivedAt":"2017-10-07T09:54:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> ---\n\nThis seems to do a lot more than just moving code, most notably, it\nuses setenv() to affect what happens in any subprocesses we may\nspawn, and it is unclear if it was verified that this patch is free\nof unwanted consequences due to that change (and any others I may\nhave missed while reading this patch, if any).\n\nI suspect that it would be sufficient to make update_head() helper\nfunction take the reflog action message as another parameter\ninstead to fix the above, but there may be other reasons why you\nchose to do it this way---I cannot read it in your empty log\nmessage, though.\n\nI will not give line-by-line style nitpick but in general we do not\nleave a SP between function name and the open parenthesis that\nstarts its argument list.  New code in this patch seems to use\nmixture of styles.\n\n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index 0b8c1ef6f57cfed328d12255e6834adb4bda4137..497778ba2c02afdd4a337969a27ca781e8389040 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1578,13 +1578,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>  \tstruct strbuf sb = STRBUF_INIT;\n>  \tstruct strbuf author_ident = STRBUF_INIT;\n>  \tconst char *index_file, *reflog_msg;\n> -\tchar *nl;\n>  \tstruct object_id oid;\n>  \tstruct commit_list *parents = NULL;\n>  \tstruct stat statbuf;\n>  \tstruct commit *current_head = NULL;\n>  \tstruct commit_extra_header *extra = NULL;\n> -\tstruct ref_transaction *transaction;\n>  \tstruct strbuf err = STRBUF_INIT;\n>  \n>  \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n> @@ -1625,10 +1623,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>  \treflog_msg = getenv(\"GIT_REFLOG_ACTION\");\n>  \tif (!current_head) {\n>  \t\tif (!reflog_msg)\n> -\t\t\treflog_msg = \"commit (initial)\";\n> +\t\t\tsetenv (\"GIT_REFLOG_ACTION\", \"commit (initial)\", 1);\n>  \t} else if (amend) {\n>  \t\tif (!reflog_msg)\n> -\t\t\treflog_msg = \"commit (amend)\";\n> +\t\t\tsetenv(\"GIT_REFLOG_ACTION\", \"commit (amend)\", 1);\n>  \t\tparents = copy_commit_list(current_head->parents);\n>  \t} else if (whence == FROM_MERGE) {\n>  \t\tstruct strbuf m = STRBUF_INIT;\n> @@ -1637,7 +1635,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>  \t\tstruct commit_list **pptr = &parents;\n>  \n>  \t\tif (!reflog_msg)\n> -\t\t\treflog_msg = \"commit (merge)\";\n> +\t\t\tsetenv(\"GIT_REFLOG_ACTION\", \"commit (merge)\", 1);\n>  \t\tpptr = commit_list_append(current_head, pptr);\n>  \t\tfp = xfopen(git_path_merge_head(), \"r\");\n>  \t\twhile (strbuf_getline_lf(&m, fp) != EOF) {\n> @@ -1660,9 +1658,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>  \t\t\tparents = reduce_heads(parents);\n>  \t} else {\n>  \t\tif (!reflog_msg)\n> -\t\t\treflog_msg = (whence == FROM_CHERRY_PICK)\n> -\t\t\t\t\t? \"commit (cherry-pick)\"\n> -\t\t\t\t\t: \"commit\";\n> +\t\t\tsetenv(\"GIT_REFLOG_ACTION\", (whence == FROM_CHERRY_PICK)\n> +\t\t\t\t\t\t? \"commit (cherry-pick)\"\n> +\t\t\t\t\t\t: \"commit\", 1);\n>  \t\tcommit_list_insert(current_head, &parents);\n>  \t}\n>  \n> @@ -1707,25 +1705,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>  \tstrbuf_release(&author_ident);\n>  \tfree_commit_extra_headers(extra);\n>  \n> -\tnl = strchr(sb.buf, '\\n');\n> -\tif (nl)\n> -\t\tstrbuf_setlen(&sb, nl + 1 - sb.buf);\n> -\telse\n> -\t\tstrbuf_addch(&sb, '\\n');\n> -\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n> -\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n> -\n> -\ttransaction = ref_transaction_begin(&err);\n> -\tif (!transaction ||\n> -\t    ref_transaction_update(transaction, \"HEAD\", oid.hash,\n> -\t\t\t\t   current_head\n> -\t\t\t\t   ? current_head->object.oid.hash : null_sha1,\n> -\t\t\t\t   0, sb.buf, &err) ||\n> -\t    ref_transaction_commit(transaction, &err)) {\n> +\tif (update_head (current_head, &oid, &sb, &err)) {\n>  \t\trollback_index_files();\n>  \t\tdie(\"%s\", err.buf);\n>  \t}\n> -\tref_transaction_free(transaction);\n>  \n>  \tunlink(git_path_cherry_pick_head());\n>  \tunlink(git_path_revert_head());\n> diff --git a/sequencer.c b/sequencer.c\n> index 319208afb3de36c97b6c62d4ecf6e641245e7a54..917ad4a16216b30adb2c2c9650217926d8db8ba7 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1,10 +1,10 @@\n>  #include \"cache.h\"\n>  #include \"config.h\"\n>  #include \"lockfile.h\"\n> -#include \"sequencer.h\"\n>  #include \"dir.h\"\n>  #include \"object.h\"\n>  #include \"commit.h\"\n> +#include \"sequencer.h\"\n>  #include \"tag.h\"\n>  #include \"run-command.h\"\n>  #include \"exec_cmd.h\"\n> @@ -750,6 +750,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n>  \treturn rest_is_empty(sb, start - sb->buf);\n>  }\n>  \n> +int update_head(const struct commit *old_head, const struct object_id *new_head,\n> +\t\tconst struct strbuf *msg, struct strbuf *err)\n> +{\n> +\tstruct ref_transaction *transaction;\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\tconst char *nl, *reflog_msg;\n> +\tint ret = 0;\n> +\n> +\treflog_msg = getenv(\"GIT_REFLOG_ACTION\");\n> +\tif (!reflog_msg)\n> +\t\treflog_msg=\"\";\n> +\n> +\tnl = strchr(msg->buf, '\\n');\n> +\tif (nl) {\n> +\t\tstrbuf_add(&sb, msg->buf, nl + 1 - msg->buf);\n> +\t} else {\n> +\t\tstrbuf_addbuf(&sb, msg);\n> +\t\tstrbuf_addch(&sb, '\\n');\n> +\t}\n> +\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n> +\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n> +\n> +\ttransaction = ref_transaction_begin(err);\n> +\tif (!transaction ||\n> +\t    ref_transaction_update(transaction, \"HEAD\", new_head->hash,\n> +\t\t\t\t   old_head\n> +\t\t\t\t   ? old_head->object.oid.hash : null_sha1,\n> +\t\t\t\t   0, sb.buf, err) ||\n> +\t    ref_transaction_commit(transaction, err)) {\n> +\t\tret = -1;\n> +\t}\n> +\tref_transaction_free(transaction);\n> +\tstrbuf_release(&sb);\n> +\n> +\treturn ret;\n> +}\n> +\n>  static int is_original_commit_empty(struct commit *commit)\n>  {\n>  \tconst struct object_id *ptree_oid;\n> diff --git a/sequencer.h b/sequencer.h\n> index dd071cfcd82d165bd23726814b74cbf3384e1a17..87edf40e5274d59f48d5af57678100ea220d2c8a 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -60,4 +60,6 @@ enum cleanup_mode {\n>  int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);\n>  int template_untouched(const struct strbuf *sb, const char *template_file,\n>  \t\t       enum cleanup_mode cleanup_mode);\n> +int update_head(const struct commit *old_head, const struct object_id *new_head,\n> +\t\tconst struct strbuf *msg, struct strbuf *err);\n>  #endif\n"},{"id":"330903","messageId":"7e9318b4-1281-11ce-8144-5f7e4449e2e1@talktalk.net","threadId":"46827","inReplyTo":"xmqqo9pjth92.fsf@gitster.mtv.corp.google.com","subject":"Re: [RFC PATCH 2/8] commit: move code to update HEAD to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-10-24T10:01:45Z","receivedAt":"2017-10-24T10:02:19Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/10/17 10:54, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n>> ---\n> \n> This seems to do a lot more than just moving code, most notably, it\n> uses setenv() to affect what happens in any subprocesses we may\n> spawn, and it is unclear if it was verified that this patch is free\n> of unwanted consequences due to that change (and any others I may\n> have missed while reading this patch, if any).\n>\n> I suspect that it would be sufficient to make update_head() helper\n> function take the reflog action message as another parameter\n> instead to fix the above, but there may be other reasons why you\n> chose to do it this way---I cannot read it in your empty log\n> message, though.\n\nGood point, sorry I should have added some explanation about that. I\nwent with using setenv() rather than passing a reflog message to\nupdate_head() as it meant there were no changes needed on the sequencer\nside as it already sets GIT_REFLOG_ACTION. As the sequencer already sets\nGIT_REFLOG_ACTION, and git-commit does not fork any subprocesses I don't\nthink this change has any unwanted consequences (I pushed a branch to\ngithub before submitting the patches and the test suite passes on\ntravis). It would however be clearer to add a parameter to update_head()\nfor the reflog message as you suggested.\n\n> \n> I will not give line-by-line style nitpick but in general we do not\n> leave a SP between function name and the open parenthesis that\n> starts its argument list.  New code in this patch seems to use\n> mixture of styles.\n\nSorry I should have spotted those before I posted this series, I go\nthough all the patches and fix them (this would be a good opportunity\nfor me to try using git-clang-format from next)\n\nThanks for looking at this, did you have time to look at the other\nchanges in this series or did this patch put you off looking further?\nI'll update and repost probably towards the end of next week. If I\ncontinue to base these patches on master then I think the patch that\nmoves the code to print the commit summary will have (trivial) conflicts\nwith the changes in ao/check-resolve-ref-unsafe-result in pu do you want\nthe new patches based pu or are you happy with them based on master?\n\nBest Wishes\n\nPhillip\n\n>> diff --git a/builtin/commit.c b/builtin/commit.c\n>> index 0b8c1ef6f57cfed328d12255e6834adb4bda4137..497778ba2c02afdd4a337969a27ca781e8389040 100644\n>> --- a/builtin/commit.c\n>> +++ b/builtin/commit.c\n>> @@ -1578,13 +1578,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>>  \tstruct strbuf sb = STRBUF_INIT;\n>>  \tstruct strbuf author_ident = STRBUF_INIT;\n>>  \tconst char *index_file, *reflog_msg;\n>> -\tchar *nl;\n>>  \tstruct object_id oid;\n>>  \tstruct commit_list *parents = NULL;\n>>  \tstruct stat statbuf;\n>>  \tstruct commit *current_head = NULL;\n>>  \tstruct commit_extra_header *extra = NULL;\n>> -\tstruct ref_transaction *transaction;\n>>  \tstruct strbuf err = STRBUF_INIT;\n>>  \n>>  \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n>> @@ -1625,10 +1623,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>>  \treflog_msg = getenv(\"GIT_REFLOG_ACTION\");\n>>  \tif (!current_head) {\n>>  \t\tif (!reflog_msg)\n>> -\t\t\treflog_msg = \"commit (initial)\";\n>> +\t\t\tsetenv (\"GIT_REFLOG_ACTION\", \"commit (initial)\", 1);\n>>  \t} else if (amend) {\n>>  \t\tif (!reflog_msg)\n>> -\t\t\treflog_msg = \"commit (amend)\";\n>> +\t\t\tsetenv(\"GIT_REFLOG_ACTION\", \"commit (amend)\", 1);\n>>  \t\tparents = copy_commit_list(current_head->parents);\n>>  \t} else if (whence == FROM_MERGE) {\n>>  \t\tstruct strbuf m = STRBUF_INIT;\n>> @@ -1637,7 +1635,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>>  \t\tstruct commit_list **pptr = &parents;\n>>  \n>>  \t\tif (!reflog_msg)\n>> -\t\t\treflog_msg = \"commit (merge)\";\n>> +\t\t\tsetenv(\"GIT_REFLOG_ACTION\", \"commit (merge)\", 1);\n>>  \t\tpptr = commit_list_append(current_head, pptr);\n>>  \t\tfp = xfopen(git_path_merge_head(), \"r\");\n>>  \t\twhile (strbuf_getline_lf(&m, fp) != EOF) {\n>> @@ -1660,9 +1658,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>>  \t\t\tparents = reduce_heads(parents);\n>>  \t} else {\n>>  \t\tif (!reflog_msg)\n>> -\t\t\treflog_msg = (whence == FROM_CHERRY_PICK)\n>> -\t\t\t\t\t? \"commit (cherry-pick)\"\n>> -\t\t\t\t\t: \"commit\";\n>> +\t\t\tsetenv(\"GIT_REFLOG_ACTION\", (whence == FROM_CHERRY_PICK)\n>> +\t\t\t\t\t\t? \"commit (cherry-pick)\"\n>> +\t\t\t\t\t\t: \"commit\", 1);\n>>  \t\tcommit_list_insert(current_head, &parents);\n>>  \t}\n>>  \n>> @@ -1707,25 +1705,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>>  \tstrbuf_release(&author_ident);\n>>  \tfree_commit_extra_headers(extra);\n>>  \n>> -\tnl = strchr(sb.buf, '\\n');\n>> -\tif (nl)\n>> -\t\tstrbuf_setlen(&sb, nl + 1 - sb.buf);\n>> -\telse\n>> -\t\tstrbuf_addch(&sb, '\\n');\n>> -\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n>> -\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n>> -\n>> -\ttransaction = ref_transaction_begin(&err);\n>> -\tif (!transaction ||\n>> -\t    ref_transaction_update(transaction, \"HEAD\", oid.hash,\n>> -\t\t\t\t   current_head\n>> -\t\t\t\t   ? current_head->object.oid.hash : null_sha1,\n>> -\t\t\t\t   0, sb.buf, &err) ||\n>> -\t    ref_transaction_commit(transaction, &err)) {\n>> +\tif (update_head (current_head, &oid, &sb, &err)) {\n>>  \t\trollback_index_files();\n>>  \t\tdie(\"%s\", err.buf);\n>>  \t}\n>> -\tref_transaction_free(transaction);\n>>  \n>>  \tunlink(git_path_cherry_pick_head());\n>>  \tunlink(git_path_revert_head());\n>> diff --git a/sequencer.c b/sequencer.c\n>> index 319208afb3de36c97b6c62d4ecf6e641245e7a54..917ad4a16216b30adb2c2c9650217926d8db8ba7 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -1,10 +1,10 @@\n>>  #include \"cache.h\"\n>>  #include \"config.h\"\n>>  #include \"lockfile.h\"\n>> -#include \"sequencer.h\"\n>>  #include \"dir.h\"\n>>  #include \"object.h\"\n>>  #include \"commit.h\"\n>> +#include \"sequencer.h\"\n>>  #include \"tag.h\"\n>>  #include \"run-command.h\"\n>>  #include \"exec_cmd.h\"\n>> @@ -750,6 +750,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n>>  \treturn rest_is_empty(sb, start - sb->buf);\n>>  }\n>>  \n>> +int update_head(const struct commit *old_head, const struct object_id *new_head,\n>> +\t\tconst struct strbuf *msg, struct strbuf *err)\n>> +{\n>> +\tstruct ref_transaction *transaction;\n>> +\tstruct strbuf sb = STRBUF_INIT;\n>> +\tconst char *nl, *reflog_msg;\n>> +\tint ret = 0;\n>> +\n>> +\treflog_msg = getenv(\"GIT_REFLOG_ACTION\");\n>> +\tif (!reflog_msg)\n>> +\t\treflog_msg=\"\";\n>> +\n>> +\tnl = strchr(msg->buf, '\\n');\n>> +\tif (nl) {\n>> +\t\tstrbuf_add(&sb, msg->buf, nl + 1 - msg->buf);\n>> +\t} else {\n>> +\t\tstrbuf_addbuf(&sb, msg);\n>> +\t\tstrbuf_addch(&sb, '\\n');\n>> +\t}\n>> +\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n>> +\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n>> +\n>> +\ttransaction = ref_transaction_begin(err);\n>> +\tif (!transaction ||\n>> +\t    ref_transaction_update(transaction, \"HEAD\", new_head->hash,\n>> +\t\t\t\t   old_head\n>> +\t\t\t\t   ? old_head->object.oid.hash : null_sha1,\n>> +\t\t\t\t   0, sb.buf, err) ||\n>> +\t    ref_transaction_commit(transaction, err)) {\n>> +\t\tret = -1;\n>> +\t}\n>> +\tref_transaction_free(transaction);\n>> +\tstrbuf_release(&sb);\n>> +\n>> +\treturn ret;\n>> +}\n>> +\n>>  static int is_original_commit_empty(struct commit *commit)\n>>  {\n>>  \tconst struct object_id *ptree_oid;\n>> diff --git a/sequencer.h b/sequencer.h\n>> index dd071cfcd82d165bd23726814b74cbf3384e1a17..87edf40e5274d59f48d5af57678100ea220d2c8a 100644\n>> --- a/sequencer.h\n>> +++ b/sequencer.h\n>> @@ -60,4 +60,6 @@ enum cleanup_mode {\n>>  int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);\n>>  int template_untouched(const struct strbuf *sb, const char *template_file,\n>>  \t\t       enum cleanup_mode cleanup_mode);\n>> +int update_head(const struct commit *old_head, const struct object_id *new_head,\n>> +\t\tconst struct strbuf *msg, struct strbuf *err);\n>>  #endif\n\n"},{"id":"330908","messageId":"xmqq7evklnun.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"7e9318b4-1281-11ce-8144-5f7e4449e2e1@talktalk.net","subject":"Re: [RFC PATCH 2/8] commit: move code to update HEAD to libgit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-24T12:41:36Z","receivedAt":"2017-10-24T12:41:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n>> I suspect that it would be sufficient to make update_head() helper\n>> function take the reflog action message as another parameter\n>> instead to fix the above, but there may be other reasons why you\n>> chose to do it this way---I cannot read it in your empty log\n>> message, though.\n>\n> Good point, sorry I should have added some explanation about that. I\n> went with using setenv() rather than passing a reflog message to\n> update_head() as it meant there were no changes needed on the sequencer\n> side as it already sets GIT_REFLOG_ACTION. As the sequencer already sets\n> GIT_REFLOG_ACTION, and git-commit does not fork any subprocesses I don't\n\nDoesn't \"git commit\" run number of hooks?  Is it just the current\ncode does not run any hooks (by chance) after these new setenv()\ncalls are made and we happen to be safe?\n"},{"id":"331916","messageId":"20171106112709.2121-1-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[PATCH v1 0/8] sequencer: dont't fork git commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-06T11:27:01Z","receivedAt":"2017-11-06T11:28:02Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nChanges since the last version\n - reworked the second patch based on Junio's feedback so it no longer sets\n   GIT_REFLOG_ACTION\n - reworded commit messages\n - print_commit_summary() no longer dies but returns an error so the sequencer\n   can exit cleanly\n - reworked the last patch to return errors rather than dying when creating a\n   commit and to use the correct commit message with intermediate squashes.\n - rebased onto next as there were some conflicting changes in builtin/commit.c\n - updated benchmarks compared to v2.15.0 (the percentage difference is\n   essentially the same)\n - fixed some style issues\n\nHere's the summary from the previous version\nThese patches teach the sequencer to create commits without forking\ngit commit when the commit message does not need to be edited. This\nspeeds up cherry picking 10 commits by 26% and picking 10 commits with\nrebase --continue by 44%. The first few patches move bits of\nbuiltin/commit.c to sequencer.c. The last two patches actually\nimplement creating commits in sequencer.c.\n\nPhillip Wood (8):\n  commit: move empty message checks to libgit\n  Add a function to update HEAD after creating a commit\n  commit: move post-rewrite code to libgit\n  commit: move print_commit_summary() to libgit\n  sequencer: don't die in print_commit_summary()\n  sequencer: simplify adding Signed-off-by: trailer\n  sequencer: load commit related config\n  sequencer: try to commit without forking 'git commit'\n\n builtin/commit.c         | 260 ++------------------------\n builtin/rebase--helper.c |  13 +-\n builtin/revert.c         |  15 +-\n sequencer.c              | 478 ++++++++++++++++++++++++++++++++++++++++++++++-\n sequencer.h              |  21 +++\n 5 files changed, 531 insertions(+), 256 deletions(-)\n\n-- \n2.14.3\n\n"},{"id":"331917","messageId":"20171106112709.2121-4-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171106112709.2121-1-phillip.wood@talktalk.net","subject":"[PATCH v1 3/8] commit: move post-rewrite code to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-06T11:27:04Z","receivedAt":"2017-11-06T11:28:48Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can\nbe shared with other commands and add a new function\ncommit_post_rewrite() based on the code in builtin/commit.c that\nencapsulates rewriting notes and running the post-rewrite hook.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/commit.c | 42 +-----------------------------------------\n sequencer.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |  2 ++\n 3 files changed, 50 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex e82754f1fb9f80b8e5b83546d2b2b010daaba522..4f0092b91b9553855fe16864b16b7779b7d1f330 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -31,9 +31,7 @@\n #include \"gpg-interface.h\"\n #include \"column.h\"\n #include \"sequencer.h\"\n-#include \"notes-utils.h\"\n #include \"mailmap.h\"\n-#include \"sigchain.h\"\n \n static const char * const builtin_commit_usage[] = {\n \tN_(\"git commit [<options>] [--] <pathspec>...\"),\n@@ -1493,37 +1491,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \treturn git_status_config(k, v, s);\n }\n \n-static int run_rewrite_hook(const struct object_id *oldoid,\n-\t\t\t    const struct object_id *newoid)\n-{\n-\tstruct child_process proc = CHILD_PROCESS_INIT;\n-\tconst char *argv[3];\n-\tint code;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\targv[0] = find_hook(\"post-rewrite\");\n-\tif (!argv[0])\n-\t\treturn 0;\n-\n-\targv[1] = \"amend\";\n-\targv[2] = NULL;\n-\n-\tproc.argv = argv;\n-\tproc.in = -1;\n-\tproc.stdout_to_stderr = 1;\n-\n-\tcode = start_command(&proc);\n-\tif (code)\n-\t\treturn code;\n-\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n-\tsigchain_push(SIGPIPE, SIG_IGN);\n-\twrite_in_full(proc.in, sb.buf, sb.len);\n-\tclose(proc.in);\n-\tstrbuf_release(&sb);\n-\tsigchain_pop(SIGPIPE);\n-\treturn finish_command(&proc);\n-}\n-\n int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)\n {\n \tstruct argv_array hook_env = ARGV_ARRAY_INIT;\n@@ -1753,14 +1720,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \trerere(0);\n \trun_commit_hook(use_editor, get_index_file(), \"post-commit\", NULL);\n \tif (amend && !no_post_rewrite) {\n-\t\tstruct notes_rewrite_cfg *cfg;\n-\t\tcfg = init_copy_notes_for_rewrite(\"amend\");\n-\t\tif (cfg) {\n-\t\t\t/* we are amending, so current_head is not NULL */\n-\t\t\tcopy_note_for_rewrite(cfg, &current_head->object.oid, &oid);\n-\t\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n-\t\t}\n-\t\trun_rewrite_hook(&current_head->object.oid, &oid);\n+\t\tcommit_post_rewrite(current_head, &oid);\n \t}\n \tif (!quiet)\n \t\tprint_summary(prefix, &oid, !current_head);\ndiff --git a/sequencer.c b/sequencer.c\nindex a85ad659114248b9b5215641cd2911bc4d02e4df..6d9e393f8368ac03d92019189f9fccd6a3ba1d77 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -21,6 +21,8 @@\n #include \"log-tree.h\"\n #include \"wt-status.h\"\n #include \"hashmap.h\"\n+#include \"notes-utils.h\"\n+#include \"sigchain.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -787,6 +789,51 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,\n \treturn ret;\n }\n \n+static int run_rewrite_hook(const struct object_id *oldoid,\n+\t\t\t    const struct object_id *newoid)\n+{\n+\tstruct child_process proc = CHILD_PROCESS_INIT;\n+\tconst char *argv[3];\n+\tint code;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\targv[0] = find_hook(\"post-rewrite\");\n+\tif (!argv[0])\n+\t\treturn 0;\n+\n+\targv[1] = \"amend\";\n+\targv[2] = NULL;\n+\n+\tproc.argv = argv;\n+\tproc.in = -1;\n+\tproc.stdout_to_stderr = 1;\n+\n+\tcode = start_command(&proc);\n+\tif (code)\n+\t\treturn code;\n+\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n+\tsigchain_push(SIGPIPE, SIG_IGN);\n+\twrite_in_full(proc.in, sb.buf, sb.len);\n+\tclose(proc.in);\n+\tstrbuf_release(&sb);\n+\tsigchain_pop(SIGPIPE);\n+\treturn finish_command(&proc);\n+}\n+\n+void commit_post_rewrite(const struct commit *old_head,\n+\t\t\t const struct object_id *new_head)\n+{\n+\tstruct notes_rewrite_cfg *cfg;\n+\n+\tcfg = init_copy_notes_for_rewrite(\"amend\");\n+\tif (cfg) {\n+\t\t/* we are amending, so old_head is not NULL */\n+\t\tcopy_note_for_rewrite(cfg, &old_head->object.oid, new_head);\n+\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n+\t}\n+\trun_rewrite_hook(&old_head->object.oid, new_head);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 1db06caea35bed556dfaabca1c6be8a80857ed5e..5734c8a3d869564354e5193c52bf45c119a8b840 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -71,4 +71,6 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n int update_head(const struct commit *old_head, const struct object_id *new_head,\n \t\tconst char* action, const struct strbuf *msg,\n \t\tstruct strbuf *err);\n+void commit_post_rewrite(const struct commit *current_head,\n+\t\t\t const struct object_id *new_head);\n #endif\n-- \n2.14.3\n\n"},{"id":"331918","messageId":"20171106112709.2121-6-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171106112709.2121-1-phillip.wood@talktalk.net","subject":"[PATCH v1 5/8] sequencer: don't die in print_commit_summary()","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-06T11:27:06Z","receivedAt":"2017-11-06T11:29:23Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nReturn an error rather than dying so that the sequencer can exit\ncleanly once it starts committing without forking 'git commit'\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/commit.c |  3 ++-\n sequencer.c      | 17 +++++++++++------\n sequencer.h      |  4 ++--\n 3 files changed, 15 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex b109feaca11e3e43b1a59dee1868244824eaf345..c924cbac9e0d55941e984b3cce3c1e565cd8cf3c 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1612,7 +1612,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\t\tflags |= SUMMARY_INITIAL_COMMIT;\n \t\tif (author_date_is_interesting())\n \t\t\tflags |= SUMMARY_SHOW_AUTHOR_DATE;\n-\t\tprint_commit_summary(prefix, &oid, flags);\n+\t\tif (print_commit_summary(prefix, &oid, flags))\n+\t\t\texit(128);\n \t}\n \n \tUNLEAK(err);\ndiff --git a/sequencer.c b/sequencer.c\nindex d4f77a5f24c89479a4a18c2b89a3cd4e7ba7ba6c..ae24405c23d021ed7916e5e2d9df6de27f867a2e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -875,8 +875,8 @@ static const char *implicit_ident_advice(void)\n \n }\n \n-void print_commit_summary(const char *prefix, const struct object_id *oid,\n-\t\t\t  int flags)\n+int print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t int flags)\n {\n \tstruct rev_info rev;\n \tstruct commit *commit;\n@@ -885,12 +885,13 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n \tstruct pretty_print_context pctx = {0};\n \tstruct strbuf author_ident = STRBUF_INIT;\n \tstruct strbuf committer_ident = STRBUF_INIT;\n+\tint ret = 0;\n \n \tcommit = lookup_commit(oid);\n \tif (!commit)\n-\t\tdie(_(\"couldn't look up newly created commit\"));\n+\t\treturn error(_(\"couldn't look up newly created commit\"));\n \tif (parse_commit(commit))\n-\t\tdie(_(\"could not parse newly created commit\"));\n+\t\treturn error(_(\"could not parse newly created commit\"));\n \n \tstrbuf_addstr(&format, \"format:%h] %s\");\n \n@@ -934,8 +935,10 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n \tdiff_setup_done(&rev.diffopt);\n \n \thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n-\tif (!head)\n-\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n+\tif (!head) {\n+\t\tret = error_errno(_(\"unable to resolve HEAD after creating commit\"));\n+\t\tgoto out;\n+\t}\n \tif (!strcmp(head, \"HEAD\"))\n \t\thead = _(\"detached HEAD\");\n \telse\n@@ -948,7 +951,9 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n \t\tlog_tree_commit(&rev, commit);\n \t}\n \n+out:\n \tstrbuf_release(&format);\n+\treturn ret;\n }\n \n static int is_original_commit_empty(struct commit *commit)\ndiff --git a/sequencer.h b/sequencer.h\nindex c7989f93fcf08f979f5869cd4ec27f0dd0b88c82..0e3c2c9fd416349fb704a7ebc72c93a9b9a67703 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -76,6 +76,6 @@ void commit_post_rewrite(const struct commit *current_head,\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n-void print_commit_summary(const char *prefix, const struct object_id *oid,\n-\t\t\t  int flags);\n+int print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t int flags);\n #endif\n-- \n2.14.3\n\n"},{"id":"331919","messageId":"20171106112709.2121-7-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171106112709.2121-1-phillip.wood@talktalk.net","subject":"[PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-06T11:27:07Z","receivedAt":"2017-11-06T11:29:43Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd the Signed-off-by: trailer in one place rather than adding it to\nthe message when doing a recursive merge and specifying '--signoff'\nwhen running 'git commit'. This means that if there are conflicts when\nmerging with a strategy other than 'recursive' the Signed-off-by:\ntrailer will be added if the user commits the resolution themselves\nwithout passing '--signoff' to 'git commit'. It also simplifies the\nin-process commit that is about to be added to the sequencer.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n sequencer.c | 8 +++-----\n 1 file changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex ae24405c23d021ed7916e5e2d9df6de27f867a2e..3e4c3bbb265db58df22cfcb5a321fb74d822327e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \t\t\t_(action_name(opts)));\n \trollback_lock_file(&index_lock);\n \n-\tif (opts->signoff)\n-\t\tappend_signoff(msgbuf, 0, 0);\n-\n \tif (!clean)\n \t\tappend_conflicts_hint(msgbuf);\n \n@@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\targv_array_push(&cmd.args, \"--amend\");\n \tif (opts->gpg_sign)\n \t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n-\tif (opts->signoff)\n-\t\targv_array_push(&cmd.args, \"-s\");\n \tif (defmsg)\n \t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n \tif ((flags & CLEANUP_MSG))\n@@ -1347,6 +1342,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n+\tif (opts->signoff)\n+\t\tappend_signoff(&msgbuf, 0, 0);\n+\n \tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n \t\tres = -1;\n \telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n-- \n2.14.3\n\n"},{"id":"331920","messageId":"20171106112709.2121-8-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171106112709.2121-1-phillip.wood@talktalk.net","subject":"[PATCH v1 7/8] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-06T11:27:08Z","receivedAt":"2017-11-06T11:29:54Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nLoad default values for message cleanup and gpg signing of commits in\npreparation for committing without forking 'git commit'.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/rebase--helper.c | 13 ++++++++++++-\n builtin/revert.c         | 15 +++++++++++++--\n sequencer.c              | 33 +++++++++++++++++++++++++++++++++\n sequencer.h              |  1 +\n 4 files changed, 59 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c\nindex f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644\n--- a/builtin/rebase--helper.c\n+++ b/builtin/rebase--helper.c\n@@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n \tNULL\n };\n \n+static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n+{\n+\tint status;\n+\n+\tstatus = git_sequencer_config(k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_default_config(k, v, NULL);\n+}\n+\n int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts = REPLAY_OPTS_INIT;\n@@ -39,7 +50,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_rebase_helper_config, NULL);\n \n \topts.action = REPLAY_INTERACTIVE_REBASE;\n \topts.allow_ff = 1;\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex b9d927eb09c9ed87c84681df1396f4e6d9b13c97..b700dc7f7fd8657ed8cd2450a8537fe98371783f 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n+static int git_revert_config(const char *k, const char *v, void *cb)\n+{\n+\tint status;\n+\n+\tstatus = git_sequencer_config(k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_default_config(k, v, NULL);\n+}\n+\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n@@ -208,7 +219,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tif (isatty(0))\n \t\topts.edit = 1;\n \topts.action = REPLAY_REVERT;\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_revert_config, NULL);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"revert failed\"));\n@@ -221,7 +232,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \topts.action = REPLAY_PICK;\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_revert_config, NULL);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"cherry-pick failed\"));\ndiff --git a/sequencer.c b/sequencer.c\nindex 3e4c3bbb265db58df22cfcb5a321fb74d822327e..b8cf679751449591d6f97102904e060ebee9d7a1 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -688,6 +688,39 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static enum cleanup_mode default_msg_cleanup = CLEANUP_NONE;\n+static char *default_gpg_sign;\n+\n+int git_sequencer_config(const char *k, const char *v, void *cb)\n+{\n+\tif (!strcmp(k, \"commit.cleanup\")) {\n+\t\tint status;\n+\t\tconst char *s;\n+\n+\t\tstatus = git_config_string(&s, k, v);\n+\t\tif (status)\n+\t\t\treturn status;\n+\n+\t\tif (!strcmp(s, \"verbatim\"))\n+\t\t\tdefault_msg_cleanup = CLEANUP_NONE;\n+\t\telse if (!strcmp(s, \"whitespace\"))\n+\t\t\tdefault_msg_cleanup = CLEANUP_SPACE;\n+\t\telse if (!strcmp(s, \"strip\"))\n+\t\t\tdefault_msg_cleanup = CLEANUP_ALL;\n+\t\telse if (!strcmp(s, \"scissors\"))\n+\t\t\tdefault_msg_cleanup = CLEANUP_NONE;\n+\n+\t\treturn status;\n+\t}\n+\n+\tif (!strcmp(k, \"commit.gpgsign\")) {\n+\t\tdefault_gpg_sign = git_config_bool(k, v) ? \"\" : NULL;\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_gpg_config(k, v, NULL);\n+}\n+\n static int rest_is_empty(const struct strbuf *sb, int start)\n {\n \tint i, eol;\ndiff --git a/sequencer.h b/sequencer.h\nindex 0e3c2c9fd416349fb704a7ebc72c93a9b9a67703..944ba50e96d160caac241291377aeece4af2558e 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -57,6 +57,7 @@ extern const char sign_off_header[];\n \n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n+int git_sequencer_config(const char *k, const char *v, void *cb);\n \n enum cleanup_mode {\n \tCLEANUP_SPACE,\n-- \n2.14.3\n\n"},{"id":"331921","messageId":"20171106112709.2121-9-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171106112709.2121-1-phillip.wood@talktalk.net","subject":"[PATCH v1 8/8] sequencer: try to commit without forking 'git commit'","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-06T11:27:09Z","receivedAt":"2017-11-06T11:30:18Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nIf the commit message does not need to be edited then create the\ncommit without forking 'git commit'. Taking the best time of ten runs\nwith a warm cache this reduces the time taken to cherry-pick 10\ncommits by 27% (from 282ms to 204ms), and the time taken by 'git\nrebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on\nmy computer running linux. Some of greater saving for rebase is\nbecause it longer wastes time creating the commit summary just to\nthrow it away.\n\nThe code to create the commit is based on builtin/commit.c. It is\nslightly simplified as it doesn't have to deal with merges and\nmodified so try and return an error rather than dying so that the\nsequencer exits cleanly, as it would when forking 'git commit'.\n\nEven when not forking 'git commit' the commit message is written to a\nfile and CHERRY_PICK_HEAD is created unnecessarily. This could be\neliminated in future. I hacked up a version that does not write these\nfiles and just passed an strbuf (with the wrong message for fixup and\nsquash commands) to do_commit() but I couldn't measure any significant\ntime difference when running cherry-pick or rebase. I think\neliminating the writes properly for rebase would require a bit of\neffort as the code would need to be restructured.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n sequencer.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 168 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b8cf679751449591d6f97102904e060ebee9d7a1..0636d027e9e1cdebaab4802e5becd89e8398a425 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)\n \treturn 0;\n }\n \n+static char *get_author(const char* message)\n+{\n+\tsize_t len;\n+\tconst char *a;\n+\n+\ta = find_commit_header(message, \"author\", &len);\n+\tif (a)\n+\t\treturn xmemdupz(a, len);\n+\n+\treturn NULL;\n+}\n+\n static const char staged_changes_advice[] =\n N_(\"you have staged changes in your working tree\\n\"\n \"If these changes are meant to be squashed into the previous commit, run:\\n\"\n@@ -984,6 +996,151 @@ int print_commit_summary(const char *prefix, const struct object_id *oid,\n \treturn ret;\n }\n \n+static int parse_head(struct commit **head)\n+{\n+\tstruct commit *current_head;\n+\tstruct object_id oid;\n+\n+\tif (get_oid(\"HEAD\", &oid)) {\n+\t\tcurrent_head = NULL;\n+\t} else {\n+\t\tcurrent_head = lookup_commit_reference(&oid);\n+\t\tif (!current_head)\n+\t\t\treturn error(_(\"could not parse HEAD\"));\n+\t\tif (oidcmp(&oid, &current_head->object.oid)) {\n+\t\t\twarning(_(\"HEAD %s is not a commit!\"),\n+\t\t\t\toid_to_hex(&oid));\n+\t\t}\n+\t\tif (parse_commit(current_head))\n+\t\t\treturn error(_(\"could not parse HEAD commit\"));\n+\t}\n+\t*head = current_head;\n+\n+\treturn 0;\n+}\n+\n+static int try_to_commit(struct strbuf *msg, const char *author,\n+\t\t\t struct replay_opts *opts, unsigned int flags,\n+\t\t\t struct object_id *oid)\n+{\n+\tstruct object_id tree;\n+\tstruct commit *current_head;\n+\tstruct commit_list *parents = NULL;\n+\tstruct commit_extra_header *extra = NULL;\n+\tstruct strbuf err = STRBUF_INIT;\n+\tstruct strbuf amend_msg = STRBUF_INIT;\n+\tchar *amend_author = NULL;\n+\tconst char *gpg_sign;\n+\tenum cleanup_mode cleanup;\n+\tint res = 0;\n+\n+\tif (parse_head(&current_head))\n+\t\treturn -1;\n+\n+\tif (flags & AMEND_MSG) {\n+\t\tconst char *exclude_gpgsig[2] = { \"gpgsig\", NULL };\n+\t\tconst char *out_enc = get_commit_output_encoding();\n+\t\tconst char *message = logmsg_reencode(current_head, NULL,\n+\t\t\t\t\t\t      out_enc);\n+\n+\t\tif (!msg) {\n+\t\t\tconst char *body = NULL;\n+\n+\t\t\tfind_commit_subject(message, &body);\n+\t\t\tmsg = &amend_msg;\n+\t\t\tstrbuf_addstr(msg, body);\n+\t\t}\n+\t\tauthor = amend_author = get_author (message);\n+\t\tunuse_commit_buffer(current_head, message);\n+\t\tif (!author) {\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t\t\tgoto out;\n+\t\t}\n+\t\tparents = copy_commit_list(current_head->parents);\n+\t\textra = read_commit_extra_headers(current_head, exclude_gpgsig);\n+\t} else if (current_head) {\n+\t\tcommit_list_insert(current_head, &parents);\n+\t}\n+\n+\tcleanup = (flags & CLEANUP_MSG) ? CLEANUP_ALL : default_msg_cleanup;\n+\tif (cleanup != CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msg, cleanup == CLEANUP_ALL);\n+\tif (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {\n+\t\tres = 1;\n+\t\tgoto out;\n+\t}\n+\n+\tgpg_sign = (opts->gpg_sign) ? opts->gpg_sign : default_gpg_sign;\n+\n+\tif (write_cache_as_tree(tree.hash, 0, NULL)) {\n+\t\tres = error(_(\"git write-tree failed to write a tree\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?\n+\t\t\t\t\t      &current_head->tree->object.oid :\n+\t\t\t\t\t      &empty_tree_oid, &tree)) {\n+\t\tres = 1;\n+\t\tgoto out;\n+\t}\n+\n+\tif (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,\n+\t\t\t\t oid->hash, author, gpg_sign, extra)) {\n+\t\tres = error(_(\"failed to write commit object\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (update_head(current_head, oid, getenv(\"GIT_REFLOG_ACTION\"), msg,\n+\t\t\t&err)){\n+\t\tres = error(\"%s\", err.buf);\n+\t\tgoto out;\n+\t}\n+\n+\tif (flags & AMEND_MSG)\n+\t\tcommit_post_rewrite(current_head, oid);\n+\n+out:\n+\tfree_commit_extra_headers(extra);\n+\tstrbuf_release(&err);\n+\tstrbuf_release(&amend_msg);\n+\tif (amend_author)\n+\t\tfree(amend_author);\n+\n+\treturn res;\n+}\n+\n+static int do_commit(const char *msg_file, const char* author,\n+\t\t     struct replay_opts *opts, unsigned int flags)\n+{\n+\tint res = 1;\n+\n+\tif (~flags & EDIT_MSG && ~flags & VERIFY_MSG) {\n+\t\tstruct object_id oid;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)\n+\t\t\treturn error_errno(_(\"unable to read commit message \"\n+\t\t\t\t\t     \"from '%s'\"),\n+\t\t\t\t\t   msg_file);\n+\n+\t\tres = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,\n+\t\t\t\t    &oid);\n+\t\tstrbuf_release(&sb);\n+\t\tif (res == 0) {\n+\t\t\tunlink(git_path_cherry_pick_head());\n+\t\t\tunlink(git_path_merge_msg());\n+\t\t\tif (!is_rebase_i(opts))\n+\t\t\t\tres = print_commit_summary(NULL, &oid,\n+\t\t\t\t\t\tSUMMARY_SHOW_AUTHOR_DATE);\n+\t\t\treturn res;\n+\t\t}\n+\t}\n+\tif (res == 1)\n+\t\treturn run_git_commit(msg_file, opts, flags);\n+\n+\treturn res;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\n@@ -1235,6 +1392,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \tstruct object_id head;\n \tstruct commit *base, *next, *parent;\n \tconst char *base_label, *next_label;\n+\tchar *author = NULL;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n \tint res, unborn = 0, allow;\n@@ -1350,6 +1508,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t\tstrbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));\n \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n \t\t}\n+\t\tif (!is_fixup (command))\n+\t\t\tauthor = get_author(msg.message);\n \t}\n \n \tif (command == TODO_REWORD)\n@@ -1435,9 +1595,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t} else if (allow)\n \t\tflags |= ALLOW_EMPTY;\n-\tif (!opts->no_commit)\n+\tif (!opts->no_commit) {\n fast_forward_edit:\n-\t\tres = run_git_commit(msg_file, opts, flags);\n+\t\tif (author || command == TODO_REVERT || (flags & AMEND_MSG))\n+\t\t\tres = do_commit(msg_file, author, opts, flags);\n+\t\telse\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t}\n \n \tif (!res && final_fixup) {\n \t\tunlink(rebase_path_fixup_msg());\n@@ -1446,6 +1610,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \n leave:\n \tfree_message(commit, &msg);\n+\tif (author)\n+\t\tfree(author);\n \tupdate_abort_safety_file();\n \n \treturn res;\n-- \n2.14.3\n\n"},{"id":"331922","messageId":"20171106112709.2121-5-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171106112709.2121-1-phillip.wood@talktalk.net","subject":"[PATCH v1 4/8] commit: move print_commit_summary() to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-06T11:27:05Z","receivedAt":"2017-11-06T11:31:10Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove print_commit_summary() from builtin/commit.c to sequencer.c so it\ncan be shared with other commands. The function is modified by\nchanging the last argument to a flag so callers can specify whether\nthey want to show the author date in addition to specifying if this is\nan initial commit.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/commit.c | 128 ++++---------------------------------------------------\n sequencer.c      | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |   5 +++\n 3 files changed, 131 insertions(+), 119 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 4f0092b91b9553855fe16864b16b7779b7d1f330..b109feaca11e3e43b1a59dee1868244824eaf345 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {\n \tNULL\n };\n \n-static const char implicit_ident_advice_noconfig[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly. Run the\\n\"\n-\"following command and follow the instructions in your editor to edit\\n\"\n-\"your configuration file:\\n\"\n-\"\\n\"\n-\"    git config --global --edit\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n-static const char implicit_ident_advice_config[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly:\\n\"\n-\"\\n\"\n-\"    git config --global user.name \\\"Your Name\\\"\\n\"\n-\"    git config --global user.email you@example.com\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n static const char empty_amend_advice[] =\n N_(\"You asked to amend the most recent commit, but doing so would make\\n\"\n \"it empty. You can repeat your command with --allow-empty, or you can\\n\"\n@@ -1370,98 +1345,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n-static const char *implicit_ident_advice(void)\n-{\n-\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n-\tchar *xdg_config = xdg_config_home(\"config\");\n-\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n-\n-\tfree(user_config);\n-\tfree(xdg_config);\n-\n-\tif (config_exists)\n-\t\treturn _(implicit_ident_advice_config);\n-\telse\n-\t\treturn _(implicit_ident_advice_noconfig);\n-\n-}\n-\n-static void print_summary(const char *prefix, const struct object_id *oid,\n-\t\t\t  int initial_commit)\n-{\n-\tstruct rev_info rev;\n-\tstruct commit *commit;\n-\tstruct strbuf format = STRBUF_INIT;\n-\tconst char *head;\n-\tstruct pretty_print_context pctx = {0};\n-\tstruct strbuf author_ident = STRBUF_INIT;\n-\tstruct strbuf committer_ident = STRBUF_INIT;\n-\n-\tcommit = lookup_commit(oid);\n-\tif (!commit)\n-\t\tdie(_(\"couldn't look up newly created commit\"));\n-\tif (parse_commit(commit))\n-\t\tdie(_(\"could not parse newly created commit\"));\n-\n-\tstrbuf_addstr(&format, \"format:%h] %s\");\n-\n-\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n-\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n-\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n-\t\tstrbuf_addstr(&format, \"\\n Author: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n-\t}\n-\tif (author_date_is_interesting()) {\n-\t\tstruct strbuf date = STRBUF_INIT;\n-\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n-\t\tstrbuf_addstr(&format, \"\\n Date: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &date);\n-\t\tstrbuf_release(&date);\n-\t}\n-\tif (!committer_ident_sufficiently_given()) {\n-\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n-\t\tif (advice_implicit_identity) {\n-\t\t\tstrbuf_addch(&format, '\\n');\n-\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n-\t\t}\n-\t}\n-\tstrbuf_release(&author_ident);\n-\tstrbuf_release(&committer_ident);\n-\n-\tinit_revisions(&rev, prefix);\n-\tsetup_revisions(0, NULL, &rev, NULL);\n-\n-\trev.diff = 1;\n-\trev.diffopt.output_format =\n-\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n-\n-\trev.verbose_header = 1;\n-\trev.show_root_diff = 1;\n-\tget_commit_format(format.buf, &rev);\n-\trev.always_show_header = 0;\n-\trev.diffopt.detect_rename = 1;\n-\trev.diffopt.break_opt = 0;\n-\tdiff_setup_done(&rev.diffopt);\n-\n-\thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n-\tif (!head)\n-\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n-\tif (!strcmp(head, \"HEAD\"))\n-\t\thead = _(\"detached HEAD\");\n-\telse\n-\t\tskip_prefix(head, \"refs/heads/\", &head);\n-\tprintf(\"[%s%s \", head, initial_commit ? _(\" (root-commit)\") : \"\");\n-\n-\tif (!log_tree_commit(&rev, commit)) {\n-\t\trev.always_show_header = 1;\n-\t\trev.use_terminator = 1;\n-\t\tlog_tree_commit(&rev, commit);\n-\t}\n-\n-\tstrbuf_release(&format);\n-}\n-\n static int git_commit_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n@@ -1722,8 +1605,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (amend && !no_post_rewrite) {\n \t\tcommit_post_rewrite(current_head, &oid);\n \t}\n-\tif (!quiet)\n-\t\tprint_summary(prefix, &oid, !current_head);\n+\tif (!quiet) {\n+\t\tint flags = 0;\n+\n+\t\tif (!current_head)\n+\t\t\tflags |= SUMMARY_INITIAL_COMMIT;\n+\t\tif (author_date_is_interesting())\n+\t\t\tflags |= SUMMARY_SHOW_AUTHOR_DATE;\n+\t\tprint_commit_summary(prefix, &oid, flags);\n+\t}\n \n \tUNLEAK(err);\n \tUNLEAK(sb);\ndiff --git a/sequencer.c b/sequencer.c\nindex 6d9e393f8368ac03d92019189f9fccd6a3ba1d77..d4f77a5f24c89479a4a18c2b89a3cd4e7ba7ba6c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -834,6 +834,123 @@ void commit_post_rewrite(const struct commit *old_head,\n \trun_rewrite_hook(&old_head->object.oid, new_head);\n }\n \n+static const char implicit_ident_advice_noconfig[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly. Run the\\n\"\n+\"following command and follow the instructions in your editor to edit\\n\"\n+\"your configuration file:\\n\"\n+\"\\n\"\n+\"    git config --global --edit\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char implicit_ident_advice_config[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly:\\n\"\n+\"\\n\"\n+\"    git config --global user.name \\\"Your Name\\\"\\n\"\n+\"    git config --global user.email you@example.com\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char *implicit_ident_advice(void)\n+{\n+\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n+\tchar *xdg_config = xdg_config_home(\"config\");\n+\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n+\n+\tfree(user_config);\n+\tfree(xdg_config);\n+\n+\tif (config_exists)\n+\t\treturn _(implicit_ident_advice_config);\n+\telse\n+\t\treturn _(implicit_ident_advice_noconfig);\n+\n+}\n+\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  int flags)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf format = STRBUF_INIT;\n+\tconst char *head;\n+\tstruct pretty_print_context pctx = {0};\n+\tstruct strbuf author_ident = STRBUF_INIT;\n+\tstruct strbuf committer_ident = STRBUF_INIT;\n+\n+\tcommit = lookup_commit(oid);\n+\tif (!commit)\n+\t\tdie(_(\"couldn't look up newly created commit\"));\n+\tif (parse_commit(commit))\n+\t\tdie(_(\"could not parse newly created commit\"));\n+\n+\tstrbuf_addstr(&format, \"format:%h] %s\");\n+\n+\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n+\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n+\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n+\t\tstrbuf_addstr(&format, \"\\n Author: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n+\t}\n+\tif (flags & SUMMARY_SHOW_AUTHOR_DATE) {\n+\t\tstruct strbuf date = STRBUF_INIT;\n+\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n+\t\tstrbuf_addstr(&format, \"\\n Date: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &date);\n+\t\tstrbuf_release(&date);\n+\t}\n+\tif (!committer_ident_sufficiently_given()) {\n+\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n+\t\tif (advice_implicit_identity) {\n+\t\t\tstrbuf_addch(&format, '\\n');\n+\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n+\t\t}\n+\t}\n+\tstrbuf_release(&author_ident);\n+\tstrbuf_release(&committer_ident);\n+\n+\tinit_revisions(&rev, prefix);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.diff = 1;\n+\trev.diffopt.output_format =\n+\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n+\n+\trev.verbose_header = 1;\n+\trev.show_root_diff = 1;\n+\tget_commit_format(format.buf, &rev);\n+\trev.always_show_header = 0;\n+\trev.diffopt.detect_rename = 1;\n+\trev.diffopt.break_opt = 0;\n+\tdiff_setup_done(&rev.diffopt);\n+\n+\thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n+\tif (!head)\n+\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n+\tif (!strcmp(head, \"HEAD\"))\n+\t\thead = _(\"detached HEAD\");\n+\telse\n+\t\tskip_prefix(head, \"refs/heads/\", &head);\n+\tprintf(\"[%s%s \", head, (flags & SUMMARY_INITIAL_COMMIT) ? _(\" (root-commit)\") : \"\");\n+\n+\tif (!log_tree_commit(&rev, commit)) {\n+\t\trev.always_show_header = 1;\n+\t\trev.use_terminator = 1;\n+\t\tlog_tree_commit(&rev, commit);\n+\t}\n+\n+\tstrbuf_release(&format);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 5734c8a3d869564354e5193c52bf45c119a8b840..c7989f93fcf08f979f5869cd4ec27f0dd0b88c82 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -73,4 +73,9 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,\n \t\tstruct strbuf *err);\n void commit_post_rewrite(const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n+\n+#define SUMMARY_INITIAL_COMMIT   (1 << 0)\n+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  int flags);\n #endif\n-- \n2.14.3\n\n"},{"id":"331923","messageId":"20171106112709.2121-3-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171106112709.2121-1-phillip.wood@talktalk.net","subject":"[PATCH v1 2/8] Add a function to update HEAD after creating a commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-06T11:27:03Z","receivedAt":"2017-11-06T11:34:23Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd update_head() based on the code that updates HEAD after committing\nin builtin/commit.c that can be called by 'git commit' and other\ncommands.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/commit.c | 19 +------------------\n sequencer.c      | 38 +++++++++++++++++++++++++++++++++++++-\n sequencer.h      |  3 +++\n 3 files changed, 41 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex fab512b668af07a1fa927f713eca71c9f783b422..e82754f1fb9f80b8e5b83546d2b2b010daaba522 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1606,13 +1606,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf author_ident = STRBUF_INIT;\n \tconst char *index_file, *reflog_msg;\n-\tchar *nl;\n \tstruct object_id oid;\n \tstruct commit_list *parents = NULL;\n \tstruct stat statbuf;\n \tstruct commit *current_head = NULL;\n \tstruct commit_extra_header *extra = NULL;\n-\tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n@@ -1735,25 +1733,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&author_ident);\n \tfree_commit_extra_headers(extra);\n \n-\tnl = strchr(sb.buf, '\\n');\n-\tif (nl)\n-\t\tstrbuf_setlen(&sb, nl + 1 - sb.buf);\n-\telse\n-\t\tstrbuf_addch(&sb, '\\n');\n-\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n-\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n-\n-\ttransaction = ref_transaction_begin(&err);\n-\tif (!transaction ||\n-\t    ref_transaction_update(transaction, \"HEAD\", &oid,\n-\t\t\t\t   current_head\n-\t\t\t\t   ? &current_head->object.oid : &null_oid,\n-\t\t\t\t   0, sb.buf, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n+\tif (update_head(current_head, &oid, reflog_msg, &sb, &err)) {\n \t\trollback_index_files();\n \t\tdie(\"%s\", err.buf);\n \t}\n-\tref_transaction_free(transaction);\n \n \tunlink(git_path_cherry_pick_head());\n \tunlink(git_path_revert_head());\ndiff --git a/sequencer.c b/sequencer.c\nindex f4a04c913c0d60adbf78d68ca87db739c8e3a280..a85ad659114248b9b5215641cd2911bc4d02e4df 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1,10 +1,10 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"lockfile.h\"\n-#include \"sequencer.h\"\n #include \"dir.h\"\n #include \"object.h\"\n #include \"commit.h\"\n+#include \"sequencer.h\"\n #include \"tag.h\"\n #include \"run-command.h\"\n #include \"exec_cmd.h\"\n@@ -751,6 +751,42 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n \treturn rest_is_empty(sb, start - sb->buf);\n }\n \n+int update_head(const struct commit *old_head, const struct object_id *new_head,\n+\t\tconst char *action, const struct strbuf *msg,\n+\t\tstruct strbuf *err)\n+{\n+\tstruct ref_transaction *transaction;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *nl;\n+\tint ret = 0;\n+\n+\tif (action) {\n+\t\tstrbuf_addstr(&sb, action);\n+\t\tstrbuf_addstr(&sb, \": \");\n+\t}\n+\n+\tnl = strchr(msg->buf, '\\n');\n+\tif (nl) {\n+\t\tstrbuf_add(&sb, msg->buf, nl + 1 - msg->buf);\n+\t} else {\n+\t\tstrbuf_addbuf(&sb, msg);\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t}\n+\n+\ttransaction = ref_transaction_begin(err);\n+\tif (!transaction ||\n+\t    ref_transaction_update(transaction, \"HEAD\", new_head,\n+\t\t\t\t   old_head ? &old_head->object.oid : &null_oid,\n+\t\t\t\t   0, sb.buf, err) ||\n+\t    ref_transaction_commit(transaction, err)) {\n+\t\tret = -1;\n+\t}\n+\tref_transaction_free(transaction);\n+\tstrbuf_release(&sb);\n+\n+\treturn ret;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 65a4b0c25185d7ad5115035abb766d1b95df9a62..1db06caea35bed556dfaabca1c6be8a80857ed5e 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -68,4 +68,7 @@ enum cleanup_mode {\n int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n \t\t       enum cleanup_mode cleanup_mode);\n+int update_head(const struct commit *old_head, const struct object_id *new_head,\n+\t\tconst char* action, const struct strbuf *msg,\n+\t\tstruct strbuf *err);\n #endif\n-- \n2.14.3\n\n"},{"id":"331924","messageId":"20171106112709.2121-2-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171106112709.2121-1-phillip.wood@talktalk.net","subject":"[PATCH v1 1/8] commit: move empty message checks to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-06T11:27:02Z","receivedAt":"2017-11-06T11:35:58Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove the functions that check for empty messages from bulitin/commit.c\nto sequencer.c so they can be shared with other commands. The\nfunctions are refactored to take an explicit cleanup mode and template\nfilename passed by the caller.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/commit.c | 70 +++-----------------------------------------------------\n sequencer.c      | 60 ++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      | 10 ++++++++\n 3 files changed, 73 insertions(+), 67 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 06ab495ae1d2baa5eef6b42eb61909883c681f4c..fab512b668af07a1fa927f713eca71c9f783b422 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -128,12 +128,7 @@ static char *sign_commit;\n  * if editor is used, and only the whitespaces if the message\n  * is specified explicitly.\n  */\n-static enum {\n-\tCLEANUP_SPACE,\n-\tCLEANUP_NONE,\n-\tCLEANUP_SCISSORS,\n-\tCLEANUP_ALL\n-} cleanup_mode;\n+static enum cleanup_mode cleanup_mode;\n static const char *cleanup_arg;\n \n static enum commit_whence whence;\n@@ -983,65 +978,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \treturn 1;\n }\n \n-static int rest_is_empty(struct strbuf *sb, int start)\n-{\n-\tint i, eol;\n-\tconst char *nl;\n-\n-\t/* Check if the rest is just whitespace and Signed-off-by's. */\n-\tfor (i = start; i < sb->len; i++) {\n-\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n-\t\tif (nl)\n-\t\t\teol = nl - sb->buf;\n-\t\telse\n-\t\t\teol = sb->len;\n-\n-\t\tif (strlen(sign_off_header) <= eol - i &&\n-\t\t    starts_with(sb->buf + i, sign_off_header)) {\n-\t\t\ti = eol;\n-\t\t\tcontinue;\n-\t\t}\n-\t\twhile (i < eol)\n-\t\t\tif (!isspace(sb->buf[i++]))\n-\t\t\t\treturn 0;\n-\t}\n-\n-\treturn 1;\n-}\n-\n-/*\n- * Find out if the message in the strbuf contains only whitespace and\n- * Signed-off-by lines.\n- */\n-static int message_is_empty(struct strbuf *sb)\n-{\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\treturn rest_is_empty(sb, 0);\n-}\n-\n-/*\n- * See if the user edited the message in the editor or left what\n- * was in the template intact\n- */\n-static int template_untouched(struct strbuf *sb)\n-{\n-\tstruct strbuf tmpl = STRBUF_INIT;\n-\tconst char *start;\n-\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\n-\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n-\t\treturn 0;\n-\n-\tstrbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);\n-\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n-\t\tstart = sb->buf;\n-\tstrbuf_release(&tmpl);\n-\treturn rest_is_empty(sb, start - sb->buf);\n-}\n-\n static const char *find_author_by_nickname(const char *name)\n {\n \tstruct rev_info revs;\n@@ -1772,12 +1708,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (cleanup_mode != CLEANUP_NONE)\n \t\tstrbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);\n \n-\tif (message_is_empty(&sb) && !allow_empty_message) {\n+\tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit due to empty commit message.\\n\"));\n \t\texit(1);\n \t}\n-\tif (template_untouched(&sb) && !allow_empty_message) {\n+\tif (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit; you did not edit the message.\\n\"));\n \t\texit(1);\ndiff --git a/sequencer.c b/sequencer.c\nindex 1eb2c4669d529485a045de66d6711039d19a2442..f4a04c913c0d60adbf78d68ca87db739c8e3a280 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -691,6 +691,66 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static int rest_is_empty(const struct strbuf *sb, int start)\n+{\n+\tint i, eol;\n+\tconst char *nl;\n+\n+\t/* Check if the rest is just whitespace and Signed-off-by's. */\n+\tfor (i = start; i < sb->len; i++) {\n+\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n+\t\tif (nl)\n+\t\t\teol = nl - sb->buf;\n+\t\telse\n+\t\t\teol = sb->len;\n+\n+\t\tif (strlen(sign_off_header) <= eol - i &&\n+\t\t    starts_with(sb->buf + i, sign_off_header)) {\n+\t\t\ti = eol;\n+\t\t\tcontinue;\n+\t\t}\n+\t\twhile (i < eol)\n+\t\t\tif (!isspace(sb->buf[i++]))\n+\t\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+/*\n+ * Find out if the message in the strbuf contains only whitespace and\n+ * Signed-off-by lines.\n+ */\n+int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode)\n+{\n+\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\treturn rest_is_empty(sb, 0);\n+}\n+\n+/*\n+ * See if the user edited the message in the editor or left what\n+ * was in the template intact\n+ */\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum cleanup_mode cleanup_mode)\n+{\n+\tstruct strbuf tmpl = STRBUF_INIT;\n+\tconst char *start;\n+\n+\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\n+\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n+\t\treturn 0;\n+\n+\tstrbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);\n+\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n+\t\tstart = sb->buf;\n+\tstrbuf_release(&tmpl);\n+\treturn rest_is_empty(sb, start - sb->buf);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..65a4b0c25185d7ad5115035abb766d1b95df9a62 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -58,4 +58,14 @@ extern const char sign_off_header[];\n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n \n+enum cleanup_mode {\n+\tCLEANUP_SPACE,\n+\tCLEANUP_NONE,\n+\tCLEANUP_SCISSORS,\n+\tCLEANUP_ALL\n+};\n+\n+int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum cleanup_mode cleanup_mode);\n #endif\n-- \n2.14.3\n\n"},{"id":"331954","messageId":"alpine.DEB.2.21.1.1711070141110.6482@virtualbox","threadId":"46827","inReplyTo":"20171106112709.2121-2-phillip.wood@talktalk.net","subject":"Re: [PATCH v1 1/8] commit: move empty message checks to libgit","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-11-07T00:43:09Z","receivedAt":"2017-11-07T00:43:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Mon, 6 Nov 2017, Phillip Wood wrote:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n> \n> Move the functions that check for empty messages from bulitin/commit.c\n> to sequencer.c so they can be shared with other commands. The\n> functions are refactored to take an explicit cleanup mode and template\n> filename passed by the caller.\n> \n> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nGood rationale. Just one thing:\n\n> diff --git a/sequencer.h b/sequencer.h\n> index 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..65a4b0c25185d7ad5115035abb766d1b95df9a62 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -58,4 +58,14 @@ extern const char sign_off_header[];\n>  void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n>  void append_conflicts_hint(struct strbuf *msgbuf);\n>  \n> +enum cleanup_mode {\n> +\tCLEANUP_SPACE,\n> +\tCLEANUP_NONE,\n> +\tCLEANUP_SCISSORS,\n> +\tCLEANUP_ALL\n> +};\n\nWhen it was file-local, `cleanup_mode` was okay (although far from great).\nNow that we want to make it more widely available, I fear we have to make\nthe name much longer, e.g. `commit_msg_cleanup_mode`.\n\nCiao,\nDscho\n"},{"id":"331956","messageId":"alpine.DEB.2.21.1.1711070149490.6482@virtualbox","threadId":"46827","inReplyTo":"20171106112709.2121-7-phillip.wood@talktalk.net","subject":"Re: [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-11-07T00:52:41Z","receivedAt":"2017-11-07T00:52:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Mon, 6 Nov 2017, Phillip Wood wrote:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n> \n> Add the Signed-off-by: trailer in one place rather than adding it to\n> the message when doing a recursive merge and specifying '--signoff'\n> when running 'git commit'.\n\nI would find this slightly easier to understand if it was written like\nthis (and separated into its own paragraph):\n\n\tAdd the Signed-off-by: trailer by passing the `--signoff` option\n\tto `git commit` instead of adding the trailer manually (and only\n\twhen the `recursive` merge strategy is in effect).\n\n> This means that if there are conflicts when merging with a strategy\n> other than 'recursive' the Signed-off-by: trailer will be added if the\n> user commits the resolution themselves without passing '--signoff' to\n> 'git commit'.\n\nNice!\n\n> It also simplifies the in-process commit that is about to be added to\n> the sequencer.\n\nAlso nice!\n\nThanks,\nDscho\n"},{"id":"331959","messageId":"alpine.DEB.2.21.1.1711070202040.6482@virtualbox","threadId":"46827","inReplyTo":"20171106112709.2121-8-phillip.wood@talktalk.net","subject":"Re: [PATCH v1 7/8] sequencer: load commit related config","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-11-07T01:02:08Z","receivedAt":"2017-11-07T01:02:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Mon, 6 Nov 2017, Phillip Wood wrote:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n> \n> Load default values for message cleanup and gpg signing of commits in\n> preparation for committing without forking 'git commit'.\n\nNicely explained.\n\n> diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c\n> index f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644\n> --- a/builtin/rebase--helper.c\n> +++ b/builtin/rebase--helper.c\n> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n>  \tNULL\n>  };\n>  \n> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n> +{\n> +\tint status;\n> +\n> +\tstatus = git_sequencer_config(k, v, NULL);\n> +\tif (status)\n> +\t\treturn status;\n> +\n> +\treturn git_default_config(k, v, NULL);\n\nIt's more a matter of taste than anything else, but this one would be a\nlittle bit shorter:\n\n\treturn git_sequencer_config(k, v, NULL) ||\n\t\tgit_default_config(k, v, NULL);\n\nA more important question would be whether this `git_default_config()`\ncall could be folded into `git_sequencer_config()` right away, so that the\nsame pattern does not have to be repeated in rebase--helper as well as in\nrevert/cherry-pick.\n\n> diff --git a/builtin/revert.c b/builtin/revert.c\n> index b9d927eb09c9ed87c84681df1396f4e6d9b13c97..b700dc7f7fd8657ed8cd2450a8537fe98371783f 100644\n> --- a/builtin/revert.c\n> +++ b/builtin/revert.c\n> @@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {\n>  \tNULL\n>  };\n>  \n> +static int git_revert_config(const char *k, const char *v, void *cb)\n\nSeeing as it is used also by `cmd_cherry_pick()`, and that it is\nfile-local anyway, maybe `common_config()` is a better name?\n\nThis point is moot if we can call `git_default_config()` in\n`git_sequencer_config()` directly, though.\n\n> diff --git a/sequencer.c b/sequencer.c\n> index 3e4c3bbb265db58df22cfcb5a321fb74d822327e..b8cf679751449591d6f97102904e060ebee9d7a1 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -688,6 +688,39 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n>  \treturn run_command(&cmd);\n>  }\n>  \n> +static enum cleanup_mode default_msg_cleanup = CLEANUP_NONE;\n> +static char *default_gpg_sign;\n\nI was ready to shout about global state not meshing well with libified\ncode, but as long as we're sure that these values are set only while Git\nexecutes single-threaded, still, it is the correct way to do it: these\nsettings reflect the config, and therefore *are* kinda global (at least\nuntil the day when the submodule fans try to call `git commit` in a\nsubmodule using the `struct repository` data structure).\n\nIn short: this code is good (and I was just describing a little bit of my\nthinking, to demonstrate that I tried to be a diligent reviewer :-)).\n\nThanks,\nDscho\n"},{"id":"331963","messageId":"alpine.DEB.2.21.1.1711070203230.6482@virtualbox","threadId":"46827","inReplyTo":"20171106112709.2121-9-phillip.wood@talktalk.net","subject":"Re: [PATCH v1 8/8] sequencer: try to commit without forking 'git commit'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-11-07T01:36:30Z","receivedAt":"2017-11-07T01:36:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Mon, 6 Nov 2017, Phillip Wood wrote:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n> \n> If the commit message does not need to be edited then create the\n> commit without forking 'git commit'. Taking the best time of ten runs\n> with a warm cache this reduces the time taken to cherry-pick 10\n> commits by 27% (from 282ms to 204ms), and the time taken by 'git\n> rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on\n> my computer running linux. Some of greater saving for rebase is\n> because it longer wastes time creating the commit summary just to\n\nI usually leave the grammar reviews to people who prefer to review grammar\nover code, but in this case I think the \"no\" in \"no longer\" is rather\ncrucial.\n\n> throw it away.\n\nThose are impressive improvements, and I am certain that they will be even\nmore noticable on Windows, where creating processes is a lot more\nexpensive than on Linux (which is the reason why you will find a lot more\nmulti-threaded processes on Windows...).\n\n> The code to create the commit is based on builtin/commit.c. It is\n> slightly simplified as it doesn't have to deal with merges and\n> modified so try and return an error rather than dying so that the\n> sequencer exits cleanly, as it would when forking 'git commit'.\n> \n> Even when not forking 'git commit' the commit message is written to a\n> file and CHERRY_PICK_HEAD is created unnecessarily. This could be\n> eliminated in future. I hacked up a version that does not write these\n> files and just passed an strbuf (with the wrong message for fixup and\n> squash commands) to do_commit() but I couldn't measure any significant\n> time difference when running cherry-pick or rebase. I think\n> eliminating the writes properly for rebase would require a bit of\n> effort as the code would need to be restructured.\n\nTrue. And it totally makes sense to go for the big bucks.\n\n> diff --git a/sequencer.c b/sequencer.c\n> index b8cf679751449591d6f97102904e060ebee9d7a1..0636d027e9e1cdebaab4802e5becd89e8398a425 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)\n>  \treturn 0;\n>  }\n>  \n> +static char *get_author(const char* message)\n> +{\n> +\tsize_t len;\n> +\tconst char *a;\n> +\n> +\ta = find_commit_header(message, \"author\", &len);\n> +\tif (a)\n> +\t\treturn xmemdupz(a, len);\n> +\n> +\treturn NULL;\n> +}\n\nI was surprised that there is no helper for that yet, so I looked, but it\nseems that the three existing callers of `find_commit_header(...,\n\"author\", ...)` want to split the ident line right away, and do not need\nthe duplicated buffer.\n\nIn short: the code added here is necessary.\n\n> @@ -984,6 +996,151 @@ int print_commit_summary(const char *prefix, const struct object_id *oid,\n>  \treturn ret;\n>  }\n>  \n> +static int parse_head(struct commit **head)\n> +{\n> +\tstruct commit *current_head;\n> +\tstruct object_id oid;\n> +\n> +\tif (get_oid(\"HEAD\", &oid)) {\n> +\t\tcurrent_head = NULL;\n> +\t} else {\n> +\t\tcurrent_head = lookup_commit_reference(&oid);\n> +\t\tif (!current_head)\n> +\t\t\treturn error(_(\"could not parse HEAD\"));\n> +\t\tif (oidcmp(&oid, &current_head->object.oid)) {\n> +\t\t\twarning(_(\"HEAD %s is not a commit!\"),\n> +\t\t\t\toid_to_hex(&oid));\n> +\t\t}\n> +\t\tif (parse_commit(current_head))\n> +\t\t\treturn error(_(\"could not parse HEAD commit\"));\n> +\t}\n> +\t*head = current_head;\n> +\n> +\treturn 0;\n> +}\n> +\n> +static int try_to_commit(struct strbuf *msg, const char *author,\n> +\t\t\t struct replay_opts *opts, unsigned int flags,\n> +\t\t\t struct object_id *oid)\n\nSince this is a file-local function, i.e. not in any way tied to a\nprocess exit status, it should probably return -1 in the case of errors,\nas Git does elsewhere, too.\n\n> +{\n> +\tstruct object_id tree;\n> +\tstruct commit *current_head;\n> +\tstruct commit_list *parents = NULL;\n> +\tstruct commit_extra_header *extra = NULL;\n> +\tstruct strbuf err = STRBUF_INIT;\n> +\tstruct strbuf amend_msg = STRBUF_INIT;\n> +\tchar *amend_author = NULL;\n> +\tconst char *gpg_sign;\n> +\tenum cleanup_mode cleanup;\n> +\tint res = 0;\n> +\n> +\tif (parse_head(&current_head))\n> +\t\treturn -1;\n> +\n> +\tif (flags & AMEND_MSG) {\n> +\t\tconst char *exclude_gpgsig[2] = { \"gpgsig\", NULL };\n\nGit's current source code seems to prefer to infer the array length; The\n`2` is unnecessary here.\n\n> +\t\tconst char *out_enc = get_commit_output_encoding();\n> +\t\tconst char *message = logmsg_reencode(current_head, NULL,\n> +\t\t\t\t\t\t      out_enc);\n> +\n> +\t\tif (!msg) {\n> +\t\t\tconst char *body = NULL;\n> +\n> +\t\t\tfind_commit_subject(message, &body);\n\nMaybe `orig_message` would be better here; I expected `body` to refer to\nthe part of the commit message *after* the subject, but reading the code\nof `find_commit_subject()`, I find that it stores the beginning of the\ncommit message.\n\nDunno.\n\n> +\t\t\tmsg = &amend_msg;\n> +\t\t\tstrbuf_addstr(msg, body);\n> +\t\t}\n> +\t\tauthor = amend_author = get_author (message);\n\nPlease lose the space after the function name.\n\n> +\t\tunuse_commit_buffer(current_head, message);\n> +\t\tif (!author) {\n> +\t\t\tres = error(_(\"unable to parse commit author\"));\n> +\t\t\tgoto out;\n> +\t\t}\n> +\t\tparents = copy_commit_list(current_head->parents);\n> +\t\textra = read_commit_extra_headers(current_head, exclude_gpgsig);\n> +\t} else if (current_head) {\n> +\t\tcommit_list_insert(current_head, &parents);\n> +\t}\n> +\n> +\tcleanup = (flags & CLEANUP_MSG) ? CLEANUP_ALL : default_msg_cleanup;\n> +\tif (cleanup != CLEANUP_NONE)\n> +\t\tstrbuf_stripspace(msg, cleanup == CLEANUP_ALL);\n> +\tif (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {\n> +\t\tres = 1;\n> +\t\tgoto out;\n> +\t}\n> +\n> +\tgpg_sign = (opts->gpg_sign) ? opts->gpg_sign : default_gpg_sign;\n\nOthers will probably complain about those extra parentheses. I am not\noffended by them, though.\n\n> +\tif (write_cache_as_tree(tree.hash, 0, NULL)) {\n> +\t\tres = error(_(\"git write-tree failed to write a tree\"));\n> +\t\tgoto out;\n> +\t}\n> +\n> +\tif (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?\n> +\t\t\t\t\t      &current_head->tree->object.oid :\n> +\t\t\t\t\t      &empty_tree_oid, &tree)) {\n\nI'll leave it to Junio to comment on the formatting here.\n\n> +\t\tres = 1;\n> +\t\tgoto out;\n> +\t}\n> +\n> +\tif (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,\n> +\t\t\t\t oid->hash, author, gpg_sign, extra)) {\n> +\t\tres = error(_(\"failed to write commit object\"));\n> +\t\tgoto out;\n> +\t}\n> +\n> +\tif (update_head(current_head, oid, getenv(\"GIT_REFLOG_ACTION\"), msg,\n> +\t\t\t&err)){\n> +\t\tres = error(\"%s\", err.buf);\n> +\t\tgoto out;\n> +\t}\n> +\n> +\tif (flags & AMEND_MSG)\n> +\t\tcommit_post_rewrite(current_head, oid);\n> +\n> +out:\n> +\tfree_commit_extra_headers(extra);\n> +\tstrbuf_release(&err);\n> +\tstrbuf_release(&amend_msg);\n> +\tif (amend_author)\n> +\t\tfree(amend_author);\n\nGit's source code uses the fact that `free(NULL);` is essentially a no-op\n(and certainly allowed) to avoid conditionals in such cases.\n\nThat would make the `if (amend_author)` unnecessary.\n\n> +\n> +\treturn res;\n> +}\n> +\n> +static int do_commit(const char *msg_file, const char* author,\n> +\t\t     struct replay_opts *opts, unsigned int flags)\n> +{\n> +\tint res = 1;\n\nSame as above, the error code should most likely be -1 instead.\n\n> +\tif (~flags & EDIT_MSG && ~flags & VERIFY_MSG) {\n\nI *think* it is more common to write `!(flags & EDIT_MSG)` in Git's source\ncode.\n\n> +\t\tstruct object_id oid;\n> +\t\tstruct strbuf sb = STRBUF_INIT;\n> +\n> +\t\tif (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)\n> +\t\t\treturn error_errno(_(\"unable to read commit message \"\n> +\t\t\t\t\t     \"from '%s'\"),\n> +\t\t\t\t\t   msg_file);\n> +\n> +\t\tres = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,\n> +\t\t\t\t    &oid);\n> +\t\tstrbuf_release(&sb);\n> +\t\tif (res == 0) {\n\nUsually, Git's source code uses `if (!res)` in such cases.\n\n> +\t\t\tunlink(git_path_cherry_pick_head());\n> +\t\t\tunlink(git_path_merge_msg());\n> +\t\t\tif (!is_rebase_i(opts))\n> +\t\t\t\tres = print_commit_summary(NULL, &oid,\n> +\t\t\t\t\t\tSUMMARY_SHOW_AUTHOR_DATE);\n> +\t\t\treturn res;\n> +\t\t}\n\nI wonder whether we should move the `return res;` one line lower, to avoid\nfalling through to call `run_git_commit()` if `try_to_commit()` failed...\n\n> +\t}\n> +\tif (res == 1)\n> +\t\treturn run_git_commit(msg_file, opts, flags);\n\nMaybe this code could be simplified even further by moving this\nconditional to the beginning of the function, as:\n\n\tif ((flags & (EDIT_MSG | VERIFY_MSG)))\n\t\treturn run_git_commit(msg_file, opts, flags);\n\nBut maybe I misunderstood and you really wanted to fall back on\n`run_git_commit()` if `try_to_commit()` failed?\n\n> +\n> +\treturn res;\n> +}\n> +\n>  static int is_original_commit_empty(struct commit *commit)\n>  {\n>  \tconst struct object_id *ptree_oid;\n> @@ -1235,6 +1392,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \tstruct object_id head;\n>  \tstruct commit *base, *next, *parent;\n>  \tconst char *base_label, *next_label;\n> +\tchar *author = NULL;\n>  \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n>  \tstruct strbuf msgbuf = STRBUF_INIT;\n>  \tint res, unborn = 0, allow;\n> @@ -1350,6 +1508,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \t\t\tstrbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));\n>  \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n>  \t\t}\n> +\t\tif (!is_fixup (command))\n> +\t\t\tauthor = get_author(msg.message);\n>  \t}\n>  \n>  \tif (command == TODO_REWORD)\n> @@ -1435,9 +1595,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \t\tgoto leave;\n>  \t} else if (allow)\n>  \t\tflags |= ALLOW_EMPTY;\n> -\tif (!opts->no_commit)\n> +\tif (!opts->no_commit) {\n>  fast_forward_edit:\n> -\t\tres = run_git_commit(msg_file, opts, flags);\n> +\t\tif (author || command == TODO_REVERT || (flags & AMEND_MSG))\n> +\t\t\tres = do_commit(msg_file, author, opts, flags);\n> +\t\telse\n> +\t\t\tres = error(_(\"unable to parse commit author\"));\n\nWould this be a bug here? Or do we expect `get_author()` to possibly fail?\n\n> +\t}\n>  \n>  \tif (!res && final_fixup) {\n>  \t\tunlink(rebase_path_fixup_msg());\n> @@ -1446,6 +1610,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \n>  leave:\n>  \tfree_message(commit, &msg);\n> +\tif (author)\n> +\t\tfree(author);\n\nAs above, please write an unconditional `free(author);` here.\n\nAll in all, this patch series was a nice an pleasant read. I am impressed\nby the performance wins.\n\nThank you very much,\nDscho\n"},{"id":"331968","messageId":"xmqqtvy6g5k8.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171106112709.2121-3-phillip.wood@talktalk.net","subject":"Re: [PATCH v1 2/8] Add a function to update HEAD after creating a commit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-07T02:56:23Z","receivedAt":"2017-11-07T02:56:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> @@ -1735,25 +1733,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>  \tstrbuf_release(&author_ident);\n>  \tfree_commit_extra_headers(extra);\n>  \n> -\tnl = strchr(sb.buf, '\\n');\n> -\tif (nl)\n> -\t\tstrbuf_setlen(&sb, nl + 1 - sb.buf);\n> -\telse\n> -\t\tstrbuf_addch(&sb, '\\n');\n> -\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n> -\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n\nThe old code treated sb (which has the log message we gave to\ncommit_tree_extended() to create the commit) as expendable at this\npoint and (1) truncated it to the title line, and (2) prepended the\nreflog action prefix, so that it can pass it to the ref transaction\ncode to use it as the reflog message.\n\nWhich was quite ugly X-<.\n\n> -\ttransaction = ref_transaction_begin(&err);\n> -\tif (!transaction ||\n> -\t    ref_transaction_update(transaction, \"HEAD\", &oid,\n> -\t\t\t\t   current_head\n> -\t\t\t\t   ? &current_head->object.oid : &null_oid,\n> -\t\t\t\t   0, sb.buf, &err) ||\n> -\t    ref_transaction_commit(transaction, &err)) {\n> +\tif (update_head(current_head, &oid, reflog_msg, &sb, &err)) {\n>  \t\trollback_index_files();\n>  \t\tdie(\"%s\", err.buf);\n>  \t}\n\n> @@ -751,6 +751,42 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n>  \treturn rest_is_empty(sb, start - sb->buf);\n>  }\n>  \n> +int update_head(const struct commit *old_head, const struct object_id *new_head,\n> +\t\tconst char *action, const struct strbuf *msg,\n> +\t\tstruct strbuf *err)\n> +{\n> +\tstruct ref_transaction *transaction;\n> +\tstruct strbuf sb = STRBUF_INIT;\n\nIt no longer is necessary to call this variable \"sb\"; the original\nhad a single instance of strbuf that was reused for different\npurposes and could not give it a more specific name, but we can\nafford to call this one reflog_message or something.\n\n> +\tconst char *nl;\n> +\tint ret = 0;\n> +\n> +\tif (action) {\n> +\t\tstrbuf_addstr(&sb, action);\n> +\t\tstrbuf_addstr(&sb, \": \");\n> +\t}\n> +\n> +\tnl = strchr(msg->buf, '\\n');\n> +\tif (nl) {\n> +\t\tstrbuf_add(&sb, msg->buf, nl + 1 - msg->buf);\n> +\t} else {\n> +\t\tstrbuf_addbuf(&sb, msg);\n> +\t\tstrbuf_addch(&sb, '\\n');\n> +\t}\n\nThe updated code is a lot more natural and straight-forward.  I\nquite like it.\n\nI however do not think update_head() is such a good name for a\nhelper function in the global scope.  builtin/clone.c has a static\none that has quite different semantics with the same name (I am not\nsaying that builtin/clone.c will in the future start including the\nsequencer.h header file; I am pointing out that update_head() is not\na good global name that will be understood by everybody).\n\n> diff --git a/sequencer.h b/sequencer.h\n> index 65a4b0c25185d7ad5115035abb766d1b95df9a62..1db06caea35bed556dfaabca1c6be8a80857ed5e 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -68,4 +68,7 @@ enum cleanup_mode {\n>  int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);\n>  int template_untouched(const struct strbuf *sb, const char *template_file,\n>  \t\t       enum cleanup_mode cleanup_mode);\n> +int update_head(const struct commit *old_head, const struct object_id *new_head,\n> +\t\tconst char* action, const struct strbuf *msg,\n> +\t\tstruct strbuf *err);\n>  #endif\n"},{"id":"331969","messageId":"xmqqpo8ug587.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171106112709.2121-4-phillip.wood@talktalk.net","subject":"Re: [PATCH v1 3/8] commit: move post-rewrite code to libgit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-07T03:03:36Z","receivedAt":"2017-11-07T03:03:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> Move run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can\n> be shared with other commands and add a new function\n> commit_post_rewrite() based on the code in builtin/commit.c that\n> encapsulates rewriting notes and running the post-rewrite hook.\n\nThis, especially the part that rips out the notes rewriting from\nbuiltin/commit.c and moves it to elsewhere, is a bit curious\nseparation of labor.  I guess we'll see why in later steps in the\nseries.\n\nThe change itself looks like a regression-free no-op, which is good.\n"},{"id":"331970","messageId":"xmqqlgjig3lv.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171106112709.2121-5-phillip.wood@talktalk.net","subject":"Re: [PATCH v1 4/8] commit: move print_commit_summary() to libgit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-07T03:38:36Z","receivedAt":"2017-11-07T03:38:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> Move print_commit_summary() from builtin/commit.c to sequencer.c so it\n> can be shared with other commands. The function is modified by\n> changing the last argument to a flag so callers can specify whether\n> they want to show the author date in addition to specifying if this is\n> an initial commit.\n\nA movement of a long function like this one really is easier if you\ndid not make any other unnecessary change in the same patch and then\nmade the change as a follow-up.\n\nThe end result seemed sane.  \n\nDo not use signed int as a collection of bits \"flags\", as it makes\nreaders wonder if you are going to do some clever thing by treating\nthe topmost bit somewhat special (e.g. \"if (flags < 0)\").  Unless\nyou are indeed doing something clever like that, use \"unsigned int\"\ninstead.\n\n"},{"id":"331971","messageId":"xmqq8tfig1rr.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171106112709.2121-6-phillip.wood@talktalk.net","subject":"Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-07T04:18:16Z","receivedAt":"2017-11-07T04:18:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> Return an error rather than dying so that the sequencer can exit\n> cleanly once it starts committing without forking 'git commit'\n>\n> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> ---\n>...\n> @@ -948,7 +951,9 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n>  \t\tlog_tree_commit(&rev, commit);\n>  \t}\n\nIs that call to log_tree_commit() a big elephant in the room?\n\nAll of the die() we see above you are making into error() are rather\nunlikely conditions (e.g. you created a commit, and try to look it\nup immediately after that, and you somehow fail to find it);\nlog_tree_commit() makes tons more object accesses, any of which\nwould be equally likely to fail and die.\n\nIt definitely is a good thing to eventually make a direct in-process\ncall into the commit machinery, and we should aim for that endgame.\n\nAnd this step is going in the right direction, but I am not sure if\nthis made the function safe enough to be called repeatedly from the\nrebase machinery and we are ready to unleash this to the end users\nand tell them it is safe to use it.\n"},{"id":"331972","messageId":"xmqq375qg071.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171106112709.2121-7-phillip.wood@talktalk.net","subject":"Re: [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-07T04:52:18Z","receivedAt":"2017-11-07T04:52:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> diff --git a/sequencer.c b/sequencer.c\n> index ae24405c23d021ed7916e5e2d9df6de27f867a2e..3e4c3bbb265db58df22cfcb5a321fb74d822327e 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n>  \t\t\t_(action_name(opts)));\n>  \trollback_lock_file(&index_lock);\n>  \n> -\tif (opts->signoff)\n> -\t\tappend_signoff(msgbuf, 0, 0);\n> -\n>  \tif (!clean)\n>  \t\tappend_conflicts_hint(msgbuf);\n>  \n\nThis function is called from only one place,  do_pick_commit(), and\nthen the message returned from here in msgbuf is written to\nmerge_msg(), even when the merge conflicted.\n\nAnd when the merge conflicts, sequencer would stop and gives the\ncontrol back to you---the MERGE_MSG file would have had the sign-off\nwhen you conclude the conflict resolution.\n\nWith the new code, we instead add the sign-off before calling the\nfunction to compensate for the above change, so MERGE_MSG file would\nhave the sign-off as before, when the sequencer stops.\n\n> @@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n>  \t\targv_array_push(&cmd.args, \"--amend\");\n>  \tif (opts->gpg_sign)\n>  \t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n> -\tif (opts->signoff)\n> -\t\targv_array_push(&cmd.args, \"-s\");\n>  \tif (defmsg)\n>  \t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n>  \tif ((flags & CLEANUP_MSG))\n\nThis has two callers.  \n\nThe caller in do_pick_commit() is a bit curious; as we saw already,\nthe message file should already have the sign-off and then we used\nto give another \"-s\" here.  Were we depending on \"-s\" to become\nno-op when the last sign-off is by the same person, I wonder?  In\nany case, the removal of \"-s\" from here won't hurt that caller.\n\nThe other caller is commit_staged_changes() which is called when\ndoing \"rebase -i continue\".  I am not quite sure where the contents\nstored in the file rebase_path_message() comes from.  The function\nerror_failed_squash() moves SQUASH_MSG to it and then makes a copy\nof it to MERGE_MSG, but that should only be relevant for squashed\ncommit and no other cases, so...?\n\nI need to block a bit more time to read the relevant code to comment\non this step, especially on this removal.\n\nThanks for working on this, anyway.\n\n> @@ -1347,6 +1342,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \t\t}\n>  \t}\n>  \n> +\tif (opts->signoff)\n> +\t\tappend_signoff(&msgbuf, 0, 0);\n> +\n>  \tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n>  \t\tres = -1;\n>  \telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n"},{"id":"331981","messageId":"alpine.DEB.2.21.1.1711070400160.6482@virtualbox","threadId":"46827","inReplyTo":"xmqqtvy6g5k8.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v1 2/8] Add a function to update HEAD after creating a commit","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-11-07T03:02:26Z","receivedAt":"2017-11-07T09:59:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 7 Nov 2017, Junio C Hamano wrote:\n\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n> > @@ -751,6 +751,42 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n> >  \treturn rest_is_empty(sb, start - sb->buf);\n> >  }\n> >  \n> > +int update_head(const struct commit *old_head, const struct object_id *new_head,\n> > +\t\tconst char *action, const struct strbuf *msg,\n> > +\t\tstruct strbuf *err)\n> > +{\n> \n> [...]\n>\n> I however do not think update_head() is such a good name for a\n> helper function in the global scope.  builtin/clone.c has a static\n> one that has quite different semantics with the same name (I am not\n> saying that builtin/clone.c will in the future start including the\n> sequencer.h header file; I am pointing out that update_head() is not\n> a good global name that will be understood by everybody).\n\nPlease try to always accompany a \"Don't Do That\" by a \"How About This\nInstead\".\n\nIn this case, I could imagine that `update_head_with_reflog()` would be a\nbetter name. If you disagree, I invite you to propose an alternative that\nstrikes your liking.\n\nCiao,\nDscho\n"},{"id":"331982","messageId":"alpine.DEB.2.21.1.1711071101050.6482@virtualbox","threadId":"46827","inReplyTo":"xmqq8tfig1rr.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-11-07T10:24:29Z","receivedAt":"2017-11-07T10:24:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 7 Nov 2017, Junio C Hamano wrote:\n\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n> > From: Phillip Wood <phillip.wood@dunelm.org.uk>\n> >\n> > Return an error rather than dying so that the sequencer can exit\n> > cleanly once it starts committing without forking 'git commit'\n> >\n> > Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> > ---\n> >...\n> > @@ -948,7 +951,9 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n> >  \t\tlog_tree_commit(&rev, commit);\n> >  \t}\n> \n> Is that call to log_tree_commit() a big elephant in the room?\n\nOr maybe not so big an elephant, given that we *already* use it in\nsequencer.c, in the `make_patch()` function. I did spend a substantial\namount of time to try to libify all of those calls back when I worked on\nthe rebase helper.\n\nAlso, bisect.c (not builtin/bisect.c) seems to use that\n`log_tree_commit()` function, so it better be libified.\n\nGranted, those two callers are *pretty* close to the end of the process,\n`bisect_next_all()` is exit(10)-ing right after calling the caller of\n`log_tree_commit()` (*shudder* what were we thinking, allowing this\ncompletely unelegant and inconvenient pattern of exit()ing from\nlibgit.a?), and `make_patch()` is what the sequencer uses to fake the\npatch that it never tried to apply (just to be consistent with\nnon-interactive rebase) just before erroring out.\n\nAnd I am quite embarrassed for not having spotted the\n`maybe_flush_or_die()` call in `log_tree_commit()`.\n\nHowever, from a not-quite-as-quick-as-I-wanted look, it would appear that\nthis call is the only hard `exit()` code path in `log_tree_commit()`, and\nit should be easily fixed.\n\nPlease also note that this is our mess, not Phillip's, as we let these\ndie()/exit() calls creep into libgit.a. It would be no fair to ask Phillip\nto clean it up for us. *We* let this slide for over a decade: 06f59e9f5da\n(Don't fflush(stdout) when it's not helpful, 2007-06-29).\n\n> It definitely is a good thing to eventually make a direct in-process\n> call into the commit machinery, and we should aim for that endgame.\n> \n> And this step is going in the right direction, but I am not sure if\n> this made the function safe enough to be called repeatedly from the\n> rebase machinery and we are ready to unleash this to the end users\n> and tell them it is safe to use it.\n\nWell, holding it up won't fix it faster, it will just delay the fix.\n\nI mean, this is Git, right? This is the same Git where we knowingly\nintroduced a BUG() call into released versions via b1ef400eece\n(setup_git_env: avoid blind fall-back to \".git\", 2016-10-20) that were\n*prone* to hit end users' use cases that we did not think about, just so\nwe could fix those code paths.\n\nSure, we tried to do our best to avoid having end users see those BUG\nreports, by investigating the code paths that eventually call\n`setup_git_env()`. We can just as easily do the same here: investigate as\nwell as is reasonable the code paths that eventually call `die()` or\n`exit()` from `log_tree_commit()`, and then flesh out the remaining\nproblems in production.\n\nCiao,\nDscho\n"},{"id":"331984","messageId":"7529b1ab-a336-3410-b827-f783d6cb5c5e@talktalk.net","threadId":"46827","inReplyTo":"alpine.DEB.2.21.1.1711070202040.6482@virtualbox","subject":"Re: [PATCH v1 7/8] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-07T10:50:43Z","receivedAt":"2017-11-07T10:50:55Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Thanks for looking at these patches\n\nOn 07/11/17 01:02, Johannes Schindelin wrote:\n> Hi Phillip,\n> \n> On Mon, 6 Nov 2017, Phillip Wood wrote:\n> \n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> Load default values for message cleanup and gpg signing of commits in\n>> preparation for committing without forking 'git commit'.\n> \n> Nicely explained.\n> \n>> diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c\n>> index f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644\n>> --- a/builtin/rebase--helper.c\n>> +++ b/builtin/rebase--helper.c\n>> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n>>  \tNULL\n>>  };\n>>  \n>> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n>> +{\n>> +\tint status;\n>> +\n>> +\tstatus = git_sequencer_config(k, v, NULL);\n>> +\tif (status)\n>> +\t\treturn status;\n>> +\n>> +\treturn git_default_config(k, v, NULL);\n> \n> It's more a matter of taste than anything else, but this one would be a\n> little bit shorter:\n> \n> \treturn git_sequencer_config(k, v, NULL) ||\n> \t\tgit_default_config(k, v, NULL);\n\nI'd do that in python or perl but in C it changes the return value which\nmay not matter but if the git convention is to return -1 for errors then\nthis deviates from that.\n\n> A more important question would be whether this `git_default_config()`\n> call could be folded into `git_sequencer_config()` right away, so that the\n> same pattern does not have to be repeated in rebase--helper as well as in\n> revert/cherry-pick.\n\nI kept it separate to be more flexible, imagining that in the future\nthere maybe other commands that want to call git_sequencer_config()\nfollowed by some_othor_config() before git_default_config(). I don't\nhave a strong opinion either way.\n\n>> diff --git a/builtin/revert.c b/builtin/revert.c\n>> index b9d927eb09c9ed87c84681df1396f4e6d9b13c97..b700dc7f7fd8657ed8cd2450a8537fe98371783f 100644\n>> --- a/builtin/revert.c\n>> +++ b/builtin/revert.c\n>> @@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {\n>>  \tNULL\n>>  };\n>>  \n>> +static int git_revert_config(const char *k, const char *v, void *cb)\n> \n> Seeing as it is used also by `cmd_cherry_pick()`, and that it is\n> file-local anyway, maybe `common_config()` is a better name?\n\nYes that would be better, the old name came from the file it was in.\n\n> \n> This point is moot if we can call `git_default_config()` in\n> `git_sequencer_config()` directly, though.\n> \n>> diff --git a/sequencer.c b/sequencer.c\n>> index 3e4c3bbb265db58df22cfcb5a321fb74d822327e..b8cf679751449591d6f97102904e060ebee9d7a1 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -688,6 +688,39 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n>>  \treturn run_command(&cmd);\n>>  }\n>>  \n>> +static enum cleanup_mode default_msg_cleanup = CLEANUP_NONE;\n>> +static char *default_gpg_sign;\n> \n> I was ready to shout about global state not meshing well with libified\n> code, but as long as we're sure that these values are set only while Git\n> executes single-threaded, still, it is the correct way to do it: these\n> settings reflect the config, and therefore *are* kinda global (at least\n> until the day when the submodule fans try to call `git commit` in a\n> submodule using the `struct repository` data structure).\n> \n> In short: this code is good (and I was just describing a little bit of my\n> thinking, to demonstrate that I tried to be a diligent reviewer :-)).\n> \n> Thanks,\n> Dscho\n> \n\n"},{"id":"331985","messageId":"1975badb-7728-45fe-3e8a-8755d85da89a@talktalk.net","threadId":"46827","inReplyTo":"alpine.DEB.2.21.1.1711070203230.6482@virtualbox","subject":"Re: [PATCH v1 8/8] sequencer: try to commit without forking 'git commit'","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-07T11:16:04Z","receivedAt":"2017-11-07T11:16:15Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/11/17 01:36, Johannes Schindelin wrote:\n> Hi Phillip,\n> \n> On Mon, 6 Nov 2017, Phillip Wood wrote:\n> \n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> If the commit message does not need to be edited then create the\n>> commit without forking 'git commit'. Taking the best time of ten runs\n>> with a warm cache this reduces the time taken to cherry-pick 10\n>> commits by 27% (from 282ms to 204ms), and the time taken by 'git\n>> rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on\n>> my computer running linux. Some of greater saving for rebase is\n>> because it longer wastes time creating the commit summary just to\n> \n> I usually leave the grammar reviews to people who prefer to review grammar\n> over code, but in this case I think the \"no\" in \"no longer\" is rather\n> crucial.\n\nYes it is, well spotted\n\n>> throw it away.\n> \n> Those are impressive improvements, and I am certain that they will be even\n> more noticable on Windows, where creating processes is a lot more\n> expensive than on Linux (which is the reason why you will find a lot more\n> multi-threaded processes on Windows...).\n\nYes, I was surprised how big the difference was. Rerunning the same\ncommands arguably over emphasizes the time spent in forking git commit\nas after the first run all the objects are already on disk but I don't\nknow how much difference that makes.\n\n>> The code to create the commit is based on builtin/commit.c. It is\n>> slightly simplified as it doesn't have to deal with merges and\n>> modified so try and return an error rather than dying so that the\n>> sequencer exits cleanly, as it would when forking 'git commit'.\n>>\n>> Even when not forking 'git commit' the commit message is written to a\n>> file and CHERRY_PICK_HEAD is created unnecessarily. This could be\n>> eliminated in future. I hacked up a version that does not write these\n>> files and just passed an strbuf (with the wrong message for fixup and\n>> squash commands) to do_commit() but I couldn't measure any significant\n>> time difference when running cherry-pick or rebase. I think\n>> eliminating the writes properly for rebase would require a bit of\n>> effort as the code would need to be restructured.\n> \n> True. And it totally makes sense to go for the big bucks.\n> \n>> diff --git a/sequencer.c b/sequencer.c\n>> index b8cf679751449591d6f97102904e060ebee9d7a1..0636d027e9e1cdebaab4802e5becd89e8398a425 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)\n>>  \treturn 0;\n>>  }\n>>  \n>> +static char *get_author(const char* message)\n>> +{\n>> +\tsize_t len;\n>> +\tconst char *a;\n>> +\n>> +\ta = find_commit_header(message, \"author\", &len);\n>> +\tif (a)\n>> +\t\treturn xmemdupz(a, len);\n>> +\n>> +\treturn NULL;\n>> +}\n> \n> I was surprised that there is no helper for that yet, so I looked, but it\n> seems that the three existing callers of `find_commit_header(...,\n> \"author\", ...)` want to split the ident line right away, and do not need\n> the duplicated buffer.\n> \n> In short: the code added here is necessary.\n> \n>> @@ -984,6 +996,151 @@ int print_commit_summary(const char *prefix, const struct object_id *oid,\n>>  \treturn ret;\n>>  }\n>>  \n>> +static int parse_head(struct commit **head)\n>> +{\n>> +\tstruct commit *current_head;\n>> +\tstruct object_id oid;\n>> +\n>> +\tif (get_oid(\"HEAD\", &oid)) {\n>> +\t\tcurrent_head = NULL;\n>> +\t} else {\n>> +\t\tcurrent_head = lookup_commit_reference(&oid);\n>> +\t\tif (!current_head)\n>> +\t\t\treturn error(_(\"could not parse HEAD\"));\n>> +\t\tif (oidcmp(&oid, &current_head->object.oid)) {\n>> +\t\t\twarning(_(\"HEAD %s is not a commit!\"),\n>> +\t\t\t\toid_to_hex(&oid));\n>> +\t\t}\n>> +\t\tif (parse_commit(current_head))\n>> +\t\t\treturn error(_(\"could not parse HEAD commit\"));\n>> +\t}\n>> +\t*head = current_head;\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> +static int try_to_commit(struct strbuf *msg, const char *author,\n>> +\t\t\t struct replay_opts *opts, unsigned int flags,\n>> +\t\t\t struct object_id *oid)\n> \n> Since this is a file-local function, i.e. not in any way tied to a\n> process exit status, it should probably return -1 in the case of errors,\n> as Git does elsewhere, too.\n\nIt returns -1 in case of error and 1 if it wants git commit to be run.\nThere are some error messages in git commit that weren't completely\nstraight forward to move to libgit as they were tied up with some git\nstatus config values so I opted just to test for the error condition\nhere and fork git commit to display the error message.\n\n>> +{\n>> +\tstruct object_id tree;\n>> +\tstruct commit *current_head;\n>> +\tstruct commit_list *parents = NULL;\n>> +\tstruct commit_extra_header *extra = NULL;\n>> +\tstruct strbuf err = STRBUF_INIT;\n>> +\tstruct strbuf amend_msg = STRBUF_INIT;\n>> +\tchar *amend_author = NULL;\n>> +\tconst char *gpg_sign;\n>> +\tenum cleanup_mode cleanup;\n>> +\tint res = 0;\n>> +\n>> +\tif (parse_head(&current_head))\n>> +\t\treturn -1;\n>> +\n>> +\tif (flags & AMEND_MSG) {\n>> +\t\tconst char *exclude_gpgsig[2] = { \"gpgsig\", NULL };\n> \n> Git's current source code seems to prefer to infer the array length; The\n> `2` is unnecessary here.\n\nRight, I copied it from builtin/commit.c but I can change it\n\n>> +\t\tconst char *out_enc = get_commit_output_encoding();\n>> +\t\tconst char *message = logmsg_reencode(current_head, NULL,\n>> +\t\t\t\t\t\t      out_enc);\n>> +\n>> +\t\tif (!msg) {\n>> +\t\t\tconst char *body = NULL;\n>> +\n>> +\t\t\tfind_commit_subject(message, &body);\n> \n> Maybe `orig_message` would be better here; I expected `body` to refer to\n> the part of the commit message *after* the subject, but reading the code\n> of `find_commit_subject()`, I find that it stores the beginning of the\n> commit message.\n> \n> Dunno.\n\nYes body is kind of misleading, as that it storing the whole message\nwith the subject as well.\n\n>> +\t\t\tmsg = &amend_msg;\n>> +\t\t\tstrbuf_addstr(msg, body);\n>> +\t\t}\n>> +\t\tauthor = amend_author = get_author (message);\n> \n> Please lose the space after the function name.\n\nWell spotted, I thought I'd seen a space between a function name and '('\nin one of the patches but then I couldn't find it.\n\n>> +\t\tunuse_commit_buffer(current_head, message);\n>> +\t\tif (!author) {\n>> +\t\t\tres = error(_(\"unable to parse commit author\"));\n>> +\t\t\tgoto out;\n>> +\t\t}\n>> +\t\tparents = copy_commit_list(current_head->parents);\n>> +\t\textra = read_commit_extra_headers(current_head, exclude_gpgsig);\n>> +\t} else if (current_head) {\n>> +\t\tcommit_list_insert(current_head, &parents);\n>> +\t}\n>> +\n>> +\tcleanup = (flags & CLEANUP_MSG) ? CLEANUP_ALL : default_msg_cleanup;\n>> +\tif (cleanup != CLEANUP_NONE)\n>> +\t\tstrbuf_stripspace(msg, cleanup == CLEANUP_ALL);\n>> +\tif (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {\n>> +\t\tres = 1;\n>> +\t\tgoto out;\n>> +\t}\n>> +\n>> +\tgpg_sign = (opts->gpg_sign) ? opts->gpg_sign : default_gpg_sign;\n> \n> Others will probably complain about those extra parentheses. I am not\n> offended by them, though.\n> \n>> +\tif (write_cache_as_tree(tree.hash, 0, NULL)) {\n>> +\t\tres = error(_(\"git write-tree failed to write a tree\"));\n>> +\t\tgoto out;\n>> +\t}\n>> +\n>> +\tif (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?\n>> +\t\t\t\t\t      &current_head->tree->object.oid :\n>> +\t\t\t\t\t      &empty_tree_oid, &tree)) {\n> \n> I'll leave it to Junio to comment on the formatting here.\n> \n>> +\t\tres = 1;\n>> +\t\tgoto out;\n>> +\t}\n>> +\n>> +\tif (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,\n>> +\t\t\t\t oid->hash, author, gpg_sign, extra)) {\n>> +\t\tres = error(_(\"failed to write commit object\"));\n>> +\t\tgoto out;\n>> +\t}\n\nLooking more deeply, this can die in write_loose_object(), hopefully\nthat is unlikely. git am commits without forking as well so I think it\nis subject to the same problem.\n\n>> +\n>> +\tif (update_head(current_head, oid, getenv(\"GIT_REFLOG_ACTION\"), msg,\n>> +\t\t\t&err)){\n>> +\t\tres = error(\"%s\", err.buf);\n>> +\t\tgoto out;\n>> +\t}\n>> +\n>> +\tif (flags & AMEND_MSG)\n>> +\t\tcommit_post_rewrite(current_head, oid);\n>> +\n>> +out:\n>> +\tfree_commit_extra_headers(extra);\n>> +\tstrbuf_release(&err);\n>> +\tstrbuf_release(&amend_msg);\n>> +\tif (amend_author)\n>> +\t\tfree(amend_author);\n> \n> Git's source code uses the fact that `free(NULL);` is essentially a no-op\n> (and certainly allowed) to avoid conditionals in such cases.\n> \n> That would make the `if (amend_author)` unnecessary.\n\nThanks, I'll change it\n\n>> +\treturn res;\n>> +}\n>> +\n>> +static int do_commit(const char *msg_file, const char* author,\n>> +\t\t     struct replay_opts *opts, unsigned int flags)\n>> +{\n>> +\tint res = 1;\n> \n> Same as above, the error code should most likely be -1 instead.\n\n1 means run git commit, -1 means there was an error. I should document that.\n\n>> +\tif (~flags & EDIT_MSG && ~flags & VERIFY_MSG) {\n> \n> I *think* it is more common to write `!(flags & EDIT_MSG)` in Git's source\n> code.\n\nYes looking though the other code in sequencer.c that seems to be the\ncommon idiom.\n\n>> +\t\tstruct object_id oid;\n>> +\t\tstruct strbuf sb = STRBUF_INIT;\n>> +\n>> +\t\tif (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)\n>> +\t\t\treturn error_errno(_(\"unable to read commit message \"\n>> +\t\t\t\t\t     \"from '%s'\"),\n>> +\t\t\t\t\t   msg_file);\n>> +\n>> +\t\tres = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,\n>> +\t\t\t\t    &oid);\n>> +\t\tstrbuf_release(&sb);\n>> +\t\tif (res == 0) {\n> \n> Usually, Git's source code uses `if (!res)` in such cases.\n> \n>> +\t\t\tunlink(git_path_cherry_pick_head());\n>> +\t\t\tunlink(git_path_merge_msg());\n>> +\t\t\tif (!is_rebase_i(opts))\n>> +\t\t\t\tres = print_commit_summary(NULL, &oid,\n>> +\t\t\t\t\t\tSUMMARY_SHOW_AUTHOR_DATE);\n>> +\t\t\treturn res;\n>> +\t\t}\n> \n> I wonder whether we should move the `return res;` one line lower, to avoid\n> falling through to call `run_git_commit()` if `try_to_commit()` failed...\n\nNo, that is what I want it to do\n\n>> +\t}\n>> +\tif (res == 1)\n>> +\t\treturn run_git_commit(msg_file, opts, flags);\n> \n> Maybe this code could be simplified even further by moving this\n> conditional to the beginning of the function, as:\n> \n> \tif ((flags & (EDIT_MSG | VERIFY_MSG)))\n> \t\treturn run_git_commit(msg_file, opts, flags);\n> \n> But maybe I misunderstood and you really wanted to fall back on\n> `run_git_commit()` if `try_to_commit()` failed?\n\nExactly\n\n>> +\n>> +\treturn res;\n>> +}\n>> +\n>>  static int is_original_commit_empty(struct commit *commit)\n>>  {\n>>  \tconst struct object_id *ptree_oid;\n>> @@ -1235,6 +1392,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>>  \tstruct object_id head;\n>>  \tstruct commit *base, *next, *parent;\n>>  \tconst char *base_label, *next_label;\n>> +\tchar *author = NULL;\n>>  \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n>>  \tstruct strbuf msgbuf = STRBUF_INIT;\n>>  \tint res, unborn = 0, allow;\n>> @@ -1350,6 +1508,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>>  \t\t\tstrbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));\n>>  \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n>>  \t\t}\n>> +\t\tif (!is_fixup (command))\n>> +\t\t\tauthor = get_author(msg.message);\n>>  \t}\n>>  \n>>  \tif (command == TODO_REWORD)\n>> @@ -1435,9 +1595,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>>  \t\tgoto leave;\n>>  \t} else if (allow)\n>>  \t\tflags |= ALLOW_EMPTY;\n>> -\tif (!opts->no_commit)\n>> +\tif (!opts->no_commit) {\n>>  fast_forward_edit:\n>> -\t\tres = run_git_commit(msg_file, opts, flags);\n>> +\t\tif (author || command == TODO_REVERT || (flags & AMEND_MSG))\n>> +\t\t\tres = do_commit(msg_file, author, opts, flags);\n>> +\t\telse\n>> +\t\t\tres = error(_(\"unable to parse commit author\"));\n> \n> Would this be a bug here? Or do we expect `get_author()` to possibly fail?\n\nI'm not sure, if the commit has somehow been created by a buggy\nimplementation without an author then we should complain. git commit\nreparses the author details to check they look reasonable before reusing\nthem, maybe this should as well rather than just checking that there is\nsomething there.\n\n>> +\t}\n>>  \n>>  \tif (!res && final_fixup) {\n>>  \t\tunlink(rebase_path_fixup_msg());\n>> @@ -1446,6 +1610,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>>  \n>>  leave:\n>>  \tfree_message(commit, &msg);\n>> +\tif (author)\n>> +\t\tfree(author);\n> \n> As above, please write an unconditional `free(author);` here.\n\nWill do\n\n> All in all, this patch series was a nice an pleasant read. I am impressed\n> by the performance wins.\n>\nThanks that's nice to hear, thanks also for taking the time to comment\non them.\n\nBest Wishes\n\nPhillip\n\n> Thank you very much,\n> Dscho\n\n\n\n\n"},{"id":"331992","messageId":"alpine.DEB.2.21.1.1711071509120.6482@virtualbox","threadId":"46827","inReplyTo":"1975badb-7728-45fe-3e8a-8755d85da89a@talktalk.net","subject":"Re: [PATCH v1 8/8] sequencer: try to commit without forking 'git commit'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-11-07T14:09:31Z","receivedAt":"2017-11-07T14:10:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Tue, 7 Nov 2017, Phillip Wood wrote:\n\n> On 07/11/17 01:36, Johannes Schindelin wrote:\n> > \n> > On Mon, 6 Nov 2017, Phillip Wood wrote:\n> > \n> >> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n> >>\n> >> +static int try_to_commit(struct strbuf *msg, const char *author,\n> >> +\t\t\t struct replay_opts *opts, unsigned int flags,\n> >> +\t\t\t struct object_id *oid)\n> > \n> > Since this is a file-local function, i.e. not in any way tied to a\n> > process exit status, it should probably return -1 in the case of errors,\n> > as Git does elsewhere, too.\n> \n> It returns -1 in case of error and 1 if it wants git commit to be run.\n\nAh, that explains a lot! I would like to ask for a code comment above the\n`try_to_commit()` function to explain that, so that future me will avoid\nconfusion when staring at this code. I would also like to ask for a code\ncomment at the `return 1;` statements, saying e.g. We could not commit\nin-process, caller should try forking `git commit`.\n\n> >> +\tif (flags & AMEND_MSG) {\n> >> +\t\tconst char *exclude_gpgsig[2] = { \"gpgsig\", NULL };\n> > \n> > Git's current source code seems to prefer to infer the array length; The\n> > `2` is unnecessary here.\n> \n> Right, I copied it from builtin/commit.c but I can change it\n\nSorry about that. It still would be good to change it, I just wish that\nyou had better code at your fingertips to copy/edit ;-)\n\n> >> +\tif (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,\n> >> +\t\t\t\t oid->hash, author, gpg_sign, extra)) {\n> >> +\t\tres = error(_(\"failed to write commit object\"));\n> >> +\t\tgoto out;\n> >> +\t}\n> \n> Looking more deeply, this can die in write_loose_object(), hopefully\n> that is unlikely. git am commits without forking as well so I think it\n> is subject to the same problem.\n\nYes. We will have to address those die() issues. But not necessarily in\nyour patch series; as I said before, this mess is not your fault.\n\nThanks,\nDscho\n"},{"id":"331993","messageId":"4f35fa04-c0a0-2e1f-5a59-7e18154242e9@talktalk.net","threadId":"46827","inReplyTo":"alpine.DEB.2.21.1.1711070141110.6482@virtualbox","subject":"Re: [PATCH v1 1/8] commit: move empty message checks to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-07T14:24:53Z","receivedAt":"2017-11-07T14:25:02Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/11/17 00:43, Johannes Schindelin wrote:\n> Hi Phillip,\n> \n> On Mon, 6 Nov 2017, Phillip Wood wrote:\n> \n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> Move the functions that check for empty messages from bulitin/commit.c\n>> to sequencer.c so they can be shared with other commands. The\n>> functions are refactored to take an explicit cleanup mode and template\n>> filename passed by the caller.\n>>\n>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> \n> Good rationale. Just one thing:\n> \n>> diff --git a/sequencer.h b/sequencer.h\n>> index 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..65a4b0c25185d7ad5115035abb766d1b95df9a62 100644\n>> --- a/sequencer.h\n>> +++ b/sequencer.h\n>> @@ -58,4 +58,14 @@ extern const char sign_off_header[];\n>>  void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n>>  void append_conflicts_hint(struct strbuf *msgbuf);\n>>  \n>> +enum cleanup_mode {\n>> +\tCLEANUP_SPACE,\n>> +\tCLEANUP_NONE,\n>> +\tCLEANUP_SCISSORS,\n>> +\tCLEANUP_ALL\n>> +};\n> \n> When it was file-local, `cleanup_mode` was okay (although far from great).\n> Now that we want to make it more widely available, I fear we have to make\n> the name much longer, e.g. `commit_msg_cleanup_mode`.\n\nThat's certainly less ambiguous! I'll bite the bullet and extend the name.\n\n"},{"id":"331994","messageId":"5a65cf3c-911b-83e5-ac97-2d7386465de1@talktalk.net","threadId":"46827","inReplyTo":"alpine.DEB.2.21.1.1711070400160.6482@virtualbox","subject":"Re: [PATCH v1 2/8] Add a function to update HEAD after creating a commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-07T14:26:44Z","receivedAt":"2017-11-07T14:26:51Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/11/17 03:02, Johannes Schindelin wrote:\n> Hi Junio,\n> \n> On Tue, 7 Nov 2017, Junio C Hamano wrote:\n> \n>> Phillip Wood <phillip.wood@talktalk.net> writes:\n>>\n>>> @@ -751,6 +751,42 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n>>>  \treturn rest_is_empty(sb, start - sb->buf);\n>>>  }\n>>>  \n>>> +int update_head(const struct commit *old_head, const struct object_id *new_head,\n>>> +\t\tconst char *action, const struct strbuf *msg,\n>>> +\t\tstruct strbuf *err)\n>>> +{\n>>\n>> [...]\n>>\n>> I however do not think update_head() is such a good name for a\n>> helper function in the global scope.  builtin/clone.c has a static\n>> one that has quite different semantics with the same name (I am not\n>> saying that builtin/clone.c will in the future start including the\n>> sequencer.h header file; I am pointing out that update_head() is not\n>> a good global name that will be understood by everybody).\n\nGood point, I'll go with the name Dscho suggests if that's OK with you.\n\n> Please try to always accompany a \"Don't Do That\" by a \"How About This\n> Instead\".\n> \n> In this case, I could imagine that `update_head_with_reflog()` would be a\n> better name. If you disagree, I invite you to propose an alternative that\n> strikes your liking.\n> \n> Ciao,\n> Dscho\n> \n\n"},{"id":"331995","messageId":"a3d29ea7-1939-1595-0978-08a418b2b324@talktalk.net","threadId":"46827","inReplyTo":"xmqqpo8ug587.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v1 3/8] commit: move post-rewrite code to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-07T14:28:50Z","receivedAt":"2017-11-07T14:28:57Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/11/17 03:03, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> Move run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can\n>> be shared with other commands and add a new function\n>> commit_post_rewrite() based on the code in builtin/commit.c that\n>> encapsulates rewriting notes and running the post-rewrite hook.\n> \n> This, especially the part that rips out the notes rewriting from\n> builtin/commit.c and moves it to elsewhere, is a bit curious\n> separation of labor.  I guess we'll see why in later steps in the\n> series.\n\nIt's so the sequencer can have a built in version of 'git commit\n--amend' when processing fixup/squash commands. I'll expand the commit\nmessage to explain why this is needed later.\n\n\n> The change itself looks like a regression-free no-op, which is good.\n> \n\n"},{"id":"331996","messageId":"7849f0e9-24eb-89db-3524-dffdb00d82ad@talktalk.net","threadId":"46827","inReplyTo":"xmqqlgjig3lv.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v1 4/8] commit: move print_commit_summary() to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-07T14:32:40Z","receivedAt":"2017-11-07T14:32:48Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/11/17 03:38, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> Move print_commit_summary() from builtin/commit.c to sequencer.c so it\n>> can be shared with other commands. The function is modified by\n>> changing the last argument to a flag so callers can specify whether\n>> they want to show the author date in addition to specifying if this is\n>> an initial commit.\n> \n> A movement of a long function like this one really is easier if you\n> did not make any other unnecessary change in the same patch and then\n> made the change as a follow-up.\n\nI'm not sure what you mean by unnecessary, the original code called a\nfile-local function author_date_is_interesting(). That had to be changed\nin order to move the code, I guess it would have been clearer to make\nthat change first and then move the modified code to sequencer.c in a\nseparate commit.\n\n> The end result seemed sane.  \n> \n> Do not use signed int as a collection of bits \"flags\", as it makes\n> readers wonder if you are going to do some clever thing by treating\n> the topmost bit somewhat special (e.g. \"if (flags < 0)\").  Unless\n> you are indeed doing something clever like that, use \"unsigned int\"\n> instead.\n> \n\nThanks, I'll change it to an unsigned int.\n\nBest Wishes\n\nPhillip\n"},{"id":"331997","messageId":"b205b5ac-782b-1bf2-793d-fec0b7e29ebd@talktalk.net","threadId":"46827","inReplyTo":"xmqq375qg071.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-07T14:46:41Z","receivedAt":"2017-11-07T14:46:49Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/11/17 04:52, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n>> diff --git a/sequencer.c b/sequencer.c\n>> index ae24405c23d021ed7916e5e2d9df6de27f867a2e..3e4c3bbb265db58df22cfcb5a321fb74d822327e 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n>>  \t\t\t_(action_name(opts)));\n>>  \trollback_lock_file(&index_lock);\n>>  \n>> -\tif (opts->signoff)\n>> -\t\tappend_signoff(msgbuf, 0, 0);\n>> -\n>>  \tif (!clean)\n>>  \t\tappend_conflicts_hint(msgbuf);\n>>  \n> \n> This function is called from only one place,  do_pick_commit(), and\n> then the message returned from here in msgbuf is written to\n> merge_msg(), even when the merge conflicted.\n> \n> And when the merge conflicts, sequencer would stop and gives the\n> control back to you---the MERGE_MSG file would have had the sign-off\n> when you conclude the conflict resolution.\n> \n> With the new code, we instead add the sign-off before calling the\n> function to compensate for the above change, so MERGE_MSG file would\n> have the sign-off as before, when the sequencer stops.\n> \n>> @@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n>>  \t\targv_array_push(&cmd.args, \"--amend\");\n>>  \tif (opts->gpg_sign)\n>>  \t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n>> -\tif (opts->signoff)\n>> -\t\targv_array_push(&cmd.args, \"-s\");\n>>  \tif (defmsg)\n>>  \t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n>>  \tif ((flags & CLEANUP_MSG))\n> \n> This has two callers.  \n> \n> The caller in do_pick_commit() is a bit curious; as we saw already,\n> the message file should already have the sign-off and then we used\n> to give another \"-s\" here.  Were we depending on \"-s\" to become\n> no-op when the last sign-off is by the same person, I wonder?  In\n> any case, the removal of \"-s\" from here won't hurt that caller.\n\nThat was more or less the conclusion I came to as well, though if the\nuser specifies a merge strategy other than recursive, then we were\nrelying on the \"-s\" passed to 'git commit' to add the sign-off. Now we\nadd the sign-off to the message ourselves in that case.\n\n> The other caller is commit_staged_changes() which is called when\n> doing \"rebase -i continue\".  I am not quite sure where the contents\n> stored in the file rebase_path_message() comes from.  The function\n> error_failed_squash() moves SQUASH_MSG to it and then makes a copy\n> of it to MERGE_MSG, but that should only be relevant for squashed\n> commit and no other cases, so...?\n\nThe interactive version of rebase does not support '--signoff' so this\nis moot at the moment. I think that for conflicts with pick/edit/reword\nthen the sign-off is added to MERGE_MSG  and that file is then picked up\nby 'git commit'. For squash/fixup then the sign-off should have already\nbeen added to the commit whose message is used for the first message in\nSQUASH_MSG, but that will not be at the end of the message where we\nexpect Signed-off-by: to be. I'd need to check properly but I suspect we\nalso end up with a Signed-off-by: added at the end of SQUASH_MSG as well.\n\n> \n> I need to block a bit more time to read the relevant code to comment\n> on this step, especially on this removal.\n> \n> Thanks for working on this, anyway.\n\nThanks for looking at these patches and your comments on them, I'll get\non with making the changes you and Johannes have suggested and wait to\nhear from you about this one.\n\nBest Wishes\n\nPhillip\n> \n>> @@ -1347,6 +1342,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>>  \t\t}\n>>  \t}\n>>  \n>> +\tif (opts->signoff)\n>> +\t\tappend_signoff(&msgbuf, 0, 0);\n>> +\n>>  \tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n>>  \t\tres = -1;\n>>  \telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n\n"},{"id":"332001","messageId":"xmqq7ev2dsv2.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"xmqq8tfig1rr.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-07T15:13:37Z","receivedAt":"2017-11-07T15:13:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> And this step is going in the right direction, but I am not sure if\n> this made the function safe enough to be called repeatedly from the\n> rebase machinery and we are ready to unleash this to the end users\n> and tell them it is safe to use it.\n\nAnother possibility perhaps is that the function is safe to reuse\nalready even without this patch, of course ;-).\n"},{"id":"332055","messageId":"xmqq4lq5oa1y.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"7849f0e9-24eb-89db-3524-dffdb00d82ad@talktalk.net","subject":"Re: [PATCH v1 4/8] commit: move print_commit_summary() to libgit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-08T01:04:25Z","receivedAt":"2017-11-08T01:04:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> On 07/11/17 03:38, Junio C Hamano wrote:\n>> Phillip Wood <phillip.wood@talktalk.net> writes:\n>> \n>>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>>\n>>> Move print_commit_summary() from builtin/commit.c to sequencer.c so it\n>>> can be shared with other commands. The function is modified by\n>>> changing the last argument to a flag so callers can specify whether\n>>> they want to show the author date in addition to specifying if this is\n>>> an initial commit.\n>> \n>> A movement of a long function like this one really is easier if you\n>> did not make any other unnecessary change in the same patch and then\n>> made the change as a follow-up.\n>\n> I'm not sure what you mean by unnecessary, the original code called a\n> file-local function author_date_is_interesting().\n\nYes, sorry, I missed the fact that author_date_is_interesting() is\nnot moving (and it is not moving for obvious reasons).\n\n"},{"id":"332159","messageId":"20171110110949.328-2-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171110110949.328-1-phillip.wood@talktalk.net","subject":"[PATCH v2 1/9] t3404: check intermediate squash messages","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T11:09:41Z","receivedAt":"2017-11-10T11:10:12Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nWhen there is more than one squash/fixup command in a row check the\nintermediate messages are correct.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n t/t3404-rebase-interactive.sh | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 6a82d1ed876dd5d1073dc63be8ba5720adbf12e3..9ed0a244e6cdf34c7caca8232f0c0a8cf4864c42 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -453,6 +453,10 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa\n \t\tgit rebase -i $base &&\n \tgit cat-file commit HEAD | sed -e 1,/^\\$/d > actual-squash-fixup &&\n \ttest_cmp expect-squash-fixup actual-squash-fixup &&\n+\tgit cat-file commit HEAD@{2} |\n+\t\tgrep \"^# This is a combination of 3 commits\\.\"  &&\n+\tgit cat-file commit HEAD@{3} |\n+\t\tgrep \"^# This is a combination of 2 commits\\.\"  &&\n \tgit checkout to-be-rebased &&\n \tgit branch -D squash-fixup\n '\n-- \n2.15.0\n\n"},{"id":"332160","messageId":"20171110110949.328-1-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[PATCH v2 0/9] sequencer: dont't fork git commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T11:09:40Z","receivedAt":"2017-11-10T11:10:17Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nThanks for the feedback on v1 I've updated the patches as\nsuggested. See the comments on each patch for what has changed. I've\nadded a patch to the start of the series to test the commit messages\nof intermediate squashes. I've added this as the RFC version of this\nseries did not create these correctly but the test suite passed.\n\nHere's the summary from the previous version\nThese patches teach the sequencer to create commits without forking\ngit commit when the commit message does not need to be edited. This\nspeeds up cherry picking 10 commits by 26% and picking 10 commits with\nrebase --continue by 44%. The first few patches move bits of\nbuiltin/commit.c to sequencer.c. The last two patches actually\nimplement creating commits in sequencer.c.\n\nPhillip Wood (9):\n  t3404: check intermediate squash messages\n  commit: move empty message checks to libgit\n  Add a function to update HEAD after creating a commit\n  commit: move post-rewrite code to libgit\n  commit: move print_commit_summary() to libgit\n  sequencer: don't die in print_commit_summary()\n  sequencer: simplify adding Signed-off-by: trailer\n  sequencer: load commit related config\n  sequencer: try to commit without forking 'git commit'\n\n builtin/commit.c              | 290 +++----------------------\n builtin/rebase--helper.c      |  13 +-\n builtin/revert.c              |  15 +-\n sequencer.c                   | 489 +++++++++++++++++++++++++++++++++++++++++-\n sequencer.h                   |  23 ++\n t/t3404-rebase-interactive.sh |   4 +\n 6 files changed, 565 insertions(+), 269 deletions(-)\n\n-- \n2.15.0\n\n"},{"id":"332161","messageId":"20171110110949.328-3-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171110110949.328-1-phillip.wood@talktalk.net","subject":"[PATCH v2 2/9] commit: move empty message checks to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T11:09:42Z","receivedAt":"2017-11-10T11:10:20Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove the functions that check for empty messages from bulitin/commit.c\nto sequencer.c so they can be shared with other commands. The\nfunctions are refactored to take an explicit cleanup mode and template\nfilename passed by the caller.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - prefix cleanup_mode enum and constants with commit_msg_\n\n builtin/commit.c | 99 +++++++++++---------------------------------------------\n sequencer.c      | 61 ++++++++++++++++++++++++++++++++++\n sequencer.h      | 11 +++++++\n 3 files changed, 91 insertions(+), 80 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 605ea8c0e9663726c64950dba07afe21516c9b26..dbc160c525e7a9249b7c7df2180495a4c7102296 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -128,12 +128,7 @@ static char *sign_commit;\n  * if editor is used, and only the whitespaces if the message\n  * is specified explicitly.\n  */\n-static enum {\n-\tCLEANUP_SPACE,\n-\tCLEANUP_NONE,\n-\tCLEANUP_SCISSORS,\n-\tCLEANUP_ALL\n-} cleanup_mode;\n+static enum commit_msg_cleanup_mode cleanup_mode;\n static const char *cleanup_arg;\n \n static enum commit_whence whence;\n@@ -673,7 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tstruct strbuf sb = STRBUF_INIT;\n \tconst char *hook_arg1 = NULL;\n \tconst char *hook_arg2 = NULL;\n-\tint clean_message_contents = (cleanup_mode != CLEANUP_NONE);\n+\tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n \n \t/* This checks and barfs if author is badly specified */\n@@ -812,7 +807,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -832,14 +827,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t}\n \n \t\tfprintf(s->fp, \"\\n\");\n-\t\tif (cleanup_mode == CLEANUP_ALL)\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t whence == FROM_COMMIT)\n \t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* CLEANUP_SPACE, that is. */\n+\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\n@@ -984,65 +980,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \treturn 1;\n }\n \n-static int rest_is_empty(struct strbuf *sb, int start)\n-{\n-\tint i, eol;\n-\tconst char *nl;\n-\n-\t/* Check if the rest is just whitespace and Signed-off-by's. */\n-\tfor (i = start; i < sb->len; i++) {\n-\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n-\t\tif (nl)\n-\t\t\teol = nl - sb->buf;\n-\t\telse\n-\t\t\teol = sb->len;\n-\n-\t\tif (strlen(sign_off_header) <= eol - i &&\n-\t\t    starts_with(sb->buf + i, sign_off_header)) {\n-\t\t\ti = eol;\n-\t\t\tcontinue;\n-\t\t}\n-\t\twhile (i < eol)\n-\t\t\tif (!isspace(sb->buf[i++]))\n-\t\t\t\treturn 0;\n-\t}\n-\n-\treturn 1;\n-}\n-\n-/*\n- * Find out if the message in the strbuf contains only whitespace and\n- * Signed-off-by lines.\n- */\n-static int message_is_empty(struct strbuf *sb)\n-{\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\treturn rest_is_empty(sb, 0);\n-}\n-\n-/*\n- * See if the user edited the message in the editor or left what\n- * was in the template intact\n- */\n-static int template_untouched(struct strbuf *sb)\n-{\n-\tstruct strbuf tmpl = STRBUF_INIT;\n-\tconst char *start;\n-\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\n-\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n-\t\treturn 0;\n-\n-\tstrbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);\n-\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n-\t\tstart = sb->buf;\n-\tstrbuf_release(&tmpl);\n-\treturn rest_is_empty(sb, start - sb->buf);\n-}\n-\n static const char *find_author_by_nickname(const char *name)\n {\n \tstruct rev_info revs;\n@@ -1214,15 +1151,17 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n \tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\tcleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;\n+\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n \telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\tcleanup_mode = CLEANUP_NONE;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_NONE;\n \telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\tcleanup_mode = CLEANUP_SPACE;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_SPACE;\n \telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\tcleanup_mode = CLEANUP_ALL;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_ALL;\n \telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\tcleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;\n+\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n+\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n \telse\n \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n \n@@ -1749,17 +1688,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (verbose || /* Truncate the message just before the diff, if any. */\n-\t    cleanup_mode == CLEANUP_SCISSORS)\n+\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n \t\tstrbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));\n-\tif (cleanup_mode != CLEANUP_NONE)\n-\t\tstrbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);\n+\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n \n-\tif (message_is_empty(&sb) && !allow_empty_message) {\n+\tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit due to empty commit message.\\n\"));\n \t\texit(1);\n \t}\n-\tif (template_untouched(&sb) && !allow_empty_message) {\n+\tif (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit; you did not edit the message.\\n\"));\n \t\texit(1);\ndiff --git a/sequencer.c b/sequencer.c\nindex 6d027b06c8d8dc69b14d05752637a65aa121ab24..23c250f16cfb7620215bb9c99b8e12d726dc9191 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -691,6 +691,67 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static int rest_is_empty(const struct strbuf *sb, int start)\n+{\n+\tint i, eol;\n+\tconst char *nl;\n+\n+\t/* Check if the rest is just whitespace and Signed-off-by's. */\n+\tfor (i = start; i < sb->len; i++) {\n+\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n+\t\tif (nl)\n+\t\t\teol = nl - sb->buf;\n+\t\telse\n+\t\t\teol = sb->len;\n+\n+\t\tif (strlen(sign_off_header) <= eol - i &&\n+\t\t    starts_with(sb->buf + i, sign_off_header)) {\n+\t\t\ti = eol;\n+\t\t\tcontinue;\n+\t\t}\n+\t\twhile (i < eol)\n+\t\t\tif (!isspace(sb->buf[i++]))\n+\t\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+/*\n+ * Find out if the message in the strbuf contains only whitespace and\n+ * Signed-off-by lines.\n+ */\n+int message_is_empty(const struct strbuf *sb,\n+\t\t     enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\treturn rest_is_empty(sb, 0);\n+}\n+\n+/*\n+ * See if the user edited the message in the editor or left what\n+ * was in the template intact\n+ */\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tstruct strbuf tmpl = STRBUF_INIT;\n+\tconst char *start;\n+\n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\n+\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n+\t\treturn 0;\n+\n+\tstrbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n+\t\tstart = sb->buf;\n+\tstrbuf_release(&tmpl);\n+\treturn rest_is_empty(sb, start - sb->buf);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..82e57713a2940c5d65ccac013c3f42c55cc12baf 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -58,4 +58,15 @@ extern const char sign_off_header[];\n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n \n+enum commit_msg_cleanup_mode {\n+\tCOMMIT_MSG_CLEANUP_SPACE,\n+\tCOMMIT_MSG_CLEANUP_NONE,\n+\tCOMMIT_MSG_CLEANUP_SCISSORS,\n+\tCOMMIT_MSG_CLEANUP_ALL\n+};\n+\n+int message_is_empty(const struct strbuf *sb,\n+\t\t     enum commit_msg_cleanup_mode cleanup_mode);\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum commit_msg_cleanup_mode cleanup_mode);\n #endif\n-- \n2.15.0\n\n"},{"id":"332162","messageId":"20171110110949.328-7-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171110110949.328-1-phillip.wood@talktalk.net","subject":"[PATCH v2 6/9] sequencer: don't die in print_commit_summary()","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T11:09:46Z","receivedAt":"2017-11-10T11:10:25Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nReturn an error rather than dying so that the sequencer can exit\ncleanly once it starts committing without forking 'git commit'\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/commit.c |  3 ++-\n sequencer.c      | 17 +++++++++++------\n sequencer.h      |  4 ++--\n 3 files changed, 15 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 22d260197e84a828f984575cfa92789391531ca3..e924d8a14c8532a78d072420aeb662ce616181a4 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1598,7 +1598,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\t\tflags |= SUMMARY_INITIAL_COMMIT;\n \t\tif (author_date_is_interesting())\n \t\t\tflags |= SUMMARY_SHOW_AUTHOR_DATE;\n-\t\tprint_commit_summary(prefix, &oid, flags);\n+\t\tif (print_commit_summary(prefix, &oid, flags))\n+\t\t\texit(128);\n \t}\n \n \tUNLEAK(err);\ndiff --git a/sequencer.c b/sequencer.c\nindex 03207db71821c0c3d1c442d3b01b11e3b010367e..61bb75352202cf896ffec41e972f366e94d569c1 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -877,8 +877,8 @@ static const char *implicit_ident_advice(void)\n \n }\n \n-void print_commit_summary(const char *prefix, const struct object_id *oid,\n-\t\t\t  unsigned int flags)\n+int print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t unsigned int flags)\n {\n \tstruct rev_info rev;\n \tstruct commit *commit;\n@@ -887,12 +887,13 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n \tstruct pretty_print_context pctx = {0};\n \tstruct strbuf author_ident = STRBUF_INIT;\n \tstruct strbuf committer_ident = STRBUF_INIT;\n+\tint ret = 0;\n \n \tcommit = lookup_commit(oid);\n \tif (!commit)\n-\t\tdie(_(\"couldn't look up newly created commit\"));\n+\t\treturn error(_(\"couldn't look up newly created commit\"));\n \tif (parse_commit(commit))\n-\t\tdie(_(\"could not parse newly created commit\"));\n+\t\treturn error(_(\"could not parse newly created commit\"));\n \n \tstrbuf_addstr(&format, \"format:%h] %s\");\n \n@@ -936,8 +937,10 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n \tdiff_setup_done(&rev.diffopt);\n \n \thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n-\tif (!head)\n-\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n+\tif (!head) {\n+\t\tret = error_errno(_(\"unable to resolve HEAD after creating commit\"));\n+\t\tgoto out;\n+\t}\n \tif (!strcmp(head, \"HEAD\"))\n \t\thead = _(\"detached HEAD\");\n \telse\n@@ -950,7 +953,9 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n \t\tlog_tree_commit(&rev, commit);\n \t}\n \n+out:\n \tstrbuf_release(&format);\n+\treturn ret;\n }\n \n static int is_original_commit_empty(struct commit *commit)\ndiff --git a/sequencer.h b/sequencer.h\nindex 8e0dbe59bbc1ccd3847454faf8bd09d5ca1ff93a..e4040cac08cb69c9b91db3fe77ae392c8d6d0f50 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -78,6 +78,6 @@ void commit_post_rewrite(const struct commit *current_head,\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n-void print_commit_summary(const char *prefix, const struct object_id *oid,\n-\t\t\t  unsigned int flags);\n+int print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t unsigned int flags);\n #endif\n-- \n2.15.0\n\n"},{"id":"332163","messageId":"20171110110949.328-4-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171110110949.328-1-phillip.wood@talktalk.net","subject":"[PATCH v2 3/9] Add a function to update HEAD after creating a commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T11:09:43Z","receivedAt":"2017-11-10T11:10:27Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd update_head() based on the code that updates HEAD after committing\nin builtin/commit.c that can be called by 'git commit' and other\ncommands.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - rename update_head() to update_head_with_reflog()\n\n builtin/commit.c | 20 ++------------------\n sequencer.c      | 39 ++++++++++++++++++++++++++++++++++++++-\n sequencer.h      |  4 ++++\n 3 files changed, 44 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex dbc160c525e7a9249b7c7df2180495a4c7102296..7c28144446644a665f7b7d8f4b6c0a5855aef01b 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1591,13 +1591,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf author_ident = STRBUF_INIT;\n \tconst char *index_file, *reflog_msg;\n-\tchar *nl;\n \tstruct object_id oid;\n \tstruct commit_list *parents = NULL;\n \tstruct stat statbuf;\n \tstruct commit *current_head = NULL;\n \tstruct commit_extra_header *extra = NULL;\n-\tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n@@ -1720,25 +1718,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&author_ident);\n \tfree_commit_extra_headers(extra);\n \n-\tnl = strchr(sb.buf, '\\n');\n-\tif (nl)\n-\t\tstrbuf_setlen(&sb, nl + 1 - sb.buf);\n-\telse\n-\t\tstrbuf_addch(&sb, '\\n');\n-\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n-\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n-\n-\ttransaction = ref_transaction_begin(&err);\n-\tif (!transaction ||\n-\t    ref_transaction_update(transaction, \"HEAD\", &oid,\n-\t\t\t\t   current_head\n-\t\t\t\t   ? &current_head->object.oid : &null_oid,\n-\t\t\t\t   0, sb.buf, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n+\tif (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,\n+\t\t\t\t    &err)) {\n \t\trollback_index_files();\n \t\tdie(\"%s\", err.buf);\n \t}\n-\tref_transaction_free(transaction);\n \n \tunlink(git_path_cherry_pick_head());\n \tunlink(git_path_revert_head());\ndiff --git a/sequencer.c b/sequencer.c\nindex 23c250f16cfb7620215bb9c99b8e12d726dc9191..fcd8e92531a3fb1f0bc1294925e87b3624ada909 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1,10 +1,10 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"lockfile.h\"\n-#include \"sequencer.h\"\n #include \"dir.h\"\n #include \"object.h\"\n #include \"commit.h\"\n+#include \"sequencer.h\"\n #include \"tag.h\"\n #include \"run-command.h\"\n #include \"exec_cmd.h\"\n@@ -752,6 +752,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n \treturn rest_is_empty(sb, start - sb->buf);\n }\n \n+int update_head_with_reflog(const struct commit *old_head,\n+\t\t\t    const struct object_id *new_head,\n+\t\t\t    const char *action, const struct strbuf *msg,\n+\t\t\t    struct strbuf *err)\n+{\n+\tstruct ref_transaction *transaction;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *nl;\n+\tint ret = 0;\n+\n+\tif (action) {\n+\t\tstrbuf_addstr(&sb, action);\n+\t\tstrbuf_addstr(&sb, \": \");\n+\t}\n+\n+\tnl = strchr(msg->buf, '\\n');\n+\tif (nl) {\n+\t\tstrbuf_add(&sb, msg->buf, nl + 1 - msg->buf);\n+\t} else {\n+\t\tstrbuf_addbuf(&sb, msg);\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t}\n+\n+\ttransaction = ref_transaction_begin(err);\n+\tif (!transaction ||\n+\t    ref_transaction_update(transaction, \"HEAD\", new_head,\n+\t\t\t\t   old_head ? &old_head->object.oid : &null_oid,\n+\t\t\t\t   0, sb.buf, err) ||\n+\t    ref_transaction_commit(transaction, err)) {\n+\t\tret = -1;\n+\t}\n+\tref_transaction_free(transaction);\n+\tstrbuf_release(&sb);\n+\n+\treturn ret;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 82e57713a2940c5d65ccac013c3f42c55cc12baf..82035ced129d35ff8ba64710477531a9a713b631 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -69,4 +69,8 @@ int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n \t\t       enum commit_msg_cleanup_mode cleanup_mode);\n+int update_head_with_reflog(const struct commit *old_head,\n+\t\t\t    const struct object_id *new_head,\n+\t\t\t    const char* action, const struct strbuf *msg,\n+\t\t\t    struct strbuf *err);\n #endif\n-- \n2.15.0\n\n"},{"id":"332164","messageId":"20171110110949.328-5-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171110110949.328-1-phillip.wood@talktalk.net","subject":"[PATCH v2 4/9] commit: move post-rewrite code to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T11:09:44Z","receivedAt":"2017-11-10T11:10:29Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can\nbe shared with other commands and add a new function\ncommit_post_rewrite() based on the code in builtin/commit.c that\nencapsulates rewriting notes and running the post-rewrite hook. Once\nthe sequencer learns how to create commits without forking 'git\ncommit' these functions will be used when squashing commits.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - reword commit message to explain why the code is being moved\n\n builtin/commit.c | 42 +-----------------------------------------\n sequencer.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |  2 ++\n 3 files changed, 50 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 7c28144446644a665f7b7d8f4b6c0a5855aef01b..3bc5dff2c00c9556e6c032381d5baf18246bf8dc 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -31,9 +31,7 @@\n #include \"gpg-interface.h\"\n #include \"column.h\"\n #include \"sequencer.h\"\n-#include \"notes-utils.h\"\n #include \"mailmap.h\"\n-#include \"sigchain.h\"\n \n static const char * const builtin_commit_usage[] = {\n \tN_(\"git commit [<options>] [--] <pathspec>...\"),\n@@ -1478,37 +1476,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \treturn git_status_config(k, v, s);\n }\n \n-static int run_rewrite_hook(const struct object_id *oldoid,\n-\t\t\t    const struct object_id *newoid)\n-{\n-\tstruct child_process proc = CHILD_PROCESS_INIT;\n-\tconst char *argv[3];\n-\tint code;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\targv[0] = find_hook(\"post-rewrite\");\n-\tif (!argv[0])\n-\t\treturn 0;\n-\n-\targv[1] = \"amend\";\n-\targv[2] = NULL;\n-\n-\tproc.argv = argv;\n-\tproc.in = -1;\n-\tproc.stdout_to_stderr = 1;\n-\n-\tcode = start_command(&proc);\n-\tif (code)\n-\t\treturn code;\n-\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n-\tsigchain_push(SIGPIPE, SIG_IGN);\n-\twrite_in_full(proc.in, sb.buf, sb.len);\n-\tclose(proc.in);\n-\tstrbuf_release(&sb);\n-\tsigchain_pop(SIGPIPE);\n-\treturn finish_command(&proc);\n-}\n-\n int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)\n {\n \tstruct argv_array hook_env = ARGV_ARRAY_INIT;\n@@ -1739,14 +1706,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \trerere(0);\n \trun_commit_hook(use_editor, get_index_file(), \"post-commit\", NULL);\n \tif (amend && !no_post_rewrite) {\n-\t\tstruct notes_rewrite_cfg *cfg;\n-\t\tcfg = init_copy_notes_for_rewrite(\"amend\");\n-\t\tif (cfg) {\n-\t\t\t/* we are amending, so current_head is not NULL */\n-\t\t\tcopy_note_for_rewrite(cfg, &current_head->object.oid, &oid);\n-\t\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n-\t\t}\n-\t\trun_rewrite_hook(&current_head->object.oid, &oid);\n+\t\tcommit_post_rewrite(current_head, &oid);\n \t}\n \tif (!quiet)\n \t\tprint_summary(prefix, &oid, !current_head);\ndiff --git a/sequencer.c b/sequencer.c\nindex fcd8e92531a3fb1f0bc1294925e87b3624ada909..5529e5df1cbe9045b000ecbff6c28a0bee736ccc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -21,6 +21,8 @@\n #include \"log-tree.h\"\n #include \"wt-status.h\"\n #include \"hashmap.h\"\n+#include \"notes-utils.h\"\n+#include \"sigchain.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -789,6 +791,51 @@ int update_head_with_reflog(const struct commit *old_head,\n \treturn ret;\n }\n \n+static int run_rewrite_hook(const struct object_id *oldoid,\n+\t\t\t    const struct object_id *newoid)\n+{\n+\tstruct child_process proc = CHILD_PROCESS_INIT;\n+\tconst char *argv[3];\n+\tint code;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\targv[0] = find_hook(\"post-rewrite\");\n+\tif (!argv[0])\n+\t\treturn 0;\n+\n+\targv[1] = \"amend\";\n+\targv[2] = NULL;\n+\n+\tproc.argv = argv;\n+\tproc.in = -1;\n+\tproc.stdout_to_stderr = 1;\n+\n+\tcode = start_command(&proc);\n+\tif (code)\n+\t\treturn code;\n+\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n+\tsigchain_push(SIGPIPE, SIG_IGN);\n+\twrite_in_full(proc.in, sb.buf, sb.len);\n+\tclose(proc.in);\n+\tstrbuf_release(&sb);\n+\tsigchain_pop(SIGPIPE);\n+\treturn finish_command(&proc);\n+}\n+\n+void commit_post_rewrite(const struct commit *old_head,\n+\t\t\t const struct object_id *new_head)\n+{\n+\tstruct notes_rewrite_cfg *cfg;\n+\n+\tcfg = init_copy_notes_for_rewrite(\"amend\");\n+\tif (cfg) {\n+\t\t/* we are amending, so old_head is not NULL */\n+\t\tcopy_note_for_rewrite(cfg, &old_head->object.oid, new_head);\n+\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n+\t}\n+\trun_rewrite_hook(&old_head->object.oid, new_head);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 82035ced129d35ff8ba64710477531a9a713b631..6a69e4a7a42551cbc798b8eb93e447c0f50b234f 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -73,4 +73,6 @@ int update_head_with_reflog(const struct commit *old_head,\n \t\t\t    const struct object_id *new_head,\n \t\t\t    const char* action, const struct strbuf *msg,\n \t\t\t    struct strbuf *err);\n+void commit_post_rewrite(const struct commit *current_head,\n+\t\t\t const struct object_id *new_head);\n #endif\n-- \n2.15.0\n\n"},{"id":"332165","messageId":"20171110110949.328-10-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171110110949.328-1-phillip.wood@talktalk.net","subject":"[PATCH v2 9/9] sequencer: try to commit without forking 'git commit'","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T11:09:49Z","receivedAt":"2017-11-10T11:10:31Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nIf the commit message does not need to be edited then create the\ncommit without forking 'git commit'. Taking the best time of ten runs\nwith a warm cache this reduces the time taken to cherry-pick 10\ncommits by 27% (from 282ms to 204ms), and the time taken by 'git\nrebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on\nmy computer running linux. Some of greater saving for rebase is\nbecause it no longer wastes time creating the commit summary just to\nthrow it away.\n\nThe code to create the commit is based on builtin/commit.c. It is\nsimplified as it doesn't have to deal with merges and modified so that\nit does not die but returns an error to make sure the sequencer exits\ncleanly, as it would when forking 'git commit'\n\nEven when not forking 'git commit' the commit message is written to a\nfile and CHERRY_PICK_HEAD is created unnecessarily. This could be\neliminated in future. I hacked up a version that does not write these\nfiles and just passed an strbuf (with the wrong message for fixup and\nsquash commands) to do_commit() but I couldn't measure any significant\ntime difference when running cherry-pick or rebase. I think\neliminating the writes properly for rebase would require a bit of\neffort as the code would need to be restructured.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - added comments to explain return value of try_to_commit()\n     - removed unnecessary NULL tests before calling free()\n     - style cleanups\n     - corrected commit message\n     - prefixed cleanup_mode constants to reflect the changes to patch 2\n       in this series\n\n sequencer.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 176 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 0fd98391cb7dfcff6976271f44a56e897593e2c0..1f65e82696043fd9fb5ebb9f2b78445aa5a9599e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)\n \treturn 0;\n }\n \n+static char *get_author(const char* message)\n+{\n+\tsize_t len;\n+\tconst char *a;\n+\n+\ta = find_commit_header(message, \"author\", &len);\n+\tif (a)\n+\t\treturn xmemdupz(a, len);\n+\n+\treturn NULL;\n+}\n+\n static const char staged_changes_advice[] =\n N_(\"you have staged changes in your working tree\\n\"\n \"If these changes are meant to be squashed into the previous commit, run:\\n\"\n@@ -987,6 +999,160 @@ int print_commit_summary(const char *prefix, const struct object_id *oid,\n \treturn ret;\n }\n \n+static int parse_head(struct commit **head)\n+{\n+\tstruct commit *current_head;\n+\tstruct object_id oid;\n+\n+\tif (get_oid(\"HEAD\", &oid)) {\n+\t\tcurrent_head = NULL;\n+\t} else {\n+\t\tcurrent_head = lookup_commit_reference(&oid);\n+\t\tif (!current_head)\n+\t\t\treturn error(_(\"could not parse HEAD\"));\n+\t\tif (oidcmp(&oid, &current_head->object.oid)) {\n+\t\t\twarning(_(\"HEAD %s is not a commit!\"),\n+\t\t\t\toid_to_hex(&oid));\n+\t\t}\n+\t\tif (parse_commit(current_head))\n+\t\t\treturn error(_(\"could not parse HEAD commit\"));\n+\t}\n+\t*head = current_head;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Try to commit without forking 'git commit'. In some cases we need\n+ * to run 'git commit' to display an error message\n+ *\n+ * Returns:\n+ *  -1 - error unable to commit\n+ *   0 - success\n+ *   1 - run 'git commit'\n+ */\n+static int try_to_commit(struct strbuf *msg, const char *author,\n+\t\t\t struct replay_opts *opts, unsigned int flags,\n+\t\t\t struct object_id *oid)\n+{\n+\tstruct object_id tree;\n+\tstruct commit *current_head;\n+\tstruct commit_list *parents = NULL;\n+\tstruct commit_extra_header *extra = NULL;\n+\tstruct strbuf err = STRBUF_INIT;\n+\tstruct strbuf amend_msg = STRBUF_INIT;\n+\tchar *amend_author = NULL;\n+\tconst char *gpg_sign;\n+\tenum commit_msg_cleanup_mode cleanup;\n+\tint res = 0;\n+\n+\tif (parse_head(&current_head))\n+\t\treturn -1;\n+\n+\tif (flags & AMEND_MSG) {\n+\t\tconst char *exclude_gpgsig[] = { \"gpgsig\", NULL };\n+\t\tconst char *out_enc = get_commit_output_encoding();\n+\t\tconst char *message = logmsg_reencode(current_head, NULL,\n+\t\t\t\t\t\t      out_enc);\n+\n+\t\tif (!msg) {\n+\t\t\tconst char *orig_message = NULL;\n+\n+\t\t\tfind_commit_subject(message, &orig_message);\n+\t\t\tmsg = &amend_msg;\n+\t\t\tstrbuf_addstr(msg, orig_message);\n+\t\t}\n+\t\tauthor = amend_author = get_author(message);\n+\t\tunuse_commit_buffer(current_head, message);\n+\t\tif (!author) {\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t\t\tgoto out;\n+\t\t}\n+\t\tparents = copy_commit_list(current_head->parents);\n+\t\textra = read_commit_extra_headers(current_head, exclude_gpgsig);\n+\t} else if (current_head) {\n+\t\tcommit_list_insert(current_head, &parents);\n+\t}\n+\n+\tcleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t\t  default_msg_cleanup;\n+\tif (cleanup != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);\n+\tif (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {\n+\t\tres = 1; /* run 'git commit' to display error message */\n+\t\tgoto out;\n+\t}\n+\n+\tgpg_sign = opts->gpg_sign ? opts->gpg_sign : default_gpg_sign;\n+\n+\tif (write_cache_as_tree(tree.hash, 0, NULL)) {\n+\t\tres = error(_(\"git write-tree failed to write a tree\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?\n+\t\t\t\t\t      &current_head->tree->object.oid :\n+\t\t\t\t\t      &empty_tree_oid, &tree)) {\n+\t\tres = 1; /* run 'git commit' to display error message */\n+\t\tgoto out;\n+\t}\n+\n+\tif (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,\n+\t\t\t\t oid->hash, author, gpg_sign, extra)) {\n+\t\tres = error(_(\"failed to write commit object\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (update_head_with_reflog(current_head, oid,\n+\t\t\t\t    getenv(\"GIT_REFLOG_ACTION\"), msg, &err)){\n+\t\tres = error(\"%s\", err.buf);\n+\t\tgoto out;\n+\t}\n+\n+\tif (flags & AMEND_MSG)\n+\t\tcommit_post_rewrite(current_head, oid);\n+\n+out:\n+\tfree_commit_extra_headers(extra);\n+\tstrbuf_release(&err);\n+\tstrbuf_release(&amend_msg);\n+\tfree(amend_author);\n+\n+\treturn res;\n+}\n+\n+static int do_commit(const char *msg_file, const char* author,\n+\t\t     struct replay_opts *opts, unsigned int flags)\n+{\n+\tint res = 1;\n+\n+\tif (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {\n+\t\tstruct object_id oid;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)\n+\t\t\treturn error_errno(_(\"unable to read commit message \"\n+\t\t\t\t\t     \"from '%s'\"),\n+\t\t\t\t\t   msg_file);\n+\n+\t\tres = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,\n+\t\t\t\t    &oid);\n+\t\tstrbuf_release(&sb);\n+\t\tif (!res) {\n+\t\t\tunlink(git_path_cherry_pick_head());\n+\t\t\tunlink(git_path_merge_msg());\n+\t\t\tif (!is_rebase_i(opts))\n+\t\t\t\tres = print_commit_summary(NULL, &oid,\n+\t\t\t\t\t\tSUMMARY_SHOW_AUTHOR_DATE);\n+\t\t\treturn res;\n+\t\t}\n+\t}\n+\tif (res == 1)\n+\t\treturn run_git_commit(msg_file, opts, flags);\n+\n+\treturn res;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\n@@ -1238,6 +1404,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \tstruct object_id head;\n \tstruct commit *base, *next, *parent;\n \tconst char *base_label, *next_label;\n+\tchar *author = NULL;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n \tint res, unborn = 0, allow;\n@@ -1354,6 +1521,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t\tstrbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));\n \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n \t\t}\n+\t\tif (!is_fixup (command))\n+\t\t\tauthor = get_author(msg.message);\n \t}\n \n \tif (command == TODO_REWORD)\n@@ -1439,9 +1608,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t} else if (allow)\n \t\tflags |= ALLOW_EMPTY;\n-\tif (!opts->no_commit)\n+\tif (!opts->no_commit) {\n fast_forward_edit:\n-\t\tres = run_git_commit(msg_file, opts, flags);\n+\t\tif (author || command == TODO_REVERT || (flags & AMEND_MSG))\n+\t\t\tres = do_commit(msg_file, author, opts, flags);\n+\t\telse\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t}\n \n \tif (!res && final_fixup) {\n \t\tunlink(rebase_path_fixup_msg());\n@@ -1450,6 +1623,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \n leave:\n \tfree_message(commit, &msg);\n+\tfree(author);\n \tupdate_abort_safety_file();\n \n \treturn res;\n-- \n2.15.0\n\n"},{"id":"332166","messageId":"20171110110949.328-6-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171110110949.328-1-phillip.wood@talktalk.net","subject":"[PATCH v2 5/9] commit: move print_commit_summary() to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T11:09:45Z","receivedAt":"2017-11-10T11:10:33Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove print_commit_summary() from builtin/commit.c to sequencer.c so it\ncan be shared with other commands. The function is modified by\nchanging the last argument to a flag so callers can specify whether\nthey want to show the author date in addition to specifying if this is\nan initial commit.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - convert flags passed to print_commit_summary() to unsigned int\n\n builtin/commit.c | 128 ++++---------------------------------------------------\n sequencer.c      | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |   5 +++\n 3 files changed, 131 insertions(+), 119 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 3bc5dff2c00c9556e6c032381d5baf18246bf8dc..22d260197e84a828f984575cfa92789391531ca3 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {\n \tNULL\n };\n \n-static const char implicit_ident_advice_noconfig[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly. Run the\\n\"\n-\"following command and follow the instructions in your editor to edit\\n\"\n-\"your configuration file:\\n\"\n-\"\\n\"\n-\"    git config --global --edit\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n-static const char implicit_ident_advice_config[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly:\\n\"\n-\"\\n\"\n-\"    git config --global user.name \\\"Your Name\\\"\\n\"\n-\"    git config --global user.email you@example.com\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n static const char empty_amend_advice[] =\n N_(\"You asked to amend the most recent commit, but doing so would make\\n\"\n \"it empty. You can repeat your command with --allow-empty, or you can\\n\"\n@@ -1355,98 +1330,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n-static const char *implicit_ident_advice(void)\n-{\n-\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n-\tchar *xdg_config = xdg_config_home(\"config\");\n-\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n-\n-\tfree(user_config);\n-\tfree(xdg_config);\n-\n-\tif (config_exists)\n-\t\treturn _(implicit_ident_advice_config);\n-\telse\n-\t\treturn _(implicit_ident_advice_noconfig);\n-\n-}\n-\n-static void print_summary(const char *prefix, const struct object_id *oid,\n-\t\t\t  int initial_commit)\n-{\n-\tstruct rev_info rev;\n-\tstruct commit *commit;\n-\tstruct strbuf format = STRBUF_INIT;\n-\tconst char *head;\n-\tstruct pretty_print_context pctx = {0};\n-\tstruct strbuf author_ident = STRBUF_INIT;\n-\tstruct strbuf committer_ident = STRBUF_INIT;\n-\n-\tcommit = lookup_commit(oid);\n-\tif (!commit)\n-\t\tdie(_(\"couldn't look up newly created commit\"));\n-\tif (parse_commit(commit))\n-\t\tdie(_(\"could not parse newly created commit\"));\n-\n-\tstrbuf_addstr(&format, \"format:%h] %s\");\n-\n-\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n-\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n-\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n-\t\tstrbuf_addstr(&format, \"\\n Author: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n-\t}\n-\tif (author_date_is_interesting()) {\n-\t\tstruct strbuf date = STRBUF_INIT;\n-\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n-\t\tstrbuf_addstr(&format, \"\\n Date: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &date);\n-\t\tstrbuf_release(&date);\n-\t}\n-\tif (!committer_ident_sufficiently_given()) {\n-\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n-\t\tif (advice_implicit_identity) {\n-\t\t\tstrbuf_addch(&format, '\\n');\n-\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n-\t\t}\n-\t}\n-\tstrbuf_release(&author_ident);\n-\tstrbuf_release(&committer_ident);\n-\n-\tinit_revisions(&rev, prefix);\n-\tsetup_revisions(0, NULL, &rev, NULL);\n-\n-\trev.diff = 1;\n-\trev.diffopt.output_format =\n-\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n-\n-\trev.verbose_header = 1;\n-\trev.show_root_diff = 1;\n-\tget_commit_format(format.buf, &rev);\n-\trev.always_show_header = 0;\n-\trev.diffopt.detect_rename = 1;\n-\trev.diffopt.break_opt = 0;\n-\tdiff_setup_done(&rev.diffopt);\n-\n-\thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n-\tif (!head)\n-\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n-\tif (!strcmp(head, \"HEAD\"))\n-\t\thead = _(\"detached HEAD\");\n-\telse\n-\t\tskip_prefix(head, \"refs/heads/\", &head);\n-\tprintf(\"[%s%s \", head, initial_commit ? _(\" (root-commit)\") : \"\");\n-\n-\tif (!log_tree_commit(&rev, commit)) {\n-\t\trev.always_show_header = 1;\n-\t\trev.use_terminator = 1;\n-\t\tlog_tree_commit(&rev, commit);\n-\t}\n-\n-\tstrbuf_release(&format);\n-}\n-\n static int git_commit_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n@@ -1708,8 +1591,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (amend && !no_post_rewrite) {\n \t\tcommit_post_rewrite(current_head, &oid);\n \t}\n-\tif (!quiet)\n-\t\tprint_summary(prefix, &oid, !current_head);\n+\tif (!quiet) {\n+\t\tunsigned int flags = 0;\n+\n+\t\tif (!current_head)\n+\t\t\tflags |= SUMMARY_INITIAL_COMMIT;\n+\t\tif (author_date_is_interesting())\n+\t\t\tflags |= SUMMARY_SHOW_AUTHOR_DATE;\n+\t\tprint_commit_summary(prefix, &oid, flags);\n+\t}\n \n \tUNLEAK(err);\n \tUNLEAK(sb);\ndiff --git a/sequencer.c b/sequencer.c\nindex 5529e5df1cbe9045b000ecbff6c28a0bee736ccc..03207db71821c0c3d1c442d3b01b11e3b010367e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -836,6 +836,123 @@ void commit_post_rewrite(const struct commit *old_head,\n \trun_rewrite_hook(&old_head->object.oid, new_head);\n }\n \n+static const char implicit_ident_advice_noconfig[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly. Run the\\n\"\n+\"following command and follow the instructions in your editor to edit\\n\"\n+\"your configuration file:\\n\"\n+\"\\n\"\n+\"    git config --global --edit\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char implicit_ident_advice_config[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly:\\n\"\n+\"\\n\"\n+\"    git config --global user.name \\\"Your Name\\\"\\n\"\n+\"    git config --global user.email you@example.com\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char *implicit_ident_advice(void)\n+{\n+\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n+\tchar *xdg_config = xdg_config_home(\"config\");\n+\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n+\n+\tfree(user_config);\n+\tfree(xdg_config);\n+\n+\tif (config_exists)\n+\t\treturn _(implicit_ident_advice_config);\n+\telse\n+\t\treturn _(implicit_ident_advice_noconfig);\n+\n+}\n+\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  unsigned int flags)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf format = STRBUF_INIT;\n+\tconst char *head;\n+\tstruct pretty_print_context pctx = {0};\n+\tstruct strbuf author_ident = STRBUF_INIT;\n+\tstruct strbuf committer_ident = STRBUF_INIT;\n+\n+\tcommit = lookup_commit(oid);\n+\tif (!commit)\n+\t\tdie(_(\"couldn't look up newly created commit\"));\n+\tif (parse_commit(commit))\n+\t\tdie(_(\"could not parse newly created commit\"));\n+\n+\tstrbuf_addstr(&format, \"format:%h] %s\");\n+\n+\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n+\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n+\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n+\t\tstrbuf_addstr(&format, \"\\n Author: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n+\t}\n+\tif (flags & SUMMARY_SHOW_AUTHOR_DATE) {\n+\t\tstruct strbuf date = STRBUF_INIT;\n+\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n+\t\tstrbuf_addstr(&format, \"\\n Date: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &date);\n+\t\tstrbuf_release(&date);\n+\t}\n+\tif (!committer_ident_sufficiently_given()) {\n+\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n+\t\tif (advice_implicit_identity) {\n+\t\t\tstrbuf_addch(&format, '\\n');\n+\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n+\t\t}\n+\t}\n+\tstrbuf_release(&author_ident);\n+\tstrbuf_release(&committer_ident);\n+\n+\tinit_revisions(&rev, prefix);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.diff = 1;\n+\trev.diffopt.output_format =\n+\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n+\n+\trev.verbose_header = 1;\n+\trev.show_root_diff = 1;\n+\tget_commit_format(format.buf, &rev);\n+\trev.always_show_header = 0;\n+\trev.diffopt.detect_rename = 1;\n+\trev.diffopt.break_opt = 0;\n+\tdiff_setup_done(&rev.diffopt);\n+\n+\thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n+\tif (!head)\n+\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n+\tif (!strcmp(head, \"HEAD\"))\n+\t\thead = _(\"detached HEAD\");\n+\telse\n+\t\tskip_prefix(head, \"refs/heads/\", &head);\n+\tprintf(\"[%s%s \", head, (flags & SUMMARY_INITIAL_COMMIT) ? _(\" (root-commit)\") : \"\");\n+\n+\tif (!log_tree_commit(&rev, commit)) {\n+\t\trev.always_show_header = 1;\n+\t\trev.use_terminator = 1;\n+\t\tlog_tree_commit(&rev, commit);\n+\t}\n+\n+\tstrbuf_release(&format);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 6a69e4a7a42551cbc798b8eb93e447c0f50b234f..8e0dbe59bbc1ccd3847454faf8bd09d5ca1ff93a 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -75,4 +75,9 @@ int update_head_with_reflog(const struct commit *old_head,\n \t\t\t    struct strbuf *err);\n void commit_post_rewrite(const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n+\n+#define SUMMARY_INITIAL_COMMIT   (1 << 0)\n+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  unsigned int flags);\n #endif\n-- \n2.15.0\n\n"},{"id":"332167","messageId":"20171110110949.328-8-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171110110949.328-1-phillip.wood@talktalk.net","subject":"[PATCH v2 7/9] sequencer: simplify adding Signed-off-by: trailer","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T11:09:47Z","receivedAt":"2017-11-10T11:10:35Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd the Signed-off-by: trailer in one place rather than adding it to\nthe message when doing a recursive merge and specifying '--signoff'\nwhen running 'git commit'. This means that if there are conflicts when\nmerging with a strategy other than 'recursive' the Signed-off-by:\ntrailer will be added if the user commits the resolution themselves\nwithout passing '--signoff' to 'git commit'. It also simplifies the\nin-process commit that is about to be added to the sequencer.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n sequencer.c | 8 +++-----\n 1 file changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 61bb75352202cf896ffec41e972f366e94d569c1..497764ea5db3a3ba2802387316c1d5024d47d7eb 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \t\t\t_(action_name(opts)));\n \trollback_lock_file(&index_lock);\n \n-\tif (opts->signoff)\n-\t\tappend_signoff(msgbuf, 0, 0);\n-\n \tif (!clean)\n \t\tappend_conflicts_hint(msgbuf);\n \n@@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\targv_array_push(&cmd.args, \"--amend\");\n \tif (opts->gpg_sign)\n \t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n-\tif (opts->signoff)\n-\t\targv_array_push(&cmd.args, \"-s\");\n \tif (defmsg)\n \t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n \tif ((flags & CLEANUP_MSG))\n@@ -1350,6 +1345,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n+\tif (opts->signoff)\n+\t\tappend_signoff(&msgbuf, 0, 0);\n+\n \tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n \t\tres = -1;\n \telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n-- \n2.15.0\n\n"},{"id":"332168","messageId":"20171110110949.328-9-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171110110949.328-1-phillip.wood@talktalk.net","subject":"[PATCH v2 8/9] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T11:09:48Z","receivedAt":"2017-11-10T11:10:37Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nLoad default values for message cleanup and gpg signing of commits in\npreparation for committing without forking 'git commit'.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - renamed git_revert_config() to common_config()\n     - prefixed cleanup_mode constants to reflect the changes to patch 2\n       in this series\n\n builtin/rebase--helper.c | 13 ++++++++++++-\n builtin/revert.c         | 15 +++++++++++++--\n sequencer.c              | 34 ++++++++++++++++++++++++++++++++++\n sequencer.h              |  1 +\n 4 files changed, 60 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c\nindex f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644\n--- a/builtin/rebase--helper.c\n+++ b/builtin/rebase--helper.c\n@@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n \tNULL\n };\n \n+static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n+{\n+\tint status;\n+\n+\tstatus = git_sequencer_config(k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_default_config(k, v, NULL);\n+}\n+\n int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts = REPLAY_OPTS_INIT;\n@@ -39,7 +50,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_rebase_helper_config, NULL);\n \n \topts.action = REPLAY_INTERACTIVE_REBASE;\n \topts.allow_ff = 1;\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex b9d927eb09c9ed87c84681df1396f4e6d9b13c97..1938825efa6ad20ede5aba57f097863aeb33d1d5 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n+static int common_config(const char *k, const char *v, void *cb)\n+{\n+\tint status;\n+\n+\tstatus = git_sequencer_config(k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_default_config(k, v, NULL);\n+}\n+\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n@@ -208,7 +219,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tif (isatty(0))\n \t\topts.edit = 1;\n \topts.action = REPLAY_REVERT;\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(common_config, NULL);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"revert failed\"));\n@@ -221,7 +232,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \topts.action = REPLAY_PICK;\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(common_config, NULL);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"cherry-pick failed\"));\ndiff --git a/sequencer.c b/sequencer.c\nindex 497764ea5db3a3ba2802387316c1d5024d47d7eb..0fd98391cb7dfcff6976271f44a56e897593e2c0 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -688,6 +688,40 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static enum commit_msg_cleanup_mode default_msg_cleanup =\n+\t\t\t\t\t\tCOMMIT_MSG_CLEANUP_NONE;\n+static char *default_gpg_sign;\n+\n+int git_sequencer_config(const char *k, const char *v, void *cb)\n+{\n+\tif (!strcmp(k, \"commit.cleanup\")) {\n+\t\tint status;\n+\t\tconst char *s;\n+\n+\t\tstatus = git_config_string(&s, k, v);\n+\t\tif (status)\n+\t\t\treturn status;\n+\n+\t\tif (!strcmp(s, \"verbatim\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n+\t\telse if (!strcmp(s, \"whitespace\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n+\t\telse if (!strcmp(s, \"strip\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n+\t\telse if (!strcmp(s, \"scissors\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n+\n+\t\treturn status;\n+\t}\n+\n+\tif (!strcmp(k, \"commit.gpgsign\")) {\n+\t\tdefault_gpg_sign = git_config_bool(k, v) ? \"\" : NULL;\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_gpg_config(k, v, NULL);\n+}\n+\n static int rest_is_empty(const struct strbuf *sb, int start)\n {\n \tint i, eol;\ndiff --git a/sequencer.h b/sequencer.h\nindex e4040cac08cb69c9b91db3fe77ae392c8d6d0f50..27f34be4003589790be686c901a19059f4642c22 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -57,6 +57,7 @@ extern const char sign_off_header[];\n \n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n+int git_sequencer_config(const char *k, const char *v, void *cb);\n \n enum commit_msg_cleanup_mode {\n \tCOMMIT_MSG_CLEANUP_SPACE,\n-- \n2.15.0\n\n"},{"id":"332184","messageId":"072b0edb-7a30-77f3-2ac5-893c28c0a695@talktalk.net","threadId":"46827","inReplyTo":"xmqq7ev2dsv2.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-10T14:53:01Z","receivedAt":"2017-11-10T14:53:09Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/11/17 15:13, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n>> And this step is going in the right direction, but I am not sure if\n>> this made the function safe enough to be called repeatedly from the\n>> rebase machinery and we are ready to unleash this to the end users\n>> and tell them it is safe to use it.\n> \n> Another possibility perhaps is that the function is safe to reuse\n> already even without this patch, of course ;-).\n> \nHmm, maybe it is. Looking at pick_commits() and do_pick_commit() if the\nsequencer dies in print_commit_summary() (which can only happen when\ncherry-picking or reverting) then neither the todo list or the abort\nsafety file are updated to reflect the commit that was just made.\n\nAs I understand it print_commit_summary() dies because: (i) it cannot\nresolve HEAD either because some other process is updating it (which is\nbad news in the middle of a cherry-pick); (ii) because something went\nwrong HEAD is corrupt; or (iii) log_tree_commit() cannot read some\nobjects. In all those cases dying will leave the sequencer in a sane\nstate for aborting - 'git cherry-pick --abort' will rewind HEAD to the\nlast successful commit before there was a problem with HEAD or the\nobject database. If the user somehow fixes the problem and runs 'git\ncherry-pick --continue' then the sequencer will try and pick the same\ncommit again which may or may not be what the user wants depending on\nwhat caused print_commit_summary() to die.\n\n\n"},{"id":"332200","messageId":"xmqqk1yyf1q0.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"072b0edb-7a30-77f3-2ac5-893c28c0a695@talktalk.net","subject":"Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-10T18:05:59Z","receivedAt":"2017-11-10T18:06:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> On 07/11/17 15:13, Junio C Hamano wrote:\n> ...\n>> Another possibility perhaps is that the function is safe to reuse\n>> already even without this patch, of course ;-).\n>> \n> Hmm, maybe it is. Looking at pick_commits() and do_pick_commit() if the\n> sequencer dies in print_commit_summary() (which can only happen when\n> cherry-picking or reverting) then neither the todo list or the abort\n> safety file are updated to reflect the commit that was just made.\n> \n> As I understand it print_commit_summary() dies because: (i) it cannot\n> resolve HEAD either because some other process is updating it (which is\n> bad news in the middle of a cherry-pick); (ii) because something went\n> wrong HEAD is corrupt; or (iii) log_tree_commit() cannot read some\n> objects. In all those cases dying will leave the sequencer in a sane\n> state for aborting - 'git cherry-pick --abort' will rewind HEAD to the\n> last successful commit before there was a problem with HEAD or the\n> object database. If the user somehow fixes the problem and runs 'git\n> cherry-pick --continue' then the sequencer will try and pick the same\n> commit again which may or may not be what the user wants depending on\n> what caused print_commit_summary() to die.\n\nThe above is all good analysis---thanks for your diligence.  Perhaps\nsome if not all of it can go to the log message?\n\n"},{"id":"332210","messageId":"xmqq7euyf0ap.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171110110949.328-4-phillip.wood@talktalk.net","subject":"Re: [PATCH v2 3/9] Add a function to update HEAD after creating a commit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-10T18:36:46Z","receivedAt":"2017-11-10T18:36:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> Add update_head() based on the code that updates HEAD after committing\n> in builtin/commit.c that can be called by 'git commit' and other\n> commands.\n>\n> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> ---\n>\n> Notes:\n>     changes since v1:\n>      - rename update_head() to update_head_with_reflog()\n\nThe above proposed log message still calls it with the old name\nthough.\n"},{"id":"332211","messageId":"dbdb4990-e539-42d5-df0a-3d5f4a993faf@ramsayjones.plus.com","threadId":"46827","inReplyTo":"20171110110949.328-3-phillip.wood@talktalk.net","subject":"Re: [PATCH v2 2/9] commit: move empty message checks to libgit","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2017-11-10T18:51:29Z","receivedAt":"2017-11-10T18:51:37Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 10/11/17 11:09, Phillip Wood wrote:\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n> \n> Move the functions that check for empty messages from bulitin/commit.c\n> to sequencer.c so they can be shared with other commands. The\n> functions are refactored to take an explicit cleanup mode and template\n> filename passed by the caller.\n> \n> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> ---\n> \n> Notes:\n>     changes since v1:\n>      - prefix cleanup_mode enum and constants with commit_msg_\n> \n>  builtin/commit.c | 99 +++++++++++---------------------------------------------\n>  sequencer.c      | 61 ++++++++++++++++++++++++++++++++++\n>  sequencer.h      | 11 +++++++\n>  3 files changed, 91 insertions(+), 80 deletions(-)\n> \n\nJust an idle thought - why are these functions moving to\nsequencer.[ch] rather than commit[.ch]?\n\nSimilar comments for other patches in the series which moves\ncode from builtin/commit.c to sequencer.[ch].\n\nATB,\nRamsay Jones\n\n\n"},{"id":"332244","messageId":"xmqqo9oadjn5.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171110110949.328-1-phillip.wood@talktalk.net","subject":"Re: [PATCH v2 0/9] sequencer: dont't fork git commit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-10T19:21:50Z","receivedAt":"2017-11-10T19:21:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> Here's the summary from the previous version\n> These patches teach the sequencer to create commits without forking\n> git commit when the commit message does not need to be edited. This\n> speeds up cherry picking 10 commits by 26% and picking 10 commits with\n> rebase --continue by 44%. The first few patches move bits of\n> builtin/commit.c to sequencer.c. The last two patches actually\n> implement creating commits in sequencer.c.\n\nThanks.  The changes since the initial iteration seems quite small\nand I didn't find much objectionable.\n\nHere are some style fixes I needed to add on top to make the output\nof \"diff master HEAD\" checkpatch.pl-clean.  I think 3/9 and 9/9 are\nthe culprits.\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 1f65e82696..a989588ee5 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -592,7 +592,7 @@ static int read_env_script(struct argv_array *env)\n \treturn 0;\n }\n \n-static char *get_author(const char* message)\n+static char *get_author(const char *message)\n {\n \tsize_t len;\n \tconst char *a;\n@@ -1104,7 +1104,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,\n \t}\n \n \tif (update_head_with_reflog(current_head, oid,\n-\t\t\t\t    getenv(\"GIT_REFLOG_ACTION\"), msg, &err)){\n+\t\t\t\t    getenv(\"GIT_REFLOG_ACTION\"), msg, &err)) {\n \t\tres = error(\"%s\", err.buf);\n \t\tgoto out;\n \t}\n@@ -1121,7 +1121,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,\n \treturn res;\n }\n \n-static int do_commit(const char *msg_file, const char* author,\n+static int do_commit(const char *msg_file, const char *author,\n \t\t     struct replay_opts *opts, unsigned int flags)\n {\n \tint res = 1;\n@@ -1521,7 +1521,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t\tstrbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));\n \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n \t\t}\n-\t\tif (!is_fixup (command))\n+\t\tif (!is_fixup(command))\n \t\t\tauthor = get_author(msg.message);\n \t}\n \ndiff --git a/sequencer.h b/sequencer.h\nindex 27f34be400..e0be354301 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -72,7 +72,7 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n \t\t       enum commit_msg_cleanup_mode cleanup_mode);\n int update_head_with_reflog(const struct commit *old_head,\n \t\t\t    const struct object_id *new_head,\n-\t\t\t    const char* action, const struct strbuf *msg,\n+\t\t\t    const char *action, const struct strbuf *msg,\n \t\t\t    struct strbuf *err);\n void commit_post_rewrite(const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n\n\n"},{"id":"332407","messageId":"9e872c1f-2d51-ef20-bae8-305b6db14a7b@talktalk.net","threadId":"46827","inReplyTo":"dbdb4990-e539-42d5-df0a-3d5f4a993faf@ramsayjones.plus.com","subject":"Re: [PATCH v2 2/9] commit: move empty message checks to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-13T11:08:08Z","receivedAt":"2017-11-13T11:08:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 10/11/17 18:51, Ramsay Jones wrote:\n> \n> \n> On 10/11/17 11:09, Phillip Wood wrote:\n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> Move the functions that check for empty messages from bulitin/commit.c\n>> to sequencer.c so they can be shared with other commands. The\n>> functions are refactored to take an explicit cleanup mode and template\n>> filename passed by the caller.\n>>\n>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n>> ---\n>>\n>> Notes:\n>>     changes since v1:\n>>      - prefix cleanup_mode enum and constants with commit_msg_\n>>\n>>  builtin/commit.c | 99 +++++++++++---------------------------------------------\n>>  sequencer.c      | 61 ++++++++++++++++++++++++++++++++++\n>>  sequencer.h      | 11 +++++++\n>>  3 files changed, 91 insertions(+), 80 deletions(-)\n>>\n> \n> Just an idle thought - why are these functions moving to\n> sequencer.[ch] rather than commit[.ch]?\n> \n> Similar comments for other patches in the series which moves\n> code from builtin/commit.c to sequencer.[ch].\n\nI did think about putting them in commit.[ch] but I felt they where\nhigher level than the existing functions in those files and as they're\nused by the sequencer code I just put them in there in the end.\n\nBest Wishes\n\nPhillip\n\n> ATB,\n> Ramsay Jones\n> \n> \n\n"},{"id":"332408","messageId":"536b1a9e-92a2-b907-3489-d4059534edb8@talktalk.net","threadId":"46827","inReplyTo":"xmqqk1yyf1q0.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-13T11:11:51Z","receivedAt":"2017-11-13T11:12:20Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 10/11/17 18:05, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n>> On 07/11/17 15:13, Junio C Hamano wrote:\n>> ...\n>>> Another possibility perhaps is that the function is safe to reuse\n>>> already even without this patch, of course ;-).\n>>>\n>> Hmm, maybe it is. Looking at pick_commits() and do_pick_commit() if the\n>> sequencer dies in print_commit_summary() (which can only happen when\n>> cherry-picking or reverting) then neither the todo list or the abort\n>> safety file are updated to reflect the commit that was just made.\n>>\n>> As I understand it print_commit_summary() dies because: (i) it cannot\n>> resolve HEAD either because some other process is updating it (which is\n>> bad news in the middle of a cherry-pick); (ii) because something went\n>> wrong HEAD is corrupt; or (iii) log_tree_commit() cannot read some\n>> objects. In all those cases dying will leave the sequencer in a sane\n>> state for aborting - 'git cherry-pick --abort' will rewind HEAD to the\n>> last successful commit before there was a problem with HEAD or the\n>> object database. If the user somehow fixes the problem and runs 'git\n>> cherry-pick --continue' then the sequencer will try and pick the same\n>> commit again which may or may not be what the user wants depending on\n>> what caused print_commit_summary() to die.\n> \n> The above is all good analysis---thanks for your diligence.  Perhaps\n> some if not all of it can go to the log message?\n> \nThanks, that's a good idea. I see the above as a reason to drop this\ncommit as returning an error will try and update the abort safety file\nwhich we don't want to do if there is a problem with HEAD so I'll add it\nto the previous commit to explain why it is okay to die.\n"},{"id":"332411","messageId":"f1c0efa2-fb8a-43bc-f229-c22dada95b03@talktalk.net","threadId":"46827","inReplyTo":"xmqqo9oadjn5.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 0/9] sequencer: dont't fork git commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-13T11:24:34Z","receivedAt":"2017-11-13T11:24:41Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 10/11/17 19:21, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n>> Here's the summary from the previous version\n>> These patches teach the sequencer to create commits without forking\n>> git commit when the commit message does not need to be edited. This\n>> speeds up cherry picking 10 commits by 26% and picking 10 commits with\n>> rebase --continue by 44%. The first few patches move bits of\n>> builtin/commit.c to sequencer.c. The last two patches actually\n>> implement creating commits in sequencer.c.\n> \n> Thanks.  The changes since the initial iteration seems quite small\n> and I didn't find much objectionable.\n> \n> Here are some style fixes I needed to add on top to make the output\n> of \"diff master HEAD\" checkpatch.pl-clean.  I think 3/9 and 9/9 are\n> the culprits.\n\nThanks, I'll update the patches.\n\nAre you happy with the '--signoff' is handled (I didn't modify my\nchanges in the last iteration as you were still thinking about it)?\n\nIn the last patch commit_tree_extended() can die in write_loose_object()\nwhich means that the current command is not rescheduled as it should be\nwhen rebasing. write_loose_object() already an error if there is a\nproblem opening the file, but dies if there is a problem creating the\nfile contents or writing that contents.\n\nBest Wishes\n\nPhillip\n\n> diff --git a/sequencer.c b/sequencer.c\n> index 1f65e82696..a989588ee5 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -592,7 +592,7 @@ static int read_env_script(struct argv_array *env)\n>  \treturn 0;\n>  }\n>  \n> -static char *get_author(const char* message)\n> +static char *get_author(const char *message)\n>  {\n>  \tsize_t len;\n>  \tconst char *a;\n> @@ -1104,7 +1104,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,\n>  \t}\n>  \n>  \tif (update_head_with_reflog(current_head, oid,\n> -\t\t\t\t    getenv(\"GIT_REFLOG_ACTION\"), msg, &err)){\n> +\t\t\t\t    getenv(\"GIT_REFLOG_ACTION\"), msg, &err)) {\n>  \t\tres = error(\"%s\", err.buf);\n>  \t\tgoto out;\n>  \t}\n> @@ -1121,7 +1121,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,\n>  \treturn res;\n>  }\n>  \n> -static int do_commit(const char *msg_file, const char* author,\n> +static int do_commit(const char *msg_file, const char *author,\n>  \t\t     struct replay_opts *opts, unsigned int flags)\n>  {\n>  \tint res = 1;\n> @@ -1521,7 +1521,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \t\t\tstrbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));\n>  \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n>  \t\t}\n> -\t\tif (!is_fixup (command))\n> +\t\tif (!is_fixup(command))\n>  \t\t\tauthor = get_author(msg.message);\n>  \t}\n>  \n> diff --git a/sequencer.h b/sequencer.h\n> index 27f34be400..e0be354301 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -72,7 +72,7 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n>  \t\t       enum commit_msg_cleanup_mode cleanup_mode);\n>  int update_head_with_reflog(const struct commit *old_head,\n>  \t\t\t    const struct object_id *new_head,\n> -\t\t\t    const char* action, const struct strbuf *msg,\n> +\t\t\t    const char *action, const struct strbuf *msg,\n>  \t\t\t    struct strbuf *err);\n>  void commit_post_rewrite(const struct commit *current_head,\n>  \t\t\t const struct object_id *new_head);\n> \n> \n\n"},{"id":"332412","messageId":"43448e40-1b89-eeb2-086d-c23c804d3fcb@talktalk.net","threadId":"46827","inReplyTo":"xmqq7euyf0ap.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 3/9] Add a function to update HEAD after creating a commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-13T11:25:11Z","receivedAt":"2017-11-13T11:25:16Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 10/11/17 18:36, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> Add update_head() based on the code that updates HEAD after committing\n>> in builtin/commit.c that can be called by 'git commit' and other\n>> commands.\n>>\n>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n>> ---\n>>\n>> Notes:\n>>     changes since v1:\n>>      - rename update_head() to update_head_with_reflog()\n> \n> The above proposed log message still calls it with the old name\n> though.\n> \nOops, sorry I'll fix that\n"},{"id":"332483","messageId":"xmqq1sl18xug.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"f1c0efa2-fb8a-43bc-f229-c22dada95b03@talktalk.net","subject":"Re: [PATCH v2 0/9] sequencer: dont't fork git commit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-14T01:15:19Z","receivedAt":"2017-11-14T01:15:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> Are you happy with the '--signoff' is handled (I didn't modify my\n> changes in the last iteration as you were still thinking about it)?\n\nI am not, but I am not unhappy, either.\n\nAs I understand from your responses, it seems that the existing code\nhad an untriggerable mess and the patch replaced it with another\nthat is also untriggerable, and if that is the case, well, we do not\nhave an immediate need to clean it up either way, so... ;-)\n\n"},{"id":"332747","messageId":"20171117113452.26597-2-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171117113452.26597-1-phillip.wood@talktalk.net","subject":"[PATCH v3 1/8] t3404: check intermediate squash messages","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-17T11:34:45Z","receivedAt":"2017-11-17T11:36:17Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nWhen there is more than one squash/fixup command in a row check the\nintermediate messages are correct.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n t/t3404-rebase-interactive.sh | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 6a82d1ed876dd5d1073dc63be8ba5720adbf12e3..9ed0a244e6cdf34c7caca8232f0c0a8cf4864c42 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -453,6 +453,10 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa\n \t\tgit rebase -i $base &&\n \tgit cat-file commit HEAD | sed -e 1,/^\\$/d > actual-squash-fixup &&\n \ttest_cmp expect-squash-fixup actual-squash-fixup &&\n+\tgit cat-file commit HEAD@{2} |\n+\t\tgrep \"^# This is a combination of 3 commits\\.\"  &&\n+\tgit cat-file commit HEAD@{3} |\n+\t\tgrep \"^# This is a combination of 2 commits\\.\"  &&\n \tgit checkout to-be-rebased &&\n \tgit branch -D squash-fixup\n '\n-- \n2.15.0\n\n"},{"id":"332748","messageId":"20171117113452.26597-1-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[PATCH v3 0/8] sequencer: don't fork git commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-17T11:34:44Z","receivedAt":"2017-11-17T11:36:20Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nI've updated these based on the feedback for v2. I've dropped the\npatch that stopped print_commit_summary() from dying as I think it is\nbetter to die than return an error (see the commit message of the\npatch that adds print_commit_summary() for the reasoning). Apart from\nthat they're minor changes - style fixes and a reworded a commit message.\n\nHere's the original summary:\nThese patches teach the sequencer to create commits without forking\ngit commit when the commit message does not need to be edited. This\nspeeds up cherry picking 10 commits by 26% and picking 10 commits with\nrebase --continue by 44%. The first few patches move bits of\nbuiltin/commit.c to sequencer.c. The last two patches actually\nimplement creating commits in sequencer.c.\n\n\nPhillip Wood (8):\n  t3404: check intermediate squash messages\n  commit: move empty message checks to libgit\n  Add a function to update HEAD after creating a commit\n  commit: move post-rewrite code to libgit\n  commit: move print_commit_summary() to libgit\n  sequencer: simplify adding Signed-off-by: trailer\n  sequencer: load commit related config\n  sequencer: try to commit without forking 'git commit'\n\n builtin/commit.c              | 289 +++----------------------\n builtin/rebase--helper.c      |  13 +-\n builtin/revert.c              |  15 +-\n sequencer.c                   | 486 +++++++++++++++++++++++++++++++++++++++++-\n sequencer.h                   |  23 ++\n t/t3404-rebase-interactive.sh |   4 +\n 6 files changed, 561 insertions(+), 269 deletions(-)\n\n-- \n2.15.0\n\n"},{"id":"332749","messageId":"20171117113452.26597-3-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171117113452.26597-1-phillip.wood@talktalk.net","subject":"[PATCH v3 2/8] commit: move empty message checks to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-17T11:34:46Z","receivedAt":"2017-11-17T11:36:22Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove the functions that check for empty messages from bulitin/commit.c\nto sequencer.c so they can be shared with other commands. The\nfunctions are refactored to take an explicit cleanup mode and template\nfilename passed by the caller.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - prefix cleanup_mode enum and constants with commit_msg_\n\n builtin/commit.c | 99 +++++++++++---------------------------------------------\n sequencer.c      | 61 ++++++++++++++++++++++++++++++++++\n sequencer.h      | 11 +++++++\n 3 files changed, 91 insertions(+), 80 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 8a877014145435516930c787dec37b8c4ac3da90..d958c2eb2adc9a29dab29340ce9b56daea41fecd 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -128,12 +128,7 @@ static char *sign_commit;\n  * if editor is used, and only the whitespaces if the message\n  * is specified explicitly.\n  */\n-static enum {\n-\tCLEANUP_SPACE,\n-\tCLEANUP_NONE,\n-\tCLEANUP_SCISSORS,\n-\tCLEANUP_ALL\n-} cleanup_mode;\n+static enum commit_msg_cleanup_mode cleanup_mode;\n static const char *cleanup_arg;\n \n static enum commit_whence whence;\n@@ -673,7 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tstruct strbuf sb = STRBUF_INIT;\n \tconst char *hook_arg1 = NULL;\n \tconst char *hook_arg2 = NULL;\n-\tint clean_message_contents = (cleanup_mode != CLEANUP_NONE);\n+\tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n \n \t/* This checks and barfs if author is badly specified */\n@@ -812,7 +807,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -832,14 +827,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t}\n \n \t\tfprintf(s->fp, \"\\n\");\n-\t\tif (cleanup_mode == CLEANUP_ALL)\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t whence == FROM_COMMIT)\n \t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* CLEANUP_SPACE, that is. */\n+\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\n@@ -984,65 +980,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \treturn 1;\n }\n \n-static int rest_is_empty(struct strbuf *sb, int start)\n-{\n-\tint i, eol;\n-\tconst char *nl;\n-\n-\t/* Check if the rest is just whitespace and Signed-off-by's. */\n-\tfor (i = start; i < sb->len; i++) {\n-\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n-\t\tif (nl)\n-\t\t\teol = nl - sb->buf;\n-\t\telse\n-\t\t\teol = sb->len;\n-\n-\t\tif (strlen(sign_off_header) <= eol - i &&\n-\t\t    starts_with(sb->buf + i, sign_off_header)) {\n-\t\t\ti = eol;\n-\t\t\tcontinue;\n-\t\t}\n-\t\twhile (i < eol)\n-\t\t\tif (!isspace(sb->buf[i++]))\n-\t\t\t\treturn 0;\n-\t}\n-\n-\treturn 1;\n-}\n-\n-/*\n- * Find out if the message in the strbuf contains only whitespace and\n- * Signed-off-by lines.\n- */\n-static int message_is_empty(struct strbuf *sb)\n-{\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\treturn rest_is_empty(sb, 0);\n-}\n-\n-/*\n- * See if the user edited the message in the editor or left what\n- * was in the template intact\n- */\n-static int template_untouched(struct strbuf *sb)\n-{\n-\tstruct strbuf tmpl = STRBUF_INIT;\n-\tconst char *start;\n-\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\n-\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n-\t\treturn 0;\n-\n-\tstrbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);\n-\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n-\t\tstart = sb->buf;\n-\tstrbuf_release(&tmpl);\n-\treturn rest_is_empty(sb, start - sb->buf);\n-}\n-\n static const char *find_author_by_nickname(const char *name)\n {\n \tstruct rev_info revs;\n@@ -1227,15 +1164,17 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n \tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\tcleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;\n+\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n \telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\tcleanup_mode = CLEANUP_NONE;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_NONE;\n \telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\tcleanup_mode = CLEANUP_SPACE;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_SPACE;\n \telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\tcleanup_mode = CLEANUP_ALL;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_ALL;\n \telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\tcleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;\n+\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n+\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n \telse\n \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n \n@@ -1768,17 +1707,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (verbose || /* Truncate the message just before the diff, if any. */\n-\t    cleanup_mode == CLEANUP_SCISSORS)\n+\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n \t\tstrbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));\n-\tif (cleanup_mode != CLEANUP_NONE)\n-\t\tstrbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);\n+\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n \n-\tif (message_is_empty(&sb) && !allow_empty_message) {\n+\tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit due to empty commit message.\\n\"));\n \t\texit(1);\n \t}\n-\tif (template_untouched(&sb) && !allow_empty_message) {\n+\tif (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit; you did not edit the message.\\n\"));\n \t\texit(1);\ndiff --git a/sequencer.c b/sequencer.c\nindex 19dd575ed9b3b280a3fdabc9121a2e193d6984db..36e03d041f32bcc0fdd1fddebb33b23c7e4d8a70 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -691,6 +691,67 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static int rest_is_empty(const struct strbuf *sb, int start)\n+{\n+\tint i, eol;\n+\tconst char *nl;\n+\n+\t/* Check if the rest is just whitespace and Signed-off-by's. */\n+\tfor (i = start; i < sb->len; i++) {\n+\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n+\t\tif (nl)\n+\t\t\teol = nl - sb->buf;\n+\t\telse\n+\t\t\teol = sb->len;\n+\n+\t\tif (strlen(sign_off_header) <= eol - i &&\n+\t\t    starts_with(sb->buf + i, sign_off_header)) {\n+\t\t\ti = eol;\n+\t\t\tcontinue;\n+\t\t}\n+\t\twhile (i < eol)\n+\t\t\tif (!isspace(sb->buf[i++]))\n+\t\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+/*\n+ * Find out if the message in the strbuf contains only whitespace and\n+ * Signed-off-by lines.\n+ */\n+int message_is_empty(const struct strbuf *sb,\n+\t\t     enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\treturn rest_is_empty(sb, 0);\n+}\n+\n+/*\n+ * See if the user edited the message in the editor or left what\n+ * was in the template intact\n+ */\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tstruct strbuf tmpl = STRBUF_INIT;\n+\tconst char *start;\n+\n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\n+\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n+\t\treturn 0;\n+\n+\tstrbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n+\t\tstart = sb->buf;\n+\tstrbuf_release(&tmpl);\n+\treturn rest_is_empty(sb, start - sb->buf);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..82e57713a2940c5d65ccac013c3f42c55cc12baf 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -58,4 +58,15 @@ extern const char sign_off_header[];\n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n \n+enum commit_msg_cleanup_mode {\n+\tCOMMIT_MSG_CLEANUP_SPACE,\n+\tCOMMIT_MSG_CLEANUP_NONE,\n+\tCOMMIT_MSG_CLEANUP_SCISSORS,\n+\tCOMMIT_MSG_CLEANUP_ALL\n+};\n+\n+int message_is_empty(const struct strbuf *sb,\n+\t\t     enum commit_msg_cleanup_mode cleanup_mode);\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum commit_msg_cleanup_mode cleanup_mode);\n #endif\n-- \n2.15.0\n\n"},{"id":"332750","messageId":"20171117113452.26597-4-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171117113452.26597-1-phillip.wood@talktalk.net","subject":"[PATCH v3 3/8] Add a function to update HEAD after creating a commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-17T11:34:47Z","receivedAt":"2017-11-17T11:36:26Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd update_head_with_reflog() based on the code that updates HEAD\nafter committing in builtin/commit.c that can be called by 'git\ncommit' and other commands.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v2:\n     - updated commit message to reflect the change in function name\n     - style fixes\n    \n    changes since v1:\n     - rename update_head() to update_head_with_reflog()\n\n builtin/commit.c | 20 ++------------------\n sequencer.c      | 39 ++++++++++++++++++++++++++++++++++++++-\n sequencer.h      |  4 ++++\n 3 files changed, 44 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d958c2eb2adc9a29dab29340ce9b56daea41fecd..eb144556bf37b7bf357bd976b94305171b4fd159 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1610,13 +1610,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf author_ident = STRBUF_INIT;\n \tconst char *index_file, *reflog_msg;\n-\tchar *nl;\n \tstruct object_id oid;\n \tstruct commit_list *parents = NULL;\n \tstruct stat statbuf;\n \tstruct commit *current_head = NULL;\n \tstruct commit_extra_header *extra = NULL;\n-\tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n@@ -1739,25 +1737,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&author_ident);\n \tfree_commit_extra_headers(extra);\n \n-\tnl = strchr(sb.buf, '\\n');\n-\tif (nl)\n-\t\tstrbuf_setlen(&sb, nl + 1 - sb.buf);\n-\telse\n-\t\tstrbuf_addch(&sb, '\\n');\n-\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n-\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n-\n-\ttransaction = ref_transaction_begin(&err);\n-\tif (!transaction ||\n-\t    ref_transaction_update(transaction, \"HEAD\", &oid,\n-\t\t\t\t   current_head\n-\t\t\t\t   ? &current_head->object.oid : &null_oid,\n-\t\t\t\t   0, sb.buf, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n+\tif (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,\n+\t\t\t\t    &err)) {\n \t\trollback_index_files();\n \t\tdie(\"%s\", err.buf);\n \t}\n-\tref_transaction_free(transaction);\n \n \tunlink(git_path_cherry_pick_head());\n \tunlink(git_path_revert_head());\ndiff --git a/sequencer.c b/sequencer.c\nindex 36e03d041f32bcc0fdd1fddebb33b23c7e4d8a70..ef262980c5255d90ee023c0b29c6c1c628b3c7d2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1,10 +1,10 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"lockfile.h\"\n-#include \"sequencer.h\"\n #include \"dir.h\"\n #include \"object.h\"\n #include \"commit.h\"\n+#include \"sequencer.h\"\n #include \"tag.h\"\n #include \"run-command.h\"\n #include \"exec_cmd.h\"\n@@ -752,6 +752,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n \treturn rest_is_empty(sb, start - sb->buf);\n }\n \n+int update_head_with_reflog(const struct commit *old_head,\n+\t\t\t    const struct object_id *new_head,\n+\t\t\t    const char *action, const struct strbuf *msg,\n+\t\t\t    struct strbuf *err)\n+{\n+\tstruct ref_transaction *transaction;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *nl;\n+\tint ret = 0;\n+\n+\tif (action) {\n+\t\tstrbuf_addstr(&sb, action);\n+\t\tstrbuf_addstr(&sb, \": \");\n+\t}\n+\n+\tnl = strchr(msg->buf, '\\n');\n+\tif (nl) {\n+\t\tstrbuf_add(&sb, msg->buf, nl + 1 - msg->buf);\n+\t} else {\n+\t\tstrbuf_addbuf(&sb, msg);\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t}\n+\n+\ttransaction = ref_transaction_begin(err);\n+\tif (!transaction ||\n+\t    ref_transaction_update(transaction, \"HEAD\", new_head,\n+\t\t\t\t   old_head ? &old_head->object.oid : &null_oid,\n+\t\t\t\t   0, sb.buf, err) ||\n+\t    ref_transaction_commit(transaction, err)) {\n+\t\tret = -1;\n+\t}\n+\tref_transaction_free(transaction);\n+\tstrbuf_release(&sb);\n+\n+\treturn ret;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 82e57713a2940c5d65ccac013c3f42c55cc12baf..81a2098e900f0aca30e45ed7f19ae4bf3ce682f0 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -69,4 +69,8 @@ int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n \t\t       enum commit_msg_cleanup_mode cleanup_mode);\n+int update_head_with_reflog(const struct commit *old_head,\n+\t\t\t    const struct object_id *new_head,\n+\t\t\t    const char *action, const struct strbuf *msg,\n+\t\t\t    struct strbuf *err);\n #endif\n-- \n2.15.0\n\n"},{"id":"332751","messageId":"20171117113452.26597-5-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171117113452.26597-1-phillip.wood@talktalk.net","subject":"[PATCH v3 4/8] commit: move post-rewrite code to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-17T11:34:48Z","receivedAt":"2017-11-17T11:36:29Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can\nbe shared with other commands and add a new function\ncommit_post_rewrite() based on the code in builtin/commit.c that\nencapsulates rewriting notes and running the post-rewrite hook. Once\nthe sequencer learns how to create commits without forking 'git\ncommit' these functions will be used when squashing commits.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - reword commit message to explain why the code is being moved\n\n builtin/commit.c | 42 +-----------------------------------------\n sequencer.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |  2 ++\n 3 files changed, 50 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex eb144556bf37b7bf357bd976b94305171b4fd159..d251cfcebad3476c365492d83803e7821fdfdf2b 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -31,9 +31,7 @@\n #include \"gpg-interface.h\"\n #include \"column.h\"\n #include \"sequencer.h\"\n-#include \"notes-utils.h\"\n #include \"mailmap.h\"\n-#include \"sigchain.h\"\n \n static const char * const builtin_commit_usage[] = {\n \tN_(\"git commit [<options>] [--] <pathspec>...\"),\n@@ -1497,37 +1495,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \treturn git_status_config(k, v, s);\n }\n \n-static int run_rewrite_hook(const struct object_id *oldoid,\n-\t\t\t    const struct object_id *newoid)\n-{\n-\tstruct child_process proc = CHILD_PROCESS_INIT;\n-\tconst char *argv[3];\n-\tint code;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\targv[0] = find_hook(\"post-rewrite\");\n-\tif (!argv[0])\n-\t\treturn 0;\n-\n-\targv[1] = \"amend\";\n-\targv[2] = NULL;\n-\n-\tproc.argv = argv;\n-\tproc.in = -1;\n-\tproc.stdout_to_stderr = 1;\n-\n-\tcode = start_command(&proc);\n-\tif (code)\n-\t\treturn code;\n-\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n-\tsigchain_push(SIGPIPE, SIG_IGN);\n-\twrite_in_full(proc.in, sb.buf, sb.len);\n-\tclose(proc.in);\n-\tstrbuf_release(&sb);\n-\tsigchain_pop(SIGPIPE);\n-\treturn finish_command(&proc);\n-}\n-\n int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)\n {\n \tstruct argv_array hook_env = ARGV_ARRAY_INIT;\n@@ -1758,14 +1725,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \trerere(0);\n \trun_commit_hook(use_editor, get_index_file(), \"post-commit\", NULL);\n \tif (amend && !no_post_rewrite) {\n-\t\tstruct notes_rewrite_cfg *cfg;\n-\t\tcfg = init_copy_notes_for_rewrite(\"amend\");\n-\t\tif (cfg) {\n-\t\t\t/* we are amending, so current_head is not NULL */\n-\t\t\tcopy_note_for_rewrite(cfg, &current_head->object.oid, &oid);\n-\t\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n-\t\t}\n-\t\trun_rewrite_hook(&current_head->object.oid, &oid);\n+\t\tcommit_post_rewrite(current_head, &oid);\n \t}\n \tif (!quiet)\n \t\tprint_summary(prefix, &oid, !current_head);\ndiff --git a/sequencer.c b/sequencer.c\nindex ef262980c5255d90ee023c0b29c6c1c628b3c7d2..6bc8346d42bb3cb1d2dc6a2238dd1b38e4308914 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -21,6 +21,8 @@\n #include \"log-tree.h\"\n #include \"wt-status.h\"\n #include \"hashmap.h\"\n+#include \"notes-utils.h\"\n+#include \"sigchain.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -789,6 +791,51 @@ int update_head_with_reflog(const struct commit *old_head,\n \treturn ret;\n }\n \n+static int run_rewrite_hook(const struct object_id *oldoid,\n+\t\t\t    const struct object_id *newoid)\n+{\n+\tstruct child_process proc = CHILD_PROCESS_INIT;\n+\tconst char *argv[3];\n+\tint code;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\targv[0] = find_hook(\"post-rewrite\");\n+\tif (!argv[0])\n+\t\treturn 0;\n+\n+\targv[1] = \"amend\";\n+\targv[2] = NULL;\n+\n+\tproc.argv = argv;\n+\tproc.in = -1;\n+\tproc.stdout_to_stderr = 1;\n+\n+\tcode = start_command(&proc);\n+\tif (code)\n+\t\treturn code;\n+\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n+\tsigchain_push(SIGPIPE, SIG_IGN);\n+\twrite_in_full(proc.in, sb.buf, sb.len);\n+\tclose(proc.in);\n+\tstrbuf_release(&sb);\n+\tsigchain_pop(SIGPIPE);\n+\treturn finish_command(&proc);\n+}\n+\n+void commit_post_rewrite(const struct commit *old_head,\n+\t\t\t const struct object_id *new_head)\n+{\n+\tstruct notes_rewrite_cfg *cfg;\n+\n+\tcfg = init_copy_notes_for_rewrite(\"amend\");\n+\tif (cfg) {\n+\t\t/* we are amending, so old_head is not NULL */\n+\t\tcopy_note_for_rewrite(cfg, &old_head->object.oid, new_head);\n+\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n+\t}\n+\trun_rewrite_hook(&old_head->object.oid, new_head);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 81a2098e900f0aca30e45ed7f19ae4bf3ce682f0..ec13b679c40dc4012a84a761ea856d6e75953490 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -73,4 +73,6 @@ int update_head_with_reflog(const struct commit *old_head,\n \t\t\t    const struct object_id *new_head,\n \t\t\t    const char *action, const struct strbuf *msg,\n \t\t\t    struct strbuf *err);\n+void commit_post_rewrite(const struct commit *current_head,\n+\t\t\t const struct object_id *new_head);\n #endif\n-- \n2.15.0\n\n"},{"id":"332752","messageId":"20171117113452.26597-7-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171117113452.26597-1-phillip.wood@talktalk.net","subject":"[PATCH v3 6/8] sequencer: simplify adding Signed-off-by: trailer","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-17T11:34:50Z","receivedAt":"2017-11-17T11:36:31Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd the Signed-off-by: trailer in one place rather than adding it to\nthe message when doing a recursive merge and specifying '--signoff'\nwhen running 'git commit'. This means that if there are conflicts when\nmerging with a strategy other than 'recursive' the Signed-off-by:\ntrailer will be added if the user commits the resolution themselves\nwithout passing '--signoff' to 'git commit'. It also simplifies the\nin-process commit that is about to be added to the sequencer.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n sequencer.c | 8 +++-----\n 1 file changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a2cf6f5e06ffec5108f0faf43d1a4cb605264c3f..7400df5522037583108534755af6f542117667c2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \t\t\t_(action_name(opts)));\n \trollback_lock_file(&index_lock);\n \n-\tif (opts->signoff)\n-\t\tappend_signoff(msgbuf, 0, 0);\n-\n \tif (!clean)\n \t\tappend_conflicts_hint(msgbuf);\n \n@@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\targv_array_push(&cmd.args, \"--amend\");\n \tif (opts->gpg_sign)\n \t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n-\tif (opts->signoff)\n-\t\targv_array_push(&cmd.args, \"-s\");\n \tif (defmsg)\n \t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n \tif ((flags & CLEANUP_MSG))\n@@ -1347,6 +1342,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n+\tif (opts->signoff)\n+\t\tappend_signoff(&msgbuf, 0, 0);\n+\n \tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n \t\tres = -1;\n \telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n-- \n2.15.0\n\n"},{"id":"332753","messageId":"20171117113452.26597-8-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171117113452.26597-1-phillip.wood@talktalk.net","subject":"[PATCH v3 7/8] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-17T11:34:51Z","receivedAt":"2017-11-17T11:36:33Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nLoad default values for message cleanup and gpg signing of commits in\npreparation for committing without forking 'git commit'.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - renamed git_revert_config() to common_config()\n     - prefixed cleanup_mode constants to reflect the changes to patch 2\n       in this series\n\n builtin/rebase--helper.c | 13 ++++++++++++-\n builtin/revert.c         | 15 +++++++++++++--\n sequencer.c              | 34 ++++++++++++++++++++++++++++++++++\n sequencer.h              |  1 +\n 4 files changed, 60 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c\nindex f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644\n--- a/builtin/rebase--helper.c\n+++ b/builtin/rebase--helper.c\n@@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n \tNULL\n };\n \n+static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n+{\n+\tint status;\n+\n+\tstatus = git_sequencer_config(k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_default_config(k, v, NULL);\n+}\n+\n int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts = REPLAY_OPTS_INIT;\n@@ -39,7 +50,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_rebase_helper_config, NULL);\n \n \topts.action = REPLAY_INTERACTIVE_REBASE;\n \topts.allow_ff = 1;\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex b9d927eb09c9ed87c84681df1396f4e6d9b13c97..1938825efa6ad20ede5aba57f097863aeb33d1d5 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n+static int common_config(const char *k, const char *v, void *cb)\n+{\n+\tint status;\n+\n+\tstatus = git_sequencer_config(k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_default_config(k, v, NULL);\n+}\n+\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n@@ -208,7 +219,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tif (isatty(0))\n \t\topts.edit = 1;\n \topts.action = REPLAY_REVERT;\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(common_config, NULL);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"revert failed\"));\n@@ -221,7 +232,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \topts.action = REPLAY_PICK;\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(common_config, NULL);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"cherry-pick failed\"));\ndiff --git a/sequencer.c b/sequencer.c\nindex 7400df5522037583108534755af6f542117667c2..2c487f9feb83806e5db54a8675c9eb55cc62a5ec 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -688,6 +688,40 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static enum commit_msg_cleanup_mode default_msg_cleanup =\n+\t\t\t\t\t\tCOMMIT_MSG_CLEANUP_NONE;\n+static char *default_gpg_sign;\n+\n+int git_sequencer_config(const char *k, const char *v, void *cb)\n+{\n+\tif (!strcmp(k, \"commit.cleanup\")) {\n+\t\tint status;\n+\t\tconst char *s;\n+\n+\t\tstatus = git_config_string(&s, k, v);\n+\t\tif (status)\n+\t\t\treturn status;\n+\n+\t\tif (!strcmp(s, \"verbatim\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n+\t\telse if (!strcmp(s, \"whitespace\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n+\t\telse if (!strcmp(s, \"strip\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n+\t\telse if (!strcmp(s, \"scissors\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n+\n+\t\treturn status;\n+\t}\n+\n+\tif (!strcmp(k, \"commit.gpgsign\")) {\n+\t\tdefault_gpg_sign = git_config_bool(k, v) ? \"\" : NULL;\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_gpg_config(k, v, NULL);\n+}\n+\n static int rest_is_empty(const struct strbuf *sb, int start)\n {\n \tint i, eol;\ndiff --git a/sequencer.h b/sequencer.h\nindex 4f616c61a3f3869daf9f427b978c308d6094a978..77cb174b2aaf3972ebb9e6ec379252be96dedd3d 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -57,6 +57,7 @@ extern const char sign_off_header[];\n \n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n+int git_sequencer_config(const char *k, const char *v, void *cb);\n \n enum commit_msg_cleanup_mode {\n \tCOMMIT_MSG_CLEANUP_SPACE,\n-- \n2.15.0\n\n"},{"id":"332754","messageId":"20171117113452.26597-6-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171117113452.26597-1-phillip.wood@talktalk.net","subject":"[PATCH v3 5/8] commit: move print_commit_summary() to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-17T11:34:49Z","receivedAt":"2017-11-17T11:36:40Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove print_commit_summary() from builtin/commit.c to sequencer.c so it\ncan be shared with other commands. The function is modified by\nchanging the last argument to a flag so callers can specify whether\nthey want to show the author date in addition to specifying if this is\nan initial commit.\n\nIf the sequencer dies in print_commit_summary() (which can only happen\nwhen cherry-picking or reverting) then neither the todo list nor the\nabort safety file are updated to reflect the commit that was just\nmade. print_commit_summary() can die if:\n\n - HEAD cannot be resolved either because some other process is\n   updating it (which is bad news in the middle of a cherry-pick) or\n   because it is corrupt.\n\n - log_tree_commit() cannot read some objects.\n\nIn all those cases dying will leave the sequencer in a sane state for\naborting; 'git cherry-pick --abort' will rewind HEAD to the last\nsuccessful commit before there was a problem with HEAD or the object\ndatabase. If the user somehow fixes the problem and runs 'git\ncherry-pick --continue' then the sequencer will try and pick the same\ncommit again which may or may not be what the user wants depending on\nwhat caused print_commit_summary() to die. If print_commit_summary()\nreturned an error instead then update_abort_safety_file() would try to\nresolve HEAD which may or may not be successful. If it is successful\nthen running 'git rebase --abort' would not rewind HEAD to the last\nsuccessful commit which is not what we want.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v2:\n     - expanded commit message to explain why it is ok to die in\n       print_commit_summary() and dropped the next patch which made it\n       return an error instead.\n     - style fixes.\n    \n    changes since v1:\n     - convert flags passed to print_commit_summary() to unsigned int\n\n builtin/commit.c | 128 ++++---------------------------------------------------\n sequencer.c      | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |   5 +++\n 3 files changed, 133 insertions(+), 119 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d251cfcebad3476c365492d83803e7821fdfdf2b..2043479d37873671d43124dc0cb509d6d9247baa 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {\n \tNULL\n };\n \n-static const char implicit_ident_advice_noconfig[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly. Run the\\n\"\n-\"following command and follow the instructions in your editor to edit\\n\"\n-\"your configuration file:\\n\"\n-\"\\n\"\n-\"    git config --global --edit\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n-static const char implicit_ident_advice_config[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly:\\n\"\n-\"\\n\"\n-\"    git config --global user.name \\\"Your Name\\\"\\n\"\n-\"    git config --global user.email you@example.com\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n static const char empty_amend_advice[] =\n N_(\"You asked to amend the most recent commit, but doing so would make\\n\"\n \"it empty. You can repeat your command with --allow-empty, or you can\\n\"\n@@ -1374,98 +1349,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n-static const char *implicit_ident_advice(void)\n-{\n-\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n-\tchar *xdg_config = xdg_config_home(\"config\");\n-\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n-\n-\tfree(user_config);\n-\tfree(xdg_config);\n-\n-\tif (config_exists)\n-\t\treturn _(implicit_ident_advice_config);\n-\telse\n-\t\treturn _(implicit_ident_advice_noconfig);\n-\n-}\n-\n-static void print_summary(const char *prefix, const struct object_id *oid,\n-\t\t\t  int initial_commit)\n-{\n-\tstruct rev_info rev;\n-\tstruct commit *commit;\n-\tstruct strbuf format = STRBUF_INIT;\n-\tconst char *head;\n-\tstruct pretty_print_context pctx = {0};\n-\tstruct strbuf author_ident = STRBUF_INIT;\n-\tstruct strbuf committer_ident = STRBUF_INIT;\n-\n-\tcommit = lookup_commit(oid);\n-\tif (!commit)\n-\t\tdie(_(\"couldn't look up newly created commit\"));\n-\tif (parse_commit(commit))\n-\t\tdie(_(\"could not parse newly created commit\"));\n-\n-\tstrbuf_addstr(&format, \"format:%h] %s\");\n-\n-\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n-\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n-\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n-\t\tstrbuf_addstr(&format, \"\\n Author: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n-\t}\n-\tif (author_date_is_interesting()) {\n-\t\tstruct strbuf date = STRBUF_INIT;\n-\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n-\t\tstrbuf_addstr(&format, \"\\n Date: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &date);\n-\t\tstrbuf_release(&date);\n-\t}\n-\tif (!committer_ident_sufficiently_given()) {\n-\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n-\t\tif (advice_implicit_identity) {\n-\t\t\tstrbuf_addch(&format, '\\n');\n-\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n-\t\t}\n-\t}\n-\tstrbuf_release(&author_ident);\n-\tstrbuf_release(&committer_ident);\n-\n-\tinit_revisions(&rev, prefix);\n-\tsetup_revisions(0, NULL, &rev, NULL);\n-\n-\trev.diff = 1;\n-\trev.diffopt.output_format =\n-\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n-\n-\trev.verbose_header = 1;\n-\trev.show_root_diff = 1;\n-\tget_commit_format(format.buf, &rev);\n-\trev.always_show_header = 0;\n-\trev.diffopt.detect_rename = 1;\n-\trev.diffopt.break_opt = 0;\n-\tdiff_setup_done(&rev.diffopt);\n-\n-\thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n-\tif (!head)\n-\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n-\tif (!strcmp(head, \"HEAD\"))\n-\t\thead = _(\"detached HEAD\");\n-\telse\n-\t\tskip_prefix(head, \"refs/heads/\", &head);\n-\tprintf(\"[%s%s \", head, initial_commit ? _(\" (root-commit)\") : \"\");\n-\n-\tif (!log_tree_commit(&rev, commit)) {\n-\t\trev.always_show_header = 1;\n-\t\trev.use_terminator = 1;\n-\t\tlog_tree_commit(&rev, commit);\n-\t}\n-\n-\tstrbuf_release(&format);\n-}\n-\n static int git_commit_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n@@ -1727,8 +1610,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (amend && !no_post_rewrite) {\n \t\tcommit_post_rewrite(current_head, &oid);\n \t}\n-\tif (!quiet)\n-\t\tprint_summary(prefix, &oid, !current_head);\n+\tif (!quiet) {\n+\t\tunsigned int flags = 0;\n+\n+\t\tif (!current_head)\n+\t\t\tflags |= SUMMARY_INITIAL_COMMIT;\n+\t\tif (author_date_is_interesting())\n+\t\t\tflags |= SUMMARY_SHOW_AUTHOR_DATE;\n+\t\tprint_commit_summary(prefix, &oid, flags);\n+\t}\n \n \tUNLEAK(err);\n \tUNLEAK(sb);\ndiff --git a/sequencer.c b/sequencer.c\nindex 6bc8346d42bb3cb1d2dc6a2238dd1b38e4308914..a2cf6f5e06ffec5108f0faf43d1a4cb605264c3f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -836,6 +836,125 @@ void commit_post_rewrite(const struct commit *old_head,\n \trun_rewrite_hook(&old_head->object.oid, new_head);\n }\n \n+static const char implicit_ident_advice_noconfig[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly. Run the\\n\"\n+\"following command and follow the instructions in your editor to edit\\n\"\n+\"your configuration file:\\n\"\n+\"\\n\"\n+\"    git config --global --edit\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char implicit_ident_advice_config[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly:\\n\"\n+\"\\n\"\n+\"    git config --global user.name \\\"Your Name\\\"\\n\"\n+\"    git config --global user.email you@example.com\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char *implicit_ident_advice(void)\n+{\n+\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n+\tchar *xdg_config = xdg_config_home(\"config\");\n+\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n+\n+\tfree(user_config);\n+\tfree(xdg_config);\n+\n+\tif (config_exists)\n+\t\treturn _(implicit_ident_advice_config);\n+\telse\n+\t\treturn _(implicit_ident_advice_noconfig);\n+\n+}\n+\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  unsigned int flags)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf format = STRBUF_INIT;\n+\tconst char *head;\n+\tstruct pretty_print_context pctx = {0};\n+\tstruct strbuf author_ident = STRBUF_INIT;\n+\tstruct strbuf committer_ident = STRBUF_INIT;\n+\n+\tcommit = lookup_commit(oid);\n+\tif (!commit)\n+\t\tdie(_(\"couldn't look up newly created commit\"));\n+\tif (parse_commit(commit))\n+\t\tdie(_(\"could not parse newly created commit\"));\n+\n+\tstrbuf_addstr(&format, \"format:%h] %s\");\n+\n+\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n+\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n+\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n+\t\tstrbuf_addstr(&format, \"\\n Author: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n+\t}\n+\tif (flags & SUMMARY_SHOW_AUTHOR_DATE) {\n+\t\tstruct strbuf date = STRBUF_INIT;\n+\n+\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n+\t\tstrbuf_addstr(&format, \"\\n Date: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &date);\n+\t\tstrbuf_release(&date);\n+\t}\n+\tif (!committer_ident_sufficiently_given()) {\n+\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n+\t\tif (advice_implicit_identity) {\n+\t\t\tstrbuf_addch(&format, '\\n');\n+\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n+\t\t}\n+\t}\n+\tstrbuf_release(&author_ident);\n+\tstrbuf_release(&committer_ident);\n+\n+\tinit_revisions(&rev, prefix);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.diff = 1;\n+\trev.diffopt.output_format =\n+\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n+\n+\trev.verbose_header = 1;\n+\trev.show_root_diff = 1;\n+\tget_commit_format(format.buf, &rev);\n+\trev.always_show_header = 0;\n+\trev.diffopt.detect_rename = 1;\n+\trev.diffopt.break_opt = 0;\n+\tdiff_setup_done(&rev.diffopt);\n+\n+\thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n+\tif (!head)\n+\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n+\tif (!strcmp(head, \"HEAD\"))\n+\t\thead = _(\"detached HEAD\");\n+\telse\n+\t\tskip_prefix(head, \"refs/heads/\", &head);\n+\tprintf(\"[%s%s \", head, (flags & SUMMARY_INITIAL_COMMIT) ?\n+\t\t\t\t\t\t_(\" (root-commit)\") : \"\");\n+\n+\tif (!log_tree_commit(&rev, commit)) {\n+\t\trev.always_show_header = 1;\n+\t\trev.use_terminator = 1;\n+\t\tlog_tree_commit(&rev, commit);\n+\t}\n+\n+\tstrbuf_release(&format);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex ec13b679c40dc4012a84a761ea856d6e75953490..4f616c61a3f3869daf9f427b978c308d6094a978 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -75,4 +75,9 @@ int update_head_with_reflog(const struct commit *old_head,\n \t\t\t    struct strbuf *err);\n void commit_post_rewrite(const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n+\n+#define SUMMARY_INITIAL_COMMIT   (1 << 0)\n+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  unsigned int flags);\n #endif\n-- \n2.15.0\n\n"},{"id":"332755","messageId":"20171117113452.26597-9-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171117113452.26597-1-phillip.wood@talktalk.net","subject":"[PATCH v3 8/8] sequencer: try to commit without forking 'git commit'","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-17T11:34:52Z","receivedAt":"2017-11-17T11:36:51Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nIf the commit message does not need to be edited then create the\ncommit without forking 'git commit'. Taking the best time of ten runs\nwith a warm cache this reduces the time taken to cherry-pick 10\ncommits by 27% (from 282ms to 204ms), and the time taken by 'git\nrebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on\nmy computer running linux. Some of greater saving for rebase is\nbecause it no longer wastes time creating the commit summary just to\nthrow it away.\n\nThe code to create the commit is based on builtin/commit.c. It is\nsimplified as it doesn't have to deal with merges and modified so that\nit does not die but returns an error to make sure the sequencer exits\ncleanly, as it would when forking 'git commit'\n\nEven when not forking 'git commit' the commit message is written to a\nfile and CHERRY_PICK_HEAD is created unnecessarily. This could be\neliminated in future. I hacked up a version that does not write these\nfiles and just passed an strbuf (with the wrong message for fixup and\nsquash commands) to do_commit() but I couldn't measure any significant\ntime difference when running cherry-pick or rebase. I think\neliminating the writes properly for rebase would require a bit of\neffort as the code would need to be restructured.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v2:\n     - style fixes\n    \n    changes since v1:\n     - added comments to explain return value of try_to_commit()\n     - removed unnecessary NULL tests before calling free()\n     - style cleanups\n     - corrected commit message\n     - prefixed cleanup_mode constants to reflect the changes to patch 2\n       in this series\n\n sequencer.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 176 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 2c487f9feb83806e5db54a8675c9eb55cc62a5ec..8362d8fe9f19c61e6339b7a325771344c7a136a8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)\n \treturn 0;\n }\n \n+static char *get_author(const char *message)\n+{\n+\tsize_t len;\n+\tconst char *a;\n+\n+\ta = find_commit_header(message, \"author\", &len);\n+\tif (a)\n+\t\treturn xmemdupz(a, len);\n+\n+\treturn NULL;\n+}\n+\n static const char staged_changes_advice[] =\n N_(\"you have staged changes in your working tree\\n\"\n \"If these changes are meant to be squashed into the previous commit, run:\\n\"\n@@ -984,6 +996,160 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n \tstrbuf_release(&format);\n }\n \n+static int parse_head(struct commit **head)\n+{\n+\tstruct commit *current_head;\n+\tstruct object_id oid;\n+\n+\tif (get_oid(\"HEAD\", &oid)) {\n+\t\tcurrent_head = NULL;\n+\t} else {\n+\t\tcurrent_head = lookup_commit_reference(&oid);\n+\t\tif (!current_head)\n+\t\t\treturn error(_(\"could not parse HEAD\"));\n+\t\tif (oidcmp(&oid, &current_head->object.oid)) {\n+\t\t\twarning(_(\"HEAD %s is not a commit!\"),\n+\t\t\t\toid_to_hex(&oid));\n+\t\t}\n+\t\tif (parse_commit(current_head))\n+\t\t\treturn error(_(\"could not parse HEAD commit\"));\n+\t}\n+\t*head = current_head;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Try to commit without forking 'git commit'. In some cases we need\n+ * to run 'git commit' to display an error message\n+ *\n+ * Returns:\n+ *  -1 - error unable to commit\n+ *   0 - success\n+ *   1 - run 'git commit'\n+ */\n+static int try_to_commit(struct strbuf *msg, const char *author,\n+\t\t\t struct replay_opts *opts, unsigned int flags,\n+\t\t\t struct object_id *oid)\n+{\n+\tstruct object_id tree;\n+\tstruct commit *current_head;\n+\tstruct commit_list *parents = NULL;\n+\tstruct commit_extra_header *extra = NULL;\n+\tstruct strbuf err = STRBUF_INIT;\n+\tstruct strbuf amend_msg = STRBUF_INIT;\n+\tchar *amend_author = NULL;\n+\tconst char *gpg_sign;\n+\tenum commit_msg_cleanup_mode cleanup;\n+\tint res = 0;\n+\n+\tif (parse_head(&current_head))\n+\t\treturn -1;\n+\n+\tif (flags & AMEND_MSG) {\n+\t\tconst char *exclude_gpgsig[] = { \"gpgsig\", NULL };\n+\t\tconst char *out_enc = get_commit_output_encoding();\n+\t\tconst char *message = logmsg_reencode(current_head, NULL,\n+\t\t\t\t\t\t      out_enc);\n+\n+\t\tif (!msg) {\n+\t\t\tconst char *orig_message = NULL;\n+\n+\t\t\tfind_commit_subject(message, &orig_message);\n+\t\t\tmsg = &amend_msg;\n+\t\t\tstrbuf_addstr(msg, orig_message);\n+\t\t}\n+\t\tauthor = amend_author = get_author(message);\n+\t\tunuse_commit_buffer(current_head, message);\n+\t\tif (!author) {\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t\t\tgoto out;\n+\t\t}\n+\t\tparents = copy_commit_list(current_head->parents);\n+\t\textra = read_commit_extra_headers(current_head, exclude_gpgsig);\n+\t} else if (current_head) {\n+\t\tcommit_list_insert(current_head, &parents);\n+\t}\n+\n+\tcleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t\t  default_msg_cleanup;\n+\tif (cleanup != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);\n+\tif (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {\n+\t\tres = 1; /* run 'git commit' to display error message */\n+\t\tgoto out;\n+\t}\n+\n+\tgpg_sign = opts->gpg_sign ? opts->gpg_sign : default_gpg_sign;\n+\n+\tif (write_cache_as_tree(tree.hash, 0, NULL)) {\n+\t\tres = error(_(\"git write-tree failed to write a tree\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?\n+\t\t\t\t\t      &current_head->tree->object.oid :\n+\t\t\t\t\t      &empty_tree_oid, &tree)) {\n+\t\tres = 1; /* run 'git commit' to display error message */\n+\t\tgoto out;\n+\t}\n+\n+\tif (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,\n+\t\t\t\t oid->hash, author, gpg_sign, extra)) {\n+\t\tres = error(_(\"failed to write commit object\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (update_head_with_reflog(current_head, oid,\n+\t\t\t\t    getenv(\"GIT_REFLOG_ACTION\"), msg, &err)) {\n+\t\tres = error(\"%s\", err.buf);\n+\t\tgoto out;\n+\t}\n+\n+\tif (flags & AMEND_MSG)\n+\t\tcommit_post_rewrite(current_head, oid);\n+\n+out:\n+\tfree_commit_extra_headers(extra);\n+\tstrbuf_release(&err);\n+\tstrbuf_release(&amend_msg);\n+\tfree(amend_author);\n+\n+\treturn res;\n+}\n+\n+static int do_commit(const char *msg_file, const char *author,\n+\t\t     struct replay_opts *opts, unsigned int flags)\n+{\n+\tint res = 1;\n+\n+\tif (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {\n+\t\tstruct object_id oid;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)\n+\t\t\treturn error_errno(_(\"unable to read commit message \"\n+\t\t\t\t\t     \"from '%s'\"),\n+\t\t\t\t\t   msg_file);\n+\n+\t\tres = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,\n+\t\t\t\t    &oid);\n+\t\tstrbuf_release(&sb);\n+\t\tif (!res) {\n+\t\t\tunlink(git_path_cherry_pick_head());\n+\t\t\tunlink(git_path_merge_msg());\n+\t\t\tif (!is_rebase_i(opts))\n+\t\t\t\tres = print_commit_summary(NULL, &oid,\n+\t\t\t\t\t\tSUMMARY_SHOW_AUTHOR_DATE);\n+\t\t\treturn res;\n+\t\t}\n+\t}\n+\tif (res == 1)\n+\t\treturn run_git_commit(msg_file, opts, flags);\n+\n+\treturn res;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\n@@ -1235,6 +1401,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \tstruct object_id head;\n \tstruct commit *base, *next, *parent;\n \tconst char *base_label, *next_label;\n+\tchar *author = NULL;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n \tint res, unborn = 0, allow;\n@@ -1351,6 +1518,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t\tstrbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));\n \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n \t\t}\n+\t\tif (!is_fixup(command))\n+\t\t\tauthor = get_author(msg.message);\n \t}\n \n \tif (command == TODO_REWORD)\n@@ -1436,9 +1605,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t} else if (allow)\n \t\tflags |= ALLOW_EMPTY;\n-\tif (!opts->no_commit)\n+\tif (!opts->no_commit) {\n fast_forward_edit:\n-\t\tres = run_git_commit(msg_file, opts, flags);\n+\t\tif (author || command == TODO_REVERT || (flags & AMEND_MSG))\n+\t\t\tres = do_commit(msg_file, author, opts, flags);\n+\t\telse\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t}\n \n \tif (!res && final_fixup) {\n \t\tunlink(rebase_path_fixup_msg());\n@@ -1447,6 +1620,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \n leave:\n \tfree_message(commit, &msg);\n+\tfree(author);\n \tupdate_abort_safety_file();\n \n \treturn res;\n-- \n2.15.0\n\n"},{"id":"332789","messageId":"xmqq4lpsqmm5.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171117113452.26597-1-phillip.wood@talktalk.net","subject":"Re: [PATCH v3 0/8] sequencer: don't fork git commit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-18T03:41:54Z","receivedAt":"2017-11-18T03:42:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> I've updated these based on the feedback for v2. I've dropped the\n> patch that stopped print_commit_summary() from dying as I think it is\n> better to die than return an error (see the commit message of the\n> patch that adds print_commit_summary() for the reasoning). Apart from\n> that they're minor changes - style fixes and a reworded a commit message.\n\nThanks for further polishing this topic; I found nothing in the\nupdate that was questionable.  Will replace.\n\nWith this, perhaps it is ready for 'next'?\n"},{"id":"332791","messageId":"xmqqvai8p7c7.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"xmqq4lpsqmm5.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 0/8] sequencer: don't fork git commit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-18T03:57:12Z","receivedAt":"2017-11-18T03:57:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n>\n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> I've updated these based on the feedback for v2. I've dropped the\n>> patch that stopped print_commit_summary() from dying as I think it is\n>> better to die than return an error (see the commit message of the\n>> patch that adds print_commit_summary() for the reasoning). Apart from\n>> that they're minor changes - style fixes and a reworded a commit message.\n>\n> Thanks for further polishing this topic; I found nothing in the\n> update that was questionable.  Will replace.\n>\n> With this, perhaps it is ready for 'next'?\n\nNot really.  I needed at least this to get it even compile, which\nhints that I do not yet know what _else_ I missed by skimming this\nround of the series.\n\n sequencer.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 37460db6b1..63cfb6ddd9 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1139,8 +1139,8 @@ static int do_commit(const char *msg_file, const char *author,\n \t\t\tunlink(git_path_cherry_pick_head());\n \t\t\tunlink(git_path_merge_msg());\n \t\t\tif (!is_rebase_i(opts))\n-\t\t\t\tres = print_commit_summary(NULL, &oid,\n-\t\t\t\t\t\tSUMMARY_SHOW_AUTHOR_DATE);\n+\t\t\t\tprint_commit_summary(NULL, &oid,\n+\t\t\t\t\t\t     SUMMARY_SHOW_AUTHOR_DATE);\n \t\t\treturn res;\n \t\t}\n \t}\n-- \n2.15.0-372-g9a6f8facfd\n\n"},{"id":"332798","messageId":"61d0e704-b6f3-19de-b1cc-90f56b04229f@talktalk.net","threadId":"46827","inReplyTo":"xmqqvai8p7c7.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 0/8] sequencer: don't fork git commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-18T11:32:38Z","receivedAt":"2017-11-18T11:32:47Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 18/11/17 03:57, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n>> Phillip Wood <phillip.wood@talktalk.net> writes:\n>>\n>>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>>\n>>> I've updated these based on the feedback for v2. I've dropped the\n>>> patch that stopped print_commit_summary() from dying as I think it is\n>>> better to die than return an error (see the commit message of the\n>>> patch that adds print_commit_summary() for the reasoning). Apart from\n>>> that they're minor changes - style fixes and a reworded a commit message.\n>>\n>> Thanks for further polishing this topic; I found nothing in the\n>> update that was questionable.  Will replace.\n>>\n>> With this, perhaps it is ready for 'next'?\n> \n> Not really.  I needed at least this to get it even compile, which\n> hints that I do not yet know what _else_ I missed by skimming this\n> round of the series.\n\nSorry I'm not sure what happened there, by branch has the missing 'res =\n' something must have happened to the patches. I'll sort it out and resend\n\nPhillip\n\n>  sequencer.c | 4 ++--\n>  1 file changed, 2 insertions(+), 2 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index 37460db6b1..63cfb6ddd9 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1139,8 +1139,8 @@ static int do_commit(const char *msg_file, const char *author,\n>  \t\t\tunlink(git_path_cherry_pick_head());\n>  \t\t\tunlink(git_path_merge_msg());\n>  \t\t\tif (!is_rebase_i(opts))\n> -\t\t\t\tres = print_commit_summary(NULL, &oid,\n> -\t\t\t\t\t\tSUMMARY_SHOW_AUTHOR_DATE);\n> +\t\t\t\tprint_commit_summary(NULL, &oid,\n> +\t\t\t\t\t\t     SUMMARY_SHOW_AUTHOR_DATE);\n>  \t\t\treturn res;\n>  \t\t}\n>  \t}\n> \n\n"},{"id":"332801","messageId":"50555a22-3b48-ce67-5deb-9bd76aa7a10e@talktalk.net","threadId":"46827","inReplyTo":"xmqqvai8p7c7.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 0/8] sequencer: don't fork git commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-18T14:33:08Z","receivedAt":"2017-11-18T14:33:19Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 18/11/17 03:57, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n>> Phillip Wood <phillip.wood@talktalk.net> writes:\n>>\n>>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>>\n>>> I've updated these based on the feedback for v2. I've dropped the\n>>> patch that stopped print_commit_summary() from dying as I think it is\n>>> better to die than return an error (see the commit message of the\n>>> patch that adds print_commit_summary() for the reasoning). Apart from\n>>> that they're minor changes - style fixes and a reworded a commit message.\n>>\n>> Thanks for further polishing this topic; I found nothing in the\n>> update that was questionable.  Will replace.\n>>\n>> With this, perhaps it is ready for 'next'?\n> \n> Not really.  I needed at least this to get it even compile, which\n> hints that I do not yet know what _else_ I missed by skimming this\n> round of the series.\n\nMy apologies, it seems that I was half alseep yesterday morning and in\nmy haste to update these patches I wasn't paying proper attention to\nwhat I was doing. For some reason after I dropped the patch that\nconverted print_commit_summary() to return an error rather than die I\nforgot to amend the last patch to match and then compounded the mistake\nby forgetting to compile and test properly before sending them. I think\nthey're okay now but I'll double check the changes before sending again\nin case there are any other embarrassing mistakes lurking.\n\nAs the white rabbit said \"The hurrier I go the behinder I get\"\n\nPhillip\n\n\n>  sequencer.c | 4 ++--\n>  1 file changed, 2 insertions(+), 2 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index 37460db6b1..63cfb6ddd9 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1139,8 +1139,8 @@ static int do_commit(const char *msg_file, const char *author,\n>  \t\t\tunlink(git_path_cherry_pick_head());\n>  \t\t\tunlink(git_path_merge_msg());\n>  \t\t\tif (!is_rebase_i(opts))\n> -\t\t\t\tres = print_commit_summary(NULL, &oid,\n> -\t\t\t\t\t\tSUMMARY_SHOW_AUTHOR_DATE);\n> +\t\t\t\tprint_commit_summary(NULL, &oid,\n> +\t\t\t\t\t\t     SUMMARY_SHOW_AUTHOR_DATE);\n>  \t\t\treturn res;\n>  \t\t}\n>  \t}\n> \n\n"},{"id":"333430","messageId":"20171124110758.9406-2-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171124110758.9406-1-phillip.wood@talktalk.net","subject":"[PATCH v4 1/9] t3404: check intermediate squash messages","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T11:07:50Z","receivedAt":"2017-11-24T11:08:19Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nWhen there is more than one squash/fixup command in a row check the\nintermediate messages are correct.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n t/t3404-rebase-interactive.sh | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 6a82d1ed876dd5d1073dc63be8ba5720adbf12e3..9ed0a244e6cdf34c7caca8232f0c0a8cf4864c42 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -453,6 +453,10 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa\n \t\tgit rebase -i $base &&\n \tgit cat-file commit HEAD | sed -e 1,/^\\$/d > actual-squash-fixup &&\n \ttest_cmp expect-squash-fixup actual-squash-fixup &&\n+\tgit cat-file commit HEAD@{2} |\n+\t\tgrep \"^# This is a combination of 3 commits\\.\"  &&\n+\tgit cat-file commit HEAD@{3} |\n+\t\tgrep \"^# This is a combination of 2 commits\\.\"  &&\n \tgit checkout to-be-rebased &&\n \tgit branch -D squash-fixup\n '\n-- \n2.15.0\n\n"},{"id":"333431","messageId":"20171124110758.9406-1-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[PATCH v4 0/9] sequencer: don't fork git commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T11:07:49Z","receivedAt":"2017-11-24T11:08:26Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nI've updated the patches to fix the embarassing build failure in\nv3. I've also added a patch to remove the known breakage from some of\nthe tests in t3512/t3513 that now pass - someone who knows about\nsubmodules should check this. The only other change is to interpret\ncommit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE when loading\nthe default value for message cleanups to be consistent with 'git\ncommit'. (I can't imagine many people have that value set in their\nconfig)\n\nHere's the original summary:\nThese patches teach the sequencer to create commits without forking\ngit commit when the commit message does not need to be edited. This\nspeeds up cherry picking 10 commits by 26% and picking 10 commits with\nrebase --continue by 44%. The first few patches move bits of\nbuiltin/commit.c to sequencer.c. The last two patches actually\nimplement creating commits in sequencer.c.\n\nPhillip Wood (9):\n  t3404: check intermediate squash messages\n  commit: move empty message checks to libgit\n  Add a function to update HEAD after creating a commit\n  commit: move post-rewrite code to libgit\n  commit: move print_commit_summary() to libgit\n  sequencer: simplify adding Signed-off-by: trailer\n  sequencer: load commit related config\n  sequencer: try to commit without forking 'git commit'\n  t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1\n\n builtin/commit.c                 | 289 +++--------------------\n builtin/rebase--helper.c         |  13 +-\n builtin/revert.c                 |  15 +-\n sequencer.c                      | 486 ++++++++++++++++++++++++++++++++++++++-\n sequencer.h                      |  23 ++\n t/t3404-rebase-interactive.sh    |   4 +\n t/t3512-cherry-pick-submodule.sh |   1 -\n t/t3513-revert-submodule.sh      |   1 -\n 8 files changed, 561 insertions(+), 271 deletions(-)\n\n-- \n2.15.0\n\n"},{"id":"333432","messageId":"20171124110758.9406-7-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171124110758.9406-1-phillip.wood@talktalk.net","subject":"[PATCH v4 6/9] sequencer: simplify adding Signed-off-by: trailer","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T11:07:55Z","receivedAt":"2017-11-24T11:08:27Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd the Signed-off-by: trailer in one place rather than adding it to\nthe message when doing a recursive merge and specifying '--signoff'\nwhen running 'git commit'. This means that if there are conflicts when\nmerging with a strategy other than 'recursive' the Signed-off-by:\ntrailer will be added if the user commits the resolution themselves\nwithout passing '--signoff' to 'git commit'. It also simplifies the\nin-process commit that is about to be added to the sequencer.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n sequencer.c | 8 +++-----\n 1 file changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a2cf6f5e06ffec5108f0faf43d1a4cb605264c3f..7400df5522037583108534755af6f542117667c2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \t\t\t_(action_name(opts)));\n \trollback_lock_file(&index_lock);\n \n-\tif (opts->signoff)\n-\t\tappend_signoff(msgbuf, 0, 0);\n-\n \tif (!clean)\n \t\tappend_conflicts_hint(msgbuf);\n \n@@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\targv_array_push(&cmd.args, \"--amend\");\n \tif (opts->gpg_sign)\n \t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n-\tif (opts->signoff)\n-\t\targv_array_push(&cmd.args, \"-s\");\n \tif (defmsg)\n \t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n \tif ((flags & CLEANUP_MSG))\n@@ -1347,6 +1342,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n+\tif (opts->signoff)\n+\t\tappend_signoff(&msgbuf, 0, 0);\n+\n \tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n \t\tres = -1;\n \telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n-- \n2.15.0\n\n"},{"id":"333433","messageId":"20171124110758.9406-3-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171124110758.9406-1-phillip.wood@talktalk.net","subject":"[PATCH v4 2/9] commit: move empty message checks to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T11:07:51Z","receivedAt":"2017-11-24T11:08:29Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove the functions that check for empty messages from bulitin/commit.c\nto sequencer.c so they can be shared with other commands. The\nfunctions are refactored to take an explicit cleanup mode and template\nfilename passed by the caller.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - prefix cleanup_mode enum and constants with commit_msg_\n\n builtin/commit.c | 99 +++++++++++---------------------------------------------\n sequencer.c      | 61 ++++++++++++++++++++++++++++++++++\n sequencer.h      | 11 +++++++\n 3 files changed, 91 insertions(+), 80 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 8a877014145435516930c787dec37b8c4ac3da90..d958c2eb2adc9a29dab29340ce9b56daea41fecd 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -128,12 +128,7 @@ static char *sign_commit;\n  * if editor is used, and only the whitespaces if the message\n  * is specified explicitly.\n  */\n-static enum {\n-\tCLEANUP_SPACE,\n-\tCLEANUP_NONE,\n-\tCLEANUP_SCISSORS,\n-\tCLEANUP_ALL\n-} cleanup_mode;\n+static enum commit_msg_cleanup_mode cleanup_mode;\n static const char *cleanup_arg;\n \n static enum commit_whence whence;\n@@ -673,7 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tstruct strbuf sb = STRBUF_INIT;\n \tconst char *hook_arg1 = NULL;\n \tconst char *hook_arg2 = NULL;\n-\tint clean_message_contents = (cleanup_mode != CLEANUP_NONE);\n+\tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n \n \t/* This checks and barfs if author is badly specified */\n@@ -812,7 +807,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -832,14 +827,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t}\n \n \t\tfprintf(s->fp, \"\\n\");\n-\t\tif (cleanup_mode == CLEANUP_ALL)\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t whence == FROM_COMMIT)\n \t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* CLEANUP_SPACE, that is. */\n+\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\n@@ -984,65 +980,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \treturn 1;\n }\n \n-static int rest_is_empty(struct strbuf *sb, int start)\n-{\n-\tint i, eol;\n-\tconst char *nl;\n-\n-\t/* Check if the rest is just whitespace and Signed-off-by's. */\n-\tfor (i = start; i < sb->len; i++) {\n-\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n-\t\tif (nl)\n-\t\t\teol = nl - sb->buf;\n-\t\telse\n-\t\t\teol = sb->len;\n-\n-\t\tif (strlen(sign_off_header) <= eol - i &&\n-\t\t    starts_with(sb->buf + i, sign_off_header)) {\n-\t\t\ti = eol;\n-\t\t\tcontinue;\n-\t\t}\n-\t\twhile (i < eol)\n-\t\t\tif (!isspace(sb->buf[i++]))\n-\t\t\t\treturn 0;\n-\t}\n-\n-\treturn 1;\n-}\n-\n-/*\n- * Find out if the message in the strbuf contains only whitespace and\n- * Signed-off-by lines.\n- */\n-static int message_is_empty(struct strbuf *sb)\n-{\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\treturn rest_is_empty(sb, 0);\n-}\n-\n-/*\n- * See if the user edited the message in the editor or left what\n- * was in the template intact\n- */\n-static int template_untouched(struct strbuf *sb)\n-{\n-\tstruct strbuf tmpl = STRBUF_INIT;\n-\tconst char *start;\n-\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\n-\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n-\t\treturn 0;\n-\n-\tstrbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);\n-\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n-\t\tstart = sb->buf;\n-\tstrbuf_release(&tmpl);\n-\treturn rest_is_empty(sb, start - sb->buf);\n-}\n-\n static const char *find_author_by_nickname(const char *name)\n {\n \tstruct rev_info revs;\n@@ -1227,15 +1164,17 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n \tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\tcleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;\n+\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n \telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\tcleanup_mode = CLEANUP_NONE;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_NONE;\n \telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\tcleanup_mode = CLEANUP_SPACE;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_SPACE;\n \telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\tcleanup_mode = CLEANUP_ALL;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_ALL;\n \telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\tcleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;\n+\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n+\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n \telse\n \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n \n@@ -1768,17 +1707,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (verbose || /* Truncate the message just before the diff, if any. */\n-\t    cleanup_mode == CLEANUP_SCISSORS)\n+\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n \t\tstrbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));\n-\tif (cleanup_mode != CLEANUP_NONE)\n-\t\tstrbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);\n+\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n \n-\tif (message_is_empty(&sb) && !allow_empty_message) {\n+\tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit due to empty commit message.\\n\"));\n \t\texit(1);\n \t}\n-\tif (template_untouched(&sb) && !allow_empty_message) {\n+\tif (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit; you did not edit the message.\\n\"));\n \t\texit(1);\ndiff --git a/sequencer.c b/sequencer.c\nindex 19dd575ed9b3b280a3fdabc9121a2e193d6984db..36e03d041f32bcc0fdd1fddebb33b23c7e4d8a70 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -691,6 +691,67 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static int rest_is_empty(const struct strbuf *sb, int start)\n+{\n+\tint i, eol;\n+\tconst char *nl;\n+\n+\t/* Check if the rest is just whitespace and Signed-off-by's. */\n+\tfor (i = start; i < sb->len; i++) {\n+\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n+\t\tif (nl)\n+\t\t\teol = nl - sb->buf;\n+\t\telse\n+\t\t\teol = sb->len;\n+\n+\t\tif (strlen(sign_off_header) <= eol - i &&\n+\t\t    starts_with(sb->buf + i, sign_off_header)) {\n+\t\t\ti = eol;\n+\t\t\tcontinue;\n+\t\t}\n+\t\twhile (i < eol)\n+\t\t\tif (!isspace(sb->buf[i++]))\n+\t\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+/*\n+ * Find out if the message in the strbuf contains only whitespace and\n+ * Signed-off-by lines.\n+ */\n+int message_is_empty(const struct strbuf *sb,\n+\t\t     enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\treturn rest_is_empty(sb, 0);\n+}\n+\n+/*\n+ * See if the user edited the message in the editor or left what\n+ * was in the template intact\n+ */\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tstruct strbuf tmpl = STRBUF_INIT;\n+\tconst char *start;\n+\n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\n+\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n+\t\treturn 0;\n+\n+\tstrbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n+\t\tstart = sb->buf;\n+\tstrbuf_release(&tmpl);\n+\treturn rest_is_empty(sb, start - sb->buf);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..82e57713a2940c5d65ccac013c3f42c55cc12baf 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -58,4 +58,15 @@ extern const char sign_off_header[];\n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n \n+enum commit_msg_cleanup_mode {\n+\tCOMMIT_MSG_CLEANUP_SPACE,\n+\tCOMMIT_MSG_CLEANUP_NONE,\n+\tCOMMIT_MSG_CLEANUP_SCISSORS,\n+\tCOMMIT_MSG_CLEANUP_ALL\n+};\n+\n+int message_is_empty(const struct strbuf *sb,\n+\t\t     enum commit_msg_cleanup_mode cleanup_mode);\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum commit_msg_cleanup_mode cleanup_mode);\n #endif\n-- \n2.15.0\n\n"},{"id":"333434","messageId":"20171124110758.9406-5-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171124110758.9406-1-phillip.wood@talktalk.net","subject":"[PATCH v4 4/9] commit: move post-rewrite code to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T11:07:53Z","receivedAt":"2017-11-24T11:08:32Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can\nbe shared with other commands and add a new function\ncommit_post_rewrite() based on the code in builtin/commit.c that\nencapsulates rewriting notes and running the post-rewrite hook. Once\nthe sequencer learns how to create commits without forking 'git\ncommit' these functions will be used when squashing commits.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - reword commit message to explain why the code is being moved\n\n builtin/commit.c | 42 +-----------------------------------------\n sequencer.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |  2 ++\n 3 files changed, 50 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex eb144556bf37b7bf357bd976b94305171b4fd159..d251cfcebad3476c365492d83803e7821fdfdf2b 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -31,9 +31,7 @@\n #include \"gpg-interface.h\"\n #include \"column.h\"\n #include \"sequencer.h\"\n-#include \"notes-utils.h\"\n #include \"mailmap.h\"\n-#include \"sigchain.h\"\n \n static const char * const builtin_commit_usage[] = {\n \tN_(\"git commit [<options>] [--] <pathspec>...\"),\n@@ -1497,37 +1495,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \treturn git_status_config(k, v, s);\n }\n \n-static int run_rewrite_hook(const struct object_id *oldoid,\n-\t\t\t    const struct object_id *newoid)\n-{\n-\tstruct child_process proc = CHILD_PROCESS_INIT;\n-\tconst char *argv[3];\n-\tint code;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\targv[0] = find_hook(\"post-rewrite\");\n-\tif (!argv[0])\n-\t\treturn 0;\n-\n-\targv[1] = \"amend\";\n-\targv[2] = NULL;\n-\n-\tproc.argv = argv;\n-\tproc.in = -1;\n-\tproc.stdout_to_stderr = 1;\n-\n-\tcode = start_command(&proc);\n-\tif (code)\n-\t\treturn code;\n-\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n-\tsigchain_push(SIGPIPE, SIG_IGN);\n-\twrite_in_full(proc.in, sb.buf, sb.len);\n-\tclose(proc.in);\n-\tstrbuf_release(&sb);\n-\tsigchain_pop(SIGPIPE);\n-\treturn finish_command(&proc);\n-}\n-\n int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)\n {\n \tstruct argv_array hook_env = ARGV_ARRAY_INIT;\n@@ -1758,14 +1725,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \trerere(0);\n \trun_commit_hook(use_editor, get_index_file(), \"post-commit\", NULL);\n \tif (amend && !no_post_rewrite) {\n-\t\tstruct notes_rewrite_cfg *cfg;\n-\t\tcfg = init_copy_notes_for_rewrite(\"amend\");\n-\t\tif (cfg) {\n-\t\t\t/* we are amending, so current_head is not NULL */\n-\t\t\tcopy_note_for_rewrite(cfg, &current_head->object.oid, &oid);\n-\t\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n-\t\t}\n-\t\trun_rewrite_hook(&current_head->object.oid, &oid);\n+\t\tcommit_post_rewrite(current_head, &oid);\n \t}\n \tif (!quiet)\n \t\tprint_summary(prefix, &oid, !current_head);\ndiff --git a/sequencer.c b/sequencer.c\nindex ef262980c5255d90ee023c0b29c6c1c628b3c7d2..6bc8346d42bb3cb1d2dc6a2238dd1b38e4308914 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -21,6 +21,8 @@\n #include \"log-tree.h\"\n #include \"wt-status.h\"\n #include \"hashmap.h\"\n+#include \"notes-utils.h\"\n+#include \"sigchain.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -789,6 +791,51 @@ int update_head_with_reflog(const struct commit *old_head,\n \treturn ret;\n }\n \n+static int run_rewrite_hook(const struct object_id *oldoid,\n+\t\t\t    const struct object_id *newoid)\n+{\n+\tstruct child_process proc = CHILD_PROCESS_INIT;\n+\tconst char *argv[3];\n+\tint code;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\targv[0] = find_hook(\"post-rewrite\");\n+\tif (!argv[0])\n+\t\treturn 0;\n+\n+\targv[1] = \"amend\";\n+\targv[2] = NULL;\n+\n+\tproc.argv = argv;\n+\tproc.in = -1;\n+\tproc.stdout_to_stderr = 1;\n+\n+\tcode = start_command(&proc);\n+\tif (code)\n+\t\treturn code;\n+\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n+\tsigchain_push(SIGPIPE, SIG_IGN);\n+\twrite_in_full(proc.in, sb.buf, sb.len);\n+\tclose(proc.in);\n+\tstrbuf_release(&sb);\n+\tsigchain_pop(SIGPIPE);\n+\treturn finish_command(&proc);\n+}\n+\n+void commit_post_rewrite(const struct commit *old_head,\n+\t\t\t const struct object_id *new_head)\n+{\n+\tstruct notes_rewrite_cfg *cfg;\n+\n+\tcfg = init_copy_notes_for_rewrite(\"amend\");\n+\tif (cfg) {\n+\t\t/* we are amending, so old_head is not NULL */\n+\t\tcopy_note_for_rewrite(cfg, &old_head->object.oid, new_head);\n+\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n+\t}\n+\trun_rewrite_hook(&old_head->object.oid, new_head);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 81a2098e900f0aca30e45ed7f19ae4bf3ce682f0..ec13b679c40dc4012a84a761ea856d6e75953490 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -73,4 +73,6 @@ int update_head_with_reflog(const struct commit *old_head,\n \t\t\t    const struct object_id *new_head,\n \t\t\t    const char *action, const struct strbuf *msg,\n \t\t\t    struct strbuf *err);\n+void commit_post_rewrite(const struct commit *current_head,\n+\t\t\t const struct object_id *new_head);\n #endif\n-- \n2.15.0\n\n"},{"id":"333435","messageId":"20171124110758.9406-4-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171124110758.9406-1-phillip.wood@talktalk.net","subject":"[PATCH v4 3/9] Add a function to update HEAD after creating a commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T11:07:52Z","receivedAt":"2017-11-24T11:08:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd update_head_with_reflog() based on the code that updates HEAD\nafter committing in builtin/commit.c that can be called by 'git\ncommit' and other commands.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v2:\n     - updated commit message to reflect the change in function name\n     - style fixes\n    \n    changes since v1:\n     - rename update_head() to update_head_with_reflog()\n\n builtin/commit.c | 20 ++------------------\n sequencer.c      | 39 ++++++++++++++++++++++++++++++++++++++-\n sequencer.h      |  4 ++++\n 3 files changed, 44 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d958c2eb2adc9a29dab29340ce9b56daea41fecd..eb144556bf37b7bf357bd976b94305171b4fd159 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1610,13 +1610,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf author_ident = STRBUF_INIT;\n \tconst char *index_file, *reflog_msg;\n-\tchar *nl;\n \tstruct object_id oid;\n \tstruct commit_list *parents = NULL;\n \tstruct stat statbuf;\n \tstruct commit *current_head = NULL;\n \tstruct commit_extra_header *extra = NULL;\n-\tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n@@ -1739,25 +1737,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&author_ident);\n \tfree_commit_extra_headers(extra);\n \n-\tnl = strchr(sb.buf, '\\n');\n-\tif (nl)\n-\t\tstrbuf_setlen(&sb, nl + 1 - sb.buf);\n-\telse\n-\t\tstrbuf_addch(&sb, '\\n');\n-\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n-\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n-\n-\ttransaction = ref_transaction_begin(&err);\n-\tif (!transaction ||\n-\t    ref_transaction_update(transaction, \"HEAD\", &oid,\n-\t\t\t\t   current_head\n-\t\t\t\t   ? &current_head->object.oid : &null_oid,\n-\t\t\t\t   0, sb.buf, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n+\tif (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,\n+\t\t\t\t    &err)) {\n \t\trollback_index_files();\n \t\tdie(\"%s\", err.buf);\n \t}\n-\tref_transaction_free(transaction);\n \n \tunlink(git_path_cherry_pick_head());\n \tunlink(git_path_revert_head());\ndiff --git a/sequencer.c b/sequencer.c\nindex 36e03d041f32bcc0fdd1fddebb33b23c7e4d8a70..ef262980c5255d90ee023c0b29c6c1c628b3c7d2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1,10 +1,10 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"lockfile.h\"\n-#include \"sequencer.h\"\n #include \"dir.h\"\n #include \"object.h\"\n #include \"commit.h\"\n+#include \"sequencer.h\"\n #include \"tag.h\"\n #include \"run-command.h\"\n #include \"exec_cmd.h\"\n@@ -752,6 +752,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n \treturn rest_is_empty(sb, start - sb->buf);\n }\n \n+int update_head_with_reflog(const struct commit *old_head,\n+\t\t\t    const struct object_id *new_head,\n+\t\t\t    const char *action, const struct strbuf *msg,\n+\t\t\t    struct strbuf *err)\n+{\n+\tstruct ref_transaction *transaction;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *nl;\n+\tint ret = 0;\n+\n+\tif (action) {\n+\t\tstrbuf_addstr(&sb, action);\n+\t\tstrbuf_addstr(&sb, \": \");\n+\t}\n+\n+\tnl = strchr(msg->buf, '\\n');\n+\tif (nl) {\n+\t\tstrbuf_add(&sb, msg->buf, nl + 1 - msg->buf);\n+\t} else {\n+\t\tstrbuf_addbuf(&sb, msg);\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t}\n+\n+\ttransaction = ref_transaction_begin(err);\n+\tif (!transaction ||\n+\t    ref_transaction_update(transaction, \"HEAD\", new_head,\n+\t\t\t\t   old_head ? &old_head->object.oid : &null_oid,\n+\t\t\t\t   0, sb.buf, err) ||\n+\t    ref_transaction_commit(transaction, err)) {\n+\t\tret = -1;\n+\t}\n+\tref_transaction_free(transaction);\n+\tstrbuf_release(&sb);\n+\n+\treturn ret;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 82e57713a2940c5d65ccac013c3f42c55cc12baf..81a2098e900f0aca30e45ed7f19ae4bf3ce682f0 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -69,4 +69,8 @@ int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n \t\t       enum commit_msg_cleanup_mode cleanup_mode);\n+int update_head_with_reflog(const struct commit *old_head,\n+\t\t\t    const struct object_id *new_head,\n+\t\t\t    const char *action, const struct strbuf *msg,\n+\t\t\t    struct strbuf *err);\n #endif\n-- \n2.15.0\n\n"},{"id":"333436","messageId":"20171124110758.9406-6-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171124110758.9406-1-phillip.wood@talktalk.net","subject":"[PATCH v4 5/9] commit: move print_commit_summary() to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T11:07:54Z","receivedAt":"2017-11-24T11:08:36Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove print_commit_summary() from builtin/commit.c to sequencer.c so it\ncan be shared with other commands. The function is modified by\nchanging the last argument to a flag so callers can specify whether\nthey want to show the author date in addition to specifying if this is\nan initial commit.\n\nIf the sequencer dies in print_commit_summary() (which can only happen\nwhen cherry-picking or reverting) then neither the todo list nor the\nabort safety file are updated to reflect the commit that was just\nmade. print_commit_summary() can die if:\n\n - The commit that was just created cannot be found or parsed.\n\n - HEAD cannot be resolved either because some other process is\n   updating it (which is bad news in the middle of a cherry-pick) or\n   because it is corrupt.\n\n - log_tree_commit() cannot read some objects.\n\nIn all those cases dying will leave the sequencer in a sane state for\naborting; 'git cherry-pick --abort' will rewind HEAD to the last\nsuccessful commit before there was a problem with HEAD or the object\ndatabase. If the user somehow fixes the problem and runs 'git\ncherry-pick --continue' then the sequencer will try and pick the same\ncommit again which may or may not be what the user wants depending on\nwhat caused print_commit_summary() to die. If print_commit_summary()\nreturned an error instead then update_abort_safety_file() would try to\nresolve HEAD which may or may not be successful. If it is successful\nthen running 'git rebase --abort' would not rewind HEAD to the last\nsuccessful commit which is not what we want.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v2:\n     - expanded commit message to explain why it is ok to die in\n       print_commit_summary() and dropped the next patch which made it\n       return an error instead.\n     - style fixes.\n    \n    changes since v1:\n     - convert flags passed to print_commit_summary() to unsigned int\n\n builtin/commit.c | 128 ++++---------------------------------------------------\n sequencer.c      | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |   5 +++\n 3 files changed, 133 insertions(+), 119 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d251cfcebad3476c365492d83803e7821fdfdf2b..2043479d37873671d43124dc0cb509d6d9247baa 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {\n \tNULL\n };\n \n-static const char implicit_ident_advice_noconfig[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly. Run the\\n\"\n-\"following command and follow the instructions in your editor to edit\\n\"\n-\"your configuration file:\\n\"\n-\"\\n\"\n-\"    git config --global --edit\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n-static const char implicit_ident_advice_config[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly:\\n\"\n-\"\\n\"\n-\"    git config --global user.name \\\"Your Name\\\"\\n\"\n-\"    git config --global user.email you@example.com\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n static const char empty_amend_advice[] =\n N_(\"You asked to amend the most recent commit, but doing so would make\\n\"\n \"it empty. You can repeat your command with --allow-empty, or you can\\n\"\n@@ -1374,98 +1349,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n-static const char *implicit_ident_advice(void)\n-{\n-\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n-\tchar *xdg_config = xdg_config_home(\"config\");\n-\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n-\n-\tfree(user_config);\n-\tfree(xdg_config);\n-\n-\tif (config_exists)\n-\t\treturn _(implicit_ident_advice_config);\n-\telse\n-\t\treturn _(implicit_ident_advice_noconfig);\n-\n-}\n-\n-static void print_summary(const char *prefix, const struct object_id *oid,\n-\t\t\t  int initial_commit)\n-{\n-\tstruct rev_info rev;\n-\tstruct commit *commit;\n-\tstruct strbuf format = STRBUF_INIT;\n-\tconst char *head;\n-\tstruct pretty_print_context pctx = {0};\n-\tstruct strbuf author_ident = STRBUF_INIT;\n-\tstruct strbuf committer_ident = STRBUF_INIT;\n-\n-\tcommit = lookup_commit(oid);\n-\tif (!commit)\n-\t\tdie(_(\"couldn't look up newly created commit\"));\n-\tif (parse_commit(commit))\n-\t\tdie(_(\"could not parse newly created commit\"));\n-\n-\tstrbuf_addstr(&format, \"format:%h] %s\");\n-\n-\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n-\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n-\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n-\t\tstrbuf_addstr(&format, \"\\n Author: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n-\t}\n-\tif (author_date_is_interesting()) {\n-\t\tstruct strbuf date = STRBUF_INIT;\n-\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n-\t\tstrbuf_addstr(&format, \"\\n Date: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &date);\n-\t\tstrbuf_release(&date);\n-\t}\n-\tif (!committer_ident_sufficiently_given()) {\n-\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n-\t\tif (advice_implicit_identity) {\n-\t\t\tstrbuf_addch(&format, '\\n');\n-\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n-\t\t}\n-\t}\n-\tstrbuf_release(&author_ident);\n-\tstrbuf_release(&committer_ident);\n-\n-\tinit_revisions(&rev, prefix);\n-\tsetup_revisions(0, NULL, &rev, NULL);\n-\n-\trev.diff = 1;\n-\trev.diffopt.output_format =\n-\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n-\n-\trev.verbose_header = 1;\n-\trev.show_root_diff = 1;\n-\tget_commit_format(format.buf, &rev);\n-\trev.always_show_header = 0;\n-\trev.diffopt.detect_rename = 1;\n-\trev.diffopt.break_opt = 0;\n-\tdiff_setup_done(&rev.diffopt);\n-\n-\thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n-\tif (!head)\n-\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n-\tif (!strcmp(head, \"HEAD\"))\n-\t\thead = _(\"detached HEAD\");\n-\telse\n-\t\tskip_prefix(head, \"refs/heads/\", &head);\n-\tprintf(\"[%s%s \", head, initial_commit ? _(\" (root-commit)\") : \"\");\n-\n-\tif (!log_tree_commit(&rev, commit)) {\n-\t\trev.always_show_header = 1;\n-\t\trev.use_terminator = 1;\n-\t\tlog_tree_commit(&rev, commit);\n-\t}\n-\n-\tstrbuf_release(&format);\n-}\n-\n static int git_commit_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n@@ -1727,8 +1610,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (amend && !no_post_rewrite) {\n \t\tcommit_post_rewrite(current_head, &oid);\n \t}\n-\tif (!quiet)\n-\t\tprint_summary(prefix, &oid, !current_head);\n+\tif (!quiet) {\n+\t\tunsigned int flags = 0;\n+\n+\t\tif (!current_head)\n+\t\t\tflags |= SUMMARY_INITIAL_COMMIT;\n+\t\tif (author_date_is_interesting())\n+\t\t\tflags |= SUMMARY_SHOW_AUTHOR_DATE;\n+\t\tprint_commit_summary(prefix, &oid, flags);\n+\t}\n \n \tUNLEAK(err);\n \tUNLEAK(sb);\ndiff --git a/sequencer.c b/sequencer.c\nindex 6bc8346d42bb3cb1d2dc6a2238dd1b38e4308914..a2cf6f5e06ffec5108f0faf43d1a4cb605264c3f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -836,6 +836,125 @@ void commit_post_rewrite(const struct commit *old_head,\n \trun_rewrite_hook(&old_head->object.oid, new_head);\n }\n \n+static const char implicit_ident_advice_noconfig[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly. Run the\\n\"\n+\"following command and follow the instructions in your editor to edit\\n\"\n+\"your configuration file:\\n\"\n+\"\\n\"\n+\"    git config --global --edit\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char implicit_ident_advice_config[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly:\\n\"\n+\"\\n\"\n+\"    git config --global user.name \\\"Your Name\\\"\\n\"\n+\"    git config --global user.email you@example.com\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char *implicit_ident_advice(void)\n+{\n+\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n+\tchar *xdg_config = xdg_config_home(\"config\");\n+\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n+\n+\tfree(user_config);\n+\tfree(xdg_config);\n+\n+\tif (config_exists)\n+\t\treturn _(implicit_ident_advice_config);\n+\telse\n+\t\treturn _(implicit_ident_advice_noconfig);\n+\n+}\n+\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  unsigned int flags)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf format = STRBUF_INIT;\n+\tconst char *head;\n+\tstruct pretty_print_context pctx = {0};\n+\tstruct strbuf author_ident = STRBUF_INIT;\n+\tstruct strbuf committer_ident = STRBUF_INIT;\n+\n+\tcommit = lookup_commit(oid);\n+\tif (!commit)\n+\t\tdie(_(\"couldn't look up newly created commit\"));\n+\tif (parse_commit(commit))\n+\t\tdie(_(\"could not parse newly created commit\"));\n+\n+\tstrbuf_addstr(&format, \"format:%h] %s\");\n+\n+\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n+\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n+\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n+\t\tstrbuf_addstr(&format, \"\\n Author: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n+\t}\n+\tif (flags & SUMMARY_SHOW_AUTHOR_DATE) {\n+\t\tstruct strbuf date = STRBUF_INIT;\n+\n+\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n+\t\tstrbuf_addstr(&format, \"\\n Date: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &date);\n+\t\tstrbuf_release(&date);\n+\t}\n+\tif (!committer_ident_sufficiently_given()) {\n+\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n+\t\tif (advice_implicit_identity) {\n+\t\t\tstrbuf_addch(&format, '\\n');\n+\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n+\t\t}\n+\t}\n+\tstrbuf_release(&author_ident);\n+\tstrbuf_release(&committer_ident);\n+\n+\tinit_revisions(&rev, prefix);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.diff = 1;\n+\trev.diffopt.output_format =\n+\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n+\n+\trev.verbose_header = 1;\n+\trev.show_root_diff = 1;\n+\tget_commit_format(format.buf, &rev);\n+\trev.always_show_header = 0;\n+\trev.diffopt.detect_rename = 1;\n+\trev.diffopt.break_opt = 0;\n+\tdiff_setup_done(&rev.diffopt);\n+\n+\thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n+\tif (!head)\n+\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n+\tif (!strcmp(head, \"HEAD\"))\n+\t\thead = _(\"detached HEAD\");\n+\telse\n+\t\tskip_prefix(head, \"refs/heads/\", &head);\n+\tprintf(\"[%s%s \", head, (flags & SUMMARY_INITIAL_COMMIT) ?\n+\t\t\t\t\t\t_(\" (root-commit)\") : \"\");\n+\n+\tif (!log_tree_commit(&rev, commit)) {\n+\t\trev.always_show_header = 1;\n+\t\trev.use_terminator = 1;\n+\t\tlog_tree_commit(&rev, commit);\n+\t}\n+\n+\tstrbuf_release(&format);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex ec13b679c40dc4012a84a761ea856d6e75953490..4f616c61a3f3869daf9f427b978c308d6094a978 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -75,4 +75,9 @@ int update_head_with_reflog(const struct commit *old_head,\n \t\t\t    struct strbuf *err);\n void commit_post_rewrite(const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n+\n+#define SUMMARY_INITIAL_COMMIT   (1 << 0)\n+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  unsigned int flags);\n #endif\n-- \n2.15.0\n\n"},{"id":"333437","messageId":"20171124110758.9406-8-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171124110758.9406-1-phillip.wood@talktalk.net","subject":"[PATCH v4 7/9] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T11:07:56Z","receivedAt":"2017-11-24T11:08:40Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nLoad default values for message cleanup and gpg signing of commits in\npreparation for committing without forking 'git commit'. Note that we\ninterpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE to\nbe consistent with 'git commit'\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v3:\n     - interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE\n       to match 'git commit'\n    \n    changes since v1:\n     - renamed git_revert_config() to common_config()\n     - prefixed cleanup_mode constants to reflect the changes to patch 2\n       in this series\n\n builtin/rebase--helper.c | 13 ++++++++++++-\n builtin/revert.c         | 15 +++++++++++++--\n sequencer.c              | 34 ++++++++++++++++++++++++++++++++++\n sequencer.h              |  1 +\n 4 files changed, 60 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c\nindex f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644\n--- a/builtin/rebase--helper.c\n+++ b/builtin/rebase--helper.c\n@@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n \tNULL\n };\n \n+static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n+{\n+\tint status;\n+\n+\tstatus = git_sequencer_config(k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_default_config(k, v, NULL);\n+}\n+\n int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts = REPLAY_OPTS_INIT;\n@@ -39,7 +50,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_rebase_helper_config, NULL);\n \n \topts.action = REPLAY_INTERACTIVE_REBASE;\n \topts.allow_ff = 1;\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex b9d927eb09c9ed87c84681df1396f4e6d9b13c97..1938825efa6ad20ede5aba57f097863aeb33d1d5 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n+static int common_config(const char *k, const char *v, void *cb)\n+{\n+\tint status;\n+\n+\tstatus = git_sequencer_config(k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_default_config(k, v, NULL);\n+}\n+\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n@@ -208,7 +219,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tif (isatty(0))\n \t\topts.edit = 1;\n \topts.action = REPLAY_REVERT;\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(common_config, NULL);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"revert failed\"));\n@@ -221,7 +232,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \topts.action = REPLAY_PICK;\n-\tgit_config(git_default_config, NULL);\n+\tgit_config(common_config, NULL);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"cherry-pick failed\"));\ndiff --git a/sequencer.c b/sequencer.c\nindex 7400df5522037583108534755af6f542117667c2..40461a41e3798e267813656de6b669c297b521c6 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -688,6 +688,40 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static enum commit_msg_cleanup_mode default_msg_cleanup =\n+\t\t\t\t\t\tCOMMIT_MSG_CLEANUP_NONE;\n+static char *default_gpg_sign;\n+\n+int git_sequencer_config(const char *k, const char *v, void *cb)\n+{\n+\tif (!strcmp(k, \"commit.cleanup\")) {\n+\t\tint status;\n+\t\tconst char *s;\n+\n+\t\tstatus = git_config_string(&s, k, v);\n+\t\tif (status)\n+\t\t\treturn status;\n+\n+\t\tif (!strcmp(s, \"verbatim\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n+\t\telse if (!strcmp(s, \"whitespace\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n+\t\telse if (!strcmp(s, \"strip\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n+\t\telse if (!strcmp(s, \"scissors\"))\n+\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n+\n+\t\treturn status;\n+\t}\n+\n+\tif (!strcmp(k, \"commit.gpgsign\")) {\n+\t\tdefault_gpg_sign = git_config_bool(k, v) ? \"\" : NULL;\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_gpg_config(k, v, NULL);\n+}\n+\n static int rest_is_empty(const struct strbuf *sb, int start)\n {\n \tint i, eol;\ndiff --git a/sequencer.h b/sequencer.h\nindex 4f616c61a3f3869daf9f427b978c308d6094a978..77cb174b2aaf3972ebb9e6ec379252be96dedd3d 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -57,6 +57,7 @@ extern const char sign_off_header[];\n \n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n+int git_sequencer_config(const char *k, const char *v, void *cb);\n \n enum commit_msg_cleanup_mode {\n \tCOMMIT_MSG_CLEANUP_SPACE,\n-- \n2.15.0\n\n"},{"id":"333438","messageId":"20171124110758.9406-9-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171124110758.9406-1-phillip.wood@talktalk.net","subject":"[PATCH v4 8/9] sequencer: try to commit without forking 'git commit'","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T11:07:57Z","receivedAt":"2017-11-24T11:08:42Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nIf the commit message does not need to be edited then create the\ncommit without forking 'git commit'. Taking the best time of ten runs\nwith a warm cache this reduces the time taken to cherry-pick 10\ncommits by 27% (from 282ms to 204ms), and the time taken by 'git\nrebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on\nmy computer running linux. Some of greater saving for rebase is\nbecause it no longer wastes time creating the commit summary just to\nthrow it away.\n\nThe code to create the commit is based on builtin/commit.c. It is\nsimplified as it doesn't have to deal with merges and modified so that\nit does not die but returns an error to make sure the sequencer exits\ncleanly, as it would when forking 'git commit'\n\nEven when not forking 'git commit' the commit message is written to a\nfile and CHERRY_PICK_HEAD is created unnecessarily. This could be\neliminated in future. I hacked up a version that does not write these\nfiles and just passed an strbuf (with the wrong message for fixup and\nsquash commands) to do_commit() but I couldn't measure any significant\ntime difference when running cherry-pick or rebase. I think\neliminating the writes properly for rebase would require a bit of\neffort as the code would need to be restructured.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v3:\n     - take account of change print_commit_summary() return type after\n       dropping the patch that made it return an error instead of dying.\n    \n    changes since v2:\n     - style fixes\n    \n    changes since v1:\n     - added comments to explain return value of try_to_commit()\n     - removed unnecessary NULL tests before calling free()\n     - style cleanups\n     - corrected commit message\n     - prefixed cleanup_mode constants to reflect the changes to patch 2\n       in this series\n\n sequencer.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 176 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 40461a41e3798e267813656de6b669c297b521c6..99095a28c6732ef697b5b763b347751bd8a440cf 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)\n \treturn 0;\n }\n \n+static char *get_author(const char *message)\n+{\n+\tsize_t len;\n+\tconst char *a;\n+\n+\ta = find_commit_header(message, \"author\", &len);\n+\tif (a)\n+\t\treturn xmemdupz(a, len);\n+\n+\treturn NULL;\n+}\n+\n static const char staged_changes_advice[] =\n N_(\"you have staged changes in your working tree\\n\"\n \"If these changes are meant to be squashed into the previous commit, run:\\n\"\n@@ -984,6 +996,160 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n \tstrbuf_release(&format);\n }\n \n+static int parse_head(struct commit **head)\n+{\n+\tstruct commit *current_head;\n+\tstruct object_id oid;\n+\n+\tif (get_oid(\"HEAD\", &oid)) {\n+\t\tcurrent_head = NULL;\n+\t} else {\n+\t\tcurrent_head = lookup_commit_reference(&oid);\n+\t\tif (!current_head)\n+\t\t\treturn error(_(\"could not parse HEAD\"));\n+\t\tif (oidcmp(&oid, &current_head->object.oid)) {\n+\t\t\twarning(_(\"HEAD %s is not a commit!\"),\n+\t\t\t\toid_to_hex(&oid));\n+\t\t}\n+\t\tif (parse_commit(current_head))\n+\t\t\treturn error(_(\"could not parse HEAD commit\"));\n+\t}\n+\t*head = current_head;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Try to commit without forking 'git commit'. In some cases we need\n+ * to run 'git commit' to display an error message\n+ *\n+ * Returns:\n+ *  -1 - error unable to commit\n+ *   0 - success\n+ *   1 - run 'git commit'\n+ */\n+static int try_to_commit(struct strbuf *msg, const char *author,\n+\t\t\t struct replay_opts *opts, unsigned int flags,\n+\t\t\t struct object_id *oid)\n+{\n+\tstruct object_id tree;\n+\tstruct commit *current_head;\n+\tstruct commit_list *parents = NULL;\n+\tstruct commit_extra_header *extra = NULL;\n+\tstruct strbuf err = STRBUF_INIT;\n+\tstruct strbuf amend_msg = STRBUF_INIT;\n+\tchar *amend_author = NULL;\n+\tconst char *gpg_sign;\n+\tenum commit_msg_cleanup_mode cleanup;\n+\tint res = 0;\n+\n+\tif (parse_head(&current_head))\n+\t\treturn -1;\n+\n+\tif (flags & AMEND_MSG) {\n+\t\tconst char *exclude_gpgsig[] = { \"gpgsig\", NULL };\n+\t\tconst char *out_enc = get_commit_output_encoding();\n+\t\tconst char *message = logmsg_reencode(current_head, NULL,\n+\t\t\t\t\t\t      out_enc);\n+\n+\t\tif (!msg) {\n+\t\t\tconst char *orig_message = NULL;\n+\n+\t\t\tfind_commit_subject(message, &orig_message);\n+\t\t\tmsg = &amend_msg;\n+\t\t\tstrbuf_addstr(msg, orig_message);\n+\t\t}\n+\t\tauthor = amend_author = get_author(message);\n+\t\tunuse_commit_buffer(current_head, message);\n+\t\tif (!author) {\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t\t\tgoto out;\n+\t\t}\n+\t\tparents = copy_commit_list(current_head->parents);\n+\t\textra = read_commit_extra_headers(current_head, exclude_gpgsig);\n+\t} else if (current_head) {\n+\t\tcommit_list_insert(current_head, &parents);\n+\t}\n+\n+\tcleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t\t  default_msg_cleanup;\n+\tif (cleanup != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);\n+\tif (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {\n+\t\tres = 1; /* run 'git commit' to display error message */\n+\t\tgoto out;\n+\t}\n+\n+\tgpg_sign = opts->gpg_sign ? opts->gpg_sign : default_gpg_sign;\n+\n+\tif (write_cache_as_tree(tree.hash, 0, NULL)) {\n+\t\tres = error(_(\"git write-tree failed to write a tree\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?\n+\t\t\t\t\t      &current_head->tree->object.oid :\n+\t\t\t\t\t      &empty_tree_oid, &tree)) {\n+\t\tres = 1; /* run 'git commit' to display error message */\n+\t\tgoto out;\n+\t}\n+\n+\tif (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,\n+\t\t\t\t oid->hash, author, gpg_sign, extra)) {\n+\t\tres = error(_(\"failed to write commit object\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (update_head_with_reflog(current_head, oid,\n+\t\t\t\t    getenv(\"GIT_REFLOG_ACTION\"), msg, &err)) {\n+\t\tres = error(\"%s\", err.buf);\n+\t\tgoto out;\n+\t}\n+\n+\tif (flags & AMEND_MSG)\n+\t\tcommit_post_rewrite(current_head, oid);\n+\n+out:\n+\tfree_commit_extra_headers(extra);\n+\tstrbuf_release(&err);\n+\tstrbuf_release(&amend_msg);\n+\tfree(amend_author);\n+\n+\treturn res;\n+}\n+\n+static int do_commit(const char *msg_file, const char *author,\n+\t\t     struct replay_opts *opts, unsigned int flags)\n+{\n+\tint res = 1;\n+\n+\tif (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {\n+\t\tstruct object_id oid;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)\n+\t\t\treturn error_errno(_(\"unable to read commit message \"\n+\t\t\t\t\t     \"from '%s'\"),\n+\t\t\t\t\t   msg_file);\n+\n+\t\tres = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,\n+\t\t\t\t    &oid);\n+\t\tstrbuf_release(&sb);\n+\t\tif (!res) {\n+\t\t\tunlink(git_path_cherry_pick_head());\n+\t\t\tunlink(git_path_merge_msg());\n+\t\t\tif (!is_rebase_i(opts))\n+\t\t\t\tprint_commit_summary(NULL, &oid,\n+\t\t\t\t\t\tSUMMARY_SHOW_AUTHOR_DATE);\n+\t\t\treturn res;\n+\t\t}\n+\t}\n+\tif (res == 1)\n+\t\treturn run_git_commit(msg_file, opts, flags);\n+\n+\treturn res;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\n@@ -1235,6 +1401,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \tstruct object_id head;\n \tstruct commit *base, *next, *parent;\n \tconst char *base_label, *next_label;\n+\tchar *author = NULL;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n \tint res, unborn = 0, allow;\n@@ -1351,6 +1518,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t\tstrbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));\n \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n \t\t}\n+\t\tif (!is_fixup(command))\n+\t\t\tauthor = get_author(msg.message);\n \t}\n \n \tif (command == TODO_REWORD)\n@@ -1436,9 +1605,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t} else if (allow)\n \t\tflags |= ALLOW_EMPTY;\n-\tif (!opts->no_commit)\n+\tif (!opts->no_commit) {\n fast_forward_edit:\n-\t\tres = run_git_commit(msg_file, opts, flags);\n+\t\tif (author || command == TODO_REVERT || (flags & AMEND_MSG))\n+\t\t\tres = do_commit(msg_file, author, opts, flags);\n+\t\telse\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t}\n \n \tif (!res && final_fixup) {\n \t\tunlink(rebase_path_fixup_msg());\n@@ -1447,6 +1620,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \n leave:\n \tfree_message(commit, &msg);\n+\tfree(author);\n \tupdate_abort_safety_file();\n \n \treturn res;\n-- \n2.15.0\n\n"},{"id":"333439","messageId":"20171124110758.9406-10-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171124110758.9406-1-phillip.wood@talktalk.net","subject":"[PATCH v4 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T11:07:58Z","receivedAt":"2017-11-24T11:08:45Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nNow that the sequencer creates commits without forking 'git commit' it\ndoes not see an empty commit in these tests which fixes the known\nbreakage. Note that logic for handling\nKNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 is not removed from\nlib-submodule-update.sh as it is still used by other tests.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    The output of the tests with after the previous patch looks like the\n    output of the merge tests (see below), so I'm hopeful that this is a\n    genuine fix, but someone who knows about submodules should check that\n    things are in fact working properly now.\n    \n    t3512-cherry-pick-submodule.sh\n    \n    expecting success:\n    \t\tprolog &&\n    \t\treset_work_tree_to add_sub1 &&\n    \t\t(\n    \t\t\tcd submodule_update &&\n    \t\t\tgit branch -t modify_sub1 origin/modify_sub1 &&\n    \t\t\t$command modify_sub1 &&\n    \t\t\ttest_superproject_content origin/modify_sub1 &&\n    \t\t\ttest_submodule_content sub1 origin/add_sub1 &&\n    \t\t\tgit submodule update &&\n    \t\t\ttest_submodule_content sub1 origin/modify_sub1\n    \t\t)\n    \n    Cloning into 'submodule_update'...\n    done.\n    Switched to a new branch 'add_sub1'\n    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.\n    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update_sub1) registered for path 'sub1'\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update/sub1'...\n    done.\n    Submodule path 'sub1': checked out 'ce9efb76b6ff5beb56e70d3662695f3ecbd38330'\n    Branch 'modify_sub1' set up to track remote branch 'modify_sub1' from 'origin'.\n    [add_sub1 e57a25c] Modify sub1\n     Author: A U Thor <author@example.com>\n     Date: Fri Nov 24 10:48:45 2017 +0000\n    Submodule path 'sub1': checked out '7c9cd6d138a7bb3145fc0c7fca1f403cbe89010e'\n    ok 11 - git cherry-pick: modified submodule does not update submodule work tree\n    \n    expecting success:\n    \t\tprolog &&\n    \t\treset_work_tree_to add_sub1 &&\n    \t\t(\n    \t\t\tcd submodule_update &&\n    \t\t\tgit branch -t invalid_sub1 origin/invalid_sub1 &&\n    \t\t\t$command invalid_sub1 &&\n    \t\t\ttest_superproject_content origin/invalid_sub1 &&\n    \t\t\ttest_submodule_content sub1 origin/add_sub1 &&\n    \t\t\ttest_must_fail git submodule update &&\n    \t\t\ttest_submodule_content sub1 origin/add_sub1\n    \t\t)\n    \n    Cloning into 'submodule_update'...\n    done.\n    Switched to a new branch 'add_sub1'\n    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.\n    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update_sub1) registered for path 'sub1'\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update/sub1'...\n    done.\n    Submodule path 'sub1': checked out 'ce9efb76b6ff5beb56e70d3662695f3ecbd38330'\n    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.\n    [add_sub1 155695c] Invalid sub1 commit\n     Author: A U Thor <author@example.com>\n     Date: Fri Nov 24 10:48:45 2017 +0000\n    error: Server does not allow request for unadvertised object 0123456789012345678901234567890123456789\n    Fetched in submodule path 'sub1', but it did not contain 0123456789012345678901234567890123456789. Direct fetching of that commit failed.\n    ok 12 - git cherry-pick: modified submodule does not update submodule work tree to invalid commit\n    \n    expecting success:\n    \t\tprolog &&\n    \t\treset_work_tree_to invalid_sub1 &&\n    \t\t(\n    \t\t\tcd submodule_update &&\n    \t\t\tgit branch -t valid_sub1 origin/valid_sub1 &&\n    \t\t\t$command valid_sub1 &&\n    \t\t\ttest_superproject_content origin/valid_sub1 &&\n    \t\t\ttest_dir_is_empty sub1 &&\n    \t\t\tgit submodule update --init --recursive &&\n    \t\t\ttest_submodule_content sub1 origin/valid_sub1\n    \t\t)\n    \n    Cloning into 'submodule_update'...\n    done.\n    Switched to a new branch 'invalid_sub1'\n    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.\n    fatal: Needed a single revision\n    Branch 'valid_sub1' set up to track remote branch 'valid_sub1' from 'origin'.\n    [invalid_sub1 497299e] Revert \"Invalid sub1 commit\"\n     Author: A U Thor <author@example.com>\n     Date: Fri Nov 24 10:48:46 2017 +0000\n    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update_sub1) registered for path 'sub1'\n    Submodule 'uninitialized_sub' (/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update_sub1) registered for path 'uninitialized_sub'\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update/sub1'...\n    done.\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update/uninitialized_sub'...\n    done.\n    Submodule path 'sub1': checked out 'ce9efb76b6ff5beb56e70d3662695f3ecbd38330'\n    Submodule path 'uninitialized_sub': checked out 'ce9efb76b6ff5beb56e70d3662695f3ecbd38330'\n    ok 13 - git cherry-pick: modified submodule does not update submodule work tree from invalid commit\n    \n    t3513-revert-submodule.sh\n    \n    expecting success:\n    \t\tprolog &&\n    \t\treset_work_tree_to add_sub1 &&\n    \t\t(\n    \t\t\tcd submodule_update &&\n    \t\t\tgit branch -t modify_sub1 origin/modify_sub1 &&\n    \t\t\t$command modify_sub1 &&\n    \t\t\ttest_superproject_content origin/modify_sub1 &&\n    \t\t\ttest_submodule_content sub1 origin/add_sub1 &&\n    \t\t\tgit submodule update &&\n    \t\t\ttest_submodule_content sub1 origin/modify_sub1\n    \t\t)\n    \n    Cloning into 'submodule_update'...\n    done.\n    Switched to a new branch 'add_sub1'\n    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.\n    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update_sub1) registered for path 'sub1'\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update/sub1'...\n    done.\n    Submodule path 'sub1': checked out '4497aa8f9341f6e5f178dbb536a7093e8f093d8a'\n    Branch 'modify_sub1' set up to track remote branch 'modify_sub1' from 'origin'.\n    Switched to branch 'modify_sub1'\n    Your branch is up to date with 'origin/modify_sub1'.\n    [modify_sub1 1c2c464] Revert \"Modify sub1\"\n     Author: A U Thor <author@example.com>\n     Date: Fri Nov 24 10:48:51 2017 +0000\n    [modify_sub1 cde56a6] Revert \"Revert \"Modify sub1\"\"\n     Author: A U Thor <author@example.com>\n     Date: Fri Nov 24 10:48:51 2017 +0000\n    Submodule path 'sub1': checked out 'a66b894bbfed816edf59cf901620978a64419731'\n    ok 11 - git_revert: modified submodule does not update submodule work tree\n    \n    expecting success:\n    \t\tprolog &&\n    \t\treset_work_tree_to add_sub1 &&\n    \t\t(\n    \t\t\tcd submodule_update &&\n    \t\t\tgit branch -t invalid_sub1 origin/invalid_sub1 &&\n    \t\t\t$command invalid_sub1 &&\n    \t\t\ttest_superproject_content origin/invalid_sub1 &&\n    \t\t\ttest_submodule_content sub1 origin/add_sub1 &&\n    \t\t\ttest_must_fail git submodule update &&\n    \t\t\ttest_submodule_content sub1 origin/add_sub1\n    \t\t)\n    \n    Cloning into 'submodule_update'...\n    done.\n    Switched to a new branch 'add_sub1'\n    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.\n    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update_sub1) registered for path 'sub1'\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update/sub1'...\n    done.\n    Submodule path 'sub1': checked out '4497aa8f9341f6e5f178dbb536a7093e8f093d8a'\n    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.\n    Switched to branch 'invalid_sub1'\n    Your branch is up to date with 'origin/invalid_sub1'.\n    [invalid_sub1 cec26d9] Revert \"Invalid sub1 commit\"\n     Author: A U Thor <author@example.com>\n     Date: Fri Nov 24 10:48:51 2017 +0000\n    [invalid_sub1 d017764] Revert \"Revert \"Invalid sub1 commit\"\"\n     Author: A U Thor <author@example.com>\n     Date: Fri Nov 24 10:48:51 2017 +0000\n    error: Server does not allow request for unadvertised object 0123456789012345678901234567890123456789\n    Fetched in submodule path 'sub1', but it did not contain 0123456789012345678901234567890123456789. Direct fetching of that commit failed.\n    ok 12 - git_revert: modified submodule does not update submodule work tree to invalid commit\n    \n    expecting success:\n    \t\tprolog &&\n    \t\treset_work_tree_to invalid_sub1 &&\n    \t\t(\n    \t\t\tcd submodule_update &&\n    \t\t\tgit branch -t valid_sub1 origin/valid_sub1 &&\n    \t\t\t$command valid_sub1 &&\n    \t\t\ttest_superproject_content origin/valid_sub1 &&\n    \t\t\ttest_dir_is_empty sub1 &&\n    \t\t\tgit submodule update --init --recursive &&\n    \t\t\ttest_submodule_content sub1 origin/valid_sub1\n    \t\t)\n    \n    Cloning into 'submodule_update'...\n    done.\n    Switched to a new branch 'invalid_sub1'\n    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.\n    fatal: Needed a single revision\n    Branch 'valid_sub1' set up to track remote branch 'valid_sub1' from 'origin'.\n    Switched to branch 'valid_sub1'\n    Your branch is up to date with 'origin/valid_sub1'.\n    [valid_sub1 ca047d9] Revert \"Revert \"Invalid sub1 commit\"\"\n     Author: A U Thor <author@example.com>\n     Date: Fri Nov 24 10:48:51 2017 +0000\n    [valid_sub1 e1e9178] Revert \"Revert \"Revert \"Invalid sub1 commit\"\"\"\n     Author: A U Thor <author@example.com>\n     Date: Fri Nov 24 10:48:51 2017 +0000\n    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update_sub1) registered for path 'sub1'\n    Submodule 'uninitialized_sub' (/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update_sub1) registered for path 'uninitialized_sub'\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update/sub1'...\n    done.\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update/uninitialized_sub'...\n    done.\n    Submodule path 'sub1': checked out '4497aa8f9341f6e5f178dbb536a7093e8f093d8a'\n    Submodule path 'uninitialized_sub': checked out '4497aa8f9341f6e5f178dbb536a7093e8f093d8a'\n    ok 13 - git_revert: modified submodule does not update submodule work tree from invalid commit\n    \n    t7613-merge-submodule.sh\n    \n    expecting success:\n    \t\tprolog &&\n    \t\treset_work_tree_to add_sub1 &&\n    \t\t(\n    \t\t\tcd submodule_update &&\n    \t\t\tgit branch -t modify_sub1 origin/modify_sub1 &&\n    \t\t\t$command modify_sub1 &&\n    \t\t\ttest_superproject_content origin/modify_sub1 &&\n    \t\t\ttest_submodule_content sub1 origin/add_sub1 &&\n    \t\t\tgit submodule update &&\n    \t\t\ttest_submodule_content sub1 origin/modify_sub1\n    \t\t)\n    \n    Cloning into 'submodule_update'...\n    done.\n    Switched to a new branch 'add_sub1'\n    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.\n    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update_sub1) registered for path 'sub1'\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update/sub1'...\n    done.\n    Submodule path 'sub1': checked out 'c97e5093cc535f043494394509e7ad784b5931ce'\n    Branch 'modify_sub1' set up to track remote branch 'modify_sub1' from 'origin'.\n    Updating ec3f722..c933e8b\n    Fast-forward\n    Submodule path 'sub1': checked out '614c78931e0df85cee80e90bbf96ed92504cd886'\n    ok 11 - git merge: modified submodule does not update submodule work tree\n    \n    expecting success:\n    \t\tprolog &&\n    \t\treset_work_tree_to add_sub1 &&\n    \t\t(\n    \t\t\tcd submodule_update &&\n    \t\t\tgit branch -t invalid_sub1 origin/invalid_sub1 &&\n    \t\t\t$command invalid_sub1 &&\n    \t\t\ttest_superproject_content origin/invalid_sub1 &&\n    \t\t\ttest_submodule_content sub1 origin/add_sub1 &&\n    \t\t\ttest_must_fail git submodule update &&\n    \t\t\ttest_submodule_content sub1 origin/add_sub1\n    \t\t)\n    \n    Cloning into 'submodule_update'...\n    done.\n    Switched to a new branch 'add_sub1'\n    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.\n    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update_sub1) registered for path 'sub1'\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update/sub1'...\n    done.\n    Submodule path 'sub1': checked out 'c97e5093cc535f043494394509e7ad784b5931ce'\n    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.\n    Updating ec3f722..5c6fb5f\n    Fast-forward\n    error: Server does not allow request for unadvertised object 0123456789012345678901234567890123456789\n    Fetched in submodule path 'sub1', but it did not contain 0123456789012345678901234567890123456789. Direct fetching of that commit failed.\n    ok 12 - git merge: modified submodule does not update submodule work tree to invalid commit\n    \n    expecting success:\n    \t\tprolog &&\n    \t\treset_work_tree_to invalid_sub1 &&\n    \t\t(\n    \t\t\tcd submodule_update &&\n    \t\t\tgit branch -t valid_sub1 origin/valid_sub1 &&\n    \t\t\t$command valid_sub1 &&\n    \t\t\ttest_superproject_content origin/valid_sub1 &&\n    \t\t\ttest_dir_is_empty sub1 &&\n    \t\t\tgit submodule update --init --recursive &&\n    \t\t\ttest_submodule_content sub1 origin/valid_sub1\n    \t\t)\n    \n    Cloning into 'submodule_update'...\n    done.\n    Switched to a new branch 'invalid_sub1'\n    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.\n    fatal: Needed a single revision\n    Branch 'valid_sub1' set up to track remote branch 'valid_sub1' from 'origin'.\n    Updating 5c6fb5f..07412aa\n    Fast-forward\n    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update_sub1) registered for path 'sub1'\n    Submodule 'uninitialized_sub' (/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update_sub1) registered for path 'uninitialized_sub'\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update/sub1'...\n    done.\n    Cloning into '/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update/uninitialized_sub'...\n    done.\n    Submodule path 'sub1': checked out 'c97e5093cc535f043494394509e7ad784b5931ce'\n    Submodule path 'uninitialized_sub': checked out 'c97e5093cc535f043494394509e7ad784b5931ce'\n    ok 13 - git merge: modified submodule does not update submodule work tree from invalid commit\n\n t/t3512-cherry-pick-submodule.sh | 1 -\n t/t3513-revert-submodule.sh      | 1 -\n 2 files changed, 2 deletions(-)\n\ndiff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh\nindex 6863b7bb6fd94cbbd3fcc8f29ab02e344cac23c9..059213767e089e69ad61d14c329930ef097813e7 100755\n--- a/t/t3512-cherry-pick-submodule.sh\n+++ b/t/t3512-cherry-pick-submodule.sh\n@@ -5,7 +5,6 @@ test_description='cherry-pick can handle submodules'\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-submodule-update.sh\n \n-KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1\n KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1\n KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1\n test_submodule_switch \"git cherry-pick\"\ndiff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh\nindex db9378142a93338d2988f40e2748bc476490bcd5..5e39fcdb66c0c7c4b112c1bbe941d886db237693 100755\n--- a/t/t3513-revert-submodule.sh\n+++ b/t/t3513-revert-submodule.sh\n@@ -25,7 +25,6 @@ git_revert () {\n \tgit revert HEAD\n }\n \n-KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1\n KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1\n test_submodule_switch \"git_revert\"\n \n-- \n2.15.0\n\n"},{"id":"333441","messageId":"xmqqbmjrn5yb.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171124110758.9406-8-phillip.wood@talktalk.net","subject":"Re: [PATCH v4 7/9] sequencer: load commit related config","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-11-24T13:48:12Z","receivedAt":"2017-11-24T13:48:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> Load default values for message cleanup and gpg signing of commits in\n> preparation for committing without forking 'git commit'. Note that we\n> interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE to\n> be consistent with 'git commit'\n\nHmph, is that because we never invoke the editor to edit the commit\nlog message?  Over there, scissors is demoted to space when the\neditor is not in use, but otherwise this demotion does not occur.\n\nJust being curious.\n\n>\n> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> ---\n>\n> Notes:\n>     changes since v3:\n>      - interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE\n>        to match 'git commit'\n"},{"id":"333445","messageId":"33de5245-828b-dca3-0b2e-13e54299a6bb@talktalk.net","threadId":"46827","inReplyTo":"xmqqbmjrn5yb.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 7/9] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-11-24T14:38:33Z","receivedAt":"2017-11-24T14:42:16Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 24/11/17 13:48, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> Load default values for message cleanup and gpg signing of commits in\n>> preparation for committing without forking 'git commit'. Note that we\n>> interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE to\n>> be consistent with 'git commit'\n> \n> Hmph, is that because we never invoke the editor to edit the commit\n> log message?  Over there, scissors is demoted to space when the\n> editor is not in use, but otherwise this demotion does not occur.\n\nYes that's right. In fact I'm fairly we always specify an explicit\ncleanup mode when calling try_to_commit() so the default is there in\ncase that changes in the future.\n\n> Just being curious.\n> \n>>\n>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n>> ---\n>>\n>> Notes:\n>>     changes since v3:\n>>      - interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE\n>>        to match 'git commit'\n\n"},{"id":"334076","messageId":"xmqqd13umjl7.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171124110758.9406-8-phillip.wood@talktalk.net","subject":"Re: [PATCH v4 7/9] sequencer: load commit related config","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-12-04T18:30:44Z","receivedAt":"2017-12-04T18:30:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> --- a/builtin/rebase--helper.c\n> +++ b/builtin/rebase--helper.c\n> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n>  \tNULL\n>  };\n>  \n> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n> +{\n> +\tint status;\n> +\n> +\tstatus = git_sequencer_config(k, v, NULL);\n> +\tif (status)\n> +\t\treturn status;\n> +\n> +\treturn git_default_config(k, v, NULL);\n> +}\n> +\n\nSorry for spotting the problem this late, but this code is\nunfortunate and we will need to revisit it later; we may want to do\nso sooner rather than later.\n\nWhen k,v is a valid configuration that is handled by\nsequencer_config() successfully, this code still needs to call into\ndefault_config() with the same k,v, only to get it ignored.\n\nThe problem lies in the (mis)design of git_sequencer_config().  The\nfunction should either allow the caller to notice that (k,v) has\nalready been handled, or be the last one in the callback by making a\ncall to default_config() itself.\n\nFor the former, because this helper does not have to be called\ndirectly as a git_config() callback, but instead it is always meant\nto be called indirectly from another git_config() callback (like\ngit_rebase_helper_config() here, and common_config() in\nbuiltin/revert.c like we see below), it does *not* have to be\nconstrained by the function signature required for it to be a config\ncallback.  It could take a pointer to an int that stores if 'k' was\nhandled inside the function,\n\n    int git_sequencer_config_helper(char *k, char *v, int *handled);\n\nfor example.\n"},{"id":"334079","messageId":"CAGZ79kY0YC+3kpYWDFi=bW0m-KFUpqexc3gxe-a5gS3YTn89bg@mail.gmail.com","threadId":"46827","inReplyTo":"20171124110758.9406-10-phillip.wood@talktalk.net","subject":"Re: [PATCH v4 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-12-04T19:24:57Z","receivedAt":"2017-12-04T19:25:04Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Fri, Nov 24, 2017 at 3:07 AM, Phillip Wood <phillip.wood@talktalk.net> wrote:\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> Now that the sequencer creates commits without forking 'git commit' it\n> does not see an empty commit in these tests which fixes the known\n> breakage. Note that logic for handling\n> KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 is not removed from\n> lib-submodule-update.sh as it is still used by other tests.\n>\n> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> ---\n>\n> Notes:\n>     The output of the tests with after the previous patch looks like the\n>     output of the merge tests (see below), so I'm hopeful that this is a\n>     genuine fix, but someone who knows about submodules should check that\n>     things are in fact working properly now.\n\nLooking at the patch only (in combination with the history of the\nsubmodule tests,\n283f56a40b (cherry-pick: add t3512 for submodule updates, 2014-06-15))\nthis patch\nlooks good to me. I wonder though if this needs to be squashed in another commit\nto keep the test suite working for each patch and have no intermittent\nfailure in the\nseries.\n\nThanks,\nStefan\n"},{"id":"334144","messageId":"632c35d3-46ca-e0bc-5b14-244647df9b19@talktalk.net","threadId":"46827","inReplyTo":"xmqqd13umjl7.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 7/9] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-05T11:21:04Z","receivedAt":"2017-12-05T11:21:14Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 04/12/17 18:30, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n>> --- a/builtin/rebase--helper.c\n>> +++ b/builtin/rebase--helper.c\n>> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n>>  \tNULL\n>>  };\n>>  \n>> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n>> +{\n>> +\tint status;\n>> +\n>> +\tstatus = git_sequencer_config(k, v, NULL);\n>> +\tif (status)\n>> +\t\treturn status;\n>> +\n>> +\treturn git_default_config(k, v, NULL);\n>> +}\n>> +\n> \n> Sorry for spotting the problem this late, but this code is\n> unfortunate and we will need to revisit it later; we may want to do\n> so sooner rather than later.\n\nIf it needs fixing then doing it before it hits next makes sense.\n\n> When k,v is a valid configuration that is handled by\n> sequencer_config() successfully, this code still needs to call into\n> default_config() with the same k,v, only to get it ignored.\n\nI'm a bit confused by this sentence. Do you mean that when k,v is a\nvalid configuration that is successfully handled by sequencer_config()\nthis code unnecessarily calls default_config() as there is no need to\ncall default_config() if k,v have already been handled?\n\n> The problem lies in the (mis)design of git_sequencer_config().  The\n> function should either allow the caller to notice that (k,v) has\n> already been handled, or be the last one in the callback by making a\n> call to default_config() itself.\n\nThe problem is that git_gpg_config() provides no indication if it has\nhandled k,v so there's no way to avoid the call to default_config() in\nthat case. builtin/am.c and builtin/commit.c both do something like\n\nstatic int git_am_config(const char *k, const char *v, void *cb)\n{\n\tint status;\n\n\tstatus = git_gpg_config(k, v, NULL);\n\tif (status)\n\t\treturn status;\n\n\treturn git_default_config(k, v, NULL);\n}\n\n\nLooking more generally at sequencer_config() I wonder if we should be\nproviding a warning or an error if the config contains an invalid\ncleanup mode. Also should it be calling git_diff_ui_config() to set\nthings up for print_commit_summary()? (I'm not sure if anything in that\nfunction is affected by diff config settings)\n\nLet me know what you think. I should have time to update this patch set\nlater in the week.\n\nBest Wishes\n\nPhillip\n\n> For the former, because this helper does not have to be called\n> directly as a git_config() callback, but instead it is always meant\n> to be called indirectly from another git_config() callback (like\n> git_rebase_helper_config() here, and common_config() in\n> builtin/revert.c like we see below), it does *not* have to be\n> constrained by the function signature required for it to be a config\n> callback.  It could take a pointer to an int that stores if 'k' was\n> handled inside the function,\n> \n>     int git_sequencer_config_helper(char *k, char *v, int *handled);\n> \n> for example.\n> \n\n"},{"id":"334145","messageId":"7003bd8e-2ddb-3f0e-ba5f-990389254786@talktalk.net","threadId":"46827","inReplyTo":"632c35d3-46ca-e0bc-5b14-244647df9b19@talktalk.net","subject":"Re: [PATCH v4 7/9] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-05T12:10:55Z","receivedAt":"2017-12-05T12:11:05Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 05/12/17 11:21, Phillip Wood wrote:\n> On 04/12/17 18:30, Junio C Hamano wrote:\n>> Phillip Wood <phillip.wood@talktalk.net> writes:\n>>\n>>> --- a/builtin/rebase--helper.c\n>>> +++ b/builtin/rebase--helper.c\n>>> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n>>>  \tNULL\n>>>  };\n>>>  \n>>> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n>>> +{\n>>> +\tint status;\n>>> +\n>>> +\tstatus = git_sequencer_config(k, v, NULL);\n>>> +\tif (status)\n>>> +\t\treturn status;\n>>> +\n>>> +\treturn git_default_config(k, v, NULL);\n>>> +}\n>>> +\n>>\n>> Sorry for spotting the problem this late, but this code is\n>> unfortunate and we will need to revisit it later; we may want to do\n>> so sooner rather than later.\n> \n> If it needs fixing then doing it before it hits next makes sense.\n> \n>> When k,v is a valid configuration that is handled by\n>> sequencer_config() successfully, this code still needs to call into\n>> default_config() with the same k,v, only to get it ignored.\n> \n> I'm a bit confused by this sentence. Do you mean that when k,v is a\n> valid configuration that is successfully handled by sequencer_config()\n> this code unnecessarily calls default_config() as there is no need to\n> call default_config() if k,v have already been handled?\n> \n>> The problem lies in the (mis)design of git_sequencer_config().  The\n>> function should either allow the caller to notice that (k,v) has\n>> already been handled, or be the last one in the callback by making a\n>> call to default_config() itself.\n> \n> The problem is that git_gpg_config() provides no indication if it has\n> handled k,v so there's no way to avoid the call to default_config() in\n> that case. builtin/am.c and builtin/commit.c both do something like\n> \n> static int git_am_config(const char *k, const char *v, void *cb)\n> {\n> \tint status;\n> \n> \tstatus = git_gpg_config(k, v, NULL);\n> \tif (status)\n> \t\treturn status;\n> \n> \treturn git_default_config(k, v, NULL);\n> }\n> \n\nLooking through the callers of gpg_config() it should be fairly easy to\nconvert it to take an extra parameter in the same way as you suggested\nfor sequencer_config()\n\n> Looking more generally at sequencer_config() I wonder if we should be\n> providing a warning or an error if the config contains an invalid\n> cleanup mode. Also should it be calling git_diff_ui_config() to set\n> things up for print_commit_summary()? (I'm not sure if anything in that\n> function is affected by diff config settings)\n> \n> Let me know what you think. I should have time to update this patch set\n> later in the week.\n> \n> Best Wishes\n> \n> Phillip\n> \n>> For the former, because this helper does not have to be called\n>> directly as a git_config() callback, but instead it is always meant\n>> to be called indirectly from another git_config() callback (like\n>> git_rebase_helper_config() here, and common_config() in\n>> builtin/revert.c like we see below), it does *not* have to be\n>> constrained by the function signature required for it to be a config\n>> callback.  It could take a pointer to an int that stores if 'k' was\n>> handled inside the function,\n>>\n>>     int git_sequencer_config_helper(char *k, char *v, int *handled);\n>>\n>> for example.\n>>\n> \n\n"},{"id":"334146","messageId":"9c184a5a-b45b-efd1-449f-a18e2dee0ef3@talktalk.net","threadId":"46827","inReplyTo":"CAGZ79kY0YC+3kpYWDFi=bW0m-KFUpqexc3gxe-a5gS3YTn89bg@mail.gmail.com","subject":"Re: [PATCH v4 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-05T12:13:51Z","receivedAt":"2017-12-05T12:14:01Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 04/12/17 19:24, Stefan Beller wrote:\n> On Fri, Nov 24, 2017 at 3:07 AM, Phillip Wood <phillip.wood@talktalk.net> wrote:\n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> Now that the sequencer creates commits without forking 'git commit' it\n>> does not see an empty commit in these tests which fixes the known\n>> breakage. Note that logic for handling\n>> KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 is not removed from\n>> lib-submodule-update.sh as it is still used by other tests.\n>>\n>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n>> ---\n>>\n>> Notes:\n>>     The output of the tests with after the previous patch looks like the\n>>     output of the merge tests (see below), so I'm hopeful that this is a\n>>     genuine fix, but someone who knows about submodules should check that\n>>     things are in fact working properly now.\n> \n> Looking at the patch only (in combination with the history of the\n> submodule tests,\n> 283f56a40b (cherry-pick: add t3512 for submodule updates, 2014-06-15))\n> this patch\n> looks good to me. I wonder though if this needs to be squashed in another commit\n> to keep the test suite working for each patch and have no intermittent\n> failure in the\n> series.\n\nHi Stefan\n\nThanks for looking at this, it's good to know you think it's OK. I'll\nleave it to Junio's discretion whether to squash this into the previous\npatch\n\nThanks again\n\nPhillip\n\n> Thanks,\n> Stefan\n> \n\n"},{"id":"334545","messageId":"e0952566-cc19-ec35-2677-10e490f2034d@talktalk.net","threadId":"46827","inReplyTo":"632c35d3-46ca-e0bc-5b14-244647df9b19@talktalk.net","subject":"Re: [PATCH v4 7/9] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-09T19:05:16Z","receivedAt":"2017-12-09T19:05:23Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 05/12/17 11:21, Phillip Wood wrote:\n> On 04/12/17 18:30, Junio C Hamano wrote:\n>> Phillip Wood <phillip.wood@talktalk.net> writes:\n>>\n>>> --- a/builtin/rebase--helper.c\n>>> +++ b/builtin/rebase--helper.c\n>>> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {\n>>>  \tNULL\n>>>  };\n>>>  \n>>> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n>>> +{\n>>> +\tint status;\n>>> +\n>>> +\tstatus = git_sequencer_config(k, v, NULL);\n>>> +\tif (status)\n>>> +\t\treturn status;\n>>> +\n>>> +\treturn git_default_config(k, v, NULL);\n>>> +}\n>>> +\n>>\n>> Sorry for spotting the problem this late, but this code is\n>> unfortunate and we will need to revisit it later; we may want to do\n>> so sooner rather than later.\n> \n> If it needs fixing then doing it before it hits next makes sense.\n> \n>> When k,v is a valid configuration that is handled by\n>> sequencer_config() successfully, this code still needs to call into\n>> default_config() with the same k,v, only to get it ignored.\n> \n> I'm a bit confused by this sentence. Do you mean that when k,v is a\n> valid configuration that is successfully handled by sequencer_config()\n> this code unnecessarily calls default_config() as there is no need to\n> call default_config() if k,v have already been handled?\n> \n>> The problem lies in the (mis)design of git_sequencer_config().  The\n>> function should either allow the caller to notice that (k,v) has\n>> already been handled, or be the last one in the callback by making a\n>> call to default_config() itself.\n> \n> The problem is that git_gpg_config() provides no indication if it has\n> handled k,v so there's no way to avoid the call to default_config() in\n> that case. builtin/am.c and builtin/commit.c both do something like\n> \n> static int git_am_config(const char *k, const char *v, void *cb)\n> {\n> \tint status;\n> \n> \tstatus = git_gpg_config(k, v, NULL);\n> \tif (status)\n> \t\treturn status;\n> \n> \treturn git_default_config(k, v, NULL);\n> }\n> \n> \n> Looking more generally at sequencer_config() I wonder if we should be\n> providing a warning or an error if the config contains an invalid\n> cleanup mode. Also should it be calling git_diff_ui_config() to set\n> things up for print_commit_summary()? (I'm not sure if anything in that\n> function is affected by diff config settings)\n\nI think the answer maybe yes so that it loads the setting for external\ndiff commands and textconv filters but I'm not sure, perhaps someone\nmore familiar with the diff code would know? Also I think we should be\nloading the basic diff config for creating the patch when we stop with\nconflicts?\n\nIf anyone has any thought on this, please share them!\n\nBest Wishes\n\nPhillip\n> \n> Let me know what you think. I should have time to update this patch set\n> later in the week.\n> \n> Best Wishes\n> \n> Phillip\n> \n>> For the former, because this helper does not have to be called\n>> directly as a git_config() callback, but instead it is always meant\n>> to be called indirectly from another git_config() callback (like\n>> git_rebase_helper_config() here, and common_config() in\n>> builtin/revert.c like we see below), it does *not* have to be\n>> constrained by the function signature required for it to be a config\n>> callback.  It could take a pointer to an int that stores if 'k' was\n>> handled inside the function,\n>>\n>>     int git_sequencer_config_helper(char *k, char *v, int *handled);\n>>\n>> for example.\n>>\n> \n\n"},{"id":"334597","messageId":"20171211141330.23566-2-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171211141330.23566-1-phillip.wood@talktalk.net","subject":"[PATCH v5 1/9] t3404: check intermediate squash messages","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T14:13:22Z","receivedAt":"2017-12-11T14:13:54Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nWhen there is more than one squash/fixup command in a row check the\nintermediate messages are correct.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n t/t3404-rebase-interactive.sh | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 6a82d1ed876dd5d1073dc63be8ba5720adbf12e3..9ed0a244e6cdf34c7caca8232f0c0a8cf4864c42 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -453,6 +453,10 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa\n \t\tgit rebase -i $base &&\n \tgit cat-file commit HEAD | sed -e 1,/^\\$/d > actual-squash-fixup &&\n \ttest_cmp expect-squash-fixup actual-squash-fixup &&\n+\tgit cat-file commit HEAD@{2} |\n+\t\tgrep \"^# This is a combination of 3 commits\\.\"  &&\n+\tgit cat-file commit HEAD@{3} |\n+\t\tgrep \"^# This is a combination of 2 commits\\.\"  &&\n \tgit checkout to-be-rebased &&\n \tgit branch -D squash-fixup\n '\n-- \n2.15.1\n\n"},{"id":"334598","messageId":"20171211141330.23566-1-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20170925101041.18344-1-phillip.wood@talktalk.net","subject":"[PATCH v5 0/9] sequencer: don't fork git commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T14:13:21Z","receivedAt":"2017-12-11T14:13:56Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nI've reworked the config handling since v4. It now stores the default\nvalues in struct replay_opt rather than using global variables and\ncalls git_diff_basic_config(). Unfortunately I've not had time to\nmodify git_gpg_config() to indicate if it successfully handled the key\nso git_diff_basic_config() is called unnecessarily in that case. Within\ngit_diff_basic_config() userdiff_config() also suffers from the same\nproblem of not indicating if it has handled the key.\n\nHere's the original summary:\nThese patches teach the sequencer to create commits without forking\ngit commit when the commit message does not need to be edited. This\nspeeds up cherry picking 10 commits by 26% and picking 10 commits with\nrebase --continue by 44%. The first few patches move bits of\nbuiltin/commit.c to sequencer.c. The last two patches actually\nimplement creating commits in sequencer.c.\n\nPhillip Wood (9):\n  t3404: check intermediate squash messages\n  commit: move empty message checks to libgit\n  Add a function to update HEAD after creating a commit\n  commit: move post-rewrite code to libgit\n  commit: move print_commit_summary() to libgit\n  sequencer: simplify adding Signed-off-by: trailer\n  sequencer: load commit related config\n  sequencer: try to commit without forking 'git commit'\n  t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1\n\n builtin/commit.c                 | 289 +++--------------------\n builtin/rebase--helper.c         |   2 +-\n builtin/revert.c                 |   4 +-\n sequencer.c                      | 495 ++++++++++++++++++++++++++++++++++++++-\n sequencer.h                      |  24 ++\n t/t3404-rebase-interactive.sh    |   4 +\n t/t3512-cherry-pick-submodule.sh |   1 -\n t/t3513-revert-submodule.sh      |   1 -\n 8 files changed, 549 insertions(+), 271 deletions(-)\n\n-- \n2.15.1\n\n"},{"id":"334599","messageId":"20171211141330.23566-5-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171211141330.23566-1-phillip.wood@talktalk.net","subject":"[PATCH v5 4/9] commit: move post-rewrite code to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T14:13:25Z","receivedAt":"2017-12-11T14:13:59Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can\nbe shared with other commands and add a new function\ncommit_post_rewrite() based on the code in builtin/commit.c that\nencapsulates rewriting notes and running the post-rewrite hook. Once\nthe sequencer learns how to create commits without forking 'git\ncommit' these functions will be used when squashing commits.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v1:\n     - reword commit message to explain why the code is being moved\n\n builtin/commit.c | 42 +-----------------------------------------\n sequencer.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |  2 ++\n 3 files changed, 50 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 340cc2988ebdb92ef222b677ee12c94fa53aa1ff..d376d282cda95d491f2fcbee5063709d02aed77a 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -31,9 +31,7 @@\n #include \"gpg-interface.h\"\n #include \"column.h\"\n #include \"sequencer.h\"\n-#include \"notes-utils.h\"\n #include \"mailmap.h\"\n-#include \"sigchain.h\"\n \n static const char * const builtin_commit_usage[] = {\n \tN_(\"git commit [<options>] [--] <pathspec>...\"),\n@@ -1497,37 +1495,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \treturn git_status_config(k, v, s);\n }\n \n-static int run_rewrite_hook(const struct object_id *oldoid,\n-\t\t\t    const struct object_id *newoid)\n-{\n-\tstruct child_process proc = CHILD_PROCESS_INIT;\n-\tconst char *argv[3];\n-\tint code;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\targv[0] = find_hook(\"post-rewrite\");\n-\tif (!argv[0])\n-\t\treturn 0;\n-\n-\targv[1] = \"amend\";\n-\targv[2] = NULL;\n-\n-\tproc.argv = argv;\n-\tproc.in = -1;\n-\tproc.stdout_to_stderr = 1;\n-\n-\tcode = start_command(&proc);\n-\tif (code)\n-\t\treturn code;\n-\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n-\tsigchain_push(SIGPIPE, SIG_IGN);\n-\twrite_in_full(proc.in, sb.buf, sb.len);\n-\tclose(proc.in);\n-\tstrbuf_release(&sb);\n-\tsigchain_pop(SIGPIPE);\n-\treturn finish_command(&proc);\n-}\n-\n int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)\n {\n \tstruct argv_array hook_env = ARGV_ARRAY_INIT;\n@@ -1758,14 +1725,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \trerere(0);\n \trun_commit_hook(use_editor, get_index_file(), \"post-commit\", NULL);\n \tif (amend && !no_post_rewrite) {\n-\t\tstruct notes_rewrite_cfg *cfg;\n-\t\tcfg = init_copy_notes_for_rewrite(\"amend\");\n-\t\tif (cfg) {\n-\t\t\t/* we are amending, so current_head is not NULL */\n-\t\t\tcopy_note_for_rewrite(cfg, &current_head->object.oid, &oid);\n-\t\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n-\t\t}\n-\t\trun_rewrite_hook(&current_head->object.oid, &oid);\n+\t\tcommit_post_rewrite(current_head, &oid);\n \t}\n \tif (!quiet)\n \t\tprint_summary(prefix, &oid, !current_head);\ndiff --git a/sequencer.c b/sequencer.c\nindex 5fe6ef3512566622f0423a09cbffe1adf1e65957..132319be0dbc15ed34f87af769235a4c6f3c6159 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -21,6 +21,8 @@\n #include \"log-tree.h\"\n #include \"wt-status.h\"\n #include \"hashmap.h\"\n+#include \"notes-utils.h\"\n+#include \"sigchain.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -790,6 +792,51 @@ int update_head_with_reflog(const struct commit *old_head,\n \treturn ret;\n }\n \n+static int run_rewrite_hook(const struct object_id *oldoid,\n+\t\t\t    const struct object_id *newoid)\n+{\n+\tstruct child_process proc = CHILD_PROCESS_INIT;\n+\tconst char *argv[3];\n+\tint code;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\targv[0] = find_hook(\"post-rewrite\");\n+\tif (!argv[0])\n+\t\treturn 0;\n+\n+\targv[1] = \"amend\";\n+\targv[2] = NULL;\n+\n+\tproc.argv = argv;\n+\tproc.in = -1;\n+\tproc.stdout_to_stderr = 1;\n+\n+\tcode = start_command(&proc);\n+\tif (code)\n+\t\treturn code;\n+\tstrbuf_addf(&sb, \"%s %s\\n\", oid_to_hex(oldoid), oid_to_hex(newoid));\n+\tsigchain_push(SIGPIPE, SIG_IGN);\n+\twrite_in_full(proc.in, sb.buf, sb.len);\n+\tclose(proc.in);\n+\tstrbuf_release(&sb);\n+\tsigchain_pop(SIGPIPE);\n+\treturn finish_command(&proc);\n+}\n+\n+void commit_post_rewrite(const struct commit *old_head,\n+\t\t\t const struct object_id *new_head)\n+{\n+\tstruct notes_rewrite_cfg *cfg;\n+\n+\tcfg = init_copy_notes_for_rewrite(\"amend\");\n+\tif (cfg) {\n+\t\t/* we are amending, so old_head is not NULL */\n+\t\tcopy_note_for_rewrite(cfg, &old_head->object.oid, new_head);\n+\t\tfinish_copy_notes_for_rewrite(cfg, \"Notes added by 'git commit --amend'\");\n+\t}\n+\trun_rewrite_hook(&old_head->object.oid, new_head);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 3757a7aecb5a7795d7b9b45964f3328ee852e14b..e97fb288498bf524e43db4217087cb66682fad52 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -72,4 +72,6 @@ int update_head_with_reflog(const struct commit *old_head,\n \t\t\t    const struct object_id *new_head,\n \t\t\t    const char *action, const struct strbuf *msg,\n \t\t\t    struct strbuf *err);\n+void commit_post_rewrite(const struct commit *current_head,\n+\t\t\t const struct object_id *new_head);\n #endif\n-- \n2.15.1\n\n"},{"id":"334600","messageId":"20171211141330.23566-4-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171211141330.23566-1-phillip.wood@talktalk.net","subject":"[PATCH v5 3/9] Add a function to update HEAD after creating a commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T14:13:24Z","receivedAt":"2017-12-11T14:14:02Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd update_head_with_reflog() based on the code that updates HEAD\nafter committing in builtin/commit.c that can be called by 'git\ncommit' and other commands.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v2:\n     - updated commit message to reflect the change in function name\n     - style fixes\n    \n    changes since v1:\n     - rename update_head() to update_head_with_reflog()\n\n builtin/commit.c | 20 ++------------------\n sequencer.c      | 39 ++++++++++++++++++++++++++++++++++++++-\n sequencer.h      |  4 ++++\n 3 files changed, 44 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 2175dac8036c465a73c4c782f061e85ae6d1a629..340cc2988ebdb92ef222b677ee12c94fa53aa1ff 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1610,13 +1610,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf author_ident = STRBUF_INIT;\n \tconst char *index_file, *reflog_msg;\n-\tchar *nl;\n \tstruct object_id oid;\n \tstruct commit_list *parents = NULL;\n \tstruct stat statbuf;\n \tstruct commit *current_head = NULL;\n \tstruct commit_extra_header *extra = NULL;\n-\tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n@@ -1739,25 +1737,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&author_ident);\n \tfree_commit_extra_headers(extra);\n \n-\tnl = strchr(sb.buf, '\\n');\n-\tif (nl)\n-\t\tstrbuf_setlen(&sb, nl + 1 - sb.buf);\n-\telse\n-\t\tstrbuf_addch(&sb, '\\n');\n-\tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n-\tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n-\n-\ttransaction = ref_transaction_begin(&err);\n-\tif (!transaction ||\n-\t    ref_transaction_update(transaction, \"HEAD\", &oid,\n-\t\t\t\t   current_head\n-\t\t\t\t   ? &current_head->object.oid : &null_oid,\n-\t\t\t\t   0, sb.buf, &err) ||\n-\t    ref_transaction_commit(transaction, &err)) {\n+\tif (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,\n+\t\t\t\t    &err)) {\n \t\trollback_index_files();\n \t\tdie(\"%s\", err.buf);\n \t}\n-\tref_transaction_free(transaction);\n \n \tunlink(git_path_cherry_pick_head());\n \tunlink(git_path_revert_head());\ndiff --git a/sequencer.c b/sequencer.c\nindex 168da5093e71f50a4d70af7288cf761110e69e87..5fe6ef3512566622f0423a09cbffe1adf1e65957 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1,10 +1,10 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"lockfile.h\"\n-#include \"sequencer.h\"\n #include \"dir.h\"\n #include \"object.h\"\n #include \"commit.h\"\n+#include \"sequencer.h\"\n #include \"tag.h\"\n #include \"run-command.h\"\n #include \"exec_cmd.h\"\n@@ -753,6 +753,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,\n \treturn rest_is_empty(sb, start - sb->buf);\n }\n \n+int update_head_with_reflog(const struct commit *old_head,\n+\t\t\t    const struct object_id *new_head,\n+\t\t\t    const char *action, const struct strbuf *msg,\n+\t\t\t    struct strbuf *err)\n+{\n+\tstruct ref_transaction *transaction;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char *nl;\n+\tint ret = 0;\n+\n+\tif (action) {\n+\t\tstrbuf_addstr(&sb, action);\n+\t\tstrbuf_addstr(&sb, \": \");\n+\t}\n+\n+\tnl = strchr(msg->buf, '\\n');\n+\tif (nl) {\n+\t\tstrbuf_add(&sb, msg->buf, nl + 1 - msg->buf);\n+\t} else {\n+\t\tstrbuf_addbuf(&sb, msg);\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t}\n+\n+\ttransaction = ref_transaction_begin(err);\n+\tif (!transaction ||\n+\t    ref_transaction_update(transaction, \"HEAD\", new_head,\n+\t\t\t\t   old_head ? &old_head->object.oid : &null_oid,\n+\t\t\t\t   0, sb.buf, err) ||\n+\t    ref_transaction_commit(transaction, err)) {\n+\t\tret = -1;\n+\t}\n+\tref_transaction_free(transaction);\n+\tstrbuf_release(&sb);\n+\n+\treturn ret;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 2040773c7b6fd3966d5a1a14f410ea8ff53843c8..3757a7aecb5a7795d7b9b45964f3328ee852e14b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -68,4 +68,8 @@ int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n \t\t       enum commit_msg_cleanup_mode cleanup_mode);\n+int update_head_with_reflog(const struct commit *old_head,\n+\t\t\t    const struct object_id *new_head,\n+\t\t\t    const char *action, const struct strbuf *msg,\n+\t\t\t    struct strbuf *err);\n #endif\n-- \n2.15.1\n\n"},{"id":"334601","messageId":"20171211141330.23566-6-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171211141330.23566-1-phillip.wood@talktalk.net","subject":"[PATCH v5 5/9] commit: move print_commit_summary() to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T14:13:26Z","receivedAt":"2017-12-11T14:14:06Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove print_commit_summary() from builtin/commit.c to sequencer.c so it\ncan be shared with other commands. The function is modified by\nchanging the last argument to a flag so callers can specify whether\nthey want to show the author date in addition to specifying if this is\nan initial commit.\n\nIf the sequencer dies in print_commit_summary() (which can only happen\nwhen cherry-picking or reverting) then neither the todo list nor the\nabort safety file are updated to reflect the commit that was just\nmade. print_commit_summary() can die if:\n\n - The commit that was just created cannot be found or parsed.\n\n - HEAD cannot be resolved either because some other process is\n   updating it (which is bad news in the middle of a cherry-pick) or\n   because it is corrupt.\n\n - log_tree_commit() cannot read some objects.\n\nIn all those cases dying will leave the sequencer in a sane state for\naborting; 'git cherry-pick --abort' will rewind HEAD to the last\nsuccessful commit before there was a problem with HEAD or the object\ndatabase. If the user somehow fixes the problem and runs 'git\ncherry-pick --continue' then the sequencer will try and pick the same\ncommit again which may or may not be what the user wants depending on\nwhat caused print_commit_summary() to die. If print_commit_summary()\nreturned an error instead then update_abort_safety_file() would try to\nresolve HEAD which may or may not be successful. If it is successful\nthen running 'git rebase --abort' would not rewind HEAD to the last\nsuccessful commit which is not what we want.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v2:\n     - expanded commit message to explain why it is ok to die in\n       print_commit_summary() and dropped the next patch which made it\n       return an error instead.\n     - style fixes.\n    \n    changes since v1:\n     - convert flags passed to print_commit_summary() to unsigned int\n\n builtin/commit.c | 128 ++++---------------------------------------------------\n sequencer.c      | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |   5 +++\n 3 files changed, 133 insertions(+), 119 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d376d282cda95d491f2fcbee5063709d02aed77a..0f78958c89cdd91c54f0154102b08e1a8121cce0 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {\n \tNULL\n };\n \n-static const char implicit_ident_advice_noconfig[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly. Run the\\n\"\n-\"following command and follow the instructions in your editor to edit\\n\"\n-\"your configuration file:\\n\"\n-\"\\n\"\n-\"    git config --global --edit\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n-static const char implicit_ident_advice_config[] =\n-N_(\"Your name and email address were configured automatically based\\n\"\n-\"on your username and hostname. Please check that they are accurate.\\n\"\n-\"You can suppress this message by setting them explicitly:\\n\"\n-\"\\n\"\n-\"    git config --global user.name \\\"Your Name\\\"\\n\"\n-\"    git config --global user.email you@example.com\\n\"\n-\"\\n\"\n-\"After doing this, you may fix the identity used for this commit with:\\n\"\n-\"\\n\"\n-\"    git commit --amend --reset-author\\n\");\n-\n static const char empty_amend_advice[] =\n N_(\"You asked to amend the most recent commit, but doing so would make\\n\"\n \"it empty. You can repeat your command with --allow-empty, or you can\\n\"\n@@ -1374,98 +1349,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \treturn 0;\n }\n \n-static const char *implicit_ident_advice(void)\n-{\n-\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n-\tchar *xdg_config = xdg_config_home(\"config\");\n-\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n-\n-\tfree(user_config);\n-\tfree(xdg_config);\n-\n-\tif (config_exists)\n-\t\treturn _(implicit_ident_advice_config);\n-\telse\n-\t\treturn _(implicit_ident_advice_noconfig);\n-\n-}\n-\n-static void print_summary(const char *prefix, const struct object_id *oid,\n-\t\t\t  int initial_commit)\n-{\n-\tstruct rev_info rev;\n-\tstruct commit *commit;\n-\tstruct strbuf format = STRBUF_INIT;\n-\tconst char *head;\n-\tstruct pretty_print_context pctx = {0};\n-\tstruct strbuf author_ident = STRBUF_INIT;\n-\tstruct strbuf committer_ident = STRBUF_INIT;\n-\n-\tcommit = lookup_commit(oid);\n-\tif (!commit)\n-\t\tdie(_(\"couldn't look up newly created commit\"));\n-\tif (parse_commit(commit))\n-\t\tdie(_(\"could not parse newly created commit\"));\n-\n-\tstrbuf_addstr(&format, \"format:%h] %s\");\n-\n-\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n-\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n-\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n-\t\tstrbuf_addstr(&format, \"\\n Author: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n-\t}\n-\tif (author_date_is_interesting()) {\n-\t\tstruct strbuf date = STRBUF_INIT;\n-\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n-\t\tstrbuf_addstr(&format, \"\\n Date: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &date);\n-\t\tstrbuf_release(&date);\n-\t}\n-\tif (!committer_ident_sufficiently_given()) {\n-\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n-\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n-\t\tif (advice_implicit_identity) {\n-\t\t\tstrbuf_addch(&format, '\\n');\n-\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n-\t\t}\n-\t}\n-\tstrbuf_release(&author_ident);\n-\tstrbuf_release(&committer_ident);\n-\n-\tinit_revisions(&rev, prefix);\n-\tsetup_revisions(0, NULL, &rev, NULL);\n-\n-\trev.diff = 1;\n-\trev.diffopt.output_format =\n-\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n-\n-\trev.verbose_header = 1;\n-\trev.show_root_diff = 1;\n-\tget_commit_format(format.buf, &rev);\n-\trev.always_show_header = 0;\n-\trev.diffopt.detect_rename = 1;\n-\trev.diffopt.break_opt = 0;\n-\tdiff_setup_done(&rev.diffopt);\n-\n-\thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n-\tif (!head)\n-\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n-\tif (!strcmp(head, \"HEAD\"))\n-\t\thead = _(\"detached HEAD\");\n-\telse\n-\t\tskip_prefix(head, \"refs/heads/\", &head);\n-\tprintf(\"[%s%s \", head, initial_commit ? _(\" (root-commit)\") : \"\");\n-\n-\tif (!log_tree_commit(&rev, commit)) {\n-\t\trev.always_show_header = 1;\n-\t\trev.use_terminator = 1;\n-\t\tlog_tree_commit(&rev, commit);\n-\t}\n-\n-\tstrbuf_release(&format);\n-}\n-\n static int git_commit_config(const char *k, const char *v, void *cb)\n {\n \tstruct wt_status *s = cb;\n@@ -1727,8 +1610,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tif (amend && !no_post_rewrite) {\n \t\tcommit_post_rewrite(current_head, &oid);\n \t}\n-\tif (!quiet)\n-\t\tprint_summary(prefix, &oid, !current_head);\n+\tif (!quiet) {\n+\t\tunsigned int flags = 0;\n+\n+\t\tif (!current_head)\n+\t\t\tflags |= SUMMARY_INITIAL_COMMIT;\n+\t\tif (author_date_is_interesting())\n+\t\t\tflags |= SUMMARY_SHOW_AUTHOR_DATE;\n+\t\tprint_commit_summary(prefix, &oid, flags);\n+\t}\n \n \tUNLEAK(err);\n \tUNLEAK(sb);\ndiff --git a/sequencer.c b/sequencer.c\nindex 132319be0dbc15ed34f87af769235a4c6f3c6159..b4ff2a4a973b2733cca7bb65fcb7947cb8d08988 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -837,6 +837,125 @@ void commit_post_rewrite(const struct commit *old_head,\n \trun_rewrite_hook(&old_head->object.oid, new_head);\n }\n \n+static const char implicit_ident_advice_noconfig[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly. Run the\\n\"\n+\"following command and follow the instructions in your editor to edit\\n\"\n+\"your configuration file:\\n\"\n+\"\\n\"\n+\"    git config --global --edit\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char implicit_ident_advice_config[] =\n+N_(\"Your name and email address were configured automatically based\\n\"\n+\"on your username and hostname. Please check that they are accurate.\\n\"\n+\"You can suppress this message by setting them explicitly:\\n\"\n+\"\\n\"\n+\"    git config --global user.name \\\"Your Name\\\"\\n\"\n+\"    git config --global user.email you@example.com\\n\"\n+\"\\n\"\n+\"After doing this, you may fix the identity used for this commit with:\\n\"\n+\"\\n\"\n+\"    git commit --amend --reset-author\\n\");\n+\n+static const char *implicit_ident_advice(void)\n+{\n+\tchar *user_config = expand_user_path(\"~/.gitconfig\", 0);\n+\tchar *xdg_config = xdg_config_home(\"config\");\n+\tint config_exists = file_exists(user_config) || file_exists(xdg_config);\n+\n+\tfree(user_config);\n+\tfree(xdg_config);\n+\n+\tif (config_exists)\n+\t\treturn _(implicit_ident_advice_config);\n+\telse\n+\t\treturn _(implicit_ident_advice_noconfig);\n+\n+}\n+\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  unsigned int flags)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf format = STRBUF_INIT;\n+\tconst char *head;\n+\tstruct pretty_print_context pctx = {0};\n+\tstruct strbuf author_ident = STRBUF_INIT;\n+\tstruct strbuf committer_ident = STRBUF_INIT;\n+\n+\tcommit = lookup_commit(oid);\n+\tif (!commit)\n+\t\tdie(_(\"couldn't look up newly created commit\"));\n+\tif (parse_commit(commit))\n+\t\tdie(_(\"could not parse newly created commit\"));\n+\n+\tstrbuf_addstr(&format, \"format:%h] %s\");\n+\n+\tformat_commit_message(commit, \"%an <%ae>\", &author_ident, &pctx);\n+\tformat_commit_message(commit, \"%cn <%ce>\", &committer_ident, &pctx);\n+\tif (strbuf_cmp(&author_ident, &committer_ident)) {\n+\t\tstrbuf_addstr(&format, \"\\n Author: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &author_ident);\n+\t}\n+\tif (flags & SUMMARY_SHOW_AUTHOR_DATE) {\n+\t\tstruct strbuf date = STRBUF_INIT;\n+\n+\t\tformat_commit_message(commit, \"%ad\", &date, &pctx);\n+\t\tstrbuf_addstr(&format, \"\\n Date: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &date);\n+\t\tstrbuf_release(&date);\n+\t}\n+\tif (!committer_ident_sufficiently_given()) {\n+\t\tstrbuf_addstr(&format, \"\\n Committer: \");\n+\t\tstrbuf_addbuf_percentquote(&format, &committer_ident);\n+\t\tif (advice_implicit_identity) {\n+\t\t\tstrbuf_addch(&format, '\\n');\n+\t\t\tstrbuf_addstr(&format, implicit_ident_advice());\n+\t\t}\n+\t}\n+\tstrbuf_release(&author_ident);\n+\tstrbuf_release(&committer_ident);\n+\n+\tinit_revisions(&rev, prefix);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.diff = 1;\n+\trev.diffopt.output_format =\n+\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n+\n+\trev.verbose_header = 1;\n+\trev.show_root_diff = 1;\n+\tget_commit_format(format.buf, &rev);\n+\trev.always_show_header = 0;\n+\trev.diffopt.detect_rename = 1;\n+\trev.diffopt.break_opt = 0;\n+\tdiff_setup_done(&rev.diffopt);\n+\n+\thead = resolve_ref_unsafe(\"HEAD\", 0, NULL, NULL);\n+\tif (!head)\n+\t\tdie_errno(_(\"unable to resolve HEAD after creating commit\"));\n+\tif (!strcmp(head, \"HEAD\"))\n+\t\thead = _(\"detached HEAD\");\n+\telse\n+\t\tskip_prefix(head, \"refs/heads/\", &head);\n+\tprintf(\"[%s%s \", head, (flags & SUMMARY_INITIAL_COMMIT) ?\n+\t\t\t\t\t\t_(\" (root-commit)\") : \"\");\n+\n+\tif (!log_tree_commit(&rev, commit)) {\n+\t\trev.always_show_header = 1;\n+\t\trev.use_terminator = 1;\n+\t\tlog_tree_commit(&rev, commit);\n+\t}\n+\n+\tstrbuf_release(&format);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex e97fb288498bf524e43db4217087cb66682fad52..bf72e339adbb81900283d8811ed51569aa3e05ee 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -74,4 +74,9 @@ int update_head_with_reflog(const struct commit *old_head,\n \t\t\t    struct strbuf *err);\n void commit_post_rewrite(const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n+\n+#define SUMMARY_INITIAL_COMMIT   (1 << 0)\n+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n+void print_commit_summary(const char *prefix, const struct object_id *oid,\n+\t\t\t  unsigned int flags);\n #endif\n-- \n2.15.1\n\n"},{"id":"334602","messageId":"20171211141330.23566-3-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171211141330.23566-1-phillip.wood@talktalk.net","subject":"[PATCH v5 2/9] commit: move empty message checks to libgit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T14:13:23Z","receivedAt":"2017-12-11T14:14:09Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nMove the functions that check for empty messages from bulitin/commit.c\nto sequencer.c so they can be shared with other commands. The\nfunctions are refactored to take an explicit cleanup mode and template\nfilename passed by the caller.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v4:\n     - move the definition of cleanup mode enum so it can be referenced in\n       struct replay_opts by a later commit\n    \n    changes since v1:\n     - prefix cleanup_mode enum and constants with commit_msg_\n\n builtin/commit.c | 99 +++++++++++---------------------------------------------\n sequencer.c      | 61 ++++++++++++++++++++++++++++++++++\n sequencer.h      | 12 ++++++-\n 3 files changed, 91 insertions(+), 81 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 2de75882b847be1a100931f6218e7e14dc01c4bd..2175dac8036c465a73c4c782f061e85ae6d1a629 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -128,12 +128,7 @@ static char *sign_commit;\n  * if editor is used, and only the whitespaces if the message\n  * is specified explicitly.\n  */\n-static enum {\n-\tCLEANUP_SPACE,\n-\tCLEANUP_NONE,\n-\tCLEANUP_SCISSORS,\n-\tCLEANUP_ALL\n-} cleanup_mode;\n+static enum commit_msg_cleanup_mode cleanup_mode;\n static const char *cleanup_arg;\n \n static enum commit_whence whence;\n@@ -673,7 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tstruct strbuf sb = STRBUF_INIT;\n \tconst char *hook_arg1 = NULL;\n \tconst char *hook_arg2 = NULL;\n-\tint clean_message_contents = (cleanup_mode != CLEANUP_NONE);\n+\tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n \n \t/* This checks and barfs if author is badly specified */\n@@ -812,7 +807,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -832,14 +827,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t}\n \n \t\tfprintf(s->fp, \"\\n\");\n-\t\tif (cleanup_mode == CLEANUP_ALL)\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t whence == FROM_COMMIT)\n \t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* CLEANUP_SPACE, that is. */\n+\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\n@@ -984,65 +980,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \treturn 1;\n }\n \n-static int rest_is_empty(struct strbuf *sb, int start)\n-{\n-\tint i, eol;\n-\tconst char *nl;\n-\n-\t/* Check if the rest is just whitespace and Signed-off-by's. */\n-\tfor (i = start; i < sb->len; i++) {\n-\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n-\t\tif (nl)\n-\t\t\teol = nl - sb->buf;\n-\t\telse\n-\t\t\teol = sb->len;\n-\n-\t\tif (strlen(sign_off_header) <= eol - i &&\n-\t\t    starts_with(sb->buf + i, sign_off_header)) {\n-\t\t\ti = eol;\n-\t\t\tcontinue;\n-\t\t}\n-\t\twhile (i < eol)\n-\t\t\tif (!isspace(sb->buf[i++]))\n-\t\t\t\treturn 0;\n-\t}\n-\n-\treturn 1;\n-}\n-\n-/*\n- * Find out if the message in the strbuf contains only whitespace and\n- * Signed-off-by lines.\n- */\n-static int message_is_empty(struct strbuf *sb)\n-{\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\treturn rest_is_empty(sb, 0);\n-}\n-\n-/*\n- * See if the user edited the message in the editor or left what\n- * was in the template intact\n- */\n-static int template_untouched(struct strbuf *sb)\n-{\n-\tstruct strbuf tmpl = STRBUF_INIT;\n-\tconst char *start;\n-\n-\tif (cleanup_mode == CLEANUP_NONE && sb->len)\n-\t\treturn 0;\n-\n-\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n-\t\treturn 0;\n-\n-\tstrbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);\n-\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n-\t\tstart = sb->buf;\n-\tstrbuf_release(&tmpl);\n-\treturn rest_is_empty(sb, start - sb->buf);\n-}\n-\n static const char *find_author_by_nickname(const char *name)\n {\n \tstruct rev_info revs;\n@@ -1227,15 +1164,17 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n \tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\tcleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;\n+\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n \telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\tcleanup_mode = CLEANUP_NONE;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_NONE;\n \telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\tcleanup_mode = CLEANUP_SPACE;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_SPACE;\n \telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\tcleanup_mode = CLEANUP_ALL;\n+\t\tcleanup_mode = COMMIT_MSG_CLEANUP_ALL;\n \telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\tcleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;\n+\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n+\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n \telse\n \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n \n@@ -1768,17 +1707,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (verbose || /* Truncate the message just before the diff, if any. */\n-\t    cleanup_mode == CLEANUP_SCISSORS)\n+\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n \t\tstrbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));\n-\tif (cleanup_mode != CLEANUP_NONE)\n-\t\tstrbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);\n+\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n \n-\tif (message_is_empty(&sb) && !allow_empty_message) {\n+\tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit due to empty commit message.\\n\"));\n \t\texit(1);\n \t}\n-\tif (template_untouched(&sb) && !allow_empty_message) {\n+\tif (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\n \t\tfprintf(stderr, _(\"Aborting commit; you did not edit the message.\\n\"));\n \t\texit(1);\ndiff --git a/sequencer.c b/sequencer.c\nindex fa94ed652d2ce87fddf824aa516456b5f42735e7..168da5093e71f50a4d70af7288cf761110e69e87 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -692,6 +692,67 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n+static int rest_is_empty(const struct strbuf *sb, int start)\n+{\n+\tint i, eol;\n+\tconst char *nl;\n+\n+\t/* Check if the rest is just whitespace and Signed-off-by's. */\n+\tfor (i = start; i < sb->len; i++) {\n+\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n+\t\tif (nl)\n+\t\t\teol = nl - sb->buf;\n+\t\telse\n+\t\t\teol = sb->len;\n+\n+\t\tif (strlen(sign_off_header) <= eol - i &&\n+\t\t    starts_with(sb->buf + i, sign_off_header)) {\n+\t\t\ti = eol;\n+\t\t\tcontinue;\n+\t\t}\n+\t\twhile (i < eol)\n+\t\t\tif (!isspace(sb->buf[i++]))\n+\t\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+/*\n+ * Find out if the message in the strbuf contains only whitespace and\n+ * Signed-off-by lines.\n+ */\n+int message_is_empty(const struct strbuf *sb,\n+\t\t     enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\treturn rest_is_empty(sb, 0);\n+}\n+\n+/*\n+ * See if the user edited the message in the editor or left what\n+ * was in the template intact\n+ */\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tstruct strbuf tmpl = STRBUF_INIT;\n+\tconst char *start;\n+\n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)\n+\t\treturn 0;\n+\n+\tif (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)\n+\t\treturn 0;\n+\n+\tstrbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+\tif (!skip_prefix(sb->buf, tmpl.buf, &start))\n+\t\tstart = sb->buf;\n+\tstrbuf_release(&tmpl);\n+\treturn rest_is_empty(sb, start - sb->buf);\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\ndiff --git a/sequencer.h b/sequencer.h\nindex 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..2040773c7b6fd3966d5a1a14f410ea8ff53843c8 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -11,6 +11,13 @@ enum replay_action {\n \tREPLAY_INTERACTIVE_REBASE\n };\n \n+enum commit_msg_cleanup_mode {\n+\tCOMMIT_MSG_CLEANUP_SPACE,\n+\tCOMMIT_MSG_CLEANUP_NONE,\n+\tCOMMIT_MSG_CLEANUP_SCISSORS,\n+\tCOMMIT_MSG_CLEANUP_ALL\n+};\n+\n struct replay_opts {\n \tenum replay_action action;\n \n@@ -57,5 +64,8 @@ extern const char sign_off_header[];\n \n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n-\n+int message_is_empty(const struct strbuf *sb,\n+\t\t     enum commit_msg_cleanup_mode cleanup_mode);\n+int template_untouched(const struct strbuf *sb, const char *template_file,\n+\t\t       enum commit_msg_cleanup_mode cleanup_mode);\n #endif\n-- \n2.15.1\n\n"},{"id":"334603","messageId":"20171211141330.23566-7-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171211141330.23566-1-phillip.wood@talktalk.net","subject":"[PATCH v5 6/9] sequencer: simplify adding Signed-off-by: trailer","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T14:13:27Z","receivedAt":"2017-12-11T14:14:17Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAdd the Signed-off-by: trailer in one place rather than adding it to\nthe message when doing a recursive merge and specifying '--signoff'\nwhen running 'git commit'. This means that if there are conflicts when\nmerging with a strategy other than 'recursive' the Signed-off-by:\ntrailer will be added if the user commits the resolution themselves\nwithout passing '--signoff' to 'git commit'. It also simplifies the\nin-process commit that is about to be added to the sequencer.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n sequencer.c | 8 +++-----\n 1 file changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b4ff2a4a973b2733cca7bb65fcb7947cb8d08988..4966dd1b9359aaa82064608c05a7f5b18cea2d7a 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -478,9 +478,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \t\t\t_(action_name(opts)));\n \trollback_lock_file(&index_lock);\n \n-\tif (opts->signoff)\n-\t\tappend_signoff(msgbuf, 0, 0);\n-\n \tif (!clean)\n \t\tappend_conflicts_hint(msgbuf);\n \n@@ -658,8 +655,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\targv_array_push(&cmd.args, \"--amend\");\n \tif (opts->gpg_sign)\n \t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n-\tif (opts->signoff)\n-\t\targv_array_push(&cmd.args, \"-s\");\n \tif (defmsg)\n \t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n \tif ((flags & CLEANUP_MSG))\n@@ -1348,6 +1343,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n+\tif (opts->signoff)\n+\t\tappend_signoff(&msgbuf, 0, 0);\n+\n \tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n \t\tres = -1;\n \telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n-- \n2.15.1\n\n"},{"id":"334604","messageId":"20171211141330.23566-10-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171211141330.23566-1-phillip.wood@talktalk.net","subject":"[PATCH v5 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T14:13:30Z","receivedAt":"2017-12-11T14:14:47Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nNow that the sequencer creates commits without forking 'git commit' it\ndoes not see an empty commit in these tests which fixes the known\nbreakage. Note that logic for handling\nKNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 is not removed from\nlib-submodule-update.sh as it is still used by other tests.\n\nReviewed-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    Changes since v4\n     - Added reviewed-by trailer\n\n t/t3512-cherry-pick-submodule.sh | 1 -\n t/t3513-revert-submodule.sh      | 1 -\n 2 files changed, 2 deletions(-)\n\ndiff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh\nindex ce48c4fcca80b183927292cc1e5902cfe286f994..bd78287841ee053fd56a44a268f8077a222cc266 100755\n--- a/t/t3512-cherry-pick-submodule.sh\n+++ b/t/t3512-cherry-pick-submodule.sh\n@@ -5,7 +5,6 @@ test_description='cherry-pick can handle submodules'\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-submodule-update.sh\n \n-KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1\n KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1\n KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1\n test_submodule_switch \"git cherry-pick\"\ndiff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh\nindex db9378142a93338d2988f40e2748bc476490bcd5..5e39fcdb66c0c7c4b112c1bbe941d886db237693 100755\n--- a/t/t3513-revert-submodule.sh\n+++ b/t/t3513-revert-submodule.sh\n@@ -25,7 +25,6 @@ git_revert () {\n \tgit revert HEAD\n }\n \n-KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1\n KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1\n test_submodule_switch \"git_revert\"\n \n-- \n2.15.1\n\n"},{"id":"334605","messageId":"20171211141330.23566-9-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171211141330.23566-1-phillip.wood@talktalk.net","subject":"[PATCH v5 8/9] sequencer: try to commit without forking 'git commit'","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T14:13:29Z","receivedAt":"2017-12-11T14:15:11Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nIf the commit message does not need to be edited then create the\ncommit without forking 'git commit'. Taking the best time of ten runs\nwith a warm cache this reduces the time taken to cherry-pick 10\ncommits by 27% (from 282ms to 204ms), and the time taken by 'git\nrebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on\nmy computer running linux. Some of greater saving for rebase is\nbecause it no longer wastes time creating the commit summary just to\nthrow it away.\n\nThe code to create the commit is based on builtin/commit.c. It is\nsimplified as it doesn't have to deal with merges and modified so that\nit does not die but returns an error to make sure the sequencer exits\ncleanly, as it would when forking 'git commit'\n\nEven when not forking 'git commit' the commit message is written to a\nfile and CHERRY_PICK_HEAD is created unnecessarily. This could be\neliminated in future. I hacked up a version that does not write these\nfiles and just passed an strbuf (with the wrong message for fixup and\nsquash commands) to do_commit() but I couldn't measure any significant\ntime difference when running cherry-pick or rebase. I think\neliminating the writes properly for rebase would require a bit of\neffort as the code would need to be restructured.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v4:\n     - changed cleanup and gpg handling to reflect the changes in the last patch\n    \n    changes since v3:\n     - take account of change print_commit_summary() return type after\n       dropping the patch that made it return an error instead of dying.\n    \n    changes since v2:\n     - style fixes\n    \n    changes since v1:\n     - added comments to explain return value of try_to_commit()\n     - removed unnecessary NULL tests before calling free()\n     - style cleanups\n     - corrected commit message\n     - prefixed cleanup_mode constants to reflect the changes to patch 2\n       in this series\n\n sequencer.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 174 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 3ce1e5b71474f1cd25b232a319fb7b0e13dc6e14..74770bd00cc3840573057a1868e0a3acb05a71bb 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -638,6 +638,18 @@ static int read_env_script(struct argv_array *env)\n \treturn 0;\n }\n \n+static char *get_author(const char *message)\n+{\n+\tsize_t len;\n+\tconst char *a;\n+\n+\ta = find_commit_header(message, \"author\", &len);\n+\tif (a)\n+\t\treturn xmemdupz(a, len);\n+\n+\treturn NULL;\n+}\n+\n static const char staged_changes_advice[] =\n N_(\"you have staged changes in your working tree\\n\"\n \"If these changes are meant to be squashed into the previous commit, run:\\n\"\n@@ -996,6 +1008,158 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,\n \tstrbuf_release(&format);\n }\n \n+static int parse_head(struct commit **head)\n+{\n+\tstruct commit *current_head;\n+\tstruct object_id oid;\n+\n+\tif (get_oid(\"HEAD\", &oid)) {\n+\t\tcurrent_head = NULL;\n+\t} else {\n+\t\tcurrent_head = lookup_commit_reference(&oid);\n+\t\tif (!current_head)\n+\t\t\treturn error(_(\"could not parse HEAD\"));\n+\t\tif (oidcmp(&oid, &current_head->object.oid)) {\n+\t\t\twarning(_(\"HEAD %s is not a commit!\"),\n+\t\t\t\toid_to_hex(&oid));\n+\t\t}\n+\t\tif (parse_commit(current_head))\n+\t\t\treturn error(_(\"could not parse HEAD commit\"));\n+\t}\n+\t*head = current_head;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Try to commit without forking 'git commit'. In some cases we need\n+ * to run 'git commit' to display an error message\n+ *\n+ * Returns:\n+ *  -1 - error unable to commit\n+ *   0 - success\n+ *   1 - run 'git commit'\n+ */\n+static int try_to_commit(struct strbuf *msg, const char *author,\n+\t\t\t struct replay_opts *opts, unsigned int flags,\n+\t\t\t struct object_id *oid)\n+{\n+\tstruct object_id tree;\n+\tstruct commit *current_head;\n+\tstruct commit_list *parents = NULL;\n+\tstruct commit_extra_header *extra = NULL;\n+\tstruct strbuf err = STRBUF_INIT;\n+\tstruct strbuf amend_msg = STRBUF_INIT;\n+\tchar *amend_author = NULL;\n+\tenum commit_msg_cleanup_mode cleanup;\n+\tint res = 0;\n+\n+\tif (parse_head(&current_head))\n+\t\treturn -1;\n+\n+\tif (flags & AMEND_MSG) {\n+\t\tconst char *exclude_gpgsig[] = { \"gpgsig\", NULL };\n+\t\tconst char *out_enc = get_commit_output_encoding();\n+\t\tconst char *message = logmsg_reencode(current_head, NULL,\n+\t\t\t\t\t\t      out_enc);\n+\n+\t\tif (!msg) {\n+\t\t\tconst char *orig_message = NULL;\n+\n+\t\t\tfind_commit_subject(message, &orig_message);\n+\t\t\tmsg = &amend_msg;\n+\t\t\tstrbuf_addstr(msg, orig_message);\n+\t\t}\n+\t\tauthor = amend_author = get_author(message);\n+\t\tunuse_commit_buffer(current_head, message);\n+\t\tif (!author) {\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t\t\tgoto out;\n+\t\t}\n+\t\tparents = copy_commit_list(current_head->parents);\n+\t\textra = read_commit_extra_headers(current_head, exclude_gpgsig);\n+\t} else if (current_head) {\n+\t\tcommit_list_insert(current_head, &parents);\n+\t}\n+\n+\tcleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t\t  opts->default_msg_cleanup;\n+\n+\tif (cleanup != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);\n+\tif (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {\n+\t\tres = 1; /* run 'git commit' to display error message */\n+\t\tgoto out;\n+\t}\n+\n+\tif (write_cache_as_tree(tree.hash, 0, NULL)) {\n+\t\tres = error(_(\"git write-tree failed to write a tree\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?\n+\t\t\t\t\t      &current_head->tree->object.oid :\n+\t\t\t\t\t      &empty_tree_oid, &tree)) {\n+\t\tres = 1; /* run 'git commit' to display error message */\n+\t\tgoto out;\n+\t}\n+\n+\tif (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,\n+\t\t\t\t oid->hash, author, opts->gpg_sign, extra)) {\n+\t\tres = error(_(\"failed to write commit object\"));\n+\t\tgoto out;\n+\t}\n+\n+\tif (update_head_with_reflog(current_head, oid,\n+\t\t\t\t    getenv(\"GIT_REFLOG_ACTION\"), msg, &err)) {\n+\t\tres = error(\"%s\", err.buf);\n+\t\tgoto out;\n+\t}\n+\n+\tif (flags & AMEND_MSG)\n+\t\tcommit_post_rewrite(current_head, oid);\n+\n+out:\n+\tfree_commit_extra_headers(extra);\n+\tstrbuf_release(&err);\n+\tstrbuf_release(&amend_msg);\n+\tfree(amend_author);\n+\n+\treturn res;\n+}\n+\n+static int do_commit(const char *msg_file, const char *author,\n+\t\t     struct replay_opts *opts, unsigned int flags)\n+{\n+\tint res = 1;\n+\n+\tif (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {\n+\t\tstruct object_id oid;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)\n+\t\t\treturn error_errno(_(\"unable to read commit message \"\n+\t\t\t\t\t     \"from '%s'\"),\n+\t\t\t\t\t   msg_file);\n+\n+\t\tres = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,\n+\t\t\t\t    &oid);\n+\t\tstrbuf_release(&sb);\n+\t\tif (!res) {\n+\t\t\tunlink(git_path_cherry_pick_head());\n+\t\t\tunlink(git_path_merge_msg());\n+\t\t\tif (!is_rebase_i(opts))\n+\t\t\t\tprint_commit_summary(NULL, &oid,\n+\t\t\t\t\t\tSUMMARY_SHOW_AUTHOR_DATE);\n+\t\t\treturn res;\n+\t\t}\n+\t}\n+\tif (res == 1)\n+\t\treturn run_git_commit(msg_file, opts, flags);\n+\n+\treturn res;\n+}\n+\n static int is_original_commit_empty(struct commit *commit)\n {\n \tconst struct object_id *ptree_oid;\n@@ -1247,6 +1411,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \tstruct object_id head;\n \tstruct commit *base, *next, *parent;\n \tconst char *base_label, *next_label;\n+\tchar *author = NULL;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n \tint res, unborn = 0, allow;\n@@ -1363,6 +1528,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t\tstrbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));\n \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n \t\t}\n+\t\tif (!is_fixup(command))\n+\t\t\tauthor = get_author(msg.message);\n \t}\n \n \tif (command == TODO_REWORD)\n@@ -1448,9 +1615,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t} else if (allow)\n \t\tflags |= ALLOW_EMPTY;\n-\tif (!opts->no_commit)\n+\tif (!opts->no_commit) {\n fast_forward_edit:\n-\t\tres = run_git_commit(msg_file, opts, flags);\n+\t\tif (author || command == TODO_REVERT || (flags & AMEND_MSG))\n+\t\t\tres = do_commit(msg_file, author, opts, flags);\n+\t\telse\n+\t\t\tres = error(_(\"unable to parse commit author\"));\n+\t}\n \n \tif (!res && final_fixup) {\n \t\tunlink(rebase_path_fixup_msg());\n@@ -1459,6 +1630,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \n leave:\n \tfree_message(commit, &msg);\n+\tfree(author);\n \tupdate_abort_safety_file();\n \n \treturn res;\n-- \n2.15.1\n\n"},{"id":"334606","messageId":"20171211141330.23566-8-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"20171211141330.23566-1-phillip.wood@talktalk.net","subject":"[PATCH v5 7/9] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T14:13:28Z","receivedAt":"2017-12-11T14:17:21Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nLoad default values for message cleanup, gpg signing of commits and\nbasic diff configuration in preparation for committing without forking\n'git commit'. Note that we interpret commit.cleanup=scissors to mean\nCOMMIT_MSG_CLEANUP_SPACE to be consistent with 'git commit'.\n\nThe sequencer should probably have been calling\ngit_diff_basic_config() before as it creates a patch when there are\nconflicts. The shell version uses 'diff-index' to create the patch so\ncalling git_diff_basic_config() should match that. Although 'git\ncommit' calls git_diff_ui_config() I don't think the output of\nprint_commit_summary() is affected by anything that is loaded by that\nas print_commit_summary() always turns on rename detection so would\nignore the value in the user's configuration anyway. The other values\nloaded by git_diff_ui_config() are about the formatting of patches so\nare not relevant to print_commit_summary().\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nNotes:\n    changes since v4:\n     - reworked config handling to call git_diff_basic_config() and store\n       defaults in struct replay_opts rather than using global variables.\n     - added a warning if there is an invalid value for commit.cleanup.\n    \n    changes since v3:\n     - interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE\n       to match 'git commit'\n    \n    changes since v1:\n     - renamed git_revert_config() to common_config()\n     - prefixed cleanup_mode constants to reflect the changes to patch 2\n       in this series\n\n builtin/rebase--helper.c |  2 +-\n builtin/revert.c         |  4 ++--\n sequencer.c              | 45 +++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h              |  3 +++\n 4 files changed, 51 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c\nindex f8519363a393862b6857acab037e74367c7f2134..decb8f7a09e42eb94bed264164985e54e13a32f6 100644\n--- a/builtin/rebase--helper.c\n+++ b/builtin/rebase--helper.c\n@@ -39,7 +39,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_default_config, NULL);\n+\tsequencer_init_config(&opts);\n \n \topts.action = REPLAY_INTERACTIVE_REBASE;\n \topts.allow_ff = 1;\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex b9d927eb09c9ed87c84681df1396f4e6d9b13c97..76f0a35b074b858ab4cb3e3894bc7c877401b7e8 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -208,7 +208,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tif (isatty(0))\n \t\topts.edit = 1;\n \topts.action = REPLAY_REVERT;\n-\tgit_config(git_default_config, NULL);\n+\tsequencer_init_config(&opts);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"revert failed\"));\n@@ -221,7 +221,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \topts.action = REPLAY_PICK;\n-\tgit_config(git_default_config, NULL);\n+\tsequencer_init_config(&opts);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"cherry-pick failed\"));\ndiff --git a/sequencer.c b/sequencer.c\nindex 4966dd1b9359aaa82064608c05a7f5b18cea2d7a..3ce1e5b71474f1cd25b232a319fb7b0e13dc6e14 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -132,6 +132,51 @@ static GIT_PATH_FUNC(rebase_path_strategy, \"rebase-merge/strategy\")\n static GIT_PATH_FUNC(rebase_path_strategy_opts, \"rebase-merge/strategy_opts\")\n static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, \"rebase-merge/allow_rerere_autoupdate\")\n \n+static int git_sequencer_config(const char *k, const char *v, void *cb)\n+{\n+\tstruct replay_opts *opts = cb;\n+\tint status;\n+\n+\tif (!strcmp(k, \"commit.cleanup\")) {\n+\t\tconst char *s;\n+\n+\t\tstatus = git_config_string(&s, k, v);\n+\t\tif (status)\n+\t\t\treturn status;\n+\n+\t\tif (!strcmp(s, \"verbatim\"))\n+\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n+\t\telse if (!strcmp(s, \"whitespace\"))\n+\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n+\t\telse if (!strcmp(s, \"strip\"))\n+\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n+\t\telse if (!strcmp(s, \"scissors\"))\n+\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n+\t\telse\n+\t\t\twarning(_(\"invalid commit message cleanup mode '%s'\"),\n+\t\t\t\t  s);\n+\n+\t\treturn status;\n+\t}\n+\n+\tif (!strcmp(k, \"commit.gpgsign\")) {\n+\t\topts->gpg_sign = git_config_bool(k, v) ? \"\" : NULL;\n+\t\treturn 0;\n+\t}\n+\n+\tstatus = git_gpg_config(k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_diff_basic_config(k, v, NULL);\n+}\n+\n+void sequencer_init_config(struct replay_opts *opts)\n+{\n+\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n+\tgit_config(git_sequencer_config, opts);\n+}\n+\n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n \treturn opts->action == REPLAY_INTERACTIVE_REBASE;\ndiff --git a/sequencer.h b/sequencer.h\nindex bf72e339adbb81900283d8811ed51569aa3e05ee..3a5072c2ab9088c237b83d92deae3c801289e543 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -36,6 +36,7 @@ struct replay_opts {\n \tint mainline;\n \n \tchar *gpg_sign;\n+\tenum commit_msg_cleanup_mode default_msg_cleanup;\n \n \t/* Merge strategy */\n \tchar *strategy;\n@@ -47,6 +48,8 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { -1 }\n \n+/* Call this to setup defaults before parsing command line options */\n+void sequencer_init_config(struct replay_opts *opts);\n int sequencer_pick_revisions(struct replay_opts *opts);\n int sequencer_continue(struct replay_opts *opts);\n int sequencer_rollback(struct replay_opts *opts);\n-- \n2.15.1\n\n"},{"id":"334617","messageId":"8bf28a43-fe0d-67c9-0a47-3e022a406ad7@talktalk.net","threadId":"46827","inReplyTo":"20171211141330.23566-8-phillip.wood@talktalk.net","subject":"Re: [PATCH v5 7/9] sequencer: load commit related config","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-11T18:53:53Z","receivedAt":"2017-12-11T18:54:05Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 11/12/17 14:13, Phillip Wood wrote:\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n> \n> Load default values for message cleanup, gpg signing of commits and\n> basic diff configuration in preparation for committing without forking\n> 'git commit'. Note that we interpret commit.cleanup=scissors to mean\n> COMMIT_MSG_CLEANUP_SPACE to be consistent with 'git commit'.\n> \n> The sequencer should probably have been calling\n> git_diff_basic_config() before as it creates a patch when there are\n> conflicts. The shell version uses 'diff-index' to create the patch so\n\ns/diff-index/diff-tree/\n"},{"id":"334649","messageId":"xmqq8te84yo6.fsf@gitster.mtv.corp.google.com","threadId":"46827","inReplyTo":"20171211141330.23566-1-phillip.wood@talktalk.net","subject":"Re: [PATCH v5 0/9] sequencer: don't fork git commit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-12-11T23:44:57Z","receivedAt":"2017-12-11T23:45:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood@talktalk.net> writes:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> I've reworked the config handling since v4. It now stores the default\n> values in struct replay_opt rather than using global variables and\n> calls git_diff_basic_config(). Unfortunately I've not had time to\n> modify git_gpg_config() to indicate if it successfully handled the key\n> so git_diff_basic_config() is called unnecessarily in that case. Within\n> git_diff_basic_config() userdiff_config() also suffers from the same\n> problem of not indicating if it has handled the key.\n\nOuch.  I thought we agreed that we were ready to go incremental and\nthe topic was merged to 'next' earlier last week.\n\nAfter scanning the difference between the two rounds, it seems that\nthe more important difference is the rework of the configuration,\nwhich looks better thought out than the previous round, and with\nassociated change to use replay_opts fields instead of free variables\nto carry gpg-sign and cleanup-mode settings around, which also looks\nsensible to me.\n\nCan you make these differences into incremental \"that earlier one\nwas suboptimal for this and that reasons, let's make it better by\ndoing this\" patches to queue them on top?\n\nThanks.\n"},{"id":"334669","messageId":"a57362a8-3e55-746f-d25a-56dd24a17278@talktalk.net","threadId":"46827","inReplyTo":"xmqq8te84yo6.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 0/9] sequencer: don't fork git commit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-12T10:32:16Z","receivedAt":"2017-12-12T10:32:29Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 11/12/17 23:44, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood@talktalk.net> writes:\n> \n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> I've reworked the config handling since v4. It now stores the default\n>> values in struct replay_opt rather than using global variables and\n>> calls git_diff_basic_config(). Unfortunately I've not had time to\n>> modify git_gpg_config() to indicate if it successfully handled the key\n>> so git_diff_basic_config() is called unnecessarily in that case. Within\n>> git_diff_basic_config() userdiff_config() also suffers from the same\n>> problem of not indicating if it has handled the key.\n> \n> Ouch.  I thought we agreed that we were ready to go incremental and\n> the topic was merged to 'next' earlier last week.\n\nSorry, I thought we were waiting, not to worry.\n> \n> After scanning the difference between the two rounds, it seems that\n> the more important difference is the rework of the configuration,\n> which looks better thought out than the previous round, and with\n> associated change to use replay_opts fields instead of free variables\n> to carry gpg-sign and cleanup-mode settings around, which also looks\n> sensible to me.\n\nYes the configuration is the major change, I'm glad you think it is an\nimprovement\n\n> Can you make these differences into incremental \"that earlier one\n> was suboptimal for this and that reasons, let's make it better by\n> doing this\" patches to queue them on top?\n\nWill do, though if I don't get round to it today or tomorrow it may be\nthe New Year before I send them, I hope that's OK\n\nBest Wishes\n\nPhillip\n\n> Thanks.\n> \n\n"},{"id":"334741","messageId":"20171213114621.30250-1-phillip.wood@talktalk.net","threadId":"46827","inReplyTo":"xmqq8te84yo6.fsf@gitster.mtv.corp.google.com","subject":"[PATCH] sequencer: improve config handling","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-13T11:46:21Z","receivedAt":"2017-12-13T11:46:44Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nThe previous config handling relied on global variables, called\ngit_default_config() even when the key had already been handled by\ngit_sequencer_config() and did not initialize the diff configuration\nvariables. Improve this by: i) loading the default values for message\ncleanup and gpg signing of commits into struct replay_opts;\nii) restructuring the code to return immediately once a key is\nhandled; and iii) calling git_diff_basic_config(). Note that\nunfortunately it is not possible to return early if the key is handled\nby git_gpg_config() as it does not indicate to the caller if the key\nhas been handled or not.\n\nThe sequencer should probably have been calling\ngit_diff_basic_config() before as it creates a patch when there are\nconflicts. The shell version uses 'diff-tree' to create the patch so\ncalling git_diff_basic_config() should match that. Although 'git\ncommit' calls git_diff_ui_config() I don't think the output of\nprint_commit_summary() is affected by anything that is loaded by that\nas print_commit_summary() always turns on rename detection so would\nignore the value in the user's configuration anyway. The other values\nloaded by git_diff_ui_config() are about the formatting of patches so\nare not relevant to print_commit_summary().\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n builtin/rebase--helper.c | 13 +-------\n builtin/revert.c         | 15 ++-------\n sequencer.c              | 87 ++++++++++++++++++++++++++----------------------\n sequencer.h              | 19 ++++++-----\n 4 files changed, 61 insertions(+), 73 deletions(-)\n\ndiff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c\nindex 68194d3aed950f327a8bc624fa1991478dfea01e..decb8f7a09e42eb94bed264164985e54e13a32f6 100644\n--- a/builtin/rebase--helper.c\n+++ b/builtin/rebase--helper.c\n@@ -9,17 +9,6 @@ static const char * const builtin_rebase_helper_usage[] = {\n \tNULL\n };\n \n-static int git_rebase_helper_config(const char *k, const char *v, void *cb)\n-{\n-\tint status;\n-\n-\tstatus = git_sequencer_config(k, v, NULL);\n-\tif (status)\n-\t\treturn status;\n-\n-\treturn git_default_config(k, v, NULL);\n-}\n-\n int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts = REPLAY_OPTS_INIT;\n@@ -50,7 +39,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_rebase_helper_config, NULL);\n+\tsequencer_init_config(&opts);\n \n \topts.action = REPLAY_INTERACTIVE_REBASE;\n \topts.allow_ff = 1;\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 1938825efa6ad20ede5aba57f097863aeb33d1d5..76f0a35b074b858ab4cb3e3894bc7c877401b7e8 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -31,17 +31,6 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n-static int common_config(const char *k, const char *v, void *cb)\n-{\n-\tint status;\n-\n-\tstatus = git_sequencer_config(k, v, NULL);\n-\tif (status)\n-\t\treturn status;\n-\n-\treturn git_default_config(k, v, NULL);\n-}\n-\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n@@ -219,7 +208,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tif (isatty(0))\n \t\topts.edit = 1;\n \topts.action = REPLAY_REVERT;\n-\tgit_config(common_config, NULL);\n+\tsequencer_init_config(&opts);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"revert failed\"));\n@@ -232,7 +221,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \topts.action = REPLAY_PICK;\n-\tgit_config(common_config, NULL);\n+\tsequencer_init_config(&opts);\n \tres = run_sequencer(argc, argv, &opts);\n \tif (res < 0)\n \t\tdie(_(\"cherry-pick failed\"));\ndiff --git a/sequencer.c b/sequencer.c\nindex 0f17b4d32580aa637ddfeedfaec68468a9995e3d..c3035bb5f3d8bb1c6a6be8f3bc8d1bb29cf4383c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -132,6 +132,51 @@ static GIT_PATH_FUNC(rebase_path_strategy, \"rebase-merge/strategy\")\n static GIT_PATH_FUNC(rebase_path_strategy_opts, \"rebase-merge/strategy_opts\")\n static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, \"rebase-merge/allow_rerere_autoupdate\")\n \n+static int git_sequencer_config(const char *k, const char *v, void *cb)\n+{\n+\tstruct replay_opts *opts = cb;\n+\tint status;\n+\n+\tif (!strcmp(k, \"commit.cleanup\")) {\n+\t\tconst char *s;\n+\n+\t\tstatus = git_config_string(&s, k, v);\n+\t\tif (status)\n+\t\t\treturn status;\n+\n+\t\tif (!strcmp(s, \"verbatim\"))\n+\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n+\t\telse if (!strcmp(s, \"whitespace\"))\n+\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n+\t\telse if (!strcmp(s, \"strip\"))\n+\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n+\t\telse if (!strcmp(s, \"scissors\"))\n+\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n+\t\telse\n+\t\t\twarning(_(\"invalid commit message cleanup mode '%s'\"),\n+\t\t\t\t  s);\n+\n+\t\treturn status;\n+\t}\n+\n+\tif (!strcmp(k, \"commit.gpgsign\")) {\n+\t\topts->gpg_sign = git_config_bool(k, v) ? \"\" : NULL;\n+\t\treturn 0;\n+\t}\n+\n+\tstatus = git_gpg_config(k, v, NULL);\n+\tif (status)\n+\t\treturn status;\n+\n+\treturn git_diff_basic_config(k, v, NULL);\n+}\n+\n+void sequencer_init_config(struct replay_opts *opts)\n+{\n+\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n+\tgit_config(git_sequencer_config, opts);\n+}\n+\n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n \treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n@@ -703,40 +748,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \treturn run_command(&cmd);\n }\n \n-static enum commit_msg_cleanup_mode default_msg_cleanup =\n-\t\t\t\t\t\tCOMMIT_MSG_CLEANUP_NONE;\n-static char *default_gpg_sign;\n-\n-int git_sequencer_config(const char *k, const char *v, void *cb)\n-{\n-\tif (!strcmp(k, \"commit.cleanup\")) {\n-\t\tint status;\n-\t\tconst char *s;\n-\n-\t\tstatus = git_config_string(&s, k, v);\n-\t\tif (status)\n-\t\t\treturn status;\n-\n-\t\tif (!strcmp(s, \"verbatim\"))\n-\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n-\t\telse if (!strcmp(s, \"whitespace\"))\n-\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n-\t\telse if (!strcmp(s, \"strip\"))\n-\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n-\t\telse if (!strcmp(s, \"scissors\"))\n-\t\t\tdefault_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n-\n-\t\treturn status;\n-\t}\n-\n-\tif (!strcmp(k, \"commit.gpgsign\")) {\n-\t\tdefault_gpg_sign = git_config_bool(k, v) ? \"\" : NULL;\n-\t\treturn 0;\n-\t}\n-\n-\treturn git_gpg_config(k, v, NULL);\n-}\n-\n static int rest_is_empty(const struct strbuf *sb, int start)\n {\n \tint i, eol;\n@@ -1042,7 +1053,6 @@ static int try_to_commit(struct strbuf *msg, const char *author,\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct strbuf amend_msg = STRBUF_INIT;\n \tchar *amend_author = NULL;\n-\tconst char *gpg_sign;\n \tenum commit_msg_cleanup_mode cleanup;\n \tint res = 0;\n \n@@ -1075,7 +1085,8 @@ static int try_to_commit(struct strbuf *msg, const char *author,\n \t}\n \n \tcleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t\t  default_msg_cleanup;\n+\t\t\t\t\t  opts->default_msg_cleanup;\n+\n \tif (cleanup != COMMIT_MSG_CLEANUP_NONE)\n \t\tstrbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);\n \tif (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {\n@@ -1083,8 +1094,6 @@ static int try_to_commit(struct strbuf *msg, const char *author,\n \t\tgoto out;\n \t}\n \n-\tgpg_sign = opts->gpg_sign ? opts->gpg_sign : default_gpg_sign;\n-\n \tif (write_cache_as_tree(tree.hash, 0, NULL)) {\n \t\tres = error(_(\"git write-tree failed to write a tree\"));\n \t\tgoto out;\n@@ -1098,7 +1107,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,\n \t}\n \n \tif (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,\n-\t\t\t\t oid->hash, author, gpg_sign, extra)) {\n+\t\t\t\t oid->hash, author, opts->gpg_sign, extra)) {\n \t\tres = error(_(\"failed to write commit object\"));\n \t\tgoto out;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 77cb174b2aaf3972ebb9e6ec379252be96dedd3d..3a5072c2ab9088c237b83d92deae3c801289e543 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -11,6 +11,13 @@ enum replay_action {\n \tREPLAY_INTERACTIVE_REBASE\n };\n \n+enum commit_msg_cleanup_mode {\n+\tCOMMIT_MSG_CLEANUP_SPACE,\n+\tCOMMIT_MSG_CLEANUP_NONE,\n+\tCOMMIT_MSG_CLEANUP_SCISSORS,\n+\tCOMMIT_MSG_CLEANUP_ALL\n+};\n+\n struct replay_opts {\n \tenum replay_action action;\n \n@@ -29,6 +36,7 @@ struct replay_opts {\n \tint mainline;\n \n \tchar *gpg_sign;\n+\tenum commit_msg_cleanup_mode default_msg_cleanup;\n \n \t/* Merge strategy */\n \tchar *strategy;\n@@ -40,6 +48,8 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { -1 }\n \n+/* Call this to setup defaults before parsing command line options */\n+void sequencer_init_config(struct replay_opts *opts);\n int sequencer_pick_revisions(struct replay_opts *opts);\n int sequencer_continue(struct replay_opts *opts);\n int sequencer_rollback(struct replay_opts *opts);\n@@ -57,15 +67,6 @@ extern const char sign_off_header[];\n \n void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);\n void append_conflicts_hint(struct strbuf *msgbuf);\n-int git_sequencer_config(const char *k, const char *v, void *cb);\n-\n-enum commit_msg_cleanup_mode {\n-\tCOMMIT_MSG_CLEANUP_SPACE,\n-\tCOMMIT_MSG_CLEANUP_NONE,\n-\tCOMMIT_MSG_CLEANUP_SCISSORS,\n-\tCOMMIT_MSG_CLEANUP_ALL\n-};\n-\n int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n-- \n2.15.1\n\n"},{"id":"335097","messageId":"1513794792.9785.2.camel@gmail.com","threadId":"46827","inReplyTo":"20171213114621.30250-1-phillip.wood@talktalk.net","subject":"Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)","fromName":"Kaartic Sivaraam","fromEmail":"kaartic.sivaraam@gmail.com","sentAt":"2017-12-20T18:33:12Z","receivedAt":"2017-12-20T18:33:36Z","isPatch":true,"sender":{"key":"kaartic.sivaraam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12448084?v=4"},"body":"I recently encountered that error when trying to do an interactive\nrebase after using filter-branch to remove a file completely in a\nrepository. I bisected this issue which pointed at this patch. I'm not\nsure how it is related as I'm not too familiar with the sequencer code.\nI could help in case any specific information is needed. As a first\nstep, I've posted the output of \"strace /mnt/Source//Git/git rebase -i\nHEAD~10\" below.\n\n\n-- 8< --\nexecve(\"/mnt/Source//Git/git-next/git\", [\"/mnt/Source//Git/git-next/git\", \"rebase\", \"-i\", \"HEAD~10\"], [/* 62 vars */]) = 0\nbrk(NULL)                               = 0x55a494d30000\naccess(\"/etc/ld.so.nohwcap\", F_OK)      = -1 ENOENT (No such file or directory)\nmmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f25bd940000\naccess(\"/etc/ld.so.preload\", R_OK)      = -1 ENOENT (No such file or directory)\nopen(\"/etc/ld.so.cache\", O_RDONLY|O_CLOEXEC) = 3\nfstat(3, {st_mode=S_IFREG|0644, st_size=114620, ...}) = 0\nmmap(NULL, 114620, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f25bd924000\nclose(3)                                = 0\naccess(\"/etc/ld.so.nohwcap\", F_OK)      = -1 ENOENT (No such file or directory)\nopen(\"/lib/x86_64-linux-gnu/libz.so.1\", O_RDONLY|O_CLOEXEC) = 3\nread(3, \"\\177ELF\\2\\1\\1\\0\\0\\0\\0\\0\\0\\0\\0\\0\\3\\0>\\0\\1\\0\\0\\0\\300!\\0\\0\\0\\0\\0\\0\"..., 832) = 832\nfstat(3, {st_mode=S_IFREG|0644, st_size=105088, ...}) = 0\nmmap(NULL, 2200072, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25bd506000\nmprotect(0x7f25bd51f000, 2093056, PROT_NONE) = 0\nmmap(0x7f25bd71e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18000) = 0x7f25bd71e000\nclose(3)                                = 0\naccess(\"/etc/ld.so.nohwcap\", F_OK)      = -1 ENOENT (No such file or directory)\nopen(\"/lib/x86_64-linux-gnu/libpthread.so.0\", O_RDONLY|O_CLOEXEC) = 3\nread(3, \"\\177ELF\\2\\1\\1\\0\\0\\0\\0\\0\\0\\0\\0\\0\\3\\0>\\0\\1\\0\\0\\0Pa\\0\\0\\0\\0\\0\\0\"..., 832) = 832\nfstat(3, {st_mode=S_IFREG|0755, st_size=135440, ...}) = 0\nmmap(NULL, 2212936, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25bd2e9000\nmprotect(0x7f25bd301000, 2093056, PROT_NONE) = 0\nmmap(0x7f25bd500000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7f25bd500000\nmmap(0x7f25bd502000, 13384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f25bd502000\nclose(3)                                = 0\naccess(\"/etc/ld.so.nohwcap\", F_OK)      = -1 ENOENT (No such file or directory)\nopen(\"/lib/x86_64-linux-gnu/librt.so.1\", O_RDONLY|O_CLOEXEC) = 3\nread(3, \"\\177ELF\\2\\1\\1\\0\\0\\0\\0\\0\\0\\0\\0\\0\\3\\0>\\0\\1\\0\\0\\0\\340 \\0\\0\\0\\0\\0\\0\"..., 832) = 832\nfstat(3, {st_mode=S_IFREG|0644, st_size=31744, ...}) = 0\nmmap(NULL, 2128832, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25bd0e1000\nmprotect(0x7f25bd0e8000, 2093056, PROT_NONE) = 0\nmmap(0x7f25bd2e7000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7f25bd2e7000\nclose(3)                                = 0\naccess(\"/etc/ld.so.nohwcap\", F_OK)      = -1 ENOENT (No such file or directory)\nopen(\"/lib/x86_64-linux-gnu/libc.so.6\", O_RDONLY|O_CLOEXEC) = 3\nread(3, \"\\177ELF\\2\\1\\1\\3\\0\\0\\0\\0\\0\\0\\0\\0\\3\\0>\\0\\1\\0\\0\\0\\0\\4\\2\\0\\0\\0\\0\\0\"..., 832) = 832\nfstat(3, {st_mode=S_IFREG|0755, st_size=1689360, ...}) = 0\nmmap(NULL, 3795296, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25bcd42000\nmprotect(0x7f25bced7000, 2097152, PROT_NONE) = 0\nmmap(0x7f25bd0d7000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x195000) = 0x7f25bd0d7000\nmmap(0x7f25bd0dd000, 14688, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f25bd0dd000\nclose(3)                                = 0\nmmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f25bd922000\narch_prctl(ARCH_SET_FS, 0x7f25bd922e80) = 0\nmprotect(0x7f25bd0d7000, 16384, PROT_READ) = 0\nmprotect(0x7f25bd500000, 4096, PROT_READ) = 0\nmprotect(0x7f25bd2e7000, 4096, PROT_READ) = 0\nmprotect(0x7f25bd71e000, 4096, PROT_READ) = 0\nmprotect(0x55a49424b000, 12288, PROT_READ) = 0\nmprotect(0x7f25bd943000, 4096, PROT_READ) = 0\nmunmap(0x7f25bd924000, 114620)          = 0\nset_tid_address(0x7f25bd923150)         = 9667\nset_robust_list(0x7f25bd923160, 24)     = 0\nrt_sigaction(SIGRTMIN, {sa_handler=0x7f25bd2eebd0, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f25bd2fa0c0}, NULL, 8) = 0\nrt_sigaction(SIGRT_1, {sa_handler=0x7f25bd2eec60, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f25bd2fa0c0}, NULL, 8) = 0\nrt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0\ngetrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0\nopen(\"/dev/null\", O_RDWR)               = 3\nclose(3)                                = 0\nrt_sigprocmask(SIG_UNBLOCK, [PIPE], NULL, 8) = 0\nrt_sigaction(SIGPIPE, {sa_handler=SIG_DFL, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0\nbrk(NULL)                               = 0x55a494d30000\nbrk(0x55a494d51000)                     = 0x55a494d51000\ngetcwd(\"/mnt/Source/bash/lxconf-bash\", 129) = 29\nstat(\"/mnt/Source/bash/lxconf-bash\", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0\nstat(\"/mnt/Source/bash/lxconf-bash/.git\", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0\nlstat(\"/mnt/Source/bash/lxconf-bash/.git/HEAD\", {st_mode=S_IFREG|0644, st_size=23, ...}) = 0\nopen(\"/mnt/Source/bash/lxconf-bash/.git/HEAD\", O_RDONLY) = 3\nread(3, \"ref: refs/heads/public\\n\", 255) = 23\nread(3, \"\", 232)                        = 0\nclose(3)                                = 0\nlstat(\"/mnt/Source/bash/lxconf-bash/.git/commondir\", 0x7ffcd3d12100) = -1 ENOENT (No such file or directory)\naccess(\"/mnt/Source/bash/lxconf-bash/.git/objects\", X_OK) = 0\naccess(\"/mnt/Source/bash/lxconf-bash/.git/refs\", X_OK) = 0\nlstat(\".git/commondir\", 0x7ffcd3d12270) = -1 ENOENT (No such file or directory)\nopen(\".git/config\", O_RDONLY)           = 3\nfstat(3, {st_mode=S_IFREG|0644, st_size=738, ...}) = 0\nfstat(3, {st_mode=S_IFREG|0644, st_size=738, ...}) = 0\nread(3, \"[core]\\n\\trepositoryformatversion \"..., 4096) = 738\nread(3, \"\", 4096)                       = 0\nclose(3)                                = 0\naccess(\"/home/unique/.local/etc/gitconfig\", R_OK) = -1 ENOENT (No such file or directory)\naccess(\"/home/unique/.config/git/config\", R_OK) = -1 ENOENT (No such file or directory)\naccess(\"/home/unique/.gitconfig\", R_OK) = 0\nopen(\"/home/unique/.gitconfig\", O_RDONLY) = 3\nfstat(3, {st_mode=S_IFREG|0644, st_size=866, ...}) = 0\nfstat(3, {st_mode=S_IFREG|0644, st_size=866, ...}) = 0\nread(3, \"[user]\\n\\temail = kaartic.sivaraam\"..., 4096) = 866\nread(3, \"\", 4096)                       = 0\nclose(3)                                = 0\naccess(\".git/config\", R_OK)             = 0\nopen(\".git/config\", O_RDONLY)           = 3\nfstat(3, {st_mode=S_IFREG|0644, st_size=738, ...}) = 0\nfstat(3, {st_mode=S_IFREG|0644, st_size=738, ...}) = 0\nread(3, \"[core]\\n\\trepositoryformatversion \"..., 4096) = 738\nread(3, \"\", 4096)                       = 0\nclose(3)                                = 0\npipe([3, 4])                            = 0\nstat(\"/home/unique/.local/libexec/git-core/git-rebase\", {st_mode=S_IFREG|0755, st_size=16486, ...}) = 0\nrt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1], [], 8) = 0\nclone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f25bd923150) = 9668\nrt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0\nrt_sigaction(SIGINT, {sa_handler=0x55a493f78a70, sa_mask=[INT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0\nrt_sigaction(SIGHUP, {sa_handler=0x55a493f78a70, sa_mask=[HUP], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0\nrt_sigaction(SIGTERM, {sa_handler=0x55a493f78a70, sa_mask=[TERM], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0\nrt_sigaction(SIGQUIT, {sa_handler=0x55a493f78a70, sa_mask=[QUIT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0\nrt_sigaction(SIGPIPE, {sa_handler=0x55a493f78a70, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, 8) = 0\nclose(4)                                = 0\nread(3, \"\", 8)                          = 0\nclose(3)                                = 0\nwait4(9668, *** Error in `git': free(): invalid pointer: 0x0000557bf5cdf98a ***\n======= Backtrace: =========\n/lib/x86_64-linux-gnu/libc.so.6(+0x70bfb)[0x7fcb647d1bfb]\n/lib/x86_64-linux-gnu/libc.so.6(+0x76fc6)[0x7fcb647d7fc6]\n/lib/x86_64-linux-gnu/libc.so.6(+0x7780e)[0x7fcb647d880e]\ngit(+0x1463f9)[0x557bf5c853f9]\ngit(+0x6b5dd)[0x557bf5baa5dd]\ngit(+0x13848)[0x557bf5b52848]\ngit(+0x13b06)[0x557bf5b52b06]\ngit(+0x12c0c)[0x557bf5b51c0c]\n/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7fcb647812e1]\ngit(+0x12c4a)[0x557bf5b51c4a]\n======= Memory map: ========\n557bf5b3f000-557bf5d4e000 r-xp 00000000 08:09 1178454                    /home/unique/.local/libexec/git-core/git\n557bf5f4d000-557bf5f50000 r--p 0020e000 08:09 1178454                    /home/unique/.local/libexec/git-core/git\n557bf5f50000-557bf5f5c000 rw-p 00211000 08:09 1178454                    /home/unique/.local/libexec/git-core/git\n557bf5f5c000-557bf5f9f000 rw-p 00000000 00:00 0 \n557bf7917000-557bf795d000 rw-p 00000000 00:00 0                          [heap]\n7fcb60000000-7fcb60021000 rw-p 00000000 00:00 0 \n7fcb60021000-7fcb64000000 ---p 00000000 00:00 0 \n7fcb6454a000-7fcb64560000 r-xp 00000000 08:0b 263716                     /lib/x86_64-linux-gnu/libgcc_s.so.1\n7fcb64560000-7fcb6475f000 ---p 00016000 08:0b 263716                     /lib/x86_64-linux-gnu/libgcc_s.so.1\n7fcb6475f000-7fcb64760000 r--p 00015000 08:0b 263716                     /lib/x86_64-linux-gnu/libgcc_s.so.1\n7fcb64760000-7fcb64761000 rw-p 00016000 08:0b 263716                     /lib/x86_64-linux-gnu/libgcc_s.so.1\n7fcb64761000-7fcb648f6000 r-xp 00000000 08:0b 268114                     /lib/x86_64-linux-gnu/libc-2.24.so\n7fcb648f6000-7fcb64af6000 ---p 00195000 08:0b 268114                     /lib/x86_64-linux-gnu/libc-2.24.so\n7fcb64af6000-7fcb64afa000 r--p 00195000 08:0b 268114                     /lib/x86_64-linux-gnu/libc-2.24.so\n7fcb64afa000-7fcb64afc000 rw-p 00199000 08:0b 268114                     /lib/x86_64-linux-gnu/libc-2.24.so\n7fcb64afc000-7fcb64b00000 rw-p 00000000 00:00 0 \n7fcb64b00000-7fcb64b07000 r-xp 00000000 08:0b 268132                     /lib/x86_64-linux-gnu/librt-2.24.so\n7fcb64b07000-7fcb64d06000 ---p 00007000 08:0b 268132                     /lib/x86_64-linux-gnu/librt-2.24.so\n7fcb64d06000-7fcb64d07000 r--p 00006000 08:0b 268132                     /lib/x86_64-linux-gnu/librt-2.24.so\n7fcb64d07000-7fcb64d08000 rw-p 00007000 08:0b 268132                     /lib/x86_64-linux-gnu/librt-2.24.so\n7fcb64d08000-7fcb64d20000 r-xp 00000000 08:0b 268130                     /lib/x86_64-linux-gnu/libpthread-2.24.so\n7fcb64d20000-7fcb64f1f000 ---p 00018000 08:0b 268130                     /lib/x86_64-linux-gnu/libpthread-2.24.so\n7fcb64f1f000-7fcb64f20000 r--p 00017000 08:0b 268130                     /lib/x86_64-linux-gnu/libpthread-2.24.so\n7fcb64f20000-7fcb64f21000 rw-p 00018000 08:0b 268130                     /lib/x86_64-linux-gnu/libpthread-2.24.so\n7fcb64f21000-7fcb64f25000 rw-p 00000000 00:00 0 \n7fcb64f25000-7fcb64f3e000 r-xp 00000000 08:0b 263835                     /lib/x86_64-linux-gnu/libz.so.1.2.8\n7fcb64f3e000-7fcb6513d000 ---p 00019000 08:0b 263835                     /lib/x86_64-linux-gnu/libz.so.1.2.8\n7fcb6513d000-7fcb6513e000 r--p 00018000 08:0b 263835                     /lib/x86_64-linux-gnu/libz.so.1.2.8\n7fcb6513e000-7fcb6513f000 rw-p 00019000 08:0b 263835                     /lib/x86_64-linux-gnu/libz.so.1.2.8\n7fcb6513f000-7fcb65162000 r-xp 00000000 08:0b 263494                     /lib/x86_64-linux-gnu/ld-2.24.so\n7fcb652c1000-7fcb65343000 rw-p 00000000 00:00 0 \n7fcb6535d000-7fcb6535e000 rw-p 00000000 00:00 0 \n7fcb6535e000-7fcb6535f000 r--p 00000000 08:06 57439                      /mnt/Source/bash/lxconf-bash/.git/packed-refs\n7fcb6535f000-7fcb65362000 rw-p 00000000 00:00 0 \n7fcb65362000-7fcb65363000 r--p 00023000 08:0b 263494                     /lib/x86_64-linux-gnu/ld-2.24.so\n7fcb65363000-7fcb65364000 rw-p 00024000 08:0b 263494                     /lib/x86_64-linux-gnu/ld-2.24.so\n7fcb65364000-7fcb65365000 rw-p 00000000 00:00 0 \n7ffdf94a1000-7ffdf94c2000 rw-p 00000000 00:00 0                          [stack]\n7ffdf95cf000-7ffdf95d1000 r--p 00000000 00:00 0                          [vvar]\n7ffdf95d1000-7ffdf95d3000 r-xp 00000000 00:00 0                          [vdso]\nffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]\n[{WIFSIGNALED(s) && WTERMSIG(s) == SIGABRT}], 0, NULL) = 9668\n--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=9668, si_uid=1000, si_status=SIGABRT, si_utime=0, si_stime=1} ---\nwrite(2, \"error: git-rebase died of signal\"..., 35error: git-rebase died of signal 6\n) = 35\nexit_group(134)                         = ?\n+++ exited with 134 +++\n"},{"id":"335124","messageId":"alpine.DEB.2.21.1.1712211442470.406@MININT-6BKU6QN.europe.corp.microsoft.com","threadId":"46827","inReplyTo":"1513794792.9785.2.camel@gmail.com","subject":"Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-12-21T14:06:04Z","receivedAt":"2017-12-21T14:06:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Kaartic,\n\nOn Thu, 21 Dec 2017, Kaartic Sivaraam wrote:\n\n> I recently encountered that error when trying to do an interactive\n> rebase after using filter-branch to remove a file completely in a\n> repository. I bisected this issue which pointed at this patch. I'm not\n> sure how it is related as I'm not too familiar with the sequencer code.\n> I could help in case any specific information is needed. As a first\n> step, I've posted the output of \"strace /mnt/Source//Git/git rebase -i\n> HEAD~10\" below.\n\nI fear that the strace does not cover the `git-rebase` process nor the\n`git-rebase--helper` process (which must have been part of your run, as\nthe commit affected only that part of the rebase operation).\n\nIf you have valgrind installed, you may want to give it a try. Since\ngit-rebase is (still, much to my dislike) a Unix shell script, you will\nhave to work quite hard to get it to valgrind correctly.\n\nThis is how I typically do it: I edit the\n/home/unique/.local/libexec/git-core/git-rebase file directly (it is in a\ndifferent path for me, of course), first adding a `set -x` as 2nd or 3rd\nline (not the 1st, to avoid messing with Git for Windows' shell script\ndetection that requires a shebang, and I do that also on other platforms\nto spare some brain cycles), then treat git-rebase--interactive\nidentically, then run the offending command to figure out which call\nreally fails.\n\nThen, I look for that call (I imagine it is the `exec git rebase--helper\n${force_rebase:+--no-ff} --continue` line), then copy-edit it and guard it\nby an environment variable:\n\n\ttest -z \"$DDDD\" || {\n\t\tvalgrind git rebase--helper ${force_rebase:+--no-ff} \\\n\t\t\t--continue\n\t\texit\n\t}\n\texec git rebase--helper ${force_rebase:+--no-ff} --continue\n\nAfter that, you can run the same command, and trigger the new code path by\nthat environment variable:\n\n\tDDDD=1 /mnt/Source/Git/git rebase -i HEAD~10\n\nThat way, you can keep the rest of your Git calls be unaffected by the\npatch.\n\nBTW from your invocation, I imagine that you wanted to actually run your\nGit just-built-from-source, in-place. But for that, you would need to pass\nthe --exec-path option, too, like so:\n\n\tDDDD=1 /mnt/Source/Git/git --exec-path=/mnt/Source/Git \\\n\t\trebase -i HEAD~10\n\nThat way, you could edit the git-rebase--interactive file that is *not*\ninstalled, and avoid affecting other Git operations (you would also not\nneed to guard the new code path behind a conditional).\n\nCiao,\nDscho\n"},{"id":"335127","messageId":"1513868549.3546.2.camel@gmail.com","threadId":"46827","inReplyTo":"18737953.1042351513802399608.JavaMail.defaultUser@defaultHost","subject":"Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)","fromName":"Kaartic Sivaraam","fromEmail":"kaartic.sivaraam@gmail.com","sentAt":"2017-12-21T15:02:29Z","receivedAt":"2017-12-21T15:02:49Z","isPatch":true,"sender":{"key":"kaartic.sivaraam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12448084?v=4"},"body":"On Thursday 21 December 2017 02:09 AM, phillip.wood@talktalk.net wrote:\n> \n> \n>     Hi Kaartic\n> \n>     I'm replying off list as I've only got access to webmail at the\n>     moment.\n\nNo issues brought the CCs including the list to ensure we don't miss things.\n\n\n>     Are you able to do a backtrace with debugging symbols\n>     please, I'm finding it hard to glean anything from the backtrace\n>     here. \n\nThat's confusing, I already had the following in my `config.mak` while \ncompiling,\n\n\nDEVELOPER=1\nDEVELOPERS=1\nCFLAGS += -g -O2\nCFLAGS += -DFLEX_ARRAY=2048\n\nNO_GETTEXT=1\nprefix=/home/$(USER)/.local\n#CFLAGS += -Wno-unused-value\n\n\nAs you can see the `-g` flag is there in CFLAGS.\n\nIf this output doesn't give much sense could you be specific about what \noutput you want i.e., by mentioning the command, showing a sample etc., \nI'm not sure how to go about debugging with \"git rebase\" as it's a script.\n\n\n> Also does it definitely bisect to this patch or the merge of\n>     this branch?\n> \n\n\"git bisect\" speaking,\n\n28d6daed4f119940ace31e523b3b272d3d153d04 is the first bad commit\ncommit 28d6daed4f119940ace31e523b3b272d3d153d04\nAuthor: Phillip Wood <phillip.wood@dunelm.org.uk>\nDate:   Wed Dec 13 11:46:21 2017 +0000\n\n    sequencer: improve config handling\n    \n    The previous config handling relied on global variables, called\n    git_default_config() even when the key had already been handled by\n    git_sequencer_config() and did not initialize the diff configuration\n    variables. Improve this by: i) loading the default values for message\n    cleanup and gpg signing of commits into struct replay_opts;\n    ii) restructuring the code to return immediately once a key is\n    handled; and iii) calling git_diff_basic_config(). Note that\n    unfortunately it is not possible to return early if the key is handled\n    by git_gpg_config() as it does not indicate to the caller if the key\n    has been handled or not.\n    \n    The sequencer should probably have been calling\n    git_diff_basic_config() before as it creates a patch when there are\n    conflicts. The shell version uses 'diff-tree' to create the patch so\n    calling git_diff_basic_config() should match that. Although 'git\n    commit' calls git_diff_ui_config() I don't think the output of\n    print_commit_summary() is affected by anything that is loaded by that\n    as print_commit_summary() always turns on rename detection so would\n    ignore the value in the user's configuration anyway. The other values\n    loaded by git_diff_ui_config() are about the formatting of patches so\n    are not relevant to print_commit_summary().\n    \n    Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n\n:040000 040000 bbd487cc285ae12cefb97a9661de4ff27d182727 9460ac528bf68f331088412f62e5d6a39305854b M\tbuiltin\n:100644 100644 99452a0e89777999418c116386f924be396ed04a 7051b20b76292e3b539875f0dc38108a734d2164 M\tsequencer.c\n:100644 100644 77cb174b2aaf3972ebb9e6ec379252be96dedd3d 3a5072c2ab9088c237b83d92deae3c801289e543 M\tsequencer.h\n\n\nFWIW, I started the bisection with `v2.11.0`  as good (because \"git\nrebase -i HEAD~10 worked there) and the tip of `next` (cfbfd45ee (Sync\nwith master, 2017-12-19)) as bad.\n\nHere's the bisect log for a shorter range of bisection (which was\npossible after I discovered the bad commit in the previous bisect)\n\ngit bisect start\n# bad: [cfbfd45ee6e49007fdeb00008904064ba98f65e0] Sync with master\ngit bisect bad cfbfd45ee6e49007fdeb00008904064ba98f65e0\n# good: [a4212f7ebd7158a1e237d776fb4bbd7a295d0bda] Merge branch 'pw/sequencer-in-process-commit' into next\ngit bisect good a4212f7ebd7158a1e237d776fb4bbd7a295d0bda\n# good: [bdae4af87053490adad2dc9fb184d6d050d46a4c] Merge branch 'sg/setup-doc-update'\ngit bisect good bdae4af87053490adad2dc9fb184d6d050d46a4c\n# bad: [6d12e08217360cfc042ec484129771f73ad7b97f] Merge branch 'rs/strbuf-read-once-reset-length' into next\ngit bisect bad 6d12e08217360cfc042ec484129771f73ad7b97f\n# bad: [1b791d2503742b060f91b8159b85c06335634bd8] Merge branch 'ab/simplify-perl-makefile' into next\ngit bisect bad 1b791d2503742b060f91b8159b85c06335634bd8\n# bad: [abfed699db9f0f0e6f03f5ee3e9d137944ba4216] Merge branch 'sb/clone-recursive-submodule-doc' into next\ngit bisect bad abfed699db9f0f0e6f03f5ee3e9d137944ba4216\n# bad: [ec4d2b9c843b8b338ffa8906eea70095b3098c1e] Merge branch 'pw/sequencer-in-process-commit' into next\ngit bisect bad ec4d2b9c843b8b338ffa8906eea70095b3098c1e\n# good: [5279b80103bde3ec5d6108fa8f43de2017668039] Merge branch 'tb/check-crlf-for-safe-crlf' into next\ngit bisect good 5279b80103bde3ec5d6108fa8f43de2017668039\n# bad: [28d6daed4f119940ace31e523b3b272d3d153d04] sequencer: improve config handling\ngit bisect bad 28d6daed4f119940ace31e523b3b272d3d153d04\n# first bad commit: [28d6daed4f119940ace31e523b3b272d3d153d04] sequencer: improve config handling\n\n-- \nKaartic\n"},{"id":"335130","messageId":"1513874528.9646.2.camel@gmail.com","threadId":"46827","inReplyTo":"alpine.DEB.2.21.1.1712211442470.406@MININT-6BKU6QN.europe.corp.microsoft.com","subject":"Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)","fromName":"Kaartic Sivaraam","fromEmail":"kaartic.sivaraam@gmail.com","sentAt":"2017-12-21T16:42:08Z","receivedAt":"2017-12-21T16:42:32Z","isPatch":true,"sender":{"key":"kaartic.sivaraam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12448084?v=4"},"body":"On Thu, 2017-12-21 at 15:06 +0100, Johannes Schindelin wrote:\n> Hi Kaartic,\n> I fear that the strace does not cover the `git-rebase` process nor the\n> `git-rebase--helper` process (which must have been part of your run, as\n> the commit affected only that part of the rebase operation).\n> \n\nYep. It striked me only later that the entry point of \"git rebase\" is\nessentially a script.\n\n\n> If you have valgrind installed, you may want to give it a try. Since\n> git-rebase is (still, much to my dislike) a Unix shell script, you will\n> have to work quite hard to get it to valgrind correctly.\n> \n\nYou're right about that it's quite hard to get to the right point of\nthe issue when scripts are involved.\n\n\n> This is how I typically do it: \n<snip>\n> Then, I look for that call (I imagine it is the `exec git rebase--helper\n> ${force_rebase:+--no-ff} --continue` line), then copy-edit it and guard it\n> by an environment variable:\n> \n> \ttest -z \"$DDDD\" || {\n> \t\tvalgrind git rebase--helper ${force_rebase:+--no-ff} \\\n> \t\t\t--continue\n> \t\texit\n> \t}\n> \texec git rebase--helper ${force_rebase:+--no-ff} --continue\n> \n> After that, you can run the same command, and trigger the new code path by\n> that environment variable:\n> \n> \tDDDD=1 /mnt/Source/Git/git rebase -i HEAD~10\n> \n> That way, you can keep the rest of your Git calls be unaffected by the\n> patch.\n> \n\nThanks for detailing the way to get the right information for this\nissue. That was intriguing.\n\n\n> BTW from your invocation, I imagine that you wanted to actually run your\n> Git just-built-from-source, in-place. But for that, you would need to pass\n> the --exec-path option, too, like so:\n> \n> \tDDDD=1 /mnt/Source/Git/git --exec-path=/mnt/Source/Git \\\n> \t\trebase -i HEAD~10\n> \n> That way, you could edit the git-rebase--interactive file that is *not*\n> installed, and avoid affecting other Git operations (you would also not\n> need to guard the new code path behind a conditional).\n> \n\nMakes sense. (I had actually used incorrect directories in my previous\nmail as I did part of it manually (big mistake!) so the trace itself\nmight not have much sense in some parts, sorry)\n\nSpeaking of trace, (thanks to Dscho!) the one I got using 'valgrind'\n(with `--leak-check=full` option) can be found below. I've kept only\nrelevant parts of it to avoid unwanted noise of `set -x`. Let me know\nif that's needed too.\n\n...\n\n+ git diff-files --quiet --ignore-submodules\n+ git diff-index --cached --quiet --ignore-submodules HEAD --\n+ test 0 = 1\n+ test -z 1\n+ valgrind --leak-check=full git rebase--helper --continue\n==10384== Memcheck, a memory error detector\n==10384== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.\n==10384== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info\n==10384== Command: git rebase--helper --continue\n==10384== \n==10384== Invalid free() / delete / delete[] / realloc()\n==10384==    at 0x4C2CDDB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)\n==10384==    by 0x24E3F8: read_populate_opts (sequencer.c:1964)\n==10384==    by 0x24E3F8: sequencer_continue (sequencer.c:2753)\n==10384==    by 0x1735DC: cmd_rebase__helper (rebase--helper.c:52)\n==10384==    by 0x11B847: run_builtin (git.c:346)\n==10384==    by 0x11B847: handle_builtin (git.c:554)\n==10384==    by 0x11BB05: run_argv (git.c:606)\n==10384==    by 0x11BB05: cmd_main (git.c:683)\n==10384==    by 0x11AC0B: main (common-main.c:43)\n==10384==  Address 0x2a898a is in a r-x mapped file /mnt/Source/Git/git-next/git segment\n==10384== \nSuccessfully rebased and updated refs/heads/public.\n==10384== \n==10384== HEAP SUMMARY:\n==10384==     in use at exit: 680,882 bytes in 332 blocks\n==10384==   total heap usage: 717 allocs, 386 frees, 1,709,293 bytes allocated\n==10384== \n==10384== 2,053 (2,048 direct, 5 indirect) bytes in 1 blocks are definitely lost in loss record 75 of 83\n==10384==    at 0x4C2BADF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)\n==10384==    by 0x4C2DE5F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)\n==10384==    by 0x27E0FE: xrealloc (wrapper.c:138)\n==10384==    by 0x2072A3: add_object_array_with_path (object.c:319)\n==10384==    by 0x23D745: add_pending_object_with_path (revision.c:163)\n==10384==    by 0x24030E: add_pending_object_with_mode (revision.c:170)\n==10384==    by 0x24030E: add_pending_object (revision.c:176)\n==10384==    by 0x24030E: add_head_to_pending (revision.c:188)\n==10384==    by 0x280EFA: has_uncommitted_changes.part.17 (wt-status.c:2288)\n==10384==    by 0x24DF88: commit_staged_changes (sequencer.c:2705)\n==10384==    by 0x24DF88: sequencer_continue (sequencer.c:2749)\n==10384==    by 0x1735DC: cmd_rebase__helper (rebase--helper.c:52)\n==10384==    by 0x11B847: run_builtin (git.c:346)\n==10384==    by 0x11B847: handle_builtin (git.c:554)\n==10384==    by 0x11BB05: run_argv (git.c:606)\n==10384==    by 0x11BB05: cmd_main (git.c:683)\n==10384==    by 0x11AC0B: main (common-main.c:43)\n==10384== \n==10384== LEAK SUMMARY:\n==10384==    definitely lost: 2,048 bytes in 1 blocks\n==10384==    indirectly lost: 5 bytes in 1 blocks\n==10384==      possibly lost: 0 bytes in 0 blocks\n==10384==    still reachable: 678,829 bytes in 330 blocks\n==10384==         suppressed: 0 bytes in 0 blocks\n==10384== Reachable blocks (those to which a pointer was found) are not shown.\n==10384== To see them, rerun with: --leak-check=full --show-leak-kinds=all\n==10384== \n==10384== For counts of detected and suppressed errors, rerun with: -v\n==10384== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)\n+ exit\n\n\nI think I didn't mention I've set `commit.gpgsign` to `true` for that\nrepo, did I?\n\nLet me know if anything else is needed.\n\n\nHTH,\nKaartic\n"},{"id":"335131","messageId":"25620501.1048091513875229038.JavaMail.defaultUser@defaultHost","threadId":"46827","inReplyTo":"1513794792.9785.2.camel@gmail.com","subject":"Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)","fromName":"phillip.wood@talktalk.net","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-21T16:53:49Z","receivedAt":"2017-12-21T16:53:56Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"\n\n>----Original Message----\n>From: kaartic.sivaraam@gmail.com\n>Date: 20/12/2017 18:33 \n>To: \"Phillip Wood\"<phillip.wood@dunelm.org.uk>, \"Git Mailing List\"\n<git@vger.kernel.org>\n>Cc: \"Johannes Schindelin\"<Johannes.Schindelin@gmx.de>\n>Subj: Error in `git&#39;: free(): invalid pointer (was Re: [PATCH] \nsequencer: improve config handling)\n>\n>I recently encountered that error when trying to do an interactive\n>rebase after using filter-branch to remove a file completely in a\n>repository. I bisected this issue which pointed at this patch. I'm not\n>sure how it is related as I'm not too familiar with the sequencer \ncode.\n>I could help in case any specific information is needed. As a first\n>step, I've posted the output of \"strace /mnt/Source//Git/git rebase -i\n>HEAD~10\" below.\n>\n\n\nHm, There is a problem with sequencer_remove_state() which does \n\nfree(opts->gpg_sign)\n\nhowever unless a gpg key was given on the commandline, opts->gpg is \ninitialized to \"\" which is statically allocated. \n\nThis untested diff should fix that, but I'm not sure if you're problem \nis related to it (I'm visiting relatives so don't have much time for \nworking at the moment. also I'm on webmail so apologies if the patch is \nmangled)\n\nBest Wishes\n\nPhillip\n>-- 8< --\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 3bc487573..115ceba91 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -160,7 +160,7 @@ static int git_sequencer_config(const char *k, \nconst char *v, void *cb)\n        }\n \n        if (!strcmp(k, \"commit.gpgsign\")) {\n-               opts->gpg_sign = git_config_bool(k, v) ? \"\" : NULL;\n+               opts->gpg_sign = git_config_bool(k, v) ? strdup(\"\") : \nNULL;\n                return 0;\n        }\n\n\n\n"},{"id":"335133","messageId":"1513876450.9646.6.camel@gmail.com","threadId":"46827","inReplyTo":"25620501.1048091513875229038.JavaMail.defaultUser@defaultHost","subject":"Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)","fromName":"Kaartic Sivaraam","fromEmail":"kaartic.sivaraam@gmail.com","sentAt":"2017-12-21T17:14:10Z","receivedAt":"2017-12-21T17:14:23Z","isPatch":true,"sender":{"key":"kaartic.sivaraam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12448084?v=4"},"body":"On Thu, 2017-12-21 at 16:53 +0000, phillip.wood@talktalk.net wrote:\n> Hm, There is a problem with sequencer_remove_state() which does \n> \n> free(opts->gpg_sign)\n> \n> however unless a gpg key was given on the commandline, opts->gpg is \n> initialized to \"\" which is statically allocated. \n> \n> This untested diff should fix that,\n\nIt did seem to. I don't get that error any more.\n\n\n>  but I'm not sure if you're problem \n> is related to it\n\nAs the fix you suggested did work, I think the problem is related. Did\nyou have anything else in mind when you said you're not sure whether or\nnot it's related?\n\n\n> also I'm on webmail so apologies if the patch is \n> mangled)\n> \n\nNo issues. The patch seems quite small :)\n\n\nThanks,\nKaartic\n"},{"id":"335163","messageId":"23359630.1065831513939659692.JavaMail.defaultUser@defaultHost","threadId":"46827","inReplyTo":"1513876450.9646.6.camel@gmail.com","subject":"Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)","fromName":"phillip.wood@talktalk.net","fromEmail":"phillip.wood@talktalk.net","sentAt":"2017-12-22T10:47:39Z","receivedAt":"2017-12-22T10:48:08Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":">----Original Message----\n>From: kaartic.sivaraam@gmail.com\n>Date: 21/12/2017 17:14 \n>To: \"phillip.wood@talktalk.net\"<phillip.wood@talktalk.net>, \"Phillip \nWood\"<phillip.wood@dunelm.org.uk>, \"Git Mailing List\"<git@vger.kernel.\norg>\n>Cc: \"Johannes Schindelin\"<Johannes.Schindelin@gmx.de>\n>Subj: Re: Error in `git&#39;: free(): invalid pointer (was Re: [PATCH] \nsequencer: improve config handling)\n>\n>On Thu, 2017-12-21 at 16:53 +0000, phillip.wood@talktalk.net wrote:\n>> Hm, There is a problem with sequencer_remove_state() which does \n>> \n>> free(opts->gpg_sign)\n>> \n>> however unless a gpg key was given on the commandline, opts->gpg is \n>> initialized to \"\" which is statically allocated. \n>> \n>> This untested diff should fix that,\n>\n>It did seem to. I don't get that error any more.\n\nThat's good, thanks for testing it\n\n>>  but I'm not sure if you're problem \n>> is related to it\n>\n>As the fix you suggested did work, I think the problem is related. Did\n>you have anything else in mind when you said you're not sure whether \nor\n>not it's related?\n\nI didn't have anything else in mind, but at that point I hadn't noticed \nthat one of your previous messages said you had gpg signing turned on - \nas you do it makes sense that the patch fixed it.\n\n\nThanks again for reporting this and testing the fix - it seems the test \nsuite has a bit of a blind spot when it comes to gpg signing, I guess \nit's difficult to set up a key for tests\n\nI'll send a proper patch when I have time in a few days\n\nBest Wishes\n\nPhillip\n"},{"id":"335165","messageId":"alpine.DEB.2.21.1.1712221201210.406@MININT-6BKU6QN.europe.corp.microsoft.com","threadId":"46827","inReplyTo":"1513874528.9646.2.camel@gmail.com","subject":"Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-12-22T11:49:19Z","receivedAt":"2017-12-22T11:49:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Kaartic,\n\nOn Thu, 21 Dec 2017, Kaartic Sivaraam wrote:\n\n> Speaking of trace, (thanks to Dscho!) the one I got using 'valgrind'\n> (with `--leak-check=full` option) can be found below. I've kept only\n> relevant parts of it to avoid unwanted noise of `set -x`. Let me know\n> if that's needed too.\n> \n> ...\n> \n> + git diff-files --quiet --ignore-submodules\n> + git diff-index --cached --quiet --ignore-submodules HEAD --\n> + test 0 = 1\n> + test -z 1\n> + valgrind --leak-check=full git rebase--helper --continue\n> ==10384== Memcheck, a memory error detector\n> ==10384== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.\n> ==10384== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info\n> ==10384== Command: git rebase--helper --continue\n> ==10384== \n> ==10384== Invalid free() / delete / delete[] / realloc()\n> ==10384==    at 0x4C2CDDB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)\n> ==10384==    by 0x24E3F8: read_populate_opts (sequencer.c:1964)\n> ==10384==    by 0x24E3F8: sequencer_continue (sequencer.c:2753)\n> ==10384==    by 0x1735DC: cmd_rebase__helper (rebase--helper.c:52)\n> ==10384==    by 0x11B847: run_builtin (git.c:346)\n> ==10384==    by 0x11B847: handle_builtin (git.c:554)\n> ==10384==    by 0x11BB05: run_argv (git.c:606)\n> ==10384==    by 0x11BB05: cmd_main (git.c:683)\n> ==10384==    by 0x11AC0B: main (common-main.c:43)\n> ==10384==  Address 0x2a898a is in a r-x mapped file /mnt/Source/Git/git-next/git segment\n> ==10384== \n> Successfully rebased and updated refs/heads/public.\n> ==10384== \n> ==10384== HEAP SUMMARY:\n> ==10384==     in use at exit: 680,882 bytes in 332 blocks\n> ==10384==   total heap usage: 717 allocs, 386 frees, 1,709,293 bytes allocated\n> ==10384== \n> ==10384== 2,053 (2,048 direct, 5 indirect) bytes in 1 blocks are definitely lost in loss record 75 of 83\n> ==10384==    at 0x4C2BADF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)\n> ==10384==    by 0x4C2DE5F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)\n> ==10384==    by 0x27E0FE: xrealloc (wrapper.c:138)\n> ==10384==    by 0x2072A3: add_object_array_with_path (object.c:319)\n> ==10384==    by 0x23D745: add_pending_object_with_path (revision.c:163)\n> ==10384==    by 0x24030E: add_pending_object_with_mode (revision.c:170)\n> ==10384==    by 0x24030E: add_pending_object (revision.c:176)\n> ==10384==    by 0x24030E: add_head_to_pending (revision.c:188)\n> ==10384==    by 0x280EFA: has_uncommitted_changes.part.17 (wt-status.c:2288)\n> ==10384==    by 0x24DF88: commit_staged_changes (sequencer.c:2705)\n> ==10384==    by 0x24DF88: sequencer_continue (sequencer.c:2749)\n> ==10384==    by 0x1735DC: cmd_rebase__helper (rebase--helper.c:52)\n> ==10384==    by 0x11B847: run_builtin (git.c:346)\n> ==10384==    by 0x11B847: handle_builtin (git.c:554)\n> ==10384==    by 0x11BB05: run_argv (git.c:606)\n> ==10384==    by 0x11BB05: cmd_main (git.c:683)\n> ==10384==    by 0x11AC0B: main (common-main.c:43)\n> ==10384== \n> ==10384== LEAK SUMMARY:\n> ==10384==    definitely lost: 2,048 bytes in 1 blocks\n> ==10384==    indirectly lost: 5 bytes in 1 blocks\n> ==10384==      possibly lost: 0 bytes in 0 blocks\n> ==10384==    still reachable: 678,829 bytes in 330 blocks\n> ==10384==         suppressed: 0 bytes in 0 blocks\n> ==10384== Reachable blocks (those to which a pointer was found) are not shown.\n> ==10384== To see them, rerun with: --leak-check=full --show-leak-kinds=all\n> ==10384== \n> ==10384== For counts of detected and suppressed errors, rerun with: -v\n> ==10384== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)\n> + exit\n> \n> \n> I think I didn't mention I've set `commit.gpgsign` to `true` for that\n> repo, did I?\n\nHah! I had troubles to associate the correct line in my versions of Git's\nsource code (the line numbers alone are only reliable if you also have a\ncommit from which the Git binaries were built), but I did this free() at\n(https://github.com/git/git/blob/cd54ea2b18/sequencer.c#L1975:\n\n\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n\t\tif (!starts_with(buf.buf, \"-S\"))\n\t\t\tstrbuf_reset(&buf);\n\t\telse {\n\t\t\tfree(opts->gpg_sign);\n\t\t\t^^^^^^^^^^^^^^^^^^^^^\n\t\t\topts->gpg_sign = xstrdup(buf.buf + 2);\n\t\t}\n\t\tstrbuf_reset(&buf);\n\t}\n\nThe culprit is that we have these really unclear ownership rules, where it\nis not at all clear who is responsible for releasing allocated memory:\ncaller or callee? (Hannes would not rightfully point out that this would\nbe a non-issue if we would switch to C++). In C, the common pattern is to\nuse `const char *` for users that are *not* supposed to take ownership,\nand `char *` for users that are supposed to take custody. And in this\ninstance, `gpg_sign` should be owned by `struct replay_opts` because of\nthis (https://github.com/git/git/blob/cd54ea2b18/sequencer.h#L38):\n\n\tchar *gpg_sign;\n\nTechnically, using `char *` (instead of `const char *`) does not say\n\"you're now responsible for de-allocating this memory\", of course, but in\npractice it is often used like this (and the signature of `free(void *)`\ncertainly encourages that type of interpreting the `const` qualifier).\n\nBut. There is a problem when you set commit.gpgSign = true (see\nhttps://github.com/git/git/blob/cd54ea2b18/sequencer.c#L162-L165):\n\n\tif (!strcmp(k, \"commit.gpgsign\")) {\n\t\topts->gpg_sign = git_config_bool(k, v) ? \"\" : NULL;\n\t\treturn 0;\n\t}\n\nSee how a \"\" is assigned to that field of type `char *`? It should not\neven be possible, as the \"\" is implicitly read-only memory. And it\ncertainly was never malloc()ed.\n\nSo how to solve it? My suggestion would be to use xstrdup(\"\") instead of\n\"\" here.\n\nI spent a little quality time with the code and came up with a patch that\nI will send out in a moment.\n\nBy the way, Kaartic, thank you so much for focusing on correctness before\nfocusing on style issues. I know it is harder to review patches for\ncorrectness than it is to concentrate on style issues, but in my mind it\nis not only much more work, but also a lot more valuable.\n\nCiao,\nDscho\n"},{"id":"335277","messageId":"71f9236c-9e55-8489-54b2-5d7c7df1eb0c@gmail.com","threadId":"46827","inReplyTo":"alpine.DEB.2.21.1.1712221201210.406@MININT-6BKU6QN.europe.corp.microsoft.com","subject":"Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)","fromName":"Kaartic Sivaraam","fromEmail":"kaartic.sivaraam@gmail.com","sentAt":"2017-12-25T08:51:39Z","receivedAt":"2017-12-25T08:52:09Z","isPatch":true,"sender":{"key":"kaartic.sivaraam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12448084?v=4"},"body":"On Friday 22 December 2017 05:19 PM, Johannes Schindelin wrote:\n> Hi Kaartic,\n> \n>>\n>> I think I didn't mention I've set `commit.gpgsign` to `true` for that\n>> repo, did I?\n> \n> Hah! I had troubles to associate the correct line in my versions of Git's\n> source code (the line numbers alone are only reliable if you also have a\n> commit from which the Git binaries were built), \n\nShould have mentioned that, sorry.\n\n\n> but I did this free() at\n> (https://github.com/git/git/blob/cd54ea2b18/sequencer.c#L1975:\n> \n> \tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n> \t\tif (!starts_with(buf.buf, \"-S\"))\n> \t\t\tstrbuf_reset(&buf);\n> \t\telse {\n> \t\t\tfree(opts->gpg_sign);\n> \t\t\t^^^^^^^^^^^^^^^^^^^^^\n> \t\t\topts->gpg_sign = xstrdup(buf.buf + 2);\n> \t\t}\n> \t\tstrbuf_reset(&buf);\n> \t}\n> \n\nSeems you got the right one, regardless. :)\n\n\n> The culprit is that we have these really unclear ownership rules, where it\n> is not at all clear who is responsible for releasing allocated memory:\n> caller or callee? (Hannes would not rightfully point out that this would\n> be a non-issue if we would switch to C++). In C, the common pattern is to\n> use `const char *` for users that are *not* supposed to take ownership,\n> and `char *` for users that are supposed to take custody. And in this\n> instance, `gpg_sign` should be owned by `struct replay_opts` because of\n> this (https://github.com/git/git/blob/cd54ea2b18/sequencer.h#L38):\n> \n> \tchar *gpg_sign;\n> \n> Technically, using `char *` (instead of `const char *`) does not say\n> \"you're now responsible for de-allocating this memory\", of course, but in\n> practice it is often used like this (and the signature of `free(void *)`\n> certainly encourages that type of interpreting the `const` qualifier).\n> \n\nNice explanation.\n\n\n> I spent a little quality time with the code and came up with a patch that\n> I will send out in a moment.\n>\n\nThat relieves Philip of the burden, I guess. :)\n\n\n> By the way, Kaartic, thank you so much for focusing on correctness before\n> focusing on style issues. I know it is harder to review patches for\n> correctness than it is to concentrate on style issues, but in my mind it\n> is not only much more work, but also a lot more valuable.\n> \n\nThough it's good to hear these words and I do like to focus on \ncorrectness, there wasn't much I did to focus on correctness in this \ncase ;-) It was you actually, after seeing such a clear explanation!.\n\nI just used Git built from 'next' for my personal use and encountered \nthe issue I stated in the start of this sub-thread.\n\n\n-- \nKaartic\n"},{"id":"336424","messageId":"20180110205351.GA73826@aiede.svl.corp.google.com","threadId":"46827","inReplyTo":"20171211141330.23566-9-phillip.wood@talktalk.net","subject":"Re: [PATCH v5 8/9] sequencer: try to commit without forking 'git commit'","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2018-01-10T20:53:51Z","receivedAt":"2018-01-10T20:54:23Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nPhillip Wood wrote:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> If the commit message does not need to be edited then create the\n> commit without forking 'git commit'. Taking the best time of ten runs\n> with a warm cache this reduces the time taken to cherry-pick 10\n> commits by 27% (from 282ms to 204ms), and the time taken by 'git\n> rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on\n> my computer running linux. Some of greater saving for rebase is\n> because it no longer wastes time creating the commit summary just to\n> throw it away.\n\nNeat!  Dmitry Torokhov (cc-ed) noticed[1] that this causes the\nprepare-commit-msg hook not to be invoked, which I think is\nunintentional.  Should we check for such a hook and take the slowpath\nwhen it is present?\n\nThanks,\nJonathan\n\n[1] https://public-inbox.org/git/CAKdAkRQuj1hfKeckjuR2oP+8C1i+ZR36O-+aRYif4ufaS_zs+w@mail.gmail.com/\n"},{"id":"336433","messageId":"nycvar.QRO.7.76.6.1801102332350.31@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz","threadId":"46827","inReplyTo":"20180110205351.GA73826@aiede.svl.corp.google.com","subject":"Re: [PATCH v5 8/9] sequencer: try to commit without forking 'git commit'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2018-01-10T22:40:53Z","receivedAt":"2018-01-10T22:41:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 10 Jan 2018, Jonathan Nieder wrote:\n\n> Phillip Wood wrote:\n> \n> > From: Phillip Wood <phillip.wood@dunelm.org.uk>\n> >\n> > If the commit message does not need to be edited then create the\n> > commit without forking 'git commit'. Taking the best time of ten runs\n> > with a warm cache this reduces the time taken to cherry-pick 10\n> > commits by 27% (from 282ms to 204ms), and the time taken by 'git\n> > rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on\n> > my computer running linux. Some of greater saving for rebase is\n> > because it no longer wastes time creating the commit summary just to\n> > throw it away.\n> \n> Neat!  Dmitry Torokhov (cc-ed) noticed[1] that this causes the\n> prepare-commit-msg hook not to be invoked, which I think is\n> unintentional.  Should we check for such a hook and take the slowpath\n> when it is present?\n\nWe could also easily recreate the functionality:\n\n\tif (find_hook(\"pre-commit\")) {\n\t\tstruct argv_array hook_env = ARGV_ARRAY_INIT;\n\n\t\targv_array_pushf(&hook_env, \"GIT_INDEX_FILE=%s\",\n\t\t\tget_index_file());\n\t\targv_array_push(&hook_env, \"GIT_EDITOR=:\");\n\t\tret = run_hook_le(hook_env.argv, \"pre-commit\", NULL);\n\t\targv_array_clear(&hook_env);\n\t}\n\n(This assumes that the in-process try_to_commit() is only called if the\ncommit message is not to be edited interactively, which is currently the\ncase.)\n\nCiao,\nDscho\n"},{"id":"336451","messageId":"5efbad65-6afe-e135-4681-ba380cd6797f@talktalk.net","threadId":"46827","inReplyTo":"nycvar.QRO.7.76.6.1801102332350.31@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz","subject":"Re: [PATCH v5 8/9] sequencer: try to commit without forking 'git commit'","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-01-11T10:41:01Z","receivedAt":"2018-01-11T10:41:11Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 10/01/18 22:40, Johannes Schindelin wrote:\n> Hi,\n> \n> On Wed, 10 Jan 2018, Jonathan Nieder wrote:\n> \n>> Phillip Wood wrote:\n>>\n>>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>>\n>>> If the commit message does not need to be edited then create the\n>>> commit without forking 'git commit'. Taking the best time of ten runs\n>>> with a warm cache this reduces the time taken to cherry-pick 10\n>>> commits by 27% (from 282ms to 204ms), and the time taken by 'git\n>>> rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on\n>>> my computer running linux. Some of greater saving for rebase is\n>>> because it no longer wastes time creating the commit summary just to\n>>> throw it away.\n>>\n>> Neat!  Dmitry Torokhov (cc-ed) noticed[1]\n\nThanks for reporting and bisecting this Dmitry. When I was preparing\nthis series I checked to see if it needed to run the 'pre-commit' hook\nbut missed the 'prepare-commit-msg' hook.\n\n> that this causes the\n>> prepare-commit-msg hook not to be invoked, which I think is\n>> unintentional.  Should we check for such a hook and take the slowpath\n>> when it is present?\n> \n> We could also easily recreate the functionality:\n> \n> \tif (find_hook(\"pre-commit\")) {\n> \t\tstruct argv_array hook_env = ARGV_ARRAY_INIT;\n> \n> \t\targv_array_pushf(&hook_env, \"GIT_INDEX_FILE=%s\",\n> \t\t\tget_index_file());\n> \t\targv_array_push(&hook_env, \"GIT_EDITOR=:\");\n> \t\tret = run_hook_le(hook_env.argv, \"pre-commit\", NULL);\n> \t\targv_array_clear(&hook_env);\n> \t}\n\nThanks Johannes, though it needs to run the 'prepare-commit-msg' hook,\nthe current code in master only runs the 'pre-commit' hook when we edit\nthe message. I'll send a patch with a test.\n\nBest Wishes\n\nPhillip\n\n> (This assumes that the in-process try_to_commit() is only called if the\n> commit message is not to be edited interactively, which is currently the\n> case.)\n> \n> Ciao,\n> Dscho\n> \n\n"},{"id":"336479","messageId":"nycvar.QRO.7.76.6.1801112120390.31@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz","threadId":"46827","inReplyTo":"5efbad65-6afe-e135-4681-ba380cd6797f@talktalk.net","subject":"Re: [PATCH v5 8/9] sequencer: try to commit without forking 'git commit'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2018-01-11T20:21:38Z","receivedAt":"2018-01-11T20:22:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Thu, 11 Jan 2018, Phillip Wood wrote:\n\n> On 10/01/18 22:40, Johannes Schindelin wrote:\n> > Hi,\n> > \n> > On Wed, 10 Jan 2018, Jonathan Nieder wrote:\n> > \n> >> that this causes the prepare-commit-msg hook not to be invoked, which\n> >> I think is unintentional.  Should we check for such a hook and take\n> >> the slowpath when it is present?\n> > \n> > We could also easily recreate the functionality:\n> > \n> > \tif (find_hook(\"pre-commit\")) {\n> > \t\tstruct argv_array hook_env = ARGV_ARRAY_INIT;\n> > \n> > \t\targv_array_pushf(&hook_env, \"GIT_INDEX_FILE=%s\",\n> > \t\t\tget_index_file());\n> > \t\targv_array_push(&hook_env, \"GIT_EDITOR=:\");\n> > \t\tret = run_hook_le(hook_env.argv, \"pre-commit\", NULL);\n> > \t\targv_array_clear(&hook_env);\n> > \t}\n> \n> Thanks Johannes, though it needs to run the 'prepare-commit-msg' hook,\n> the current code in master only runs the 'pre-commit' hook when we edit\n> the message. I'll send a patch with a test.\n\nSorry, yes, that's the hook I meant ;-) the quoted text by Jonathan even\nmentions it explicitly.\n\nCiao,\nJohannes\n"}]}