{"thread":{"id":"12186","subject":"[PATCH 2/5] Improve message-id generation flow control for format-patch","startedAt":"2008-02-19T03:56:02Z","lastAt":"2008-02-20T16:57:02Z","messageCount":12,"participants":["Daniel Barkalow","Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"69225","messageId":"alpine.LNX.1.00.0802182254000.5816@iabervon.org","threadId":"12186","inReplyTo":"cover.1203392527.git.barkalow@iabervon.org","subject":"[PATCH 1/5] Add more tests for format-patch","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-02-19T03:56:02Z","receivedAt":"2008-02-19T03:56:02Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"Tests -o, and an excessively long subject, and --thread, with and\nwithout --in-reply-to=\n\nSigned-off-by: Daniel Barkalow <barkalow@iabervon.org>\n---\n t/t4014-format-patch.sh |   45 +++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 45 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 0a6fe53..6e8b5f4 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -88,4 +88,49 @@ test_expect_success 'replay did not screw up the log message' '\n \n '\n \n+test_expect_success 'multiple files' '\n+\n+\trm -rf patches/ &&\n+\tgit checkout side &&\n+\tgit format-patch -o patches/ master &&\n+\tls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch\n+'\n+\n+test_expect_success 'thread' '\n+\n+\trm -rf patches/ &&\n+\tgit checkout side &&\n+\tgit format-patch --thread -o patches/ master &&\n+\tFIRST_MID=$(grep \"Message-Id:\" patches/0001-* | sed \"s/^[^<]*\\(<[^>]*>\\).*$/\\1/\") &&\n+\tfor i in patches/0002-* patches/0003-*\n+\tdo\n+\t  grep \"References: $FIRST_MID\" $i &&\n+\t  grep \"In-Reply-To: $FIRST_MID\" $i\n+\tdone\n+'\n+\n+test_expect_success 'thread in-reply-to' '\n+\n+\trm -rf patches/ &&\n+\tgit checkout side &&\n+\tgit format-patch --in-reply-to=\"<test.message>\" --thread -o patches/ master &&\n+\tFIRST_MID=\"<test.message>\" &&\n+\tfor i in patches/*\n+\tdo\n+\t  grep \"References: $FIRST_MID\" $i &&\n+\t  grep \"In-Reply-To: $FIRST_MID\" $i\n+\tdone\n+'\n+\n+test_expect_success 'excessive subject' '\n+\n+\trm -rf patches/ &&\n+\tgit checkout side &&\n+\tfor i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo \"$i\"; done >>file &&\n+\tgit update-index file &&\n+\tgit commit -m \"This is an excessively long subject line for a message due to the habit some projects have of not having a short, one-line subject at the start of the commit message, but rather sticking a whole paragraph right at the start as the only thing in the commit message. It had better not become the filename for the patch.\" &&\n+\tgit format-patch -o patches/ master..side &&\n+\tls patches/0004-This-is-an-excessively-long-subject-line-for-a-messa.patch\n+'\n+\n test_done\n-- \n1.5.4.1.191.gfbf10\n"},{"id":"69224","messageId":"alpine.LNX.1.00.0802182254110.5816@iabervon.org","threadId":"12186","inReplyTo":"cover.1203392527.git.barkalow@iabervon.org","subject":"[PATCH 2/5] Improve message-id generation flow control for format-patch","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-02-19T03:56:06Z","receivedAt":"2008-02-19T03:56:06Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>\n---\n builtin-log.c |   29 ++++++++++++++---------------\n revision.h    |    2 +-\n 2 files changed, 15 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 99d69f0..4f08ca4 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -575,16 +575,19 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha\n \to2->flags = flags2;\n }\n \n-static void gen_message_id(char *dest, unsigned int length, char *base)\n+static void gen_message_id(struct rev_info *info, char *base)\n {\n \tconst char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);\n \tconst char *email_start = strrchr(committer, '<');\n \tconst char *email_end = strrchr(committer, '>');\n-\tif(!email_start || !email_end || email_start > email_end - 1)\n+\tstruct strbuf buf;\n+\tif (!email_start || !email_end || email_start > email_end - 1)\n \t\tdie(\"Could not extract email from committer identity.\");\n-\tsnprintf(dest, length, \"%s.%lu.git.%.*s\", base,\n-\t\t (unsigned long) time(NULL),\n-\t\t (int)(email_end - email_start - 1), email_start + 1);\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addf(&buf, \"%s.%lu.git.%.*s\", base,\n+\t\t    (unsigned long) time(NULL),\n+\t\t    (int)(email_end - email_start - 1), email_start + 1);\n+\tinfo->message_id = strbuf_detach(&buf, NULL);\n }\n \n static const char *clean_message_id(const char *msg_id)\n@@ -625,8 +628,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tconst char *in_reply_to = NULL;\n \tstruct patch_ids ids;\n \tchar *add_signoff = NULL;\n-\tchar message_id[1024];\n-\tchar ref_message_id[1024];\n \n \tgit_config(git_format_config);\n \tinit_revisions(&rev, prefix);\n@@ -809,15 +810,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\trev.nr = total - nr + (start_number - 1);\n \t\t/* Make the second and subsequent mails replies to the first */\n \t\tif (thread) {\n-\t\t\tif (nr == (total - 2)) {\n-\t\t\t\tstrncpy(ref_message_id, message_id,\n-\t\t\t\t\tsizeof(ref_message_id));\n-\t\t\t\tref_message_id[sizeof(ref_message_id)-1]='\\0';\n-\t\t\t\trev.ref_message_id = ref_message_id;\n+\t\t\tif (rev.message_id) {\n+\t\t\t\tif (rev.ref_message_id)\n+\t\t\t\t\tfree(rev.message_id);\n+\t\t\t\telse\n+\t\t\t\t\trev.ref_message_id = rev.message_id;\n \t\t\t}\n-\t\t\tgen_message_id(message_id, sizeof(message_id),\n-\t\t\t\t       sha1_to_hex(commit->object.sha1));\n-\t\t\trev.message_id = message_id;\n+\t\t\tgen_message_id(&rev, sha1_to_hex(commit->object.sha1));\n \t\t}\n \t\tif (!use_stdout)\n \t\t\tif (reopen_stdout(commit, rev.nr, keep_subject,\ndiff --git a/revision.h b/revision.h\nindex 8572315..e3559d0 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -74,7 +74,7 @@ struct rev_info {\n \tstruct log_info *loginfo;\n \tint\t\tnr, total;\n \tconst char\t*mime_boundary;\n-\tconst char\t*message_id;\n+\tchar\t\t*message_id;\n \tconst char\t*ref_message_id;\n \tconst char\t*add_signoff;\n \tconst char\t*extra_headers;\n-- \n1.5.4.1.191.gfbf10\n"},{"id":"69228","messageId":"alpine.LNX.1.00.0802182254180.5816@iabervon.org","threadId":"12186","inReplyTo":"cover.1203392527.git.barkalow@iabervon.org","subject":"[PATCH 3/5] Export some email and pretty-printing functions","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-02-19T03:56:08Z","receivedAt":"2008-02-19T03:56:08Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"These will be used for generating the cover letter in addition to the\npatch emails.\n\nSigned-off-by: Daniel Barkalow <barkalow@iabervon.org>\n---\n commit.h   |   15 +++++++\n log-tree.c |  126 ++++++++++++++++++++++++++++++++---------------------------\n log-tree.h |    2 +\n pretty.c   |   34 ++++++++--------\n 4 files changed, 102 insertions(+), 75 deletions(-)\n\ndiff --git a/commit.h b/commit.h\nindex 10e2b5d..80d65b9 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -71,6 +71,21 @@ extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,\n                                 int abbrev, const char *subject,\n                                 const char *after_subject, enum date_mode,\n \t\t\t\tint non_ascii_present);\n+void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,\n+\t\t   const char *line, enum date_mode dmode,\n+\t\t   const char *encoding);\n+void pp_title_line(enum cmit_fmt fmt,\n+\t\t   const char **msg_p,\n+\t\t   struct strbuf *sb,\n+\t\t   const char *subject,\n+\t\t   const char *after_subject,\n+\t\t   const char *encoding,\n+\t\t   int plain_non_ascii);\n+void pp_remainder(enum cmit_fmt fmt,\n+\t\t  const char **msg_p,\n+\t\t  struct strbuf *sb,\n+\t\t  int indent);\n+\n \n /** Removes the first commit from a list sorted by date, and adds all\n  * of its parents.\ndiff --git a/log-tree.c b/log-tree.c\nindex 1f3fcf1..1b084dc 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -137,6 +137,72 @@ static int has_non_ascii(const char *s)\n \treturn 0;\n }\n \n+void log_write_email_headers(struct rev_info *opt, const char *name,\n+\t\t\t     const char **subject_p, const char **extra_headers_p)\n+{\n+\tconst char *subject = NULL;\n+\tconst char *extra_headers = opt->extra_headers;\n+\tif (opt->total > 0) {\n+\t\tstatic char buffer[64];\n+\t\tsnprintf(buffer, sizeof(buffer),\n+\t\t\t \"Subject: [%s %0*d/%d] \",\n+\t\t\t opt->subject_prefix,\n+\t\t\t digits_in_number(opt->total),\n+\t\t\t opt->nr, opt->total);\n+\t\tsubject = buffer;\n+\t} else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {\n+\t\tstatic char buffer[256];\n+\t\tsnprintf(buffer, sizeof(buffer),\n+\t\t\t \"Subject: [%s] \",\n+\t\t\t opt->subject_prefix);\n+\t\tsubject = buffer;\n+\t} else {\n+\t\tsubject = \"Subject: \";\n+\t}\n+\n+\tprintf(\"From %s Mon Sep 17 00:00:00 2001\\n\", name);\n+\tif (opt->message_id)\n+\t\tprintf(\"Message-Id: <%s>\\n\", opt->message_id);\n+\tif (opt->ref_message_id)\n+\t\tprintf(\"In-Reply-To: <%s>\\nReferences: <%s>\\n\",\n+\t\t       opt->ref_message_id, opt->ref_message_id);\n+\tif (opt->mime_boundary) {\n+\t\tstatic char subject_buffer[1024];\n+\t\tstatic char buffer[1024];\n+\t\tsnprintf(subject_buffer, sizeof(subject_buffer) - 1,\n+\t\t\t \"%s\"\n+\t\t\t \"MIME-Version: 1.0\\n\"\n+\t\t\t \"Content-Type: multipart/mixed;\"\n+\t\t\t \" boundary=\\\"%s%s\\\"\\n\"\n+\t\t\t \"\\n\"\n+\t\t\t \"This is a multi-part message in MIME \"\n+\t\t\t \"format.\\n\"\n+\t\t\t \"--%s%s\\n\"\n+\t\t\t \"Content-Type: text/plain; \"\n+\t\t\t \"charset=UTF-8; format=fixed\\n\"\n+\t\t\t \"Content-Transfer-Encoding: 8bit\\n\\n\",\n+\t\t\t extra_headers ? extra_headers : \"\",\n+\t\t\t mime_boundary_leader, opt->mime_boundary,\n+\t\t\t mime_boundary_leader, opt->mime_boundary);\n+\t\textra_headers = subject_buffer;\n+\n+\t\tsnprintf(buffer, sizeof(buffer) - 1,\n+\t\t\t \"--%s%s\\n\"\n+\t\t\t \"Content-Type: text/x-patch;\"\n+\t\t\t \" name=\\\"%s.diff\\\"\\n\"\n+\t\t\t \"Content-Transfer-Encoding: 8bit\\n\"\n+\t\t\t \"Content-Disposition: %s;\"\n+\t\t\t \" filename=\\\"%s.diff\\\"\\n\\n\",\n+\t\t\t mime_boundary_leader, opt->mime_boundary,\n+\t\t\t name,\n+\t\t\t opt->no_inline ? \"attachment\" : \"inline\",\n+\t\t\t name);\n+\t\topt->diffopt.stat_sep = buffer;\n+\t}\n+\t*subject_p = subject;\n+\t*extra_headers_p = extra_headers;\n+}\n+\n void show_log(struct rev_info *opt, const char *sep)\n {\n \tstruct strbuf msgbuf;\n@@ -186,64 +252,8 @@ void show_log(struct rev_info *opt, const char *sep)\n \t */\n \n \tif (opt->commit_format == CMIT_FMT_EMAIL) {\n-\t\tchar *sha1 = sha1_to_hex(commit->object.sha1);\n-\t\tif (opt->total > 0) {\n-\t\t\tstatic char buffer[64];\n-\t\t\tsnprintf(buffer, sizeof(buffer),\n-\t\t\t\t\t\"Subject: [%s %0*d/%d] \",\n-\t\t\t\t\topt->subject_prefix,\n-\t\t\t\t\tdigits_in_number(opt->total),\n-\t\t\t\t\topt->nr, opt->total);\n-\t\t\tsubject = buffer;\n-\t\t} else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {\n-\t\t\tstatic char buffer[256];\n-\t\t\tsnprintf(buffer, sizeof(buffer),\n-\t\t\t\t\t\"Subject: [%s] \",\n-\t\t\t\t\topt->subject_prefix);\n-\t\t\tsubject = buffer;\n-\t\t} else {\n-\t\t\tsubject = \"Subject: \";\n-\t\t}\n-\n-\t\tprintf(\"From %s Mon Sep 17 00:00:00 2001\\n\", sha1);\n-\t\tif (opt->message_id)\n-\t\t\tprintf(\"Message-Id: <%s>\\n\", opt->message_id);\n-\t\tif (opt->ref_message_id)\n-\t\t\tprintf(\"In-Reply-To: <%s>\\nReferences: <%s>\\n\",\n-\t\t\t       opt->ref_message_id, opt->ref_message_id);\n-\t\tif (opt->mime_boundary) {\n-\t\t\tstatic char subject_buffer[1024];\n-\t\t\tstatic char buffer[1024];\n-\t\t\tsnprintf(subject_buffer, sizeof(subject_buffer) - 1,\n-\t\t\t\t \"%s\"\n-\t\t\t\t \"MIME-Version: 1.0\\n\"\n-\t\t\t\t \"Content-Type: multipart/mixed;\"\n-\t\t\t\t \" boundary=\\\"%s%s\\\"\\n\"\n-\t\t\t\t \"\\n\"\n-\t\t\t\t \"This is a multi-part message in MIME \"\n-\t\t\t\t \"format.\\n\"\n-\t\t\t\t \"--%s%s\\n\"\n-\t\t\t\t \"Content-Type: text/plain; \"\n-\t\t\t\t \"charset=UTF-8; format=fixed\\n\"\n-\t\t\t\t \"Content-Transfer-Encoding: 8bit\\n\\n\",\n-\t\t\t\t extra_headers ? extra_headers : \"\",\n-\t\t\t\t mime_boundary_leader, opt->mime_boundary,\n-\t\t\t\t mime_boundary_leader, opt->mime_boundary);\n-\t\t\textra_headers = subject_buffer;\n-\n-\t\t\tsnprintf(buffer, sizeof(buffer) - 1,\n-\t\t\t\t \"--%s%s\\n\"\n-\t\t\t\t \"Content-Type: text/x-patch;\"\n-\t\t\t\t \" name=\\\"%s.diff\\\"\\n\"\n-\t\t\t\t \"Content-Transfer-Encoding: 8bit\\n\"\n-\t\t\t\t \"Content-Disposition: %s;\"\n-\t\t\t\t \" filename=\\\"%s.diff\\\"\\n\\n\",\n-\t\t\t\t mime_boundary_leader, opt->mime_boundary,\n-\t\t\t\t sha1,\n-\t\t\t\t opt->no_inline ? \"attachment\" : \"inline\",\n-\t\t\t\t sha1);\n-\t\t\topt->diffopt.stat_sep = buffer;\n-\t\t}\n+\t\tlog_write_email_headers(opt, sha1_to_hex(commit->object.sha1),\n+\t\t\t\t\t&subject, &extra_headers);\n \t} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {\n \t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);\n \t\tif (opt->commit_format != CMIT_FMT_ONELINE)\ndiff --git a/log-tree.h b/log-tree.h\nindex b33f7cd..0cc9344 100644\n--- a/log-tree.h\n+++ b/log-tree.h\n@@ -13,5 +13,7 @@ int log_tree_commit(struct rev_info *, struct commit *);\n int log_tree_opt_parse(struct rev_info *, const char **, int);\n void show_log(struct rev_info *opt, const char *sep);\n void show_decorations(struct commit *commit);\n+void log_write_email_headers(struct rev_info *opt, const char *name,\n+\t\t\t     const char **subject_p, const char **extra_headers_p);\n \n #endif\ndiff --git a/pretty.c b/pretty.c\nindex b987ff2..d5db1bd 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -110,9 +110,9 @@ needquote:\n \tstrbuf_addstr(sb, \"?=\");\n }\n \n-static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,\n-\t\t\t const char *line, enum date_mode dmode,\n-\t\t\t const char *encoding)\n+void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,\n+\t\t  const char *line, enum date_mode dmode,\n+\t\t  const char *encoding)\n {\n \tchar *date;\n \tint namelen;\n@@ -295,7 +295,7 @@ static void format_person_part(struct strbuf *sb, char part,\n \t/*\n \t * If it does not even have a '<' and '>', that is\n \t * quite a bogus commit author and we discard it;\n-\t * this is in line with add_user_info() that is used\n+\t * this is in line with pp_user_info() that is used\n \t * in the normal codepath.  When end points at the '<'\n \t * that we found, it should have matching '>' later,\n \t * which means start (beginning of email address) must\n@@ -643,23 +643,23 @@ static void pp_header(enum cmit_fmt fmt,\n \t\t */\n \t\tif (!memcmp(line, \"author \", 7)) {\n \t\t\tstrbuf_grow(sb, linelen + 80);\n-\t\t\tadd_user_info(\"Author\", fmt, sb, line + 7, dmode, encoding);\n+\t\t\tpp_user_info(\"Author\", fmt, sb, line + 7, dmode, encoding);\n \t\t}\n \t\tif (!memcmp(line, \"committer \", 10) &&\n \t\t    (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {\n \t\t\tstrbuf_grow(sb, linelen + 80);\n-\t\t\tadd_user_info(\"Commit\", fmt, sb, line + 10, dmode, encoding);\n+\t\t\tpp_user_info(\"Commit\", fmt, sb, line + 10, dmode, encoding);\n \t\t}\n \t}\n }\n \n-static void pp_title_line(enum cmit_fmt fmt,\n-\t\t\t  const char **msg_p,\n-\t\t\t  struct strbuf *sb,\n-\t\t\t  const char *subject,\n-\t\t\t  const char *after_subject,\n-\t\t\t  const char *encoding,\n-\t\t\t  int plain_non_ascii)\n+void pp_title_line(enum cmit_fmt fmt,\n+\t\t   const char **msg_p,\n+\t\t   struct strbuf *sb,\n+\t\t   const char *subject,\n+\t\t   const char *after_subject,\n+\t\t   const char *encoding,\n+\t\t   int plain_non_ascii)\n {\n \tstruct strbuf title;\n \n@@ -708,10 +708,10 @@ static void pp_title_line(enum cmit_fmt fmt,\n \tstrbuf_release(&title);\n }\n \n-static void pp_remainder(enum cmit_fmt fmt,\n-\t\t\t const char **msg_p,\n-\t\t\t struct strbuf *sb,\n-\t\t\t int indent)\n+void pp_remainder(enum cmit_fmt fmt,\n+\t\t  const char **msg_p,\n+\t\t  struct strbuf *sb,\n+\t\t  int indent)\n {\n \tint first = 1;\n \tfor (;;) {\n-- \n1.5.4.1.191.gfbf10\n"},{"id":"69227","messageId":"alpine.LNX.1.00.0802182254240.5816@iabervon.org","threadId":"12186","inReplyTo":"cover.1203392527.git.barkalow@iabervon.org","subject":"[PATCH 4/5] Retain extra blank lines between the summary and the body","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-02-19T03:56:10Z","receivedAt":"2008-02-19T03:56:10Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"This is so that it's possible to have a message that will generate an\nemail with the first line blank. If the email you're generating is\nactually a template, you may have a single line filling in for a\n~3-line paragraph, meaning that the space starts with a blank line,\nwhich was impossible before as output from the message-splitting code.\n\nFurthermore, extra blank lines that aren't at the beginning of the\nbody portion aren't stripped, so this is more consistant overall.\n\nSigned-off-by: Daniel Barkalow <barkalow@iabervon.org>\n---\n pretty.c |    4 ----\n 1 files changed, 0 insertions(+), 4 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex d5db1bd..1c57920 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -713,7 +713,6 @@ void pp_remainder(enum cmit_fmt fmt,\n \t\t  struct strbuf *sb,\n \t\t  int indent)\n {\n-\tint first = 1;\n \tfor (;;) {\n \t\tconst char *line = *msg_p;\n \t\tint linelen = get_one_line(line);\n@@ -723,12 +722,9 @@ void pp_remainder(enum cmit_fmt fmt,\n \t\t\tbreak;\n \n \t\tif (is_empty_line(line, &linelen)) {\n-\t\t\tif (first)\n-\t\t\t\tcontinue;\n \t\t\tif (fmt == CMIT_FMT_SHORT)\n \t\t\t\tbreak;\n \t\t}\n-\t\tfirst = 0;\n \n \t\tstrbuf_grow(sb, linelen + indent + 20);\n \t\tif (indent) {\n-- \n1.5.4.1.191.gfbf10\n"},{"id":"69229","messageId":"alpine.LNX.1.00.0802182254310.5816@iabervon.org","threadId":"12186","inReplyTo":"cover.1203392527.git.barkalow@iabervon.org","subject":"[PATCH 5/5] Add a --cover-letter option to format-patch","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-02-19T03:56:13Z","receivedAt":"2008-02-19T03:56:13Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"If --cover-letter is provided, generate a cover letter message before\nthe patches, numbered 0.\n\nOriginal patch thanks to Johannes Schindelin\n\nSigned-off-by: Daniel Barkalow <barkalow@iabervon.org>\n---\n Documentation/git-format-patch.txt                 |   20 +-\n builtin-log.c                                      |  232 +++++++++++++++-----\n t/t4013-diff-various.sh                            |    1 +\n ...tch_--stdout_--cover-letter_-n_initial..master^ |  101 +++++++++\n t/t4014-format-patch.sh                            |   26 +++\n 5 files changed, 315 insertions(+), 65 deletions(-)\n create mode 100644 t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 651efe6..b27bb94 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -10,13 +10,14 @@ SYNOPSIS\n --------\n [verse]\n 'git-format-patch' [-k] [-o <dir> | --stdout] [--thread]\n-                   [--attach[=<boundary>] | --inline[=<boundary>]]\n-                   [-s | --signoff] [<common diff options>]\n-                   [-n | --numbered | -N | --no-numbered]\n-                   [--start-number <n>] [--numbered-files]\n-                   [--in-reply-to=Message-Id] [--suffix=.<sfx>]\n-                   [--ignore-if-in-upstream]\n-                   [--subject-prefix=Subject-Prefix]\n+\t\t   [--attach[=<boundary>] | --inline[=<boundary>]]\n+\t\t   [-s | --signoff] [<common diff options>]\n+\t\t   [-n | --numbered | -N | --no-numbered]\n+\t\t   [--start-number <n>] [--numbered-files]\n+\t\t   [--in-reply-to=Message-Id] [--suffix=.<sfx>]\n+\t\t   [--ignore-if-in-upstream]\n+\t\t   [--subject-prefix=Subject-Prefix]\n+\t\t   [--cover-letter]\n \t\t   [ <since> | <revision range> ]\n \n DESCRIPTION\n@@ -135,6 +136,11 @@ include::diff-options.txt[]\n \tallows for useful naming of a patch series, and can be\n \tcombined with the --numbered option.\n \n+--cover-letter::\n+\tGenerate a cover letter template.  You still have to fill in\n+\ta description, but the shortlog and the diffstat will be\n+\tgenerated for you.\n+\n --suffix=.<sfx>::\n \tInstead of using `.patch` as the suffix for generated\n \tfilenames, use specified suffix.  A common alternative is\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 4f08ca4..a67b463 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -14,6 +14,7 @@\n #include \"reflog-walk.h\"\n #include \"patch-ids.h\"\n #include \"refs.h\"\n+#include \"run-command.h\"\n \n static int default_show_root = 1;\n static const char *fmt_patch_subject_prefix = \"PATCH\";\n@@ -452,74 +453,81 @@ static int git_format_config(const char *var, const char *value)\n }\n \n \n+static const char *get_oneline_for_filename(struct commit *commit,\n+\t\t\t\t\t    int keep_subject)\n+{\n+\tstatic char filename[PATH_MAX];\n+\tchar *sol;\n+\tint len = 0;\n+\tint suffix_len = strlen(fmt_patch_suffix) + 1;\n+\n+\tsol = strstr(commit->buffer, \"\\n\\n\");\n+\tif (!sol)\n+\t\tfilename[0] = '\\0';\n+\telse {\n+\t\tint j, space = 0;\n+\n+\t\tsol += 2;\n+\t\t/* strip [PATCH] or [PATCH blabla] */\n+\t\tif (!keep_subject && !prefixcmp(sol, \"[PATCH\")) {\n+\t\t\tchar *eos = strchr(sol + 6, ']');\n+\t\t\tif (eos) {\n+\t\t\t\twhile (isspace(*eos))\n+\t\t\t\t\teos++;\n+\t\t\t\tsol = eos;\n+\t\t\t}\n+\t\t}\n+\n+\t\tfor (j = 0;\n+\t\t     j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&\n+\t\t\t     len < sizeof(filename) - suffix_len &&\n+\t\t\t     sol[j] && sol[j] != '\\n';\n+\t\t     j++) {\n+\t\t\tif (istitlechar(sol[j])) {\n+\t\t\t\tif (space) {\n+\t\t\t\t\tfilename[len++] = '-';\n+\t\t\t\t\tspace = 0;\n+\t\t\t\t}\n+\t\t\t\tfilename[len++] = sol[j];\n+\t\t\t\tif (sol[j] == '.')\n+\t\t\t\t\twhile (sol[j + 1] == '.')\n+\t\t\t\t\t\tj++;\n+\t\t\t} else\n+\t\t\t\tspace = 1;\n+\t\t}\n+\t\twhile (filename[len - 1] == '.'\n+\t\t       || filename[len - 1] == '-')\n+\t\t\tlen--;\n+\t\tfilename[len] = '\\0';\n+\t}\n+\treturn filename;\n+}\n+\n static FILE *realstdout = NULL;\n static const char *output_directory = NULL;\n \n-static int reopen_stdout(struct commit *commit, int nr, int keep_subject,\n-\t\t\t int numbered_files)\n+static int reopen_stdout(const char *oneline, int nr, int total)\n {\n \tchar filename[PATH_MAX];\n-\tchar *sol;\n \tint len = 0;\n \tint suffix_len = strlen(fmt_patch_suffix) + 1;\n \n \tif (output_directory) {\n-\t\tif (strlen(output_directory) >=\n+\t\tlen = snprintf(filename, sizeof(filename), \"%s\",\n+\t\t\t\toutput_directory);\n+\t\tif (len >=\n \t\t    sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)\n \t\t\treturn error(\"name of output directory is too long\");\n-\t\tstrlcpy(filename, output_directory, sizeof(filename) - suffix_len);\n-\t\tlen = strlen(filename);\n \t\tif (filename[len - 1] != '/')\n \t\t\tfilename[len++] = '/';\n \t}\n \n-\tif (numbered_files) {\n-\t\tsprintf(filename + len, \"%d\", nr);\n-\t\tlen = strlen(filename);\n-\n-\t} else {\n-\t\tsprintf(filename + len, \"%04d\", nr);\n-\t\tlen = strlen(filename);\n-\n-\t\tsol = strstr(commit->buffer, \"\\n\\n\");\n-\t\tif (sol) {\n-\t\t\tint j, space = 1;\n-\n-\t\t\tsol += 2;\n-\t\t\t/* strip [PATCH] or [PATCH blabla] */\n-\t\t\tif (!keep_subject && !prefixcmp(sol, \"[PATCH\")) {\n-\t\t\t\tchar *eos = strchr(sol + 6, ']');\n-\t\t\t\tif (eos) {\n-\t\t\t\t\twhile (isspace(*eos))\n-\t\t\t\t\t\teos++;\n-\t\t\t\t\tsol = eos;\n-\t\t\t\t}\n-\t\t\t}\n-\n-\t\t\tfor (j = 0;\n-\t\t\t     j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&\n-\t\t\t\t     len < sizeof(filename) - suffix_len &&\n-\t\t\t\t     sol[j] && sol[j] != '\\n';\n-\t\t\t     j++) {\n-\t\t\t\tif (istitlechar(sol[j])) {\n-\t\t\t\t\tif (space) {\n-\t\t\t\t\t\tfilename[len++] = '-';\n-\t\t\t\t\t\tspace = 0;\n-\t\t\t\t\t}\n-\t\t\t\t\tfilename[len++] = sol[j];\n-\t\t\t\t\tif (sol[j] == '.')\n-\t\t\t\t\t\twhile (sol[j + 1] == '.')\n-\t\t\t\t\t\t\tj++;\n-\t\t\t\t} else\n-\t\t\t\t\tspace = 1;\n-\t\t\t}\n-\t\t\twhile (filename[len - 1] == '.'\n-\t\t\t       || filename[len - 1] == '-')\n-\t\t\t\tlen--;\n-\t\t\tfilename[len] = 0;\n-\t\t}\n-\t\tif (len + suffix_len >= sizeof(filename))\n-\t\t\treturn error(\"Patch pathname too long\");\n+\tif (!oneline)\n+\t\tlen += sprintf(filename + len, \"%d\", nr);\n+\telse {\n+\t\tlen += sprintf(filename + len, \"%04d-\", nr);\n+\t\tlen += snprintf(filename + len, sizeof(filename) - len - 1\n+\t\t\t\t- suffix_len, \"%s\", oneline);\n \t\tstrcpy(filename + len, fmt_patch_suffix);\n \t}\n \n@@ -590,6 +598,76 @@ static void gen_message_id(struct rev_info *info, char *base)\n \tinfo->message_id = strbuf_detach(&buf, NULL);\n }\n \n+static void make_cover_letter(struct rev_info *rev,\n+\t\tint use_stdout, int numbered, int numbered_files,\n+\t\t\t      struct commit *origin, struct commit *head)\n+{\n+\tconst char *committer;\n+\tconst char *origin_sha1, *head_sha1;\n+\tconst char *argv[7];\n+\tconst char *subject_start = NULL;\n+\tconst char *body = \"*** SUBJECT HERE ***\\n\\n\\n*** BLURB HERE ***\\n\";\n+\tconst char *msg;\n+\tconst char *extra_headers = rev->extra_headers;\n+\tstruct strbuf sb;\n+\tconst char *encoding = \"utf-8\";\n+\n+\tif (rev->commit_format != CMIT_FMT_EMAIL)\n+\t\tdie(\"Cover letter needs email format\");\n+\n+\tif (!use_stdout && reopen_stdout(numbered_files ?\n+\t\t\t\tNULL : \"cover-letter\", 0, rev->total))\n+\t\treturn;\n+\n+\torigin_sha1 = sha1_to_hex(origin ? origin->object.sha1 : null_sha1);\n+\thead_sha1 = sha1_to_hex(head->object.sha1);\n+\n+\tlog_write_email_headers(rev, head_sha1, &subject_start, &extra_headers);\n+\n+\tcommitter = git_committer_info(0);\n+\n+\tmsg = body;\n+\tstrbuf_init(&sb, 0);\n+\tpp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,\n+\t\t     encoding);\n+\tpp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,\n+\t\t      encoding, 0);\n+\tpp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);\n+\tprintf(\"%s\\n\", sb.buf);\n+\n+\tstrbuf_release(&sb);\n+\n+\t/*\n+\t * We can only do diffstat with a unique reference point, and\n+\t * log is a bit tricky, so just skip it.\n+\t */\n+\tif (!origin)\n+\t\treturn;\n+\n+\targv[0] = \"shortlog\";\n+\targv[1] = head_sha1;\n+\targv[2] = \"--not\";\n+\targv[3] = origin_sha1;\n+\targv[4] = \"--\";\n+\targv[5] = NULL;\n+\tfflush(stdout);\n+\trun_command_v_opt(argv, RUN_GIT_CMD);\n+\n+\targv[0] = \"diff\";\n+\targv[1] = \"--stat\";\n+\targv[2] = \"--summary\";\n+\targv[3] = head_sha1;\n+\targv[4] = \"--not\";\n+\targv[5] = origin_sha1;\n+\targv[6] = \"--\";\n+\targv[7] = NULL;\n+\tfflush(stdout);\n+\trun_command_v_opt(argv, RUN_GIT_CMD);\n+\n+\tfflush(stdout);\n+\tprintf(\"\\n\");\n+}\n+\n static const char *clean_message_id(const char *msg_id)\n {\n \tchar ch;\n@@ -625,6 +703,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tint subject_prefix = 0;\n \tint ignore_if_in_upstream = 0;\n \tint thread = 0;\n+\tint cover_letter = 0;\n+\tstruct commit *origin = NULL, *head = NULL;\n \tconst char *in_reply_to = NULL;\n \tstruct patch_ids ids;\n \tchar *add_signoff = NULL;\n@@ -724,6 +804,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\trev.subject_prefix = argv[i] + 17;\n \t\t} else if (!prefixcmp(argv[i], \"--suffix=\"))\n \t\t\tfmt_patch_suffix = argv[i] + 9;\n+\t\telse if (!strcmp(argv[i], \"--cover-letter\"))\n+\t\t\tcover_letter = 1;\n \t\telse\n \t\t\targv[j++] = argv[i];\n \t}\n@@ -775,6 +857,25 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t * get_revision() to do the usual traversal.\n \t\t */\n \t}\n+\tif (cover_letter) {\n+\t\t/* remember the range */\n+\t\tint negative_count = 0;\n+\t\tint i;\n+\t\tfor (i = 0; i < rev.pending.nr; i++) {\n+\t\t\tstruct object *o = rev.pending.objects[i].item;\n+\t\t\tif (o->flags & UNINTERESTING) {\n+\t\t\t\torigin = (struct commit *)o;\n+\t\t\t\tnegative_count++;\n+\t\t\t} else\n+\t\t\t\thead = (struct commit *)o;\n+\t\t}\n+\t\t/* Multiple origins don't work for diffstat. */\n+\t\tif (negative_count > 1)\n+\t\t\torigin = NULL;\n+\t\t/* We can't generate a cover letter without any patches */\n+\t\tif (!head)\n+\t\t\treturn 0;\n+\t}\n \n \tif (ignore_if_in_upstream)\n \t\tget_patch_ids(&rev, &ids, prefix);\n@@ -801,16 +902,31 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tnumbered = 1;\n \tif (numbered)\n \t\trev.total = total + start_number - 1;\n-\trev.add_signoff = add_signoff;\n \tif (in_reply_to)\n \t\trev.ref_message_id = clean_message_id(in_reply_to);\n+\tif (cover_letter) {\n+\t\tif (thread)\n+\t\t\tgen_message_id(&rev, \"cover\");\n+\t\tmake_cover_letter(&rev, use_stdout, numbered, numbered_files,\n+\t\t\t\t  origin, head);\n+\t\ttotal++;\n+\t\tstart_number--;\n+\t}\n+\trev.add_signoff = add_signoff;\n \twhile (0 <= --nr) {\n \t\tint shown;\n \t\tcommit = list[nr];\n \t\trev.nr = total - nr + (start_number - 1);\n \t\t/* Make the second and subsequent mails replies to the first */\n \t\tif (thread) {\n+\t\t\t/* Have we already had a message ID? */\n \t\t\tif (rev.message_id) {\n+\t\t\t\t/*\n+\t\t\t\t * If we've got the ID to be a reply\n+\t\t\t\t * to, discard the current ID;\n+\t\t\t\t * otherwise, make everything a reply\n+\t\t\t\t * to that.\n+\t\t\t\t */\n \t\t\t\tif (rev.ref_message_id)\n \t\t\t\t\tfree(rev.message_id);\n \t\t\t\telse\n@@ -818,10 +934,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\t}\n \t\t\tgen_message_id(&rev, sha1_to_hex(commit->object.sha1));\n \t\t}\n-\t\tif (!use_stdout)\n-\t\t\tif (reopen_stdout(commit, rev.nr, keep_subject,\n-\t\t\t\t\t  numbered_files))\n-\t\t\t\tdie(\"Failed to create output files\");\n+\t\tif (!use_stdout && reopen_stdout(numbered_files ? NULL :\n+\t\t\t\tget_oneline_for_filename(commit, keep_subject),\n+\t\t\t\trev.nr, rev.total))\n+\t\t\tdie(\"Failed to create output files\");\n \t\tshown = log_tree_commit(&rev, commit);\n \t\tfree(commit->buffer);\n \t\tcommit->buffer = NULL;\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 9eec754..6b4d1c5 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -245,6 +245,7 @@ format-patch --inline --stdout initial..master\n format-patch --inline --stdout --subject-prefix=TESTCASE initial..master\n config format.subjectprefix DIFFERENT_PREFIX\n format-patch --inline --stdout initial..master^^\n+format-patch --stdout --cover-letter -n initial..master^\n \n diff --abbrev initial..side\n diff -r initial..side\ndiff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\nnew file mode 100644\nindex 0000000..311e207\n--- /dev/null\n+++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n@@ -0,0 +1,101 @@\n+$ git format-patch --stdout --cover-letter -n initial..master^\n+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001\n+From: C O Mitter <committer@example.com>\n+Date: Mon, 26 Jun 2006 00:05:00 +0000\n+Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***\n+\n+\n+*** BLURB HERE ***\n+\n+A U Thor (2):\n+      Second\n+      Third\n+\n+ dir/sub |    4 ++++\n+ file0   |    3 +++\n+ file1   |    3 +++\n+ file2   |    3 ---\n+ 4 files changed, 10 insertions(+), 3 deletions(-)\n+ create mode 100644 file1\n+ delete mode 100644 file2\n+\n+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001\n+From: A U Thor <author@example.com>\n+Date: Mon, 26 Jun 2006 00:01:00 +0000\n+Subject: [DIFFERENT_PREFIX 1/2] Second\n+\n+This is the second commit.\n+---\n+ dir/sub |    2 ++\n+ file0   |    3 +++\n+ file2   |    3 ---\n+ 3 files changed, 5 insertions(+), 3 deletions(-)\n+ delete mode 100644 file2\n+\n+diff --git a/dir/sub b/dir/sub\n+index 35d242b..8422d40 100644\n+--- a/dir/sub\n++++ b/dir/sub\n+@@ -1,2 +1,4 @@\n+ A\n+ B\n++C\n++D\n+diff --git a/file0 b/file0\n+index 01e79c3..b414108 100644\n+--- a/file0\n++++ b/file0\n+@@ -1,3 +1,6 @@\n+ 1\n+ 2\n+ 3\n++4\n++5\n++6\n+diff --git a/file2 b/file2\n+deleted file mode 100644\n+index 01e79c3..0000000\n+--- a/file2\n++++ /dev/null\n+@@ -1,3 +0,0 @@\n+-1\n+-2\n+-3\n+-- \n+g-i-t--v-e-r-s-i-o-n\n+\n+\n+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001\n+From: A U Thor <author@example.com>\n+Date: Mon, 26 Jun 2006 00:02:00 +0000\n+Subject: [DIFFERENT_PREFIX 2/2] Third\n+\n+---\n+ dir/sub |    2 ++\n+ file1   |    3 +++\n+ 2 files changed, 5 insertions(+), 0 deletions(-)\n+ create mode 100644 file1\n+\n+diff --git a/dir/sub b/dir/sub\n+index 8422d40..cead32e 100644\n+--- a/dir/sub\n++++ b/dir/sub\n+@@ -2,3 +2,5 @@ A\n+ B\n+ C\n+ D\n++E\n++F\n+diff --git a/file1 b/file1\n+new file mode 100644\n+index 0000000..b1e6722\n+--- /dev/null\n++++ b/file1\n+@@ -0,0 +1,3 @@\n++A\n++B\n++C\n+-- \n+g-i-t--v-e-r-s-i-o-n\n+\n+$\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 6e8b5f4..ac78752 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -122,6 +122,32 @@ test_expect_success 'thread in-reply-to' '\n \tdone\n '\n \n+test_expect_success 'thread cover-letter' '\n+\n+\trm -rf patches/ &&\n+\tgit checkout side &&\n+\tgit format-patch --cover-letter --thread -o patches/ master &&\n+\tFIRST_MID=$(grep \"Message-Id:\" patches/0000-* | sed \"s/^[^<]*\\(<[^>]*>\\).*$/\\1/\") &&\n+\tfor i in patches/0001-* patches/0002-* patches/0003-* \n+\tdo\n+\t  grep \"References: $FIRST_MID\" $i &&\n+\t  grep \"In-Reply-To: $FIRST_MID\" $i\n+\tdone\n+'\n+\n+test_expect_success 'thread cover-letter in-reply-to' '\n+\n+\trm -rf patches/ &&\n+\tgit checkout side &&\n+\tgit format-patch --cover-letter --in-reply-to=\"<test.message>\" --thread -o patches/ master &&\n+\tFIRST_MID=\"<test.message>\" &&\n+\tfor i in patches/*\n+\tdo\n+\t  grep \"References: $FIRST_MID\" $i &&\n+\t  grep \"In-Reply-To: $FIRST_MID\" $i\n+\tdone\n+'\n+\n test_expect_success 'excessive subject' '\n \n \trm -rf patches/ &&\n-- \n1.5.4.1.191.gfbf10\n"},{"id":"69254","messageId":"alpine.LSU.1.00.0802191300510.30505@racer.site","threadId":"12186","inReplyTo":"alpine.LNX.1.00.0802182254110.5816@iabervon.org","subject":"Re: [PATCH 2/5] Improve message-id generation flow control for format-patch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-19T13:01:26Z","receivedAt":"2008-02-19T13:01:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 18 Feb 2008, Daniel Barkalow wrote:\n\n> diff --git a/revision.h b/revision.h\n> index 8572315..e3559d0 100644\n> --- a/revision.h\n> +++ b/revision.h\n> @@ -74,7 +74,7 @@ struct rev_info {\n>  \tstruct log_info *loginfo;\n>  \tint\t\tnr, total;\n>  \tconst char\t*mime_boundary;\n> -\tconst char\t*message_id;\n> +\tchar\t\t*message_id;\n>  \tconst char\t*ref_message_id;\n>  \tconst char\t*add_signoff;\n>  \tconst char\t*extra_headers;\n\nMini-nit: technically, ref_message_id should be \"char *\", too.\n\nCiao,\nDscho\n"},{"id":"69255","messageId":"alpine.LSU.1.00.0802191305160.30505@racer.site","threadId":"12186","inReplyTo":"alpine.LNX.1.00.0802182254180.5816@iabervon.org","subject":"Re: [PATCH 3/5] Export some email and pretty-printing functions","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-19T13:06:42Z","receivedAt":"2008-02-19T13:06:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 18 Feb 2008, Daniel Barkalow wrote:\n\n> These will be used for generating the cover letter in addition to the \n> patch emails.\n\nI copied the hunks of log_write_email_headers() and used \"git diff -w\" to \nverify that only one thing changed: \"sha1\" was renamed to \"name\".  It \nmight make sense to rename it to \"from\", but then, I do not really care.\n\nCiao,\nDscho\n"},{"id":"69279","messageId":"alpine.LNX.1.00.0802191117540.19024@iabervon.org","threadId":"12186","inReplyTo":"alpine.LSU.1.00.0802191305160.30505@racer.site","subject":"Re: [PATCH 3/5] Export some email and pretty-printing functions","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-02-19T16:19:30Z","receivedAt":"2008-02-19T16:19:30Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Tue, 19 Feb 2008, Johannes Schindelin wrote:\n\n> Hi,\n> \n> On Mon, 18 Feb 2008, Daniel Barkalow wrote:\n> \n> > These will be used for generating the cover letter in addition to the \n> > patch emails.\n> \n> I copied the hunks of log_write_email_headers() and used \"git diff -w\" to \n> verify that only one thing changed: \"sha1\" was renamed to \"name\".  It \n> might make sense to rename it to \"from\", but then, I do not really care.\n\nNo, it's actually supposed to be the filename, I think. Beats me as to why \nit's used as the envelope from, but I'm not changing the logic there.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"69284","messageId":"alpine.LNX.1.00.0802191108450.19024@iabervon.org","threadId":"12186","inReplyTo":"alpine.LSU.1.00.0802191300510.30505@racer.site","subject":"Re: [PATCH 2/5] Improve message-id generation flow control for format-patch","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-02-19T16:26:41Z","receivedAt":"2008-02-19T16:26:41Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Tue, 19 Feb 2008, Johannes Schindelin wrote:\n\n> Hi,\n> \n> On Mon, 18 Feb 2008, Daniel Barkalow wrote:\n> \n> > diff --git a/revision.h b/revision.h\n> > index 8572315..e3559d0 100644\n> > --- a/revision.h\n> > +++ b/revision.h\n> > @@ -74,7 +74,7 @@ struct rev_info {\n> >  \tstruct log_info *loginfo;\n> >  \tint\t\tnr, total;\n> >  \tconst char\t*mime_boundary;\n> > -\tconst char\t*message_id;\n> > +\tchar\t\t*message_id;\n> >  \tconst char\t*ref_message_id;\n> >  \tconst char\t*add_signoff;\n> >  \tconst char\t*extra_headers;\n> \n> Mini-nit: technically, ref_message_id should be \"char *\", too.\n\nI'd intended ref_message_id to just reference the string owned by a \ndifferent message's message_id, but that doesn't really work with struct \nrev_info. I bet you'd like me to not leak memory out through \nref_message_id, too. I think I want to see how the code to collect the set \nof commits in the series (for more capable generation of cover letter \ncontents) goes before changing this either way.\n\n(It's incidentally kind of amusing that you managed to convey the way you \nthought the code should be designed, different from how I'd designed it, \nthrough a note on whether a struct field should be const or not.)\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"69292","messageId":"alpine.LSU.1.00.0802191646570.30505@racer.site","threadId":"12186","inReplyTo":"alpine.LNX.1.00.0802191108450.19024@iabervon.org","subject":"Re: [PATCH 2/5] Improve message-id generation flow control for format-patch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-19T16:51:12Z","receivedAt":"2008-02-19T16:51:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 19 Feb 2008, Daniel Barkalow wrote:\n\n> On Tue, 19 Feb 2008, Johannes Schindelin wrote:\n> \n> > On Mon, 18 Feb 2008, Daniel Barkalow wrote:\n> > \n> > > diff --git a/revision.h b/revision.h\n> > > index 8572315..e3559d0 100644\n> > > --- a/revision.h\n> > > +++ b/revision.h\n> > > @@ -74,7 +74,7 @@ struct rev_info {\n> > >  \tstruct log_info *loginfo;\n> > >  \tint\t\tnr, total;\n> > >  \tconst char\t*mime_boundary;\n> > > -\tconst char\t*message_id;\n> > > +\tchar\t\t*message_id;\n> > >  \tconst char\t*ref_message_id;\n> > >  \tconst char\t*add_signoff;\n> > >  \tconst char\t*extra_headers;\n> > \n> > Mini-nit: technically, ref_message_id should be \"char *\", too.\n> \n> I'd intended ref_message_id to just reference the string owned by a \n> different message's message_id, but that doesn't really work with struct \n> rev_info. I bet you'd like me to not leak memory out through \n> ref_message_id, too. I think I want to see how the code to collect the \n> set of commits in the series (for more capable generation of cover \n> letter contents) goes before changing this either way.\n> \n> (It's incidentally kind of amusing that you managed to convey the way \n> you thought the code should be designed, different from how I'd designed \n> it, through a note on whether a struct field should be const or not.)\n\nHehe!\n\nYou're right in that the revision machinery needs some cleaning up.  AFAIK \nthere is no method yet to release the memory allocated during one revision \nwalk (including the preparation).\n\nAnd you're right that this should be done separately, so please kindly \naccept my apologies while I retract my comment.\n\nCiao,\nDscho\n"},{"id":"69358","messageId":"7vfxvodz1i.fsf@gitster.siamese.dyndns.org","threadId":"12186","inReplyTo":"alpine.LNX.1.00.0802182254310.5816@iabervon.org","subject":"Re: [PATCH 5/5] Add a --cover-letter option to format-patch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-20T04:36:41Z","receivedAt":"2008-02-20T04:36:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Daniel Barkalow <barkalow@iabervon.org> writes:\n\n> +\tconst char *body = \"*** SUBJECT HERE ***\\n\\n\\n*** BLURB HERE ***\\n\";\n> ...\n> diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n> new file mode 100644\n> index 0000000..311e207\n> --- /dev/null\n> +++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n> @@ -0,0 +1,101 @@\n> +$ git format-patch --stdout --cover-letter -n initial..master^\n> +From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001\n> +From: C O Mitter <committer@example.com>\n> +Date: Mon, 26 Jun 2006 00:05:00 +0000\n> +Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***\n> +\n> +\n> +*** BLURB HERE ***\n> +\n> +A U Thor (2):\n> +      Second\n> +      Third\n\nI still disagree with your earlier point on the extra blank line\nhere.\n\nWhen we give commit log template, we do leave an extra blank line\nbefore we start the template material \"# Please enter ...\", to\nease typing, but it is different.  The template material does\nnot have to be removed by the end-user, so giving a blank line\nupfront and starting the editor at that line is a usability\nimprovement, compared to the case where there is none.\n\nBut this \"*** BLURB HERE ***\" needs to be removed when the user\nedits the cover-letter, and I really do not see any reason to\nhave an extra blank line in front of it.\n\nIncidentally, I strongly suspect that if you do not have that\nextra blank line, we would not even need to have the [PATCH 4/5]\n(which is a slight regression) in the series.\n\nSo how about this squashed in, with [PATCH 4/5] skipped?\n\n\n builtin-log.c                                      |    2 +-\n ...tch_--stdout_--cover-letter_-n_initial..master^ |    1 -\n 2 files changed, 1 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin-log.c b/builtin-log.c\nindex e26c986..0b348eb 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -629,7 +629,7 @@ static void make_cover_letter(struct rev_info *rev,\n \tconst char *origin_sha1, *head_sha1;\n \tconst char *argv[7];\n \tconst char *subject_start = NULL;\n-\tconst char *body = \"*** SUBJECT HERE ***\\n\\n\\n*** BLURB HERE ***\\n\";\n+\tconst char *body = \"*** SUBJECT HERE ***\\n\\n*** BLURB HERE ***\\n\";\n \tconst char *msg;\n \tconst char *extra_headers = rev->extra_headers;\n \tstruct strbuf sb;\ndiff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\nindex 311e207..0151453 100644\n--- a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n+++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n@@ -4,7 +4,6 @@ From: C O Mitter <committer@example.com>\n Date: Mon, 26 Jun 2006 00:05:00 +0000\n Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***\n \n-\n *** BLURB HERE ***\n \n A U Thor (2):\n"},{"id":"69380","messageId":"alpine.LNX.1.00.0802201143180.19024@iabervon.org","threadId":"12186","inReplyTo":"7vfxvodz1i.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 5/5] Add a --cover-letter option to format-patch","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-02-20T16:57:02Z","receivedAt":"2008-02-20T16:57:02Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Tue, 19 Feb 2008, Junio C Hamano wrote:\n\n> Daniel Barkalow <barkalow@iabervon.org> writes:\n> \n> > +\tconst char *body = \"*** SUBJECT HERE ***\\n\\n\\n*** BLURB HERE ***\\n\";\n> > ...\n> > diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n> > new file mode 100644\n> > index 0000000..311e207\n> > --- /dev/null\n> > +++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n> > @@ -0,0 +1,101 @@\n> > +$ git format-patch --stdout --cover-letter -n initial..master^\n> > +From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001\n> > +From: C O Mitter <committer@example.com>\n> > +Date: Mon, 26 Jun 2006 00:05:00 +0000\n> > +Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***\n> > +\n> > +\n> > +*** BLURB HERE ***\n> > +\n> > +A U Thor (2):\n> > +      Second\n> > +      Third\n> \n> I still disagree with your earlier point on the extra blank line\n> here.\n> \n> When we give commit log template, we do leave an extra blank line\n> before we start the template material \"# Please enter ...\", to\n> ease typing, but it is different.  The template material does\n> not have to be removed by the end-user, so giving a blank line\n> upfront and starting the editor at that line is a usability\n> improvement, compared to the case where there is none.\n> \n> But this \"*** BLURB HERE ***\" needs to be removed when the user\n> edits the cover-letter, and I really do not see any reason to\n> have an extra blank line in front of it.\n> \n> Incidentally, I strongly suspect that if you do not have that\n> extra blank line, we would not even need to have the [PATCH 4/5]\n> (which is a slight regression) in the series.\n> \n> So how about this squashed in, with [PATCH 4/5] skipped?\n\nSure. I still like the extra line, but it's not that big a deal. (And I \nstill think it's odd that the code removes extra blank lines from that \nspot but not elsewhere, but likewise not a big deal)\n\n\t-Daniel\n*This .sig left intentionally blank*\n"}]}