{"thread":{"id":"32160","subject":"[PATCH v2 3/4] format-patch: update append_signoff prototype","startedAt":"2012-11-22T11:24:10Z","lastAt":"2012-12-02T08:03:54Z","messageCount":9,"participants":["Nguyễn Thái Ngọc Duy","Nguyen Thai Ngoc Duy","Junio C Hamano","Torsten Bögershausen","Brandon Casey"],"isPatch":true,"patchVersion":2,"patchTotal":4},"messages":[{"id":"203636","messageId":"CACsJy8BiJRK7N3_HZ2WXpMd1YkDSW00AxuYqiCWJgij+Kq6AiQ@mail.gmail.com","threadId":"32160","inReplyTo":"7vobirq0q2.fsf_-_@alter.siamese.dyndns.org","subject":"Re: Topics currently in the Stalled category","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-22T11:24:10Z","receivedAt":"2012-11-22T11:24:10Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Nov 21, 2012 at 7:05 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> * nd/unify-appending-of-s-o-b (2012-11-15) 1 commit\n>  - Unify appending signoff in format-patch, commit and sequencer\n>\n>  I am not sure if the logic to refrain from adding a sign-off based\n>  on the existing run of sign-offs is done correctly in this change.\n\nI'm not sure what's the right logic here. The original code in\nformat-patch has exactly one test just to verify that --signoff does\n_something_. There's another signoff code in git-am.sh and we should\nmake sure at least the behavior is consistent. I guess I should start\nover by writing new tests to record the current behavior, then fixes\nand finally the unification. I think you can drop this for now.\n-- \nDuy\n"},{"id":"203647","messageId":"1353602289-9418-1-git-send-email-pclouds@gmail.com","threadId":"32160","inReplyTo":"CACsJy8BiJRK7N3_HZ2WXpMd1YkDSW00AxuYqiCWJgij+Kq6AiQ@mail.gmail.com","subject":"[PATCH v2 0/4] nd/unify-appending-of-s-o-b","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-22T16:38:05Z","receivedAt":"2012-11-22T16:38:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This round cherry-pick and commit only skip S-o-b if the last one is\nthe same while format-patch skips it if the same one appears\nanywhere in S-o-b list. I don't have strong opinion about which way is\nmore correct. Though if we decide to change format-patch behavior, the\nseries may become a bit different.\n\nNguyễn Thái Ngọc Duy (4):\n  t4014: more tests about appending s-o-b lines\n  format-patch: stricter S-o-b detection\n  format-patch: update append_signoff prototype\n  Unify appending signoff in format-patch, commit and sequencer\n\n builtin/commit.c        |  10 ++-\n builtin/log.c           |  13 +---\n log-tree.c              |  66 +++++++++++++---\n log-tree.h              |   4 +\n revision.h              |   2 +-\n sequencer.c             |  65 +---------------\n sequencer.h             |   4 -\n t/t4014-format-patch.sh | 199 ++++++++++++++++++++++++++++++++++++++++++++++++\n t/t7501-commit.sh       |   2 +-\n 9 files changed, 272 insertions(+), 93 deletions(-)\n\n-- \n1.8.0.4.g5d0415a\n"},{"id":"203646","messageId":"1353602289-9418-2-git-send-email-pclouds@gmail.com","threadId":"32160","inReplyTo":"1353602289-9418-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 1/4] t4014: more tests about appending s-o-b lines","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-22T16:38:06Z","receivedAt":"2012-11-22T16:38:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t4014-format-patch.sh | 145 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 145 insertions(+)\n\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 959aa26..c8d5d29 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -891,4 +891,149 @@ test_expect_success 'format patch ignores color.ui' '\n \ttest_cmp expect actual\n '\n \n+append_signoff()\n+{\n+\tC=`git commit-tree HEAD^^{tree} -p HEAD` &&\n+\tgit format-patch --stdout --signoff ${C}^..${C} |\n+\t\ttee append_signoff.patch |\n+\t\tsed -n \"1,/^---$/p\" |\n+\t\tgrep -n -E \"^Subject|Sign|^$\"\n+}\n+\n+test_expect_success 'signoff: commit with no body' '\n+\tappend_signoff </dev/null >actual &&\n+\tcat <<\\EOF | sed \"s/EOL$//\" >expected &&\n+4:Subject: [PATCH] EOL\n+8:\n+9:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: commit with only subject' '\n+\techo subject | append_signoff >actual &&\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+9:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: commit with only subject that does not end with NL' '\n+\techo -n subject | append_signoff >actual &&\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+9:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: no existing signoffs' '\n+\tappend_signoff <<\\EOF >actual &&\n+subject\n+\n+body\n+EOF\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+10:\n+11:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: no existing signoffs and no trailing NL' '\n+\tprintf \"subject\\n\\nbody\" | append_signoff >actual &&\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+10:\n+11:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: some random signoff' '\n+\tappend_signoff <<\\EOF >actual &&\n+subject\n+\n+body\n+\n+Signed-off-by: my@house\n+EOF\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+10:\n+11:Signed-off-by: my@house\n+12:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: some random signoff-alike' '\n+\tappend_signoff <<\\EOF >actual &&\n+subject\n+\n+body\n+Fooled-by-me: my@house\n+EOF\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+11:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: the same signoff at the end' '\n+\tappend_signoff <<\\EOF >actual &&\n+subject\n+\n+body\n+\n+Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+10:\n+11:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: the same signoff at the end, no trailing NL' '\n+\tprintf \"subject\\n\\nSigned-off-by: C O Mitter <committer@example.com>\" |\n+\t\tappend_signoff >actual &&\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+9:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: the same signoff NOT at the end' '\n+\tappend_signoff <<\\EOF >actual &&\n+subject\n+\n+body\n+\n+Signed-off-by: C O Mitter <committer@example.com>\n+Signed-off-by: my@house\n+EOF\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+10:\n+11:Signed-off-by: C O Mitter <committer@example.com>\n+12:Signed-off-by: my@house\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.8.0.4.g5d0415a\n"},{"id":"203649","messageId":"1353602289-9418-3-git-send-email-pclouds@gmail.com","threadId":"32160","inReplyTo":"1353602289-9418-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 2/4] format-patch: stricter S-o-b detection","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-22T16:38:07Z","receivedAt":"2012-11-22T16:38:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"S-o-b in the middle of a sentence, at the beginning of the sentence\nbut it is just because of text wrapping, or a full paragraph of valid\nS-o-b in the middle of the message. All these are not S-o-b, but\ndetected as is. Fix them.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n log-tree.c              | 37 +++++++++++++++++++++++++++++++--\n t/t4014-format-patch.sh | 54 +++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 89 insertions(+), 2 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex c894930..aff7c0a 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -260,14 +260,47 @@ static void append_signoff(struct strbuf *sb, const char *signoff)\n \tint has_signoff = 0;\n \tchar *cp;\n \n-\tcp = sb->buf;\n+\t/*\n+\t * Only search in the last paragrah, don't be fooled by a\n+\t * paragraph full of valid S-o-b in the middle.\n+\t */\n+\tcp = sb->buf + sb->len - 1;\n+\twhile (cp > sb->buf) {\n+\t\tif (cp[0] == '\\n' && cp[1] == '\\n') {\n+\t\t\tcp += 2;\n+\t\t\tbreak;\n+\t\t}\n+\t\tcp--;\n+\t}\n \n \t/* First see if we already have the sign-off by the signer */\n \twhile ((cp = strstr(cp, signed_off_by))) {\n+\t\tconst char *s = cp;\n+\t\tcp += strlen(signed_off_by);\n+\n+\t\tif (!has_signoff && s > sb->buf) {\n+\t\t\t/*\n+\t\t\t * S-o-b in the middle of a sentence is not\n+\t\t\t * really S-o-b\n+\t\t\t */\n+\t\t\tif (s[-1] != '\\n')\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (s - 1 > sb->buf && s[-2] == '\\n')\n+\t\t\t\t; /* the first S-o-b */\n+\t\t\telse if (!detect_any_signoff(sb->buf, s - sb->buf))\n+\t\t\t\t/*\n+\t\t\t\t * The previous line looks like an\n+\t\t\t\t * S-o-b. There's still no guarantee\n+\t\t\t\t * that it's an actual S-o-b. For that\n+\t\t\t\t * we need to look back until we find\n+\t\t\t\t * a blank line, which is too costly.\n+\t\t\t\t */\n+\t\t\t\tcontinue;\n+\t\t}\n \n \t\thas_signoff = 1;\n \n-\t\tcp += strlen(signed_off_by);\n \t\tif (cp + signoff_len >= sb->buf + sb->len)\n \t\t\tbreak;\n \t\tif (strncmp(cp, signoff, signoff_len))\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex c8d5d29..d0b3c85 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -989,6 +989,60 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'signoff: not really a signoff' '\n+\tappend_signoff <<\\EOF >actual &&\n+subject\n+\n+I want to mention about Signed-off-by: here.\n+EOF\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+9:I want to mention about Signed-off-by: here.\n+10:\n+11:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: not really a signoff (2)' '\n+\tappend_signoff <<\\EOF >actual &&\n+subject\n+\n+My unfortunate\n+Signed-off-by: example happens to be wrapped here.\n+EOF\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+10:Signed-off-by: example happens to be wrapped here.\n+11:\n+12:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'signoff: valid S-o-b paragraph in the middle' '\n+\tappend_signoff <<\\EOF >actual &&\n+subject\n+\n+Signed-off-by: my@house\n+Signed-off-by: your@house\n+\n+A lot of houses.\n+EOF\n+\tcat >expected <<\\EOF &&\n+4:Subject: [PATCH] subject\n+8:\n+9:Signed-off-by: my@house\n+10:Signed-off-by: your@house\n+11:\n+13:\n+14:Signed-off-by: C O Mitter <committer@example.com>\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'signoff: the same signoff at the end' '\n \tappend_signoff <<\\EOF >actual &&\n subject\n-- \n1.8.0.4.g5d0415a\n"},{"id":"203629","messageId":"1353602289-9418-4-git-send-email-pclouds@gmail.com","threadId":"32160","inReplyTo":"1353602289-9418-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 3/4] format-patch: update append_signoff prototype","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-22T16:38:08Z","receivedAt":"2012-11-22T16:38:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a preparation step for merging with append_signoff from\nsequencer.c\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/log.c | 13 +------------\n log-tree.c    | 21 +++++++++++++--------\n revision.h    |  2 +-\n 3 files changed, 15 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 09cf43e..f201070 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1053,7 +1053,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\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 \tstruct strbuf buf = STRBUF_INIT;\n \tint use_patch_format = 0;\n \tint quiet = 0;\n@@ -1148,16 +1147,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\t     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tif (do_signoff) {\n-\t\tconst char *committer;\n-\t\tconst char *endpos;\n-\t\tcommitter = git_committer_info(IDENT_STRICT);\n-\t\tendpos = strchr(committer, '>');\n-\t\tif (!endpos)\n-\t\t\tdie(_(\"bogus committer info %s\"), committer);\n-\t\tadd_signoff = xmemdupz(committer, endpos - committer + 1);\n-\t}\n-\n \tfor (i = 0; i < extra_hdr.nr; i++) {\n \t\tstrbuf_addstr(&buf, extra_hdr.items[i].string);\n \t\tstrbuf_addch(&buf, '\\n');\n@@ -1348,7 +1337,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\ttotal++;\n \t\tstart_number--;\n \t}\n-\trev.add_signoff = add_signoff;\n+\trev.add_signoff = do_signoff;\n \twhile (0 <= --nr) {\n \t\tint shown;\n \t\tcommit = list[nr];\ndiff --git a/log-tree.c b/log-tree.c\nindex aff7c0a..7e50545 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -253,9 +253,11 @@ static int detect_any_signoff(char *letter, int size)\n \treturn seen_head && seen_name;\n }\n \n-static void append_signoff(struct strbuf *sb, const char *signoff)\n+static void append_signoff(struct strbuf *sb, int flags)\n {\n-\tstatic const char signed_off_by[] = \"Signed-off-by: \";\n+\tchar* signoff = xstrdup(fmt_name(getenv(\"GIT_COMMITTER_NAME\"),\n+\t\t\t\t\t getenv(\"GIT_COMMITTER_EMAIL\")));\n+\tstatic const char sign_off_header[] = \"Signed-off-by: \";\n \tsize_t signoff_len = strlen(signoff);\n \tint has_signoff = 0;\n \tchar *cp;\n@@ -274,9 +276,9 @@ static void append_signoff(struct strbuf *sb, const char *signoff)\n \t}\n \n \t/* First see if we already have the sign-off by the signer */\n-\twhile ((cp = strstr(cp, signed_off_by))) {\n+\twhile ((cp = strstr(cp, sign_off_header))) {\n \t\tconst char *s = cp;\n-\t\tcp += strlen(signed_off_by);\n+\t\tcp += strlen(sign_off_header);\n \n \t\tif (!has_signoff && s > sb->buf) {\n \t\t\t/*\n@@ -317,9 +319,10 @@ static void append_signoff(struct strbuf *sb, const char *signoff)\n \tif (!has_signoff)\n \t\tstrbuf_addch(sb, '\\n');\n \n-\tstrbuf_addstr(sb, signed_off_by);\n+\tstrbuf_addstr(sb, sign_off_header);\n \tstrbuf_add(sb, signoff, signoff_len);\n \tstrbuf_addch(sb, '\\n');\n+\tfree(signoff);\n }\n \n static unsigned int digits_in_number(unsigned int number)\n@@ -684,8 +687,10 @@ void show_log(struct rev_info *opt)\n \t/*\n \t * And then the pretty-printed message itself\n \t */\n-\tif (ctx.need_8bit_cte >= 0)\n-\t\tctx.need_8bit_cte = has_non_ascii(opt->add_signoff);\n+\tif (ctx.need_8bit_cte >= 0 && opt->add_signoff)\n+\t\tctx.need_8bit_cte =\n+\t\t\thas_non_ascii(fmt_name(getenv(\"GIT_COMMITTER_NAME\"),\n+\t\t\t\t\t       getenv(\"GIT_COMMITTER_EMAIL\")));\n \tctx.date_mode = opt->date_mode;\n \tctx.date_mode_explicit = opt->date_mode_explicit;\n \tctx.abbrev = opt->diffopt.abbrev;\n@@ -696,7 +701,7 @@ void show_log(struct rev_info *opt)\n \tpretty_print_commit(&ctx, commit, &msgbuf);\n \n \tif (opt->add_signoff)\n-\t\tappend_signoff(&msgbuf, opt->add_signoff);\n+\t\tappend_signoff(&msgbuf, 0);\n \tif (opt->show_log_size) {\n \t\tprintf(\"log size %i\\n\", (int)msgbuf.len);\n \t\tgraph_show_oneline(opt->graph);\ndiff --git a/revision.h b/revision.h\nindex a95bd0b..af35325 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -136,7 +136,7 @@ struct rev_info {\n \tint\t\tnumbered_files;\n \tchar\t\t*message_id;\n \tstruct string_list *ref_message_ids;\n-\tconst char\t*add_signoff;\n+\tint              add_signoff;\n \tconst char\t*extra_headers;\n \tconst char\t*log_reencode;\n \tconst char\t*subject_prefix;\n-- \n1.8.0.4.g5d0415a\n"},{"id":"203644","messageId":"1353602289-9418-5-git-send-email-pclouds@gmail.com","threadId":"32160","inReplyTo":"1353602289-9418-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 4/4] Unify appending signoff in format-patch, commit and sequencer","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-11-22T16:38:09Z","receivedAt":"2012-11-22T16:38:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"There are two implementations of append_signoff in log-tree.c and\nsequencer.c, which do more or less the same thing. This patch removes\nthe sequencer.c's in favor of the format-patch's.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/commit.c  | 10 ++++++++-\n log-tree.c        | 14 ++++++++----\n log-tree.h        |  4 ++++\n sequencer.c       | 65 ++-----------------------------------------------------\n sequencer.h       |  4 ----\n t/t7501-commit.sh |  2 +-\n 6 files changed, 26 insertions(+), 73 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex a17a5df..6d323d9 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -698,7 +698,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tprevious = eol;\n \t\t}\n \n-\t\tappend_signoff(&sb, ignore_footer);\n+\t\tif (ignore_footer) {\n+\t\t\tstruct strbuf footer = STRBUF_INIT;\n+\t\t\tstrbuf_addstr(&footer, sb.buf + sb.len - ignore_footer);\n+\t\t\tstrbuf_setlen(&sb, sb.len - ignore_footer);\n+\t\t\tappend_signoff(&sb, SOB_IGNORE_SAME);\n+\t\t\tstrbuf_addstr(&sb, footer.buf);\n+\t\t\tstrbuf_release(&footer);\n+\t\t} else\n+\t\t\tappend_signoff(&sb, SOB_IGNORE_SAME);\n \t}\n \n \tif (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)\ndiff --git a/log-tree.c b/log-tree.c\nindex 7e50545..e8d31d9 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -12,6 +12,8 @@\n \n struct decoration name_decoration = { \"object names\" };\n \n+const char sign_off_header[] = \"Signed-off-by: \";\n+\n enum decoration_type {\n \tDECORATION_NONE = 0,\n \tDECORATION_REF_LOCAL,\n@@ -207,7 +209,7 @@ void show_decorations(struct rev_info *opt, struct commit *commit)\n }\n \n /*\n- * Search for \"^[-A-Za-z]+: [^@]+@\" pattern. It usually matches\n+ * Search for \"^[-A-Za-z0-9]+: [^@]+@\" pattern. It usually matches\n  * Signed-off-by: and Acked-by: lines.\n  */\n static int detect_any_signoff(char *letter, int size)\n@@ -243,6 +245,7 @@ static int detect_any_signoff(char *letter, int size)\n \t\t}\n \t\tif (('A' <= ch && ch <= 'Z') ||\n \t\t    ('a' <= ch && ch <= 'z') ||\n+\t\t    ('0' <= ch && ch <= '9') ||\n \t\t    ch == '-') {\n \t\t\tseen_head = 1;\n \t\t\tcontinue;\n@@ -253,11 +256,10 @@ static int detect_any_signoff(char *letter, int size)\n \treturn seen_head && seen_name;\n }\n \n-static void append_signoff(struct strbuf *sb, int flags)\n+void append_signoff(struct strbuf *sb, int flags)\n {\n \tchar* signoff = xstrdup(fmt_name(getenv(\"GIT_COMMITTER_NAME\"),\n \t\t\t\t\t getenv(\"GIT_COMMITTER_EMAIL\")));\n-\tstatic const char sign_off_header[] = \"Signed-off-by: \";\n \tsize_t signoff_len = strlen(signoff);\n \tint has_signoff = 0;\n \tchar *cp;\n@@ -310,7 +312,11 @@ static void append_signoff(struct strbuf *sb, int flags)\n \t\tif (!isspace(cp[signoff_len]))\n \t\t\tcontinue;\n \t\t/* we already have him */\n-\t\treturn;\n+\t\tif (flags & SOB_IGNORE_SAME) {\n+\t\t\tif (cp[signoff_len + 1] == '\\0')\n+\t\t\t\treturn;\n+\t\t} else\n+\t\t\treturn;\n \t}\n \n \tif (!has_signoff)\ndiff --git a/log-tree.h b/log-tree.h\nindex f5ac238..739f729 100644\n--- a/log-tree.h\n+++ b/log-tree.h\n@@ -24,4 +24,8 @@ void load_ref_decorations(int flags);\n void get_patch_filename(struct commit *commit, const char *subject, int nr,\n \t\t\tconst char *suffix, struct strbuf *buf);\n \n+#define SOB_IGNORE_SAME   1\n+extern const char sign_off_header[];\n+void append_signoff(struct strbuf *msgbuf, int flags);\n+\n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex e3723d2..bc02a66 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -14,11 +14,10 @@\n #include \"merge-recursive.h\"\n #include \"refs.h\"\n #include \"argv-array.h\"\n+#include \"log-tree.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n-const char sign_off_header[] = \"Signed-off-by: \";\n-\n static void remove_sequencer_state(void)\n {\n \tstruct strbuf seq_dir = STRBUF_INIT;\n@@ -236,7 +235,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \trollback_lock_file(&index_lock);\n \n \tif (opts->signoff)\n-\t\tappend_signoff(msgbuf, 0);\n+\t\tappend_signoff(msgbuf, SOB_IGNORE_SAME);\n \n \tif (!clean) {\n \t\tint i;\n@@ -1016,63 +1015,3 @@ int sequencer_pick_revisions(struct replay_opts *opts)\n \tsave_opts(opts);\n \treturn pick_commits(todo_list, opts);\n }\n-\n-static int ends_rfc2822_footer(struct strbuf *sb, int ignore_footer)\n-{\n-\tint ch;\n-\tint hit = 0;\n-\tint i, j, k;\n-\tint len = sb->len - ignore_footer;\n-\tint first = 1;\n-\tconst char *buf = sb->buf;\n-\n-\tfor (i = len - 1; i > 0; i--) {\n-\t\tif (hit && buf[i] == '\\n')\n-\t\t\tbreak;\n-\t\thit = (buf[i] == '\\n');\n-\t}\n-\n-\twhile (i < len - 1 && buf[i] == '\\n')\n-\t\ti++;\n-\n-\tfor (; i < len; i = k) {\n-\t\tfor (k = i; k < len && buf[k] != '\\n'; k++)\n-\t\t\t; /* do nothing */\n-\t\tk++;\n-\n-\t\tif ((buf[k] == ' ' || buf[k] == '\\t') && !first)\n-\t\t\tcontinue;\n-\n-\t\tfirst = 0;\n-\n-\t\tfor (j = 0; i + j < len; j++) {\n-\t\t\tch = buf[i + j];\n-\t\t\tif (ch == ':')\n-\t\t\t\tbreak;\n-\t\t\tif (isalnum(ch) ||\n-\t\t\t    (ch == '-'))\n-\t\t\t\tcontinue;\n-\t\t\treturn 0;\n-\t\t}\n-\t}\n-\treturn 1;\n-}\n-\n-void append_signoff(struct strbuf *msgbuf, int ignore_footer)\n-{\n-\tstruct strbuf sob = STRBUF_INIT;\n-\tint i;\n-\n-\tstrbuf_addstr(&sob, sign_off_header);\n-\tstrbuf_addstr(&sob, fmt_name(getenv(\"GIT_COMMITTER_NAME\"),\n-\t\t\t\tgetenv(\"GIT_COMMITTER_EMAIL\")));\n-\tstrbuf_addch(&sob, '\\n');\n-\tfor (i = msgbuf->len - 1 - ignore_footer; i > 0 && msgbuf->buf[i - 1] != '\\n'; i--)\n-\t\t; /* do nothing */\n-\tif (prefixcmp(msgbuf->buf + i, sob.buf)) {\n-\t\tif (!i || !ends_rfc2822_footer(msgbuf, ignore_footer))\n-\t\t\tstrbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, \"\\n\", 1);\n-\t\tstrbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, sob.buf, sob.len);\n-\t}\n-\tstrbuf_release(&sob);\n-}\ndiff --git a/sequencer.h b/sequencer.h\nindex 9d57d57..99eb7fa 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -46,8 +46,4 @@ struct replay_opts {\n \n int sequencer_pick_revisions(struct replay_opts *opts);\n \n-extern const char sign_off_header[];\n-\n-void append_signoff(struct strbuf *msgbuf, int ignore_footer);\n-\n #endif\ndiff --git a/t/t7501-commit.sh b/t/t7501-commit.sh\nindex 195e747..6dd4580 100755\n--- a/t/t7501-commit.sh\n+++ b/t/t7501-commit.sh\n@@ -353,7 +353,7 @@ test_expect_success 'signoff gap' '\n \n \techo 3 >positive &&\n \tgit add positive &&\n-\talt=\"Alt-RFC-822-Header: Value\" &&\n+\talt=\"Alt-RFC-822-Header: Va@lue\" &&\n \tgit commit -s -m \"welcome\n \n $alt\" &&\n-- \n1.8.0.4.g5d0415a\n"},{"id":"203725","messageId":"7vtxsfzrfr.fsf@alter.siamese.dyndns.org","threadId":"32160","inReplyTo":"1353602289-9418-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 0/4] nd/unify-appending-of-s-o-b","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-24T02:05:12Z","receivedAt":"2012-11-24T02:05:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This round cherry-pick and commit only skip S-o-b if the last one is\n> the same...\n\nThat sounds sensible.  When the user asks us to add sign-off, we\nshould honor it, unless it is known that adding one is absolutely\nunnecessary, which is \"it already exists as the last one\".  Ignoring\nall other cases will lose information.\n\nAmong the non-merge commits in the kernel history, you see about 500\nsuch commits where the same contributor signs off the patch more\nthan once, recording the flow of the patch; 106a4ee258 seems to be\nthe most recent example.\n\nThanks.\n"},{"id":"204415","messageId":"50BAFE04.4080100@web.de","threadId":"32160","inReplyTo":"1353602289-9418-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 1/4] t4014: more tests about appending s-o-b lines","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2012-12-02T07:06:44Z","receivedAt":"2012-12-02T07:06:44Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 22.11.12 17:38, Nguyễn Thái Ngọc Duy wrote:\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  t/t4014-format-patch.sh | 145 ++++++++++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 145 insertions(+)\n> +\techo -n subject | append_signoff >actual &&\n\n\n\"echo -n\" is not portable, and we use printf everywhere. \nI found one \"echo -n\" in line  996.\n\nCan we squeeze that in, before going to next?\n\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 6cfad13..f460930 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -993,7 +993,7 @@ EOF\n '\n \n test_expect_success 'signoff: commit with only subject that does not end with NL' '\n-       echo -n subject | append_signoff >actual &&\n+       printf subject | append_signoff >actual &&\n        cat >expected <<\\EOF &&\n 4:Subject: [PATCH] subject\n 8:\n"},{"id":"204416","messageId":"CA+sFfMd-yvQpsfVEWYBk+70_05Ov5aX4xg8nNY0HPwHTVAK60Q@mail.gmail.com","threadId":"32160","inReplyTo":"50BAFE04.4080100@web.de","subject":"Re: [PATCH v2 1/4] t4014: more tests about appending s-o-b lines","fromName":"Brandon Casey","fromEmail":"drafnel@gmail.com","sentAt":"2012-12-02T08:03:54Z","receivedAt":"2012-12-02T08:03:54Z","isPatch":true,"sender":{"key":"drafnel@gmail.com","avatar":"https://avatars.githubusercontent.com/u/921167?v=4"},"body":"On Sat, Dec 1, 2012 at 11:06 PM, Torsten Bögershausen <tboegi@web.de> wrote:\n> On 22.11.12 17:38, Nguyễn Thái Ngọc Duy wrote:\n>>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  t/t4014-format-patch.sh | 145 ++++++++++++++++++++++++++++++++++++++++++++++++\n>>  1 file changed, 145 insertions(+)\n>> +     echo -n subject | append_signoff >actual &&\n>\n>\n> \"echo -n\" is not portable, and we use printf everywhere.\n> I found one \"echo -n\" in line  996.\n>\n> Can we squeeze that in, before going to next?\n\nI got it.  I'll squash it into the next series.\n\n-Brandon\n"}]}