{"thread":{"id":"44513","subject":"[PATCH 0/2] add format specifiers to display trailers","startedAt":"2016-11-19T00:58:23Z","lastAt":"2016-11-19T00:58:32Z","messageCount":3,"participants":["Jacob Keller"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"306187","messageId":"20161119005815.3646-1-jacob.e.keller@intel.com","threadId":"44513","inReplyTo":null,"subject":"[PATCH 0/2] add format specifiers to display trailers","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2016-11-19T00:58:13Z","receivedAt":"2016-11-19T00:58:23Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nThis is based off of jt/use-trailer-api-in-commands so that we can make\nuse of the public trailer API that will parse a string for trailers.\n\nI use trailers as a way to store extra commit metadata, and would like a\nconvenient way to obtain the trailers of a commit message easily. This\nadds format specifiers to both the ref-filter API and the pretty\nformat specifiers, using %(trailers) for both (and also\ncontents:trailers for ref-filter).\n\nAdditionally, I am somewhat not a fan of the way that if you have a\nseries of trailers which are trailer format, but not recognized, such\nas the following:\n\n\t<text>\n\n\tMy-tag: my value\n\tMy-other-tag: my other value\n\t[non-trailer line]\n\tMy-tag: my third value\n\nGit interpret-trailers will not recognize this as a trailer block\nbecause it doesn't have any standard git tags within it.\n\nJunio suggested that we should treat all the configured trailer prefixes\nas recognized so that it would work as well, but it doesn't appear to\ndo this at least for jt/use-trailer-api-in-commands\n\nI think that's the right solution, since it's extensible, though it\nwould mean that interpret-trailers would behave differently on different\nsystems... not really sure it's all bad though.\n\ninterdiff v1:\ndiff --git c/Documentation/pretty-formats.txt w/Documentation/pretty-formats.txt\nindex 9ee68a4cb64a..47b286b33e4e 100644\n--- c/Documentation/pretty-formats.txt\n+++ w/Documentation/pretty-formats.txt\n@@ -138,7 +138,6 @@ The placeholders are:\n - '%s': subject\n - '%f': sanitized subject line, suitable for a filename\n - '%b': body\n-- '%bT': trailers of body as interpreted by linkgit:git-interpret-trailers[1]\n - '%B': raw body (unwrapped subject and body)\n ifndef::git-rev-list[]\n - '%N': commit notes\n@@ -200,6 +199,8 @@ endif::git-rev-list[]\n   than given and there are spaces on its left, use those spaces\n - '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'\n   respectively, but padding both sides (i.e. the text is centered)\n+-%(trailers): display the trailers of the body as interpreted by\n+  linkgit:git-interpret-trailers[1]\n \n NOTE: Some placeholders may depend on other options given to the\n revision traversal engine. For example, the `%g*` reflog options will\ndiff --git c/pretty.c w/pretty.c\nindex ea8764334865..5e683830d9d6 100644\n--- c/pretty.c\n+++ w/pretty.c\n@@ -1300,16 +1300,15 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n\t\tformat_sanitized_subject(sb, msg + c->subject_off);\n\t\treturn 1;\n\tcase 'b':\t/* body */\n-\t\tswitch (placeholder[1]) {\n-\t\tcase 'T':\n-\t\t\tformat_trailers(sb, msg + c->subject_off);\n-\t\t\treturn 2;\n-\t\tdefault:\n-\t\t\tbreak;\n-\t\t}\n\t\tstrbuf_addstr(sb, msg + c->body_off);\n\t\treturn 1;\n\t}\n+\n+\tif (starts_with(placeholder, \"(trailers)\")) {\n+\t\tformat_trailers(sb, msg + c->subject_off);\n+\t\treturn strlen(\"(trailers)\");\n+\t}\n+\n\treturn 0;\t/* unknown placeholder */\n }\n \ndiff --git c/t/t4205-log-pretty-formats.sh w/t/t4205-log-pretty-formats.sh\nindex 7a35941ddcbd..21eb8c8587f2 100755\n--- c/t/t4205-log-pretty-formats.sh\n+++ w/t/t4205-log-pretty-formats.sh\n@@ -542,7 +542,7 @@ Acked-by: A U Thor <author@example.com>\n Signed-off-by: A U Thor <author@example.com>\n EOF\n \n-test_expect_success 'pretty format %bT shows trailers' '\n+test_expect_success 'pretty format %(trailers) shows trailers' '\n\techo \"Some contents\" >trailerfile &&\n\tgit add trailerfile &&\n\tgit commit -F - <<-EOF &&\n@@ -553,7 +553,7 @@ test_expect_success 'pretty format %bT shows trailers' '\n \n\t$(cat trailers)\n\tEOF\n-\tgit log --no-walk --pretty=\"%bT\" >actual &&\n+\tgit log --no-walk --pretty=\"%(trailers)\" >actual &&\n\tcat >expect <<-EOF &&\n\t$(cat trailers)\n \n\nJacob Keller (2):\n  pretty: add %bT format for displaying trailers of a commit message\n  ref-filter: add support to display trailers as part of contents\n\n Documentation/git-for-each-ref.txt |  2 ++\n Documentation/pretty-formats.txt   |  1 +\n pretty.c                           | 18 ++++++++++++++++++\n ref-filter.c                       | 22 +++++++++++++++++++++-\n t/t4205-log-pretty-formats.sh      | 26 ++++++++++++++++++++++++++\n t/t6300-for-each-ref.sh            | 26 ++++++++++++++++++++++++++\n 6 files changed, 94 insertions(+), 1 deletion(-)\n\n-- \n2.11.0.rc2.152.g4d04e67\n\n"},{"id":"306188","messageId":"20161119005815.3646-2-jacob.e.keller@intel.com","threadId":"44513","inReplyTo":"20161119005815.3646-1-jacob.e.keller@intel.com","subject":"[PATCH v2 1/2] pretty: add %(trailers) format for displaying trailers of a commit message","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2016-11-19T00:58:14Z","receivedAt":"2016-11-19T00:58:28Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nRecent patches have expanded on the trailers.c code and we have the\nbuiltin commant git-interpret-trailers which can be used to add or\nmodify trailer lines. However, there is no easy way to simply display\nthe trailers of a commit message.\n\nAdd support for %(trailers) format modifier which will use the\ntrailer_info_get() calls to read trailers in an identical way as git\ninterpret-trailers does. Use a long format option instead of a short\nname so that future work can more easily unify ref-filter and pretty\nformats.\n\nAdd documentation and tests for the same.\n\nSigned-off-by: Jacob Keller <jacob.keller@gmail.com>\n---\n Documentation/pretty-formats.txt |  2 ++\n pretty.c                         | 17 +++++++++++++++++\n t/t4205-log-pretty-formats.sh    | 26 ++++++++++++++++++++++++++\n 3 files changed, 45 insertions(+)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 3bcee2ddb124..47b286b33e4e 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -199,6 +199,8 @@ endif::git-rev-list[]\n   than given and there are spaces on its left, use those spaces\n - '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'\n   respectively, but padding both sides (i.e. the text is centered)\n+-%(trailers): display the trailers of the body as interpreted by\n+  linkgit:git-interpret-trailers[1]\n \n NOTE: Some placeholders may depend on other options given to the\n revision traversal engine. For example, the `%g*` reflog options will\ndiff --git a/pretty.c b/pretty.c\nindex 37b2c3b1f995..5e683830d9d6 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -10,6 +10,7 @@\n #include \"color.h\"\n #include \"reflog-walk.h\"\n #include \"gpg-interface.h\"\n+#include \"trailer.h\"\n \n static char *user_format;\n static struct cmt_fmt_map {\n@@ -889,6 +890,16 @@ const char *format_subject(struct strbuf *sb, const char *msg,\n \treturn msg;\n }\n \n+static void format_trailers(struct strbuf *sb, const char *msg)\n+{\n+\tstruct trailer_info info;\n+\n+\ttrailer_info_get(&info, msg);\n+\tstrbuf_add(sb, info.trailer_start,\n+\t\t   info.trailer_end - info.trailer_start);\n+\ttrailer_info_release(&info);\n+}\n+\n static void parse_commit_message(struct format_commit_context *c)\n {\n \tconst char *msg = c->message + c->message_off;\n@@ -1292,6 +1303,12 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\tstrbuf_addstr(sb, msg + c->body_off);\n \t\treturn 1;\n \t}\n+\n+\tif (starts_with(placeholder, \"(trailers)\")) {\n+\t\tformat_trailers(sb, msg + c->subject_off);\n+\t\treturn strlen(\"(trailers)\");\n+\t}\n+\n \treturn 0;\t/* unknown placeholder */\n }\n \ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex f5435fd250ba..21eb8c8587f2 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -535,4 +535,30 @@ test_expect_success 'clean log decoration' '\n \ttest_cmp expected actual1\n '\n \n+cat >trailers <<EOF\n+Signed-off-by: A U Thor <author@example.com>\n+Acked-by: A U Thor <author@example.com>\n+[ v2 updated patch description ]\n+Signed-off-by: A U Thor <author@example.com>\n+EOF\n+\n+test_expect_success 'pretty format %(trailers) shows trailers' '\n+\techo \"Some contents\" >trailerfile &&\n+\tgit add trailerfile &&\n+\tgit commit -F - <<-EOF &&\n+\ttrailers: this commit message has trailers\n+\n+\tThis commit is a test commit with trailers at the end. We parse this\n+\tmessage and display the trailers using %bT\n+\n+\t$(cat trailers)\n+\tEOF\n+\tgit log --no-walk --pretty=\"%(trailers)\" >actual &&\n+\tcat >expect <<-EOF &&\n+\t$(cat trailers)\n+\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.11.0.rc2.152.g4d04e67\n\n"},{"id":"306189","messageId":"20161119005815.3646-3-jacob.e.keller@intel.com","threadId":"44513","inReplyTo":"20161119005815.3646-1-jacob.e.keller@intel.com","subject":"[PATCH v2 2/2] ref-filter: add support to display trailers as part of contents","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2016-11-19T00:58:15Z","receivedAt":"2016-11-19T00:58:32Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nAdd %(trailers) and %(contents:trailers) to display the trailers as\ninterpreted by trailer_info_get. Update documentation and add a test for\nthe new feature.\n\nSigned-off-by: Jacob Keller <jacob.keller@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  2 ++\n ref-filter.c                       | 22 +++++++++++++++++++++-\n t/t6300-for-each-ref.sh            | 26 ++++++++++++++++++++++++++\n 3 files changed, 49 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex f57e69bc83e3..e5807eede787 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -165,6 +165,8 @@ of all lines of the commit message up to the first blank line.  The next\n line is 'contents:body', where body is all of the lines after the first\n blank line.  The optional GPG signature is `contents:signature`.  The\n first `N` lines of the message is obtained using `contents:lines=N`.\n+Additionally, the trailers as interpreted by linkgit:git-interpret-trailers[1]\n+are obtained as 'contents:trailers'.\n \n For sorting purposes, fields with numeric values sort in numeric order\n (`objectsize`, `authordate`, `committerdate`, `creatordate`, `taggerdate`).\ndiff --git a/ref-filter.c b/ref-filter.c\nindex d4c2931f3aab..b6f1bb73ed37 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -13,6 +13,7 @@\n #include \"utf8.h\"\n #include \"git-compat-util.h\"\n #include \"version.h\"\n+#include \"trailer.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n@@ -40,7 +41,7 @@ static struct used_atom {\n \t\tenum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }\n \t\t\tremote_ref;\n \t\tstruct {\n-\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n+\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n \t\t\tunsigned int nlines;\n \t\t} contents;\n \t\tenum { O_FULL, O_SHORT } objectname;\n@@ -85,6 +86,13 @@ static void subject_atom_parser(struct used_atom *atom, const char *arg)\n \tatom->u.contents.option = C_SUB;\n }\n \n+static void trailers_atom_parser(struct used_atom *atom, const char *arg)\n+{\n+\tif (arg)\n+\t\tdie(_(\"%%(trailers) does not take arguments\"));\n+\tatom->u.contents.option = C_TRAILERS;\n+}\n+\n static void contents_atom_parser(struct used_atom *atom, const char *arg)\n {\n \tif (!arg)\n@@ -95,6 +103,8 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)\n \t\tatom->u.contents.option = C_SIG;\n \telse if (!strcmp(arg, \"subject\"))\n \t\tatom->u.contents.option = C_SUB;\n+\telse if (!strcmp(arg, \"trailers\"))\n+\t\tatom->u.contents.option = C_TRAILERS;\n \telse if (skip_prefix(arg, \"lines=\", &arg)) {\n \t\tatom->u.contents.option = C_LINES;\n \t\tif (strtoul_ui(arg, 10, &atom->u.contents.nlines))\n@@ -194,6 +204,7 @@ static struct {\n \t{ \"creatordate\", FIELD_TIME },\n \t{ \"subject\", FIELD_STR, subject_atom_parser },\n \t{ \"body\", FIELD_STR, body_atom_parser },\n+\t{ \"trailers\", FIELD_STR, trailers_atom_parser },\n \t{ \"contents\", FIELD_STR, contents_atom_parser },\n \t{ \"upstream\", FIELD_STR, remote_ref_atom_parser },\n \t{ \"push\", FIELD_STR, remote_ref_atom_parser },\n@@ -785,6 +796,7 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj\n \t\t\tname++;\n \t\tif (strcmp(name, \"subject\") &&\n \t\t    strcmp(name, \"body\") &&\n+\t\t    strcmp(name, \"trailers\") &&\n \t\t    !starts_with(name, \"contents\"))\n \t\t\tcontinue;\n \t\tif (!subpos)\n@@ -808,6 +820,14 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj\n \t\t\t/*  Size is the length of the message after removing the signature */\n \t\t\tappend_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines);\n \t\t\tv->s = strbuf_detach(&s, NULL);\n+\t\t} else if (atom->u.contents.option == C_TRAILERS) {\n+\t\t\tstruct trailer_info info;\n+\n+\t\t\t/* Search for trailer info */\n+\t\t\ttrailer_info_get(&info, subpos);\n+\t\t\tv->s = xmemdupz(info.trailer_start,\n+\t\t\t\t\tinfo.trailer_end - info.trailer_start);\n+\t\t\ttrailer_info_release(&info);\n \t\t} else if (atom->u.contents.option == C_BARE)\n \t\t\tv->s = xstrdup(subpos);\n \t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 19a2823025e7..eb4bac0fe477 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -553,4 +553,30 @@ test_expect_success 'Verify sort with multiple keys' '\n \t\trefs/tags/bogo refs/tags/master > actual &&\n \ttest_cmp expected actual\n '\n+\n+cat >trailers <<EOF\n+Reviewed-by: A U Thor <author@example.com>\n+Signed-off-by: A U Thor <author@example.com>\n+EOF\n+\n+test_expect_success 'basic atom: head contents:trailers' '\n+\techo \"Some contents\" > two &&\n+\tgit add two &&\n+\tgit commit -F - <<-EOF &&\n+\ttrailers: this commit message has trailers\n+\n+\tSome message contents\n+\n+\t$(cat trailers)\n+\tEOF\n+\tgit for-each-ref --format=\"%(contents:trailers)\" refs/heads/master >actual &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\t# git for-each-ref ends with a blank line\n+\tcat >expect <<-EOF &&\n+\t$(cat trailers)\n+\n+\tEOF\n+\ttest_cmp expect actual.clean\n+'\n+\n test_done\n-- \n2.11.0.rc2.152.g4d04e67\n\n"}]}