{"thread":{"id":"53931","subject":"[PATCH 1/5] ref-filter: support different email formats","startedAt":"2020-07-27T20:43:15Z","lastAt":"2020-08-21T21:42:18Z","messageCount":51,"participants":["Hariom Verma via GitGitGadget","Junio C Hamano","Đoàn Trần Công Danh","Hariom verma"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"402160","messageId":"aeb116c5aaaa23dfefbc7a6f4ac743a6f5a3ade8.1595882588.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.git.1595882588.gitgitgadget@gmail.com","subject":"[PATCH 1/5] ref-filter: support different email formats","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-07-27T20:43:04Z","receivedAt":"2020-07-27T20:43:15Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nCurrently, ref-filter only supports printing email with arrow brackets.\n\nLet's add support for two more email options.\n- trim : print email without arrow brackets.\n- localpart : prints the part before the @ sign\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c            | 36 ++++++++++++++++++++++++++++++++----\n t/t6300-for-each-ref.sh | 16 ++++++++++++++++\n 2 files changed, 48 insertions(+), 4 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 8447cb09be..8563088eb1 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -102,6 +102,10 @@ static struct ref_to_worktree_map {\n \tstruct worktree **worktrees;\n } ref_to_worktree_map;\n \n+static struct email_option{\n+\tenum { EO_INVALID, EO_RAW, EO_TRIM, EO_LOCALPART } option;\n+} email_option;\n+\n /*\n  * An atom is a valid field atom listed below, possibly prefixed with\n  * a \"*\" to denote deref_tag().\n@@ -1040,10 +1044,26 @@ static const char *copy_email(const char *buf)\n \tconst char *eoemail;\n \tif (!email)\n \t\treturn xstrdup(\"\");\n-\teoemail = strchr(email, '>');\n+\tswitch (email_option.option) {\n+\tcase EO_RAW:\n+\t\teoemail = strchr(email, '>') + 1;\n+\t\tbreak;\n+\tcase EO_TRIM:\n+\t\temail++;\n+\t\teoemail = strchr(email, '>');\n+\t\tbreak;\n+\tcase EO_LOCALPART:\n+\t\temail++;\n+\t\teoemail = strchr(email, '@');\n+\t\tbreak;\n+\tcase EO_INVALID:\n+\tdefault:\n+\t\treturn xstrdup(\"\");\n+\t}\n+\n \tif (!eoemail)\n \t\treturn xstrdup(\"\");\n-\treturn xmemdupz(email, eoemail + 1 - email);\n+\treturn xmemdupz(email, eoemail - email);\n }\n \n static char *copy_subject(const char *buf, unsigned long len)\n@@ -1113,7 +1133,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n \t\t\tcontinue;\n \t\tif (name[wholen] != 0 &&\n \t\t    strcmp(name + wholen, \"name\") &&\n-\t\t    strcmp(name + wholen, \"email\") &&\n+\t\t    !starts_with(name + wholen, \"email\") &&\n \t\t    !starts_with(name + wholen, \"date\"))\n \t\t\tcontinue;\n \t\tif (!wholine)\n@@ -1124,8 +1144,16 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n \t\t\tv->s = copy_line(wholine);\n \t\telse if (!strcmp(name + wholen, \"name\"))\n \t\t\tv->s = copy_name(wholine);\n-\t\telse if (!strcmp(name + wholen, \"email\"))\n+\t\telse if (starts_with(name + wholen, \"email\")) {\n+\t\t\temail_option.option = EO_INVALID;\n+\t\t\tif (!strcmp(name + wholen, \"email\"))\n+\t\t\t\temail_option.option = EO_RAW;\n+\t\t\tif (!strcmp(name + wholen, \"email:trim\"))\n+\t\t\t\temail_option.option = EO_TRIM;\n+\t\t\tif (!strcmp(name + wholen, \"email:localpart\"))\n+\t\t\t\temail_option.option = EO_LOCALPART;\n \t\t\tv->s = copy_email(wholine);\n+\t\t}\n \t\telse if (starts_with(name + wholen, \"date\"))\n \t\t\tgrab_date(wholine, v, name);\n \t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex da59fadc5d..b8a2cb8439 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -106,15 +106,21 @@ test_atom head '*objecttype' ''\n test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n test_atom head authorname 'A U Thor'\n test_atom head authoremail '<author@example.com>'\n+test_atom head authoremail:trim 'author@example.com'\n+test_atom head authoremail:localpart 'author'\n test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head committername 'C O Mitter'\n test_atom head committeremail '<committer@example.com>'\n+test_atom head committeremail:trim 'committer@example.com'\n+test_atom head committeremail:localpart 'committer'\n test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n test_atom head tag ''\n test_atom head tagger ''\n test_atom head taggername ''\n test_atom head taggeremail ''\n+test_atom head taggeremail:trim ''\n+test_atom head taggeremail:localpart ''\n test_atom head taggerdate ''\n test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n@@ -151,15 +157,21 @@ test_atom tag '*objecttype' 'commit'\n test_atom tag author ''\n test_atom tag authorname ''\n test_atom tag authoremail ''\n+test_atom tag authoremail:trim ''\n+test_atom tag authoremail:localpart ''\n test_atom tag authordate ''\n test_atom tag committer ''\n test_atom tag committername ''\n test_atom tag committeremail ''\n+test_atom tag committeremail:trim ''\n+test_atom tag committeremail:localpart ''\n test_atom tag committerdate ''\n test_atom tag tag 'testtag'\n test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag taggername 'C O Mitter'\n test_atom tag taggeremail '<committer@example.com>'\n+test_atom tag taggeremail:trim 'committer@example.com'\n+test_atom tag taggeremail:localpart 'committer'\n test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n@@ -545,10 +557,14 @@ test_atom refs/tags/taggerless tag 'taggerless'\n test_atom refs/tags/taggerless tagger ''\n test_atom refs/tags/taggerless taggername ''\n test_atom refs/tags/taggerless taggeremail ''\n+test_atom refs/tags/taggerless taggeremail:trim ''\n+test_atom refs/tags/taggerless taggeremail:localpart ''\n test_atom refs/tags/taggerless taggerdate ''\n test_atom refs/tags/taggerless committer ''\n test_atom refs/tags/taggerless committername ''\n test_atom refs/tags/taggerless committeremail ''\n+test_atom refs/tags/taggerless committeremail:trim ''\n+test_atom refs/tags/taggerless committeremail:localpart ''\n test_atom refs/tags/taggerless committerdate ''\n test_atom refs/tags/taggerless subject 'Broken tag'\n \n-- \ngitgitgadget\n\n"},{"id":"402161","messageId":"49344f1b5559e7b4c63bad323a4dab5956331dde.1595882588.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.git.1595882588.gitgitgadget@gmail.com","subject":"[PATCH 2/5] ref-filter: add `short` option for 'tree' and 'parent'","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-07-27T20:43:05Z","receivedAt":"2020-07-27T20:43:15Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nSometimes while using 'parent' and 'tree' atom, user might\nwant to see abbrev hash instead of full 40 character hash.\n\n'objectname' and 'refname' already supports printing abbrev hash.\nIt might be convenient for users to have the same option for printing\n'parent' and 'tree' hash.\n\nLet's introduce `short` option to 'parent' and 'tree' atom.\n\n`tree:short` - for abbrev tree hash\n`parent:short` - for abbrev parent hash\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c            | 12 ++++++++----\n t/t6300-for-each-ref.sh |  4 ++++\n 2 files changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 8563088eb1..d5d5ff6a9d 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -983,21 +983,25 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object\n \t\t\tcontinue;\n \t\tif (deref)\n \t\t\tname++;\n-\t\tif (!strcmp(name, \"tree\")) {\n+\t\tif (!strcmp(name, \"tree\"))\n \t\t\tv->s = xstrdup(oid_to_hex(get_commit_tree_oid(commit)));\n-\t\t}\n+\t\telse if (!strcmp(name, \"tree:short\"))\n+\t\t\tv->s = xstrdup(find_unique_abbrev(get_commit_tree_oid(commit), DEFAULT_ABBREV));\n \t\telse if (!strcmp(name, \"numparent\")) {\n \t\t\tv->value = commit_list_count(commit->parents);\n \t\t\tv->s = xstrfmt(\"%lu\", (unsigned long)v->value);\n \t\t}\n-\t\telse if (!strcmp(name, \"parent\")) {\n+\t\telse if (starts_with(name, \"parent\")) {\n \t\t\tstruct commit_list *parents;\n \t\t\tstruct strbuf s = STRBUF_INIT;\n \t\t\tfor (parents = commit->parents; parents; parents = parents->next) {\n \t\t\t\tstruct commit *parent = parents->item;\n \t\t\t\tif (parents != commit->parents)\n \t\t\t\t\tstrbuf_addch(&s, ' ');\n-\t\t\t\tstrbuf_addstr(&s, oid_to_hex(&parent->object.oid));\n+\t\t\t\tif (!strcmp(name, \"parent\"))\n+\t\t\t\t\tstrbuf_addstr(&s, oid_to_hex(&parent->object.oid));\n+\t\t\t\telse if (!strcmp(name, \"parent:short\"))\n+\t\t\t\t\tstrbuf_add_unique_abbrev(&s, &parent->object.oid, DEFAULT_ABBREV);\n \t\t\t}\n \t\t\tv->s = strbuf_detach(&s, NULL);\n \t\t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex b8a2cb8439..533827d297 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -97,7 +97,9 @@ test_atom head objectname:short $(git rev-parse --short refs/heads/master)\n test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom head tree $(git rev-parse refs/heads/master^{tree})\n+test_atom head tree:short $(git rev-parse --short refs/heads/master^{tree})\n test_atom head parent ''\n+test_atom head parent:short ''\n test_atom head numparent 0\n test_atom head object ''\n test_atom head type ''\n@@ -148,7 +150,9 @@ test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom tag tree ''\n+test_atom tag tree:short ''\n test_atom tag parent ''\n+test_atom tag parent:short ''\n test_atom tag numparent ''\n test_atom tag object $(git rev-parse refs/tags/testtag^0)\n test_atom tag type 'commit'\n-- \ngitgitgadget\n\n"},{"id":"402165","messageId":"69b9d221c01144b72f731a1a8901789929b4e6f9.1595882588.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.git.1595882588.gitgitgadget@gmail.com","subject":"[PATCH 3/5] pretty: refactor `format_sanitized_subject()`","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-07-27T20:43:06Z","receivedAt":"2020-07-27T20:43:16Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nThis commit refactors `format_sanitized_subject()` in the\nhope to use same logic in ref-filter.c\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n pretty.c | 24 +++++++++++++++---------\n 1 file changed, 15 insertions(+), 9 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 2a3d46bf42..8d08e8278a 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -839,24 +839,29 @@ static int istitlechar(char c)\n \t\t(c >= '0' && c <= '9') || c == '.' || c == '_';\n }\n \n-static void format_sanitized_subject(struct strbuf *sb, const char *msg)\n+static void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n {\n+\tchar *r = xmemdupz(msg, len);\n \tsize_t trimlen;\n \tsize_t start_len = sb->len;\n \tint space = 2;\n+\tint i;\n \n-\tfor (; *msg && *msg != '\\n'; msg++) {\n-\t\tif (istitlechar(*msg)) {\n+\tfor (i = 0; i < len; i++) {\n+\t\tif (r[i] == '\\n')\n+\t\t\tr[i] = ' ';\n+\t\tif (istitlechar(r[i])) {\n \t\t\tif (space == 1)\n \t\t\t\tstrbuf_addch(sb, '-');\n \t\t\tspace = 0;\n-\t\t\tstrbuf_addch(sb, *msg);\n-\t\t\tif (*msg == '.')\n-\t\t\t\twhile (*(msg+1) == '.')\n-\t\t\t\t\tmsg++;\n+\t\t\tstrbuf_addch(sb, r[i]);\n+\t\t\tif (r[i] == '.')\n+\t\t\t\twhile (r[i+1] == '.')\n+\t\t\t\t\ti++;\n \t\t} else\n \t\t\tspace |= 1;\n \t}\n+\tfree(r);\n \n \t/* trim any trailing '.' or '-' characters */\n \ttrimlen = 0;\n@@ -1155,7 +1160,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \tconst struct commit *commit = c->commit;\n \tconst char *msg = c->message;\n \tstruct commit_list *p;\n-\tconst char *arg;\n+\tconst char *arg, *eol;\n \tsize_t res;\n \tchar **slot;\n \n@@ -1405,7 +1410,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\tformat_subject(sb, msg + c->subject_off, \" \");\n \t\treturn 1;\n \tcase 'f':\t/* sanitized subject */\n-\t\tformat_sanitized_subject(sb, msg + c->subject_off);\n+\t\teol = strchrnul(msg + c->subject_off, '\\n');\n+\t\tformat_sanitized_subject(sb, msg + c->subject_off, eol - (msg + c->subject_off));\n \t\treturn 1;\n \tcase 'b':\t/* body */\n \t\tstrbuf_addstr(sb, msg + c->body_off);\n-- \ngitgitgadget\n\n"},{"id":"402163","messageId":"9dc619b44888024201d9ae1f83a4b34c81fa693f.1595882588.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.git.1595882588.gitgitgadget@gmail.com","subject":"[PATCH 4/5] format-support: move `format_sanitized_subject()` from pretty","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-07-27T20:43:07Z","receivedAt":"2020-07-27T20:43:18Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nIn hope of some new features in `subject` atom, move funtion\n`format_sanitized_subject()` and all the function it uses\nto new file format-support.[c/h].\n\nConsider this new file as a common interface between functions that\npretty.c and ref-filter.c shares.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Makefile         |  1 +\n format-support.c | 43 +++++++++++++++++++++++++++++++++++++++++++\n format-support.h |  6 ++++++\n pretty.c         | 40 +---------------------------------------\n 4 files changed, 51 insertions(+), 39 deletions(-)\n create mode 100644 format-support.c\n create mode 100644 format-support.h\n\ndiff --git a/Makefile b/Makefile\nindex 372139f1f2..4dfc384b49 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -882,6 +882,7 @@ LIB_OBJS += exec-cmd.o\n LIB_OBJS += fetch-negotiator.o\n LIB_OBJS += fetch-pack.o\n LIB_OBJS += fmt-merge-msg.o\n+LIB_OBJS += format-support.o\n LIB_OBJS += fsck.o\n LIB_OBJS += fsmonitor.o\n LIB_OBJS += gettext.o\ndiff --git a/format-support.c b/format-support.c\nnew file mode 100644\nindex 0000000000..d693aa1744\n--- /dev/null\n+++ b/format-support.c\n@@ -0,0 +1,43 @@\n+#include \"diff.h\"\n+#include \"log-tree.h\"\n+#include \"color.h\"\n+#include \"format-support.h\"\n+\n+static int istitlechar(char c)\n+{\n+\treturn (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||\n+\t\t(c >= '0' && c <= '9') || c == '.' || c == '_';\n+}\n+\n+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n+{\n+\tchar *r = xmemdupz(msg, len);\n+\tsize_t trimlen;\n+\tsize_t start_len = sb->len;\n+\tint space = 2;\n+\tint i;\n+\n+\tfor (i = 0; i < len; i++) {\n+\t\tif (r[i] == '\\n')\n+\t\t\tr[i] = ' ';\n+\t\tif (istitlechar(r[i])) {\n+\t\t\tif (space == 1)\n+\t\t\t\tstrbuf_addch(sb, '-');\n+\t\t\tspace = 0;\n+\t\t\tstrbuf_addch(sb, r[i]);\n+\t\t\tif (r[i] == '.')\n+\t\t\t\twhile (r[i+1] == '.')\n+\t\t\t\t\ti++;\n+\t\t} else\n+\t\t\tspace |= 1;\n+\t}\n+\tfree(r);\n+\n+\t/* trim any trailing '.' or '-' characters */\n+\ttrimlen = 0;\n+\twhile (sb->len - trimlen > start_len &&\n+\t\t(sb->buf[sb->len - 1 - trimlen] == '.'\n+\t\t|| sb->buf[sb->len - 1 - trimlen] == '-'))\n+\t\ttrimlen++;\n+\tstrbuf_remove(sb, sb->len - trimlen, trimlen);\n+}\ndiff --git a/format-support.h b/format-support.h\nnew file mode 100644\nindex 0000000000..c344ccbc33\n--- /dev/null\n+++ b/format-support.h\n@@ -0,0 +1,6 @@\n+#ifndef FORMAT_SUPPORT_H\n+#define FORMAT_SUPPORT_H\n+\n+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len);\n+\n+#endif /* FORMAT_SUPPORT_H */\ndiff --git a/pretty.c b/pretty.c\nindex 8d08e8278a..2de01b7115 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -12,6 +12,7 @@\n #include \"reflog-walk.h\"\n #include \"gpg-interface.h\"\n #include \"trailer.h\"\n+#include \"format-support.h\"\n \n static char *user_format;\n static struct cmt_fmt_map {\n@@ -833,45 +834,6 @@ static void parse_commit_header(struct format_commit_context *context)\n \tcontext->commit_header_parsed = 1;\n }\n \n-static int istitlechar(char c)\n-{\n-\treturn (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||\n-\t\t(c >= '0' && c <= '9') || c == '.' || c == '_';\n-}\n-\n-static void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n-{\n-\tchar *r = xmemdupz(msg, len);\n-\tsize_t trimlen;\n-\tsize_t start_len = sb->len;\n-\tint space = 2;\n-\tint i;\n-\n-\tfor (i = 0; i < len; i++) {\n-\t\tif (r[i] == '\\n')\n-\t\t\tr[i] = ' ';\n-\t\tif (istitlechar(r[i])) {\n-\t\t\tif (space == 1)\n-\t\t\t\tstrbuf_addch(sb, '-');\n-\t\t\tspace = 0;\n-\t\t\tstrbuf_addch(sb, r[i]);\n-\t\t\tif (r[i] == '.')\n-\t\t\t\twhile (r[i+1] == '.')\n-\t\t\t\t\ti++;\n-\t\t} else\n-\t\t\tspace |= 1;\n-\t}\n-\tfree(r);\n-\n-\t/* trim any trailing '.' or '-' characters */\n-\ttrimlen = 0;\n-\twhile (sb->len - trimlen > start_len &&\n-\t\t(sb->buf[sb->len - 1 - trimlen] == '.'\n-\t\t|| sb->buf[sb->len - 1 - trimlen] == '-'))\n-\t\ttrimlen++;\n-\tstrbuf_remove(sb, sb->len - trimlen, trimlen);\n-}\n-\n const char *format_subject(struct strbuf *sb, const char *msg,\n \t\t\t   const char *line_separator)\n {\n-- \ngitgitgadget\n\n"},{"id":"402162","messageId":"pull.684.git.1595882588.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":null,"subject":"[PATCH 0/5] [GSoC] Improvements to ref-filter","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-07-27T20:43:03Z","receivedAt":"2020-07-27T20:43:19Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"This is the first patch series that introduces some improvements and\nfeatures to file ref-filter.{c,h}. These changes are useful to ref-filter,\nbut in near future also will allow us to use ref-filter's logic in pretty.c\n\nI plan to add more to format-support.{c,h} in the upcoming patch series.\nThat will lead to more improved and feature-rich ref-filter.c\n\nHariom Verma (5):\n  ref-filter: support different email formats\n  ref-filter: add `short` option for 'tree' and 'parent'\n  pretty: refactor `format_sanitized_subject()`\n  format-support: move `format_sanitized_subject()` from pretty\n  ref-filter: add `sanitize` option for 'subject' atom\n\n Makefile                |  1 +\n format-support.c        | 43 +++++++++++++++++++++++++\n format-support.h        |  6 ++++\n pretty.c                | 40 +++---------------------\n ref-filter.c            | 69 ++++++++++++++++++++++++++++++++++-------\n t/t6300-for-each-ref.sh | 27 ++++++++++++++++\n 6 files changed, 138 insertions(+), 48 deletions(-)\n create mode 100644 format-support.c\n create mode 100644 format-support.h\n\n\nbase-commit: 5c06d60fc55d2213c089f63c282468080f812686\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-684%2Fharry-hov%2Fonly-rf6-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-684/harry-hov/only-rf6-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/684\n-- \ngitgitgadget\n"},{"id":"402164","messageId":"7b9103cbadfc111755d2db61239fcac4f4d14a33.1595882588.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.git.1595882588.gitgitgadget@gmail.com","subject":"[PATCH 5/5] ref-filter: add `sanitize` option for 'subject' atom","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-07-27T20:43:08Z","receivedAt":"2020-07-27T20:43:19Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nCurrently, subject does not take any arguments. This commit introduce\n`sanitize` formatting option to 'subject' atom.\n\n`subject:sanitize` - print sanitized subject line, suitable for a filename.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c            | 21 +++++++++++++++++----\n t/t6300-for-each-ref.sh |  7 +++++++\n 2 files changed, 24 insertions(+), 4 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex d5d5ff6a9d..5f8fc65b68 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -23,6 +23,7 @@\n #include \"worktree.h\"\n #include \"hashmap.h\"\n #include \"argv-array.h\"\n+#include \"format-support.h\"\n \n static struct ref_msg {\n \tconst char *gone;\n@@ -131,7 +132,7 @@ static struct used_atom {\n \t\t\tunsigned int nobracket : 1, push : 1, push_remote : 1;\n \t\t} remote_ref;\n \t\tstruct {\n-\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n+\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_SUB_SANITIZE, C_TRAILERS } option;\n \t\t\tstruct process_trailer_options trailer_opts;\n \t\t\tunsigned int nlines;\n \t\t} contents;\n@@ -301,8 +302,14 @@ static int body_atom_parser(const struct ref_format *format, struct used_atom *a\n static int subject_atom_parser(const struct ref_format *format, struct used_atom *atom,\n \t\t\t       const char *arg, struct strbuf *err)\n {\n-\tif (arg)\n-\t\treturn strbuf_addf_ret(err, -1, _(\"%%(subject) does not take arguments\"));\n+\tif (arg) {\n+\t\tif (!strcmp(arg, \"sanitize\")) {\n+\t\t\tatom->u.contents.option = C_SUB_SANITIZE;\n+\t\t\treturn 0;\n+\t\t} else {\n+\t\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized %%(subject) argument: %s\"), arg);\n+\t\t}\n+\t}\n \tatom->u.contents.option = C_SUB;\n \treturn 0;\n }\n@@ -1266,11 +1273,13 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n \t\tstruct used_atom *atom = &used_atom[i];\n \t\tconst char *name = atom->name;\n \t\tstruct atom_value *v = &val[i];\n+\n \t\tif (!!deref != (*name == '*'))\n \t\t\tcontinue;\n \t\tif (deref)\n \t\t\tname++;\n \t\tif (strcmp(name, \"subject\") &&\n+\t\t    strcmp(name, \"subject:sanitize\") &&\n \t\t    strcmp(name, \"body\") &&\n \t\t    !starts_with(name, \"trailers\") &&\n \t\t    !starts_with(name, \"contents\"))\n@@ -1283,7 +1292,11 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n \n \t\tif (atom->u.contents.option == C_SUB)\n \t\t\tv->s = copy_subject(subpos, sublen);\n-\t\telse if (atom->u.contents.option == C_BODY_DEP)\n+\t\telse if (atom->u.contents.option == C_SUB_SANITIZE) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tformat_sanitized_subject(&sb, subpos, sublen);\n+\t\t\tv->s = strbuf_detach(&sb, NULL);\n+\t\t} else if (atom->u.contents.option == C_BODY_DEP)\n \t\t\tv->s = xmemdupz(bodypos, bodylen);\n \t\telse if (atom->u.contents.option == C_BODY)\n \t\t\tv->s = xmemdupz(bodypos, nonsiglen);\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 533827d297..e2dd410356 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -127,6 +127,7 @@ test_atom head taggerdate ''\n test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n test_atom head subject 'Initial'\n+test_atom head subject:sanitize 'Initial'\n test_atom head contents:subject 'Initial'\n test_atom head body ''\n test_atom head contents:body ''\n@@ -180,6 +181,7 @@ test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag subject 'Tagging at 1151968727'\n+test_atom tag subject:sanitize 'Tagging-at-1151968727'\n test_atom tag contents:subject 'Tagging at 1151968727'\n test_atom tag body ''\n test_atom tag contents:body ''\n@@ -592,6 +594,7 @@ test_expect_success 'create tag with subject and body content' '\n \tgit tag -F msg subject-body\n '\n test_atom refs/tags/subject-body subject 'the subject line'\n+test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n test_atom refs/tags/subject-body body 'first body line\n second body line\n '\n@@ -612,6 +615,7 @@ test_expect_success 'create tag with multiline subject' '\n \tgit tag -F msg multiline\n '\n test_atom refs/tags/multiline subject 'first subject line second subject line'\n+test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n test_atom refs/tags/multiline body 'first body line\n second body line\n@@ -644,6 +648,7 @@ sig='-----BEGIN PGP SIGNATURE-----\n \n PREREQ=GPG\n test_atom refs/tags/signed-empty subject ''\n+test_atom refs/tags/signed-empty subject:sanitize ''\n test_atom refs/tags/signed-empty contents:subject ''\n test_atom refs/tags/signed-empty body \"$sig\"\n test_atom refs/tags/signed-empty contents:body ''\n@@ -651,6 +656,7 @@ test_atom refs/tags/signed-empty contents:signature \"$sig\"\n test_atom refs/tags/signed-empty contents \"$sig\"\n \n test_atom refs/tags/signed-short subject 'subject line'\n+test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n test_atom refs/tags/signed-short contents:subject 'subject line'\n test_atom refs/tags/signed-short body \"$sig\"\n test_atom refs/tags/signed-short contents:body ''\n@@ -659,6 +665,7 @@ test_atom refs/tags/signed-short contents \"subject line\n $sig\"\n \n test_atom refs/tags/signed-long subject 'subject line'\n+test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n test_atom refs/tags/signed-long contents:subject 'subject line'\n test_atom refs/tags/signed-long body \"body contents\n $sig\"\n-- \ngitgitgadget\n"},{"id":"402169","messageId":"xmqqeeowfu75.fsf@gitster.c.googlers.com","threadId":"53931","inReplyTo":"aeb116c5aaaa23dfefbc7a6f4ac743a6f5a3ade8.1595882588.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/5] ref-filter: support different email formats","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-07-27T22:51:10Z","receivedAt":"2020-07-27T22:51:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Hariom Verma via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Hariom Verma <hariom18599@gmail.com>\n>\n> Currently, ref-filter only supports printing email with arrow brackets.\n\nThis is the first time I heard the term \"arrow bracket\".  Aren't\nthey more commonly called angle brackets?\n\n> Let's add support for two more email options.\n> - trim : print email without arrow brackets.\n\nWhy would this be useful?\n\n> - localpart : prints the part before the @ sign\n\nMeaning I'd get \"<gitster\" for me?\n\nBuilding small pieces of new feature in each patch is good, and\nadding tests to each step is also good.  Why not do the same for\ndocs?\n\n> +static struct email_option{\n\nMissing SP.\n\n> +\tenum { EO_INVALID, EO_RAW, EO_TRIM, EO_LOCALPART } option;\n> +} email_option;\n> +\n> @@ -1040,10 +1044,26 @@ static const char *copy_email(const char *buf)\n>  \tconst char *eoemail;\n>  \tif (!email)\n>  \t\treturn xstrdup(\"\");\n> -\teoemail = strchr(email, '>');\n\nThe original code prepares to see NULL from this strchr(); that is\nwhy it checks eoemail for NULL and returns an empty string---the\ndata is broken (i.e. not an address in angle brackets), which this\ncode cannot do anything about---in the later part of the code.\n\n> +\tswitch (email_option.option) {\n> +\tcase EO_RAW:\n> +\t\teoemail = strchr(email, '>') + 1;\n\nAnd this breaks the carefully laid out error handling by the\noriginal code.  Adding 1 to NULL is quite undefined.\n\n> +\t\tbreak;\n> +\tcase EO_TRIM:\n> +\t\temail++;\n> +\t\teoemail = strchr(email, '>');\n> +\t\tbreak;\n> +\tcase EO_LOCALPART:\n> +\t\temail++;\n> +\t\teoemail = strchr(email, '@');\n\nThe undocumented design here is that you want to return \"hariom\" for\n\"<hariom@gmail.com>\", i.e. out of the \"trimmed\" e-mail, the part\nbefore the at-sign is returned.\n\nIf the data were \"<hariom>\", you'd still want to return \"hariom\" no?\nBut because you do not check for NULL, you end up returning an empty\nstring.\n\nI think you want to cut at the first '@' or '>', whichever comes\nfirst.\n\n\n> +\t\tbreak;\n> +\tcase EO_INVALID:\n> +\tdefault:\n\nInvalid and unhandled ones are silently ignored and not treated as\nan error?  I would have thought that at least the \"default\" one\nwould be a BUG(), as you covered all the possible values for the\nenum with case arms.  I wouldn't be surprised if seeing EO_INVALID\nis also a BUG(), i.e. the control flow that led to the caller to\ncall this function with EO_INVALID in email_option.option is likely\nto be broken.  It's not like you return \"\" to protect yourself when\nfed a bad data from objects---a bad value in .option can only come\nhere if the parser you wrote for \"--format=<string>\" produced a\nwrong result.\n\n> +\t\treturn xstrdup(\"\");\n> +\t}\n> +\n>  \tif (!eoemail)\n>  \t\treturn xstrdup(\"\");\n> -\treturn xmemdupz(email, eoemail + 1 - email);\n> +\treturn xmemdupz(email, eoemail - email);\n>  }\n>  \n>  static char *copy_subject(const char *buf, unsigned long len)\n> @@ -1113,7 +1133,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n>  \t\t\tcontinue;\n>  \t\tif (name[wholen] != 0 &&\n>  \t\t    strcmp(name + wholen, \"name\") &&\n> -\t\t    strcmp(name + wholen, \"email\") &&\n> +\t\t    !starts_with(name + wholen, \"email\") &&\n>  \t\t    !starts_with(name + wholen, \"date\"))\n>  \t\t\tcontinue;\n>  \t\tif (!wholine)\n> @@ -1124,8 +1144,16 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n>  \t\t\tv->s = copy_line(wholine);\n>  \t\telse if (!strcmp(name + wholen, \"name\"))\n>  \t\t\tv->s = copy_name(wholine);\n> -\t\telse if (!strcmp(name + wholen, \"email\"))\n> +\t\telse if (starts_with(name + wholen, \"email\")) {\n> +\t\t\temail_option.option = EO_INVALID;\n> +\t\t\tif (!strcmp(name + wholen, \"email\"))\n> +\t\t\t\temail_option.option = EO_RAW;\n> +\t\t\tif (!strcmp(name + wholen, \"email:trim\"))\n> +\t\t\t\temail_option.option = EO_TRIM;\n> +\t\t\tif (!strcmp(name + wholen, \"email:localpart\"))\n> +\t\t\t\temail_option.option = EO_LOCALPART;\n\nThe ref-filter formatting language already knows many \"colon plus\nmodifier\" suffix like \"refname:short\" and \"contents:body\", but I do\nnot think we have ugly repetition like the above code to parse them.\nPerhaps the addition for \"email:<whatever>\" can benefit from\nstudying and mimicking existing practices a bit more?\n\n"},{"id":"402174","messageId":"xmqq1rkwfss2.fsf@gitster.c.googlers.com","threadId":"53931","inReplyTo":"49344f1b5559e7b4c63bad323a4dab5956331dde.1595882588.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/5] ref-filter: add `short` option for 'tree' and 'parent'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-07-27T23:21:49Z","receivedAt":"2020-07-27T23:21:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Hariom Verma via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> -\t\tif (!strcmp(name, \"tree\")) {\n> +\t\tif (!strcmp(name, \"tree\"))\n>  \t\t\tv->s = xstrdup(oid_to_hex(get_commit_tree_oid(commit)));\n> -\t\t}\n> +\t\telse if (!strcmp(name, \"tree:short\"))\n> +\t\t\tv->s = xstrdup(find_unique_abbrev(get_commit_tree_oid(commit), DEFAULT_ABBREV));\n\nAgain, isn't this going in totally unacceptable direction?  \n\nBy the time grab_foo() helper functions are reached, the requested\nformat should have been parsed to atom->u.foo.option and the only\nthing grab_foo() helper functions should look at are the option.\n\nPerhaps studying how \"objectname\" and its \":\"-modified forms are\nhandled before writing this series would be beneficial.\n\n - objectname_atom_parser() is called when the parser for --format\n   notices \"objectname:modifier\"; it is responsible for setting up\n   atom->u.objectname.option.  Note that this is done only once at\n   the very begining of processing\n\n - grab_objectname() is called for each and every object ref-filter\n   iterates over and \"objectname\" and/or its modified form\n   (e.g. \"objectname:short\") is requested.  Since the modifier is\n   already parsed, it can do a simple switch on the value in\n   .option.\n\nI do not know if patches [3-5/5] follow the pattern used in [1-2/5],\nbut if they do, then they all need to be fixed, I think.\n\nThanks.\n\n\n"},{"id":"402193","messageId":"20200728135803.GD24134@danh.dev","threadId":"53931","inReplyTo":"aeb116c5aaaa23dfefbc7a6f4ac743a6f5a3ade8.1595882588.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/5] ref-filter: support different email formats","fromName":"Đoàn Trần Công Danh","fromEmail":"congdanhqx@gmail.com","sentAt":"2020-07-28T13:58:03Z","receivedAt":"2020-07-28T13:58:08Z","isPatch":true,"sender":{"key":"congdanhqx@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42673067?v=4"},"body":"[this is a resent, my previous mail couldn't reach the archive]\n\nOn 2020-07-27 20:43:04+0000, Hariom Verma via GitGitGadget <gitgitgadget@gmail.com> wrote:\n> From: Hariom Verma <hariom18599@gmail.com>\n> \n> Currently, ref-filter only supports printing email with arrow brackets.\n> \n> Let's add support for two more email options.\n> - trim : print email without arrow brackets.\n> - localpart : prints the part before the @ sign\n> \n> Mentored-by: Christian Couder <chriscool@tuxfamily.org>\n> Mentored-by: Heba Waly <heba.waly@gmail.com>\n> Signed-off-by: Hariom Verma <hariom18599@gmail.com>\n> ---\n>  ref-filter.c            | 36 ++++++++++++++++++++++++++++++++----\n>  t/t6300-for-each-ref.sh | 16 ++++++++++++++++\n>  2 files changed, 48 insertions(+), 4 deletions(-)\n> \n> diff --git a/ref-filter.c b/ref-filter.c\n> index 8447cb09be..8563088eb1 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -102,6 +102,10 @@ static struct ref_to_worktree_map {\n>  \tstruct worktree **worktrees;\n>  } ref_to_worktree_map;\n>  \n> +static struct email_option{\n> +\tenum { EO_INVALID, EO_RAW, EO_TRIM, EO_LOCALPART } option;\n> +} email_option;\n> +\n>  /*\n>   * An atom is a valid field atom listed below, possibly prefixed with\n>   * a \"*\" to denote deref_tag().\n> @@ -1040,10 +1044,26 @@ static const char *copy_email(const char *buf)\n>  \tconst char *eoemail;\n>  \tif (!email)\n>  \t\treturn xstrdup(\"\");\n> -\teoemail = strchr(email, '>');\n> +\tswitch (email_option.option) {\n> +\tcase EO_RAW:\n> +\t\teoemail = strchr(email, '>') + 1;\n> +\t\tbreak;\n> +\tcase EO_TRIM:\n> +\t\temail++;\n> +\t\teoemail = strchr(email, '>');\n> +\t\tbreak;\n> +\tcase EO_LOCALPART:\n> +\t\temail++;\n> +\t\teoemail = strchr(email, '@');\n> +\t\tbreak;\n\n\nThis is not correct.\nRFC-822 allows @ in local part,\nalbeit, that localpart must be quoted:\n\n        addr-spec       =       local-part \"@\" domain\n        local-part      =       dot-atom / quoted-string / obs-local-part\n        quoted-string   =       [CFWS]\n                                DQUOTE *([FWS] qcontent) [FWS] DQUOTE\n                                [CFWS]\n        qcontent        =       qtext / quoted-pair\n        qtext           =       NO-WS-CTL /     ; Non white space\n        qtext           =       NO-WS-CTL /     ; Non white space controls\n                                %d33 /          ; The rest of the US-ASCII\n                                %d35-91 /       ;  characters not including \"\\\"\n                                %d93-126        ;  or the quote character\n        quoted-pair     =       (\"\\\" text) / obs-qp\n\nIOW, those below email addresses are valid email address,\nand the local part is `quoted@local'\n\n        \"quoted@local\"@example.com\n        quoted\\@local@example.com\n\nAnyway, it seems like current Git strips first `\"'\nfrom `\"quoted@local\"@example.com'\n\n\n-- \nDanh\n"},{"id":"402218","messageId":"xmqqime7eggb.fsf@gitster.c.googlers.com","threadId":"53931","inReplyTo":"20200728135803.GD24134@danh.dev","subject":"Re: [PATCH 1/5] ref-filter: support different email formats","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-07-28T16:45:40Z","receivedAt":"2020-07-28T16:45:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Đoàn Trần Công Danh  <congdanhqx@gmail.com> writes:\n\n> This is not correct.\n> RFC-822 allows @ in local part,\n> albeit, that localpart must be quoted:\n\nWe do care about truly local e-mail addresses, without '@' anywhere\ninside <braket>, simply because they are common in the result of SCM\nconversion from CVS/SVN.\n\nBut I do not think we are pedantic/academic enough to have cared\nabout any local part that is unusual enough to require quoting; we\ninstead relied on the fact that we live in real world with practical\npeople who would avoid such an address ;-).\n"},{"id":"402242","messageId":"CA+CkUQ9nqW8=GuvNapsySf=EXm8c02qKV2xMrwvRY-Kd9Yy9mA@mail.gmail.com","threadId":"53931","inReplyTo":"xmqqeeowfu75.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 1/5] ref-filter: support different email formats","fromName":"Hariom verma","fromEmail":"hariom18599@gmail.com","sentAt":"2020-07-28T20:31:53Z","receivedAt":"2020-07-28T20:32:07Z","isPatch":true,"sender":{"key":"hariom18599@gmail.com","avatar":"https://avatars.githubusercontent.com/u/37576387?v=4"},"body":"Hi,\n\nOn Tue, Jul 28, 2020 at 4:21 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Hariom Verma via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> > From: Hariom Verma <hariom18599@gmail.com>\n> >\n> > Currently, ref-filter only supports printing email with arrow brackets.\n>\n> This is the first time I heard the term \"arrow bracket\".  Aren't\n> they more commonly called angle brackets?\n\nYeah. Sorry about that.\n\n> > Let's add support for two more email options.\n> > - trim : print email without arrow brackets.\n>\n> Why would this be useful?\n\nIt might be useful for using the ref-filter's logic in pretty.c\n(especially for `--pretty` formats like `%an` and `%cn`)\n\n> > - localpart : prints the part before the @ sign\n>\n> Meaning I'd get \"<gitster\" for me?\n\nNo, you'll get \"gitster\".\n\n> Building small pieces of new feature in each patch is good, and\n> adding tests to each step is also good.  Why not do the same for\n> docs?\n\nYeah, I agree with you. I should have focused on documentation too.\n\n> > +static struct email_option{\n>\n> Missing SP.\n\nI'll fix it.\n\n> > +     enum { EO_INVALID, EO_RAW, EO_TRIM, EO_LOCALPART } option;\n> > +} email_option;\n> > +\n> > @@ -1040,10 +1044,26 @@ static const char *copy_email(const char *buf)\n> >       const char *eoemail;\n> >       if (!email)\n> >               return xstrdup(\"\");\n> > -     eoemail = strchr(email, '>');\n>\n> The original code prepares to see NULL from this strchr(); that is\n> why it checks eoemail for NULL and returns an empty string---the\n> data is broken (i.e. not an address in angle brackets), which this\n> code cannot do anything about---in the later part of the code.\n\nI think this commit still takes care of NULL.\nAfter the switch-case statements, code consists of:\n```\nif (!eoemail)\n    return xstrdup(\"\");\n```\nWhich checks eoemail for NULL. And will return empty string if address\nis not in angle brackets.\nSame applies for local-part too. If '@' is not present in email\naddress, it will still return empty string.\n\n> > +     switch (email_option.option) {\n> > +     case EO_RAW:\n> > +             eoemail = strchr(email, '>') + 1;\n>\n> And this breaks the carefully laid out error handling by the\n> original code.  Adding 1 to NULL is quite undefined.\n\nYeah. I'll take care of it in the next version.\n\n> > +             break;\n> > +     case EO_TRIM:\n> > +             email++;\n> > +             eoemail = strchr(email, '>');\n> > +             break;\n> > +     case EO_LOCALPART:\n> > +             email++;\n> > +             eoemail = strchr(email, '@');\n>\n> The undocumented design here is that you want to return \"hariom\" for\n> \"<hariom@gmail.com>\", i.e. out of the \"trimmed\" e-mail, the part\n> before the at-sign is returned.\n>\n> If the data were \"<hariom>\", you'd still want to return \"hariom\" no?\n> But because you do not check for NULL, you end up returning an empty\n> string.\n\nI never heard of email address without '@' symbol. Thats why I\nreturned empty string.\n\nWill fix that too.\n\n> I think you want to cut at the first '@' or '>', whichever comes\n> first.\n\nIf email data can be without '@' symbol, then I guess \"yes\".\n\n> > +             break;\n> > +     case EO_INVALID:\n> > +     default:\n>\n> Invalid and unhandled ones are silently ignored and not treated as\n> an error?  I would have thought that at least the \"default\" one\n> would be a BUG(), as you covered all the possible values for the\n> enum with case arms.  I wouldn't be surprised if seeing EO_INVALID\n> is also a BUG(), i.e. the control flow that led to the caller to\n> call this function with EO_INVALID in email_option.option is likely\n> to be broken.  It's not like you return \"\" to protect yourself when\n> fed a bad data from objects---a bad value in .option can only come\n> here if the parser you wrote for \"--format=<string>\" produced a\n> wrong result.\n\nChristian <chriscool@tuxfamily.org> also suggested me the same. Will\nfix this too.\n\nBTW, on master \"{author,committer,tagger}email:<xyz>\" does not print any error.\n\n> > +             return xstrdup(\"\");\n> > +     }\n> > +\n> >       if (!eoemail)\n> >               return xstrdup(\"\");\n> > -     return xmemdupz(email, eoemail + 1 - email);\n> > +     return xmemdupz(email, eoemail - email);\n> >  }\n> >\n> >  static char *copy_subject(const char *buf, unsigned long len)\n> > @@ -1113,7 +1133,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n> >                       continue;\n> >               if (name[wholen] != 0 &&\n> >                   strcmp(name + wholen, \"name\") &&\n> > -                 strcmp(name + wholen, \"email\") &&\n> > +                 !starts_with(name + wholen, \"email\") &&\n> >                   !starts_with(name + wholen, \"date\"))\n> >                       continue;\n> >               if (!wholine)\n> > @@ -1124,8 +1144,16 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n> >                       v->s = copy_line(wholine);\n> >               else if (!strcmp(name + wholen, \"name\"))\n> >                       v->s = copy_name(wholine);\n> > -             else if (!strcmp(name + wholen, \"email\"))\n> > +             else if (starts_with(name + wholen, \"email\")) {\n> > +                     email_option.option = EO_INVALID;\n> > +                     if (!strcmp(name + wholen, \"email\"))\n> > +                             email_option.option = EO_RAW;\n> > +                     if (!strcmp(name + wholen, \"email:trim\"))\n> > +                             email_option.option = EO_TRIM;\n> > +                     if (!strcmp(name + wholen, \"email:localpart\"))\n> > +                             email_option.option = EO_LOCALPART;\n>\n> The ref-filter formatting language already knows many \"colon plus\n> modifier\" suffix like \"refname:short\" and \"contents:body\", but I do\n> not think we have ugly repetition like the above code to parse them.\n> Perhaps the addition for \"email:<whatever>\" can benefit from\n> studying and mimicking existing practices a bit more?\n>\n\nFor \"email:<whatever>\",\neven If I parse that <whatever>. I still make comparison something like:\n```\nif (!modifier)\n    email_option.option = EO_RAW;\nelse if (!strcmp(modifier, \"trim\"))\n    email_option.option = EO_TRIM;\nelse if (!strcmp(arg, \"localpart\"))\n    email_option.option = EO_LOCALPART;\n```\n\nThanks,\nHariom\n"},{"id":"402243","messageId":"xmqqzh7jcqv7.fsf@gitster.c.googlers.com","threadId":"53931","inReplyTo":"CA+CkUQ9nqW8=GuvNapsySf=EXm8c02qKV2xMrwvRY-Kd9Yy9mA@mail.gmail.com","subject":"Re: [PATCH 1/5] ref-filter: support different email formats","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-07-28T20:43:40Z","receivedAt":"2020-07-28T20:43:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Hariom verma <hariom18599@gmail.com> writes:\n\n>> The ref-filter formatting language already knows many \"colon plus\n>> modifier\" suffix like \"refname:short\" and \"contents:body\", but I do\n>> not think we have ugly repetition like the above code to parse them.\n>> Perhaps the addition for \"email:<whatever>\" can benefit from\n>> studying and mimicking existing practices a bit more?\n>>\n>\n> For \"email:<whatever>\",\n> even If I parse that <whatever>. I still make comparison something like:\n> ```\n> if (!modifier)\n>     email_option.option = EO_RAW;\n> else if (!strcmp(modifier, \"trim\"))\n>     email_option.option = EO_TRIM;\n> else if (!strcmp(arg, \"localpart\"))\n>     email_option.option = EO_LOCALPART;\n> ```\n\nSomebody needs to do a comparison, but it should be done at parsing\nphase when the --format is grokked, not in grab phase that is run\nfor each and every ref to be shown.\n\nThese patches should only be done after looking at existing\n\"<basicatom>:<modifiers>\" like \"objectname:short\" etc are handled,\nnot before, I think.\n"},{"id":"402977","messageId":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.git.1595882588.gitgitgadget@gmail.com","subject":"[PATCH v2 0/9] [GSoC] Improvements to ref-filter","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-05T21:51:36Z","receivedAt":"2020-08-05T21:51:54Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"This is the first patch series that introduces some improvements and\nfeatures to file ref-filter.{c,h}. These changes are useful to ref-filter,\nbut in near future also will allow us to use ref-filter's logic in pretty.c\n\nI plan to add more to format-support.{c,h} in the upcoming patch series.\nThat will lead to more improved and feature-rich ref-filter.c\n\nHariom Verma (9):\n  ref-filter: support different email formats\n  ref-filter: refactor `grab_objectname()`\n  ref-filter: modify error messages in `grab_objectname()`\n  ref-filter: rename `objectname` related functions and fields\n  ref-filter: add `short` modifier to 'tree' atom\n  ref-filter: add `short` modifier to 'parent' atom\n  pretty: refactor `format_sanitized_subject()`\n  format-support: move `format_sanitized_subject()` from pretty\n  ref-filter: add `sanitize` option for 'subject' atom\n\n Documentation/git-for-each-ref.txt |  10 +-\n Makefile                           |   1 +\n format-support.c                   |  43 ++++++++\n format-support.h                   |   6 ++\n pretty.c                           |  40 +-------\n ref-filter.c                       | 159 +++++++++++++++++++----------\n t/t6300-for-each-ref.sh            |  35 +++++++\n 7 files changed, 202 insertions(+), 92 deletions(-)\n create mode 100644 format-support.c\n create mode 100644 format-support.h\n\n\nbase-commit: dc04167d378fb29d30e1647ff6ff51dd182bc9a3\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-684%2Fharry-hov%2Fonly-rf6-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-684/harry-hov/only-rf6-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/684\n\nRange-diff vs v1:\n\n  1:  aeb116c5aa !  1:  78e69032df ref-filter: support different email formats\n     @@ Metadata\n       ## Commit message ##\n          ref-filter: support different email formats\n      \n     -    Currently, ref-filter only supports printing email with arrow brackets.\n     +    Currently, ref-filter only supports printing email with angle brackets.\n      \n          Let's add support for two more email options.\n     -    - trim : print email without arrow brackets.\n     -    - localpart : prints the part before the @ sign\n     +    - trim : for email without angle brackets.\n     +    - localpart : for the part before the @ sign out of trimmed email\n      \n          Mentored-by: Christian Couder <chriscool@tuxfamily.org>\n          Mentored-by: Heba Waly <heba.waly@gmail.com>\n          Signed-off-by: Hariom Verma <hariom18599@gmail.com>\n      \n     + ## Documentation/git-for-each-ref.txt ##\n     +@@ Documentation/git-for-each-ref.txt: These are intended for working on a mix of annotated and lightweight tags.\n     + \n     + Fields that have name-email-date tuple as its value (`author`,\n     + `committer`, and `tagger`) can be suffixed with `name`, `email`,\n     +-and `date` to extract the named component.\n     ++and `date` to extract the named component.  For email fields (`authoremail`,\n     ++`committeremail` and `taggeremail`), `:trim` can be appended to get the email\n     ++without angle brackets, and `:localpart` to get the part before the `@` symbol\n     ++out of the trimmed email.\n     + \n     + The message in a commit or a tag object is `contents`, from which\n     + `contents:<part>` can be used to extract various parts out of:\n     +\n       ## ref-filter.c ##\n     -@@ ref-filter.c: static struct ref_to_worktree_map {\n     - \tstruct worktree **worktrees;\n     - } ref_to_worktree_map;\n     +@@ ref-filter.c: static struct used_atom {\n     + \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n     + \t\t\tunsigned int length;\n     + \t\t} objectname;\n     ++\t\tstruct email_option {\n     ++\t\t\tenum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n     ++\t\t} email_option;\n     + \t\tstruct refname_atom refname;\n     + \t\tchar *head;\n     + \t} u;\n     +@@ ref-filter.c: static int objectname_atom_parser(const struct ref_format *format, struct used_a\n     + \treturn 0;\n     + }\n       \n     -+static struct email_option{\n     -+\tenum { EO_INVALID, EO_RAW, EO_TRIM, EO_LOCALPART } option;\n     -+} email_option;\n     ++static int person_email_atom_parser(const struct ref_format *format, struct used_atom *atom,\n     ++\t\t\t\t    const char *arg, struct strbuf *err)\n     ++{\n     ++\tif (!arg)\n     ++\t\tatom->u.email_option.option = EO_RAW;\n     ++\telse if (!strcmp(arg, \"trim\"))\n     ++\t\tatom->u.email_option.option = EO_TRIM;\n     ++\telse if (!strcmp(arg, \"localpart\"))\n     ++\t\tatom->u.email_option.option = EO_LOCALPART;\n     ++\telse\n     ++\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized email option: %s\"), arg);\n     ++\treturn 0;\n     ++}\n      +\n     - /*\n     -  * An atom is a valid field atom listed below, possibly prefixed with\n     -  * a \"*\" to denote deref_tag().\n     -@@ ref-filter.c: static const char *copy_email(const char *buf)\n     + static int refname_atom_parser(const struct ref_format *format, struct used_atom *atom,\n     + \t\t\t       const char *arg, struct strbuf *err)\n     + {\n     +@@ ref-filter.c: static struct {\n     + \t{ \"tag\", SOURCE_OBJ },\n     + \t{ \"author\", SOURCE_OBJ },\n     + \t{ \"authorname\", SOURCE_OBJ },\n     +-\t{ \"authoremail\", SOURCE_OBJ },\n     ++\t{ \"authoremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n     + \t{ \"authordate\", SOURCE_OBJ, FIELD_TIME },\n     + \t{ \"committer\", SOURCE_OBJ },\n     + \t{ \"committername\", SOURCE_OBJ },\n     +-\t{ \"committeremail\", SOURCE_OBJ },\n     ++\t{ \"committeremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n     + \t{ \"committerdate\", SOURCE_OBJ, FIELD_TIME },\n     + \t{ \"tagger\", SOURCE_OBJ },\n     + \t{ \"taggername\", SOURCE_OBJ },\n     +-\t{ \"taggeremail\", SOURCE_OBJ },\n     ++\t{ \"taggeremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n     + \t{ \"taggerdate\", SOURCE_OBJ, FIELD_TIME },\n     + \t{ \"creator\", SOURCE_OBJ },\n     + \t{ \"creatordate\", SOURCE_OBJ, FIELD_TIME },\n     +@@ ref-filter.c: static const char *copy_name(const char *buf)\n     + \treturn xstrdup(\"\");\n     + }\n     + \n     +-static const char *copy_email(const char *buf)\n     ++static const char *copy_email(const char *buf, struct used_atom *atom)\n     + {\n     + \tconst char *email = strchr(buf, '<');\n       \tconst char *eoemail;\n       \tif (!email)\n       \t\treturn xstrdup(\"\");\n      -\teoemail = strchr(email, '>');\n     -+\tswitch (email_option.option) {\n     ++\tswitch (atom->u.email_option.option) {\n      +\tcase EO_RAW:\n     -+\t\teoemail = strchr(email, '>') + 1;\n     ++\t\teoemail = strchr(email, '>');\n     ++\t\tif (eoemail)\n     ++\t\t\teoemail++;\n      +\t\tbreak;\n      +\tcase EO_TRIM:\n      +\t\temail++;\n     @@ ref-filter.c: static const char *copy_email(const char *buf)\n      +\tcase EO_LOCALPART:\n      +\t\temail++;\n      +\t\teoemail = strchr(email, '@');\n     ++\t\tif (!eoemail)\n     ++\t\t\teoemail = strchr(email, '>');\n      +\t\tbreak;\n     -+\tcase EO_INVALID:\n      +\tdefault:\n     -+\t\treturn xstrdup(\"\");\n     ++\t\tBUG(\"unknown email option\");\n      +\t}\n      +\n       \tif (!eoemail)\n     @@ ref-filter.c: static void grab_person(const char *who, struct atom_value *val, i\n       \t\telse if (!strcmp(name + wholen, \"name\"))\n       \t\t\tv->s = copy_name(wholine);\n      -\t\telse if (!strcmp(name + wholen, \"email\"))\n     -+\t\telse if (starts_with(name + wholen, \"email\")) {\n     -+\t\t\temail_option.option = EO_INVALID;\n     -+\t\t\tif (!strcmp(name + wholen, \"email\"))\n     -+\t\t\t\temail_option.option = EO_RAW;\n     -+\t\t\tif (!strcmp(name + wholen, \"email:trim\"))\n     -+\t\t\t\temail_option.option = EO_TRIM;\n     -+\t\t\tif (!strcmp(name + wholen, \"email:localpart\"))\n     -+\t\t\t\temail_option.option = EO_LOCALPART;\n     - \t\t\tv->s = copy_email(wholine);\n     -+\t\t}\n     +-\t\t\tv->s = copy_email(wholine);\n     ++\t\telse if (starts_with(name + wholen, \"email\"))\n     ++\t\t\tv->s = copy_email(wholine, &used_atom[i]);\n       \t\telse if (starts_with(name + wholen, \"date\"))\n       \t\t\tgrab_date(wholine, v, name);\n       \t}\n  -:  ---------- >  2:  b6b6acab9a ref-filter: refactor `grab_objectname()`\n  -:  ---------- >  3:  65fee332a3 ref-filter: modify error messages in `grab_objectname()`\n  -:  ---------- >  4:  976f2041a4 ref-filter: rename `objectname` related functions and fields\n  -:  ---------- >  5:  dda7400b14 ref-filter: add `short` modifier to 'tree' atom\n  2:  49344f1b55 !  6:  764bb23b59 ref-filter: add `short` option for 'tree' and 'parent'\n     @@ Metadata\n      Author: Hariom Verma <hariom18599@gmail.com>\n      \n       ## Commit message ##\n     -    ref-filter: add `short` option for 'tree' and 'parent'\n     +    ref-filter: add `short` modifier to 'parent' atom\n      \n     -    Sometimes while using 'parent' and 'tree' atom, user might\n     -    want to see abbrev hash instead of full 40 character hash.\n     +    Sometimes while using 'parent' atom, user might want to see abbrev hash\n     +    instead of full 40 character hash.\n      \n     -    'objectname' and 'refname' already supports printing abbrev hash.\n     -    It might be convenient for users to have the same option for printing\n     -    'parent' and 'tree' hash.\n     +    Just like 'objectname', it might be convenient for users to have the\n     +    `:short` and `:short=<length>` option for printing 'parent' hash.\n      \n     -    Let's introduce `short` option to 'parent' and 'tree' atom.\n     -\n     -    `tree:short` - for abbrev tree hash\n     -    `parent:short` - for abbrev parent hash\n     +    Let's introduce `short` option to 'parent' atom.\n      \n          Mentored-by: Christian Couder <chriscool@tuxfamily.org>\n          Mentored-by: Heba Waly <heba.waly@gmail.com>\n          Signed-off-by: Hariom Verma <hariom18599@gmail.com>\n      \n     + ## Documentation/git-for-each-ref.txt ##\n     +@@ Documentation/git-for-each-ref.txt: worktreepath::\n     + In addition to the above, for commit and tag objects, the header\n     + field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n     + be used to specify the value in the header field.\n     +-Field `tree` can also be used with modifier `:short` and\n     ++Fields `tree` and `parent` can also be used with modifier `:short` and\n     + `:short=<length>` just like `objectname`.\n     + \n     + For commit and tag objects, the special `creatordate` and `creator`\n     +\n       ## ref-filter.c ##\n     +@@ ref-filter.c: static struct {\n     + \t{ \"objectname\", SOURCE_OTHER, FIELD_STR, oid_atom_parser },\n     + \t{ \"deltabase\", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },\n     + \t{ \"tree\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n     +-\t{ \"parent\", SOURCE_OBJ },\n     ++\t{ \"parent\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n     + \t{ \"numparent\", SOURCE_OBJ, FIELD_ULONG },\n     + \t{ \"object\", SOURCE_OBJ },\n     + \t{ \"type\", SOURCE_OBJ },\n      @@ ref-filter.c: static void grab_commit_values(struct atom_value *val, int deref, struct object\n     - \t\t\tcontinue;\n     - \t\tif (deref)\n     - \t\t\tname++;\n     --\t\tif (!strcmp(name, \"tree\")) {\n     -+\t\tif (!strcmp(name, \"tree\"))\n     - \t\t\tv->s = xstrdup(oid_to_hex(get_commit_tree_oid(commit)));\n     --\t\t}\n     -+\t\telse if (!strcmp(name, \"tree:short\"))\n     -+\t\t\tv->s = xstrdup(find_unique_abbrev(get_commit_tree_oid(commit), DEFAULT_ABBREV));\n     - \t\telse if (!strcmp(name, \"numparent\")) {\n       \t\t\tv->value = commit_list_count(commit->parents);\n       \t\t\tv->s = xstrfmt(\"%lu\", (unsigned long)v->value);\n       \t\t}\n     @@ ref-filter.c: static void grab_commit_values(struct atom_value *val, int deref,\n       \t\t\tstruct commit_list *parents;\n       \t\t\tstruct strbuf s = STRBUF_INIT;\n       \t\t\tfor (parents = commit->parents; parents; parents = parents->next) {\n     - \t\t\t\tstruct commit *parent = parents->item;\n     +-\t\t\t\tstruct commit *parent = parents->item;\n     ++\t\t\t\tstruct object_id *oid = &parents->item->object.oid;\n       \t\t\t\tif (parents != commit->parents)\n       \t\t\t\t\tstrbuf_addch(&s, ' ');\n      -\t\t\t\tstrbuf_addstr(&s, oid_to_hex(&parent->object.oid));\n     -+\t\t\t\tif (!strcmp(name, \"parent\"))\n     -+\t\t\t\t\tstrbuf_addstr(&s, oid_to_hex(&parent->object.oid));\n     -+\t\t\t\telse if (!strcmp(name, \"parent:short\"))\n     -+\t\t\t\t\tstrbuf_add_unique_abbrev(&s, &parent->object.oid, DEFAULT_ABBREV);\n     ++\t\t\t\tstrbuf_addstr(&s, do_grab_oid(\"parent\", oid, &used_atom[i]));\n       \t\t\t}\n       \t\t\tv->s = strbuf_detach(&s, NULL);\n       \t\t}\n      \n       ## t/t6300-for-each-ref.sh ##\n     -@@ t/t6300-for-each-ref.sh: test_atom head objectname:short $(git rev-parse --short refs/heads/master)\n     - test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n     - test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n     - test_atom head tree $(git rev-parse refs/heads/master^{tree})\n     -+test_atom head tree:short $(git rev-parse --short refs/heads/master^{tree})\n     +@@ t/t6300-for-each-ref.sh: test_atom head tree:short $(git rev-parse --short refs/heads/master^{tree})\n     + test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/master^{tree})\n     + test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/master^{tree})\n       test_atom head parent ''\n      +test_atom head parent:short ''\n     ++test_atom head parent:short=1 ''\n     ++test_atom head parent:short=10 ''\n       test_atom head numparent 0\n       test_atom head object ''\n       test_atom head type ''\n     -@@ t/t6300-for-each-ref.sh: test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n     - test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n     - test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n     - test_atom tag tree ''\n     -+test_atom tag tree:short ''\n     +@@ t/t6300-for-each-ref.sh: test_atom tag tree:short ''\n     + test_atom tag tree:short=1 ''\n     + test_atom tag tree:short=10 ''\n       test_atom tag parent ''\n      +test_atom tag parent:short ''\n     ++test_atom tag parent:short=1 ''\n     ++test_atom tag parent:short=10 ''\n       test_atom tag numparent ''\n       test_atom tag object $(git rev-parse refs/tags/testtag^0)\n       test_atom tag type 'commit'\n  3:  69b9d221c0 !  7:  95035765a0 pretty: refactor `format_sanitized_subject()`\n     @@ Metadata\n       ## Commit message ##\n          pretty: refactor `format_sanitized_subject()`\n      \n     -    This commit refactors `format_sanitized_subject()` in the\n     -    hope to use same logic in ref-filter.c\n     +    The function 'format_sanitized_subject()' is responsible for\n     +    sanitized subject line in pretty.c\n     +    e.g.\n     +    the subject line\n     +    the-sanitized-subject-line\n     +\n     +    It would be a nice enhancement to `subject` atom to have the\n     +    same feature. So in the later commits, we plan to add this feature\n     +    to ref-filter.\n     +\n     +    Refactor `format_sanitized_subject()`, so it can be reused in\n     +    ref-filter.c for adding new modifier `sanitize` to \"subject\" atom.\n     +\n     +    Currently, the loop inside `format_sanitized_subject()` runs\n     +    until `\\n` is found. But now, we stored the first occurrence\n     +    of `\\n` in a variable `eol` and passed it in\n     +    `format_sanitized_subject()`. And the loop runs upto `eol`.\n     +\n     +    But this change isn't sufficient to reuse this function in\n     +    ref-filter.c because there exist tags with multiline subject.\n     +\n     +    It's wise to replace `\\n` with ' ', if `format_sanitized_subject()`\n     +    encounters `\\n` before end of subject line, just like `copy_subject()`.\n     +    Because we'll be only using `format_sanitized_subject()` for\n     +    \"%(subject:sanitize)\", instead of `copy_subject()` and\n     +    `format_sanitized_subject()` both. So, added the code:\n     +    ```\n     +    if (char == '\\n') /* never true if called inside pretty.c */\n     +        char = ' ';\n     +    ```\n     +\n     +    Now, it's ready to be reused in ref-filter.c\n      \n          Mentored-by: Christian Couder <chriscool@tuxfamily.org>\n          Mentored-by: Heba Waly <heba.waly@gmail.com>\n  4:  9dc619b448 !  8:  1c43f55d7c format-support: move `format_sanitized_subject()` from pretty\n     @@ Commit message\n      \n          In hope of some new features in `subject` atom, move funtion\n          `format_sanitized_subject()` and all the function it uses\n     -    to new file format-support.[c/h].\n     +    to new file format-support.{c,h}.\n      \n          Consider this new file as a common interface between functions that\n          pretty.c and ref-filter.c shares.\n  5:  7b9103cbad !  9:  feace82752 ref-filter: add `sanitize` option for 'subject' atom\n     @@ Commit message\n      \n          `subject:sanitize` - print sanitized subject line, suitable for a filename.\n      \n     +    e.g.\n     +    %(subject): \"the subject line\"\n     +    %(subject:sanitize): \"the-subject-line\"\n     +\n          Mentored-by: Christian Couder <chriscool@tuxfamily.org>\n          Mentored-by: Heba Waly <heba.waly@gmail.com>\n          Signed-off-by: Hariom Verma <hariom18599@gmail.com>\n      \n     + ## Documentation/git-for-each-ref.txt ##\n     +@@ Documentation/git-for-each-ref.txt: contents:subject::\n     + \tThe first paragraph of the message, which typically is a\n     + \tsingle line, is taken as the \"subject\" of the commit or the\n     + \ttag message.\n     ++\tInstead of `contents:subject`, field `subject` can also be used to\n     ++\tobtain same results. `:sanitize` can be appended to `subject` for\n     ++\tsubject line suitable for filename.\n     + \n     + contents:body::\n     + \tThe remainder of the commit or the tag message that follows\n     +\n       ## ref-filter.c ##\n      @@\n       #include \"worktree.h\"\n     @@ ref-filter.c: static struct used_atom {\n       \t\t\tunsigned int nobracket : 1, push : 1, push_remote : 1;\n       \t\t} remote_ref;\n       \t\tstruct {\n     --\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n     -+\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_SUB_SANITIZE, C_TRAILERS } option;\n     +-\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH,\n     +-\t\t\t       C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n     ++\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH, C_LINES,\n     ++\t\t\t       C_SIG, C_SUB, C_SUB_SANITIZE, C_TRAILERS } option;\n       \t\t\tstruct process_trailer_options trailer_opts;\n       \t\t\tunsigned int nlines;\n       \t\t} contents;\n     @@ ref-filter.c: static int body_atom_parser(const struct ref_format *format, struc\n       {\n      -\tif (arg)\n      -\t\treturn strbuf_addf_ret(err, -1, _(\"%%(subject) does not take arguments\"));\n     -+\tif (arg) {\n     -+\t\tif (!strcmp(arg, \"sanitize\")) {\n     -+\t\t\tatom->u.contents.option = C_SUB_SANITIZE;\n     -+\t\t\treturn 0;\n     -+\t\t} else {\n     -+\t\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized %%(subject) argument: %s\"), arg);\n     -+\t\t}\n     -+\t}\n     - \tatom->u.contents.option = C_SUB;\n     +-\tatom->u.contents.option = C_SUB;\n     ++\tif (!arg)\n     ++\t\tatom->u.contents.option = C_SUB;\n     ++\telse if (!strcmp(arg, \"sanitize\"))\n     ++\t\tatom->u.contents.option = C_SUB_SANITIZE;\n     ++\telse\n     ++\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized %%(subject) argument: %s\"), arg);\n       \treturn 0;\n       }\n     + \n      @@ ref-filter.c: static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n     - \t\tstruct used_atom *atom = &used_atom[i];\n     - \t\tconst char *name = atom->name;\n     - \t\tstruct atom_value *v = &val[i];\n     -+\n     - \t\tif (!!deref != (*name == '*'))\n       \t\t\tcontinue;\n       \t\tif (deref)\n       \t\t\tname++;\n     - \t\tif (strcmp(name, \"subject\") &&\n     -+\t\t    strcmp(name, \"subject:sanitize\") &&\n     - \t\t    strcmp(name, \"body\") &&\n     +-\t\tif (strcmp(name, \"subject\") &&\n     +-\t\t    strcmp(name, \"body\") &&\n     ++\t\tif (strcmp(name, \"body\") &&\n     ++\t\t    !starts_with(name, \"subject\") &&\n       \t\t    !starts_with(name, \"trailers\") &&\n       \t\t    !starts_with(name, \"contents\"))\n     + \t\t\tcontinue;\n      @@ ref-filter.c: static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n       \n       \t\tif (atom->u.contents.option == C_SUB)\n     @@ ref-filter.c: static void grab_sub_body_contents(struct atom_value *val, int der\n      +\t\t\tv->s = strbuf_detach(&sb, NULL);\n      +\t\t} else if (atom->u.contents.option == C_BODY_DEP)\n       \t\t\tv->s = xmemdupz(bodypos, bodylen);\n     - \t\telse if (atom->u.contents.option == C_BODY)\n     - \t\t\tv->s = xmemdupz(bodypos, nonsiglen);\n     + \t\telse if (atom->u.contents.option == C_LENGTH)\n     + \t\t\tv->s = xstrfmt(\"%\"PRIuMAX, (uintmax_t)strlen(subpos));\n      \n       ## t/t6300-for-each-ref.sh ##\n      @@ t/t6300-for-each-ref.sh: test_atom head taggerdate ''\n\n-- \ngitgitgadget\n"},{"id":"402978","messageId":"78e69032df68f8edd0f88798936a3e85e5ddf34c.1596664306.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"[PATCH v2 1/9] ref-filter: support different email formats","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-05T21:51:37Z","receivedAt":"2020-08-05T21:51:55Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nCurrently, ref-filter only supports printing email with angle brackets.\n\nLet's add support for two more email options.\n- trim : for email without angle brackets.\n- localpart : for the part before the @ sign out of trimmed email\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  5 ++-\n ref-filter.c                       | 54 +++++++++++++++++++++++++-----\n t/t6300-for-each-ref.sh            | 16 +++++++++\n 3 files changed, 65 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 2ea71c5f6c..e6ce8af612 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -230,7 +230,10 @@ These are intended for working on a mix of annotated and lightweight tags.\n \n Fields that have name-email-date tuple as its value (`author`,\n `committer`, and `tagger`) can be suffixed with `name`, `email`,\n-and `date` to extract the named component.\n+and `date` to extract the named component.  For email fields (`authoremail`,\n+`committeremail` and `taggeremail`), `:trim` can be appended to get the email\n+without angle brackets, and `:localpart` to get the part before the `@` symbol\n+out of the trimmed email.\n \n The message in a commit or a tag object is `contents`, from which\n `contents:<part>` can be used to extract various parts out of:\ndiff --git a/ref-filter.c b/ref-filter.c\nindex f2b078db11..307069219f 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -140,6 +140,9 @@ static struct used_atom {\n \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n \t\t\tunsigned int length;\n \t\t} objectname;\n+\t\tstruct email_option {\n+\t\t\tenum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n+\t\t} email_option;\n \t\tstruct refname_atom refname;\n \t\tchar *head;\n \t} u;\n@@ -377,6 +380,20 @@ static int objectname_atom_parser(const struct ref_format *format, struct used_a\n \treturn 0;\n }\n \n+static int person_email_atom_parser(const struct ref_format *format, struct used_atom *atom,\n+\t\t\t\t    const char *arg, struct strbuf *err)\n+{\n+\tif (!arg)\n+\t\tatom->u.email_option.option = EO_RAW;\n+\telse if (!strcmp(arg, \"trim\"))\n+\t\tatom->u.email_option.option = EO_TRIM;\n+\telse if (!strcmp(arg, \"localpart\"))\n+\t\tatom->u.email_option.option = EO_LOCALPART;\n+\telse\n+\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized email option: %s\"), arg);\n+\treturn 0;\n+}\n+\n static int refname_atom_parser(const struct ref_format *format, struct used_atom *atom,\n \t\t\t       const char *arg, struct strbuf *err)\n {\n@@ -488,15 +505,15 @@ static struct {\n \t{ \"tag\", SOURCE_OBJ },\n \t{ \"author\", SOURCE_OBJ },\n \t{ \"authorname\", SOURCE_OBJ },\n-\t{ \"authoremail\", SOURCE_OBJ },\n+\t{ \"authoremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n \t{ \"authordate\", SOURCE_OBJ, FIELD_TIME },\n \t{ \"committer\", SOURCE_OBJ },\n \t{ \"committername\", SOURCE_OBJ },\n-\t{ \"committeremail\", SOURCE_OBJ },\n+\t{ \"committeremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n \t{ \"committerdate\", SOURCE_OBJ, FIELD_TIME },\n \t{ \"tagger\", SOURCE_OBJ },\n \t{ \"taggername\", SOURCE_OBJ },\n-\t{ \"taggeremail\", SOURCE_OBJ },\n+\t{ \"taggeremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n \t{ \"taggerdate\", SOURCE_OBJ, FIELD_TIME },\n \t{ \"creator\", SOURCE_OBJ },\n \t{ \"creatordate\", SOURCE_OBJ, FIELD_TIME },\n@@ -1037,16 +1054,35 @@ static const char *copy_name(const char *buf)\n \treturn xstrdup(\"\");\n }\n \n-static const char *copy_email(const char *buf)\n+static const char *copy_email(const char *buf, struct used_atom *atom)\n {\n \tconst char *email = strchr(buf, '<');\n \tconst char *eoemail;\n \tif (!email)\n \t\treturn xstrdup(\"\");\n-\teoemail = strchr(email, '>');\n+\tswitch (atom->u.email_option.option) {\n+\tcase EO_RAW:\n+\t\teoemail = strchr(email, '>');\n+\t\tif (eoemail)\n+\t\t\teoemail++;\n+\t\tbreak;\n+\tcase EO_TRIM:\n+\t\temail++;\n+\t\teoemail = strchr(email, '>');\n+\t\tbreak;\n+\tcase EO_LOCALPART:\n+\t\temail++;\n+\t\teoemail = strchr(email, '@');\n+\t\tif (!eoemail)\n+\t\t\teoemail = strchr(email, '>');\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"unknown email option\");\n+\t}\n+\n \tif (!eoemail)\n \t\treturn xstrdup(\"\");\n-\treturn xmemdupz(email, eoemail + 1 - email);\n+\treturn xmemdupz(email, eoemail - email);\n }\n \n static char *copy_subject(const char *buf, unsigned long len)\n@@ -1116,7 +1152,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n \t\t\tcontinue;\n \t\tif (name[wholen] != 0 &&\n \t\t    strcmp(name + wholen, \"name\") &&\n-\t\t    strcmp(name + wholen, \"email\") &&\n+\t\t    !starts_with(name + wholen, \"email\") &&\n \t\t    !starts_with(name + wholen, \"date\"))\n \t\t\tcontinue;\n \t\tif (!wholine)\n@@ -1127,8 +1163,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n \t\t\tv->s = copy_line(wholine);\n \t\telse if (!strcmp(name + wholen, \"name\"))\n \t\t\tv->s = copy_name(wholine);\n-\t\telse if (!strcmp(name + wholen, \"email\"))\n-\t\t\tv->s = copy_email(wholine);\n+\t\telse if (starts_with(name + wholen, \"email\"))\n+\t\t\tv->s = copy_email(wholine, &used_atom[i]);\n \t\telse if (starts_with(name + wholen, \"date\"))\n \t\t\tgrab_date(wholine, v, name);\n \t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex a83579fbdf..64fbc91146 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -125,15 +125,21 @@ test_atom head '*objecttype' ''\n test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n test_atom head authorname 'A U Thor'\n test_atom head authoremail '<author@example.com>'\n+test_atom head authoremail:trim 'author@example.com'\n+test_atom head authoremail:localpart 'author'\n test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head committername 'C O Mitter'\n test_atom head committeremail '<committer@example.com>'\n+test_atom head committeremail:trim 'committer@example.com'\n+test_atom head committeremail:localpart 'committer'\n test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n test_atom head tag ''\n test_atom head tagger ''\n test_atom head taggername ''\n test_atom head taggeremail ''\n+test_atom head taggeremail:trim ''\n+test_atom head taggeremail:localpart ''\n test_atom head taggerdate ''\n test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n@@ -170,15 +176,21 @@ test_atom tag '*objecttype' 'commit'\n test_atom tag author ''\n test_atom tag authorname ''\n test_atom tag authoremail ''\n+test_atom tag authoremail:trim ''\n+test_atom tag authoremail:localpart ''\n test_atom tag authordate ''\n test_atom tag committer ''\n test_atom tag committername ''\n test_atom tag committeremail ''\n+test_atom tag committeremail:trim ''\n+test_atom tag committeremail:localpart ''\n test_atom tag committerdate ''\n test_atom tag tag 'testtag'\n test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag taggername 'C O Mitter'\n test_atom tag taggeremail '<committer@example.com>'\n+test_atom tag taggeremail:trim 'committer@example.com'\n+test_atom tag taggeremail:localpart 'committer'\n test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n@@ -564,10 +576,14 @@ test_atom refs/tags/taggerless tag 'taggerless'\n test_atom refs/tags/taggerless tagger ''\n test_atom refs/tags/taggerless taggername ''\n test_atom refs/tags/taggerless taggeremail ''\n+test_atom refs/tags/taggerless taggeremail:trim ''\n+test_atom refs/tags/taggerless taggeremail:localpart ''\n test_atom refs/tags/taggerless taggerdate ''\n test_atom refs/tags/taggerless committer ''\n test_atom refs/tags/taggerless committername ''\n test_atom refs/tags/taggerless committeremail ''\n+test_atom refs/tags/taggerless committeremail:trim ''\n+test_atom refs/tags/taggerless committeremail:localpart ''\n test_atom refs/tags/taggerless committerdate ''\n test_atom refs/tags/taggerless subject 'Broken tag'\n \n-- \ngitgitgadget\n\n"},{"id":"402979","messageId":"b6b6acab9af222c4c2b43836c357addd6e7e239d.1596664306.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"[PATCH v2 2/9] ref-filter: refactor `grab_objectname()`","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-05T21:51:38Z","receivedAt":"2020-08-05T21:51:59Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nPrepares `grab_objectname()` for more generic usage.\nThis change will allow us to reuse `grab_objectname()` for\nthe `tree` and `parent` atoms in a following commit.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c | 36 +++++++++++++++++++++---------------\n 1 file changed, 21 insertions(+), 15 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 307069219f..d078f893ff 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -918,21 +918,27 @@ int verify_ref_format(struct ref_format *format)\n \treturn 0;\n }\n \n-static int grab_objectname(const char *name, const struct object_id *oid,\n+static const char *do_grab_objectname(const char *field, const struct object_id *oid,\n+\t\t\t\t      struct used_atom *atom)\n+{\n+\tswitch (atom->u.objectname.option) {\n+\tcase O_FULL:\n+\t\treturn oid_to_hex(oid);\n+\tcase O_LENGTH:\n+\t\treturn find_unique_abbrev(oid, atom->u.objectname.length);\n+\tcase O_SHORT:\n+\t\treturn find_unique_abbrev(oid, DEFAULT_ABBREV);\n+\tdefault:\n+\t\tBUG(\"unknown %%(%s) option\", field);\n+\t}\n+}\n+\n+static int grab_objectname(const char *name, const char *field, const struct object_id *oid,\n \t\t\t   struct atom_value *v, struct used_atom *atom)\n {\n-\tif (starts_with(name, \"objectname\")) {\n-\t\tif (atom->u.objectname.option == O_SHORT) {\n-\t\t\tv->s = xstrdup(find_unique_abbrev(oid, DEFAULT_ABBREV));\n-\t\t\treturn 1;\n-\t\t} else if (atom->u.objectname.option == O_FULL) {\n-\t\t\tv->s = xstrdup(oid_to_hex(oid));\n-\t\t\treturn 1;\n-\t\t} else if (atom->u.objectname.option == O_LENGTH) {\n-\t\t\tv->s = xstrdup(find_unique_abbrev(oid, atom->u.objectname.length));\n-\t\t\treturn 1;\n-\t\t} else\n-\t\t\tBUG(\"unknown %%(objectname) option\");\n+\tif (starts_with(name, field)) {\n+\t\tv->s = xstrdup(do_grab_objectname(field, oid, atom));\n+\t\treturn 1;\n \t}\n \treturn 0;\n }\n@@ -960,7 +966,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_\n \t\t} else if (!strcmp(name, \"deltabase\"))\n \t\t\tv->s = xstrdup(oid_to_hex(&oi->delta_base_oid));\n \t\telse if (deref)\n-\t\t\tgrab_objectname(name, &oi->oid, v, &used_atom[i]);\n+\t\t\tgrab_objectname(name, \"objectname\", &oi->oid, v, &used_atom[i]);\n \t}\n }\n \n@@ -1740,7 +1746,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)\n \t\t\t\tv->s = xstrdup(buf + 1);\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (!deref && grab_objectname(name, &ref->objectname, v, atom)) {\n+\t\t} else if (!deref && grab_objectname(name, \"objectname\", &ref->objectname, v, atom)) {\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"HEAD\")) {\n \t\t\tif (atom->u.head && !strcmp(ref->refname, atom->u.head))\n-- \ngitgitgadget\n\n"},{"id":"402980","messageId":"65fee332a3819fcaaf557d4c29e414350e62c5db.1596664306.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"[PATCH v2 3/9] ref-filter: modify error messages in `grab_objectname()`","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-05T21:51:39Z","receivedAt":"2020-08-05T21:52:03Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nAs we plan to use `grab_objectname()` for `tree` and `parent` atom,\nit's better to parameterize the error messages in the function\n`grab_objectname()` where \"objectname\" is hard coded.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex d078f893ff..4183ee2797 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -372,11 +372,11 @@ static int objectname_atom_parser(const struct ref_format *format, struct used_a\n \t\tatom->u.objectname.option = O_LENGTH;\n \t\tif (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n \t\t    atom->u.objectname.length == 0)\n-\t\t\treturn strbuf_addf_ret(err, -1, _(\"positive value expected objectname:short=%s\"), arg);\n+\t\t\treturn strbuf_addf_ret(err, -1, _(\"positive value expected '%s' in %%(%s)\"), arg, atom->name);\n \t\tif (atom->u.objectname.length < MINIMUM_ABBREV)\n \t\t\tatom->u.objectname.length = MINIMUM_ABBREV;\n \t} else\n-\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized %%(objectname) argument: %s\"), arg);\n+\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized argument '%s' in %%(%s)\"), arg, atom->name);\n \treturn 0;\n }\n \n-- \ngitgitgadget\n\n"},{"id":"402981","messageId":"976f2041a41a6b2043c1bece262cb0a3f7a8af35.1596664306.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"[PATCH v2 4/9] ref-filter: rename `objectname` related functions and fields","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-05T21:51:40Z","receivedAt":"2020-08-05T21:52:03Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nIn previous commits, we prepared some `objectname` related functions\nfor more generic usage, so that these functions can be used for `tree`\nand `parent` atom.\n\nBut the name of some functions and fields may mislead someone.\nFor ex: function `objectname_atom_parser()` implies that it is\nfor atom `objectname`.\n\nLet's rename all such functions and fields.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c | 40 ++++++++++++++++++++--------------------\n 1 file changed, 20 insertions(+), 20 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 4183ee2797..cc544eadaf 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -139,7 +139,7 @@ static struct used_atom {\n \t\tstruct {\n \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n \t\t\tunsigned int length;\n-\t\t} objectname;\n+\t\t} oid;\n \t\tstruct email_option {\n \t\t\tenum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n \t\t} email_option;\n@@ -361,20 +361,20 @@ static int contents_atom_parser(const struct ref_format *format, struct used_ato\n \treturn 0;\n }\n \n-static int objectname_atom_parser(const struct ref_format *format, struct used_atom *atom,\n-\t\t\t\t  const char *arg, struct strbuf *err)\n+static int oid_atom_parser(const struct ref_format *format, struct used_atom *atom,\n+\t\t\t   const char *arg, struct strbuf *err)\n {\n \tif (!arg)\n-\t\tatom->u.objectname.option = O_FULL;\n+\t\tatom->u.oid.option = O_FULL;\n \telse if (!strcmp(arg, \"short\"))\n-\t\tatom->u.objectname.option = O_SHORT;\n+\t\tatom->u.oid.option = O_SHORT;\n \telse if (skip_prefix(arg, \"short=\", &arg)) {\n-\t\tatom->u.objectname.option = O_LENGTH;\n-\t\tif (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n-\t\t    atom->u.objectname.length == 0)\n+\t\tatom->u.oid.option = O_LENGTH;\n+\t\tif (strtoul_ui(arg, 10, &atom->u.oid.length) ||\n+\t\t    atom->u.oid.length == 0)\n \t\t\treturn strbuf_addf_ret(err, -1, _(\"positive value expected '%s' in %%(%s)\"), arg, atom->name);\n-\t\tif (atom->u.objectname.length < MINIMUM_ABBREV)\n-\t\t\tatom->u.objectname.length = MINIMUM_ABBREV;\n+\t\tif (atom->u.oid.length < MINIMUM_ABBREV)\n+\t\t\tatom->u.oid.length = MINIMUM_ABBREV;\n \t} else\n \t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized argument '%s' in %%(%s)\"), arg, atom->name);\n \treturn 0;\n@@ -495,7 +495,7 @@ static struct {\n \t{ \"refname\", SOURCE_NONE, FIELD_STR, refname_atom_parser },\n \t{ \"objecttype\", SOURCE_OTHER, FIELD_STR, objecttype_atom_parser },\n \t{ \"objectsize\", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser },\n-\t{ \"objectname\", SOURCE_OTHER, FIELD_STR, objectname_atom_parser },\n+\t{ \"objectname\", SOURCE_OTHER, FIELD_STR, oid_atom_parser },\n \t{ \"deltabase\", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },\n \t{ \"tree\", SOURCE_OBJ },\n \t{ \"parent\", SOURCE_OBJ },\n@@ -918,14 +918,14 @@ int verify_ref_format(struct ref_format *format)\n \treturn 0;\n }\n \n-static const char *do_grab_objectname(const char *field, const struct object_id *oid,\n-\t\t\t\t      struct used_atom *atom)\n+static const char *do_grab_oid(const char *field, const struct object_id *oid,\n+\t\t\t       struct used_atom *atom)\n {\n-\tswitch (atom->u.objectname.option) {\n+\tswitch (atom->u.oid.option) {\n \tcase O_FULL:\n \t\treturn oid_to_hex(oid);\n \tcase O_LENGTH:\n-\t\treturn find_unique_abbrev(oid, atom->u.objectname.length);\n+\t\treturn find_unique_abbrev(oid, atom->u.oid.length);\n \tcase O_SHORT:\n \t\treturn find_unique_abbrev(oid, DEFAULT_ABBREV);\n \tdefault:\n@@ -933,11 +933,11 @@ static const char *do_grab_objectname(const char *field, const struct object_id\n \t}\n }\n \n-static int grab_objectname(const char *name, const char *field, const struct object_id *oid,\n-\t\t\t   struct atom_value *v, struct used_atom *atom)\n+static int grab_oid(const char *name, const char *field, const struct object_id *oid,\n+\t\t    struct atom_value *v, struct used_atom *atom)\n {\n \tif (starts_with(name, field)) {\n-\t\tv->s = xstrdup(do_grab_objectname(field, oid, atom));\n+\t\tv->s = xstrdup(do_grab_oid(field, oid, atom));\n \t\treturn 1;\n \t}\n \treturn 0;\n@@ -966,7 +966,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_\n \t\t} else if (!strcmp(name, \"deltabase\"))\n \t\t\tv->s = xstrdup(oid_to_hex(&oi->delta_base_oid));\n \t\telse if (deref)\n-\t\t\tgrab_objectname(name, \"objectname\", &oi->oid, v, &used_atom[i]);\n+\t\t\tgrab_oid(name, \"objectname\", &oi->oid, v, &used_atom[i]);\n \t}\n }\n \n@@ -1746,7 +1746,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)\n \t\t\t\tv->s = xstrdup(buf + 1);\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (!deref && grab_objectname(name, \"objectname\", &ref->objectname, v, atom)) {\n+\t\t} else if (!deref && grab_oid(name, \"objectname\", &ref->objectname, v, atom)) {\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"HEAD\")) {\n \t\t\tif (atom->u.head && !strcmp(ref->refname, atom->u.head))\n-- \ngitgitgadget\n\n"},{"id":"402982","messageId":"dda7400b14aef8688a8d10728f03e01293a82da6.1596664306.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"[PATCH v2 5/9] ref-filter: add `short` modifier to 'tree' atom","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-05T21:51:41Z","receivedAt":"2020-08-05T21:52:04Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nSometimes while using 'tree' atom, user might want to see abbrev hash\ninstead of full 40 character hash.\n\nJust like 'objectname', it might be convenient for users to have the\n`:short` and `:short=<length>` option for printing 'tree' hash.\n\nLet's introduce `short` option to 'tree' atom.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 2 ++\n ref-filter.c                       | 9 ++++-----\n t/t6300-for-each-ref.sh            | 6 ++++++\n 3 files changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex e6ce8af612..40ebdfcc41 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -222,6 +222,8 @@ worktreepath::\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\n+Field `tree` can also be used with modifier `:short` and\n+`:short=<length>` just like `objectname`.\n \n For commit and tag objects, the special `creatordate` and `creator`\n fields will correspond to the appropriate date or name-email-date tuple\ndiff --git a/ref-filter.c b/ref-filter.c\nindex cc544eadaf..f9d85661eb 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -497,7 +497,7 @@ static struct {\n \t{ \"objectsize\", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser },\n \t{ \"objectname\", SOURCE_OTHER, FIELD_STR, oid_atom_parser },\n \t{ \"deltabase\", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },\n-\t{ \"tree\", SOURCE_OBJ },\n+\t{ \"tree\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n \t{ \"parent\", SOURCE_OBJ },\n \t{ \"numparent\", SOURCE_OBJ, FIELD_ULONG },\n \t{ \"object\", SOURCE_OBJ },\n@@ -1005,10 +1005,9 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object\n \t\t\tcontinue;\n \t\tif (deref)\n \t\t\tname++;\n-\t\tif (!strcmp(name, \"tree\")) {\n-\t\t\tv->s = xstrdup(oid_to_hex(get_commit_tree_oid(commit)));\n-\t\t}\n-\t\telse if (!strcmp(name, \"numparent\")) {\n+\t\tif (grab_oid(name, \"tree\", get_commit_tree_oid(commit), v, &used_atom[i]))\n+\t\t\tcontinue;\n+\t\tif (!strcmp(name, \"numparent\")) {\n \t\t\tv->value = commit_list_count(commit->parents);\n \t\t\tv->s = xstrfmt(\"%lu\", (unsigned long)v->value);\n \t\t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 64fbc91146..e30bbff6d9 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -116,6 +116,9 @@ test_atom head objectname:short $(git rev-parse --short refs/heads/master)\n test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom head tree $(git rev-parse refs/heads/master^{tree})\n+test_atom head tree:short $(git rev-parse --short refs/heads/master^{tree})\n+test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/master^{tree})\n+test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/master^{tree})\n test_atom head parent ''\n test_atom head numparent 0\n test_atom head object ''\n@@ -167,6 +170,9 @@ test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom tag tree ''\n+test_atom tag tree:short ''\n+test_atom tag tree:short=1 ''\n+test_atom tag tree:short=10 ''\n test_atom tag parent ''\n test_atom tag numparent ''\n test_atom tag object $(git rev-parse refs/tags/testtag^0)\n-- \ngitgitgadget\n\n"},{"id":"402983","messageId":"764bb23b5974214335cc6a93fb4eab46e8f2a49b.1596664306.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"[PATCH v2 6/9] ref-filter: add `short` modifier to 'parent' atom","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-05T21:51:42Z","receivedAt":"2020-08-05T21:52:06Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nSometimes while using 'parent' atom, user might want to see abbrev hash\ninstead of full 40 character hash.\n\nJust like 'objectname', it might be convenient for users to have the\n`:short` and `:short=<length>` option for printing 'parent' hash.\n\nLet's introduce `short` option to 'parent' atom.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 2 +-\n ref-filter.c                       | 8 ++++----\n t/t6300-for-each-ref.sh            | 6 ++++++\n 3 files changed, 11 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 40ebdfcc41..dd09763e7d 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -222,7 +222,7 @@ worktreepath::\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\n-Field `tree` can also be used with modifier `:short` and\n+Fields `tree` and `parent` can also be used with modifier `:short` and\n `:short=<length>` just like `objectname`.\n \n For commit and tag objects, the special `creatordate` and `creator`\ndiff --git a/ref-filter.c b/ref-filter.c\nindex f9d85661eb..6d5bbb14a2 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -498,7 +498,7 @@ static struct {\n \t{ \"objectname\", SOURCE_OTHER, FIELD_STR, oid_atom_parser },\n \t{ \"deltabase\", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },\n \t{ \"tree\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n-\t{ \"parent\", SOURCE_OBJ },\n+\t{ \"parent\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n \t{ \"numparent\", SOURCE_OBJ, FIELD_ULONG },\n \t{ \"object\", SOURCE_OBJ },\n \t{ \"type\", SOURCE_OBJ },\n@@ -1011,14 +1011,14 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object\n \t\t\tv->value = commit_list_count(commit->parents);\n \t\t\tv->s = xstrfmt(\"%lu\", (unsigned long)v->value);\n \t\t}\n-\t\telse if (!strcmp(name, \"parent\")) {\n+\t\telse if (starts_with(name, \"parent\")) {\n \t\t\tstruct commit_list *parents;\n \t\t\tstruct strbuf s = STRBUF_INIT;\n \t\t\tfor (parents = commit->parents; parents; parents = parents->next) {\n-\t\t\t\tstruct commit *parent = parents->item;\n+\t\t\t\tstruct object_id *oid = &parents->item->object.oid;\n \t\t\t\tif (parents != commit->parents)\n \t\t\t\t\tstrbuf_addch(&s, ' ');\n-\t\t\t\tstrbuf_addstr(&s, oid_to_hex(&parent->object.oid));\n+\t\t\t\tstrbuf_addstr(&s, do_grab_oid(\"parent\", oid, &used_atom[i]));\n \t\t\t}\n \t\t\tv->s = strbuf_detach(&s, NULL);\n \t\t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex e30bbff6d9..79d5b29387 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -120,6 +120,9 @@ test_atom head tree:short $(git rev-parse --short refs/heads/master^{tree})\n test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/master^{tree})\n test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/master^{tree})\n test_atom head parent ''\n+test_atom head parent:short ''\n+test_atom head parent:short=1 ''\n+test_atom head parent:short=10 ''\n test_atom head numparent 0\n test_atom head object ''\n test_atom head type ''\n@@ -174,6 +177,9 @@ test_atom tag tree:short ''\n test_atom tag tree:short=1 ''\n test_atom tag tree:short=10 ''\n test_atom tag parent ''\n+test_atom tag parent:short ''\n+test_atom tag parent:short=1 ''\n+test_atom tag parent:short=10 ''\n test_atom tag numparent ''\n test_atom tag object $(git rev-parse refs/tags/testtag^0)\n test_atom tag type 'commit'\n-- \ngitgitgadget\n\n"},{"id":"402984","messageId":"1c43f55d7c1d5a16031115d5de56b5a5302b5597.1596664306.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"[PATCH v2 8/9] format-support: move `format_sanitized_subject()` from pretty","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-05T21:51:44Z","receivedAt":"2020-08-05T21:52:08Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nIn hope of some new features in `subject` atom, move funtion\n`format_sanitized_subject()` and all the function it uses\nto new file format-support.{c,h}.\n\nConsider this new file as a common interface between functions that\npretty.c and ref-filter.c shares.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Makefile         |  1 +\n format-support.c | 43 +++++++++++++++++++++++++++++++++++++++++++\n format-support.h |  6 ++++++\n pretty.c         | 40 +---------------------------------------\n 4 files changed, 51 insertions(+), 39 deletions(-)\n create mode 100644 format-support.c\n create mode 100644 format-support.h\n\ndiff --git a/Makefile b/Makefile\nindex 372139f1f2..4dfc384b49 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -882,6 +882,7 @@ LIB_OBJS += exec-cmd.o\n LIB_OBJS += fetch-negotiator.o\n LIB_OBJS += fetch-pack.o\n LIB_OBJS += fmt-merge-msg.o\n+LIB_OBJS += format-support.o\n LIB_OBJS += fsck.o\n LIB_OBJS += fsmonitor.o\n LIB_OBJS += gettext.o\ndiff --git a/format-support.c b/format-support.c\nnew file mode 100644\nindex 0000000000..d693aa1744\n--- /dev/null\n+++ b/format-support.c\n@@ -0,0 +1,43 @@\n+#include \"diff.h\"\n+#include \"log-tree.h\"\n+#include \"color.h\"\n+#include \"format-support.h\"\n+\n+static int istitlechar(char c)\n+{\n+\treturn (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||\n+\t\t(c >= '0' && c <= '9') || c == '.' || c == '_';\n+}\n+\n+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n+{\n+\tchar *r = xmemdupz(msg, len);\n+\tsize_t trimlen;\n+\tsize_t start_len = sb->len;\n+\tint space = 2;\n+\tint i;\n+\n+\tfor (i = 0; i < len; i++) {\n+\t\tif (r[i] == '\\n')\n+\t\t\tr[i] = ' ';\n+\t\tif (istitlechar(r[i])) {\n+\t\t\tif (space == 1)\n+\t\t\t\tstrbuf_addch(sb, '-');\n+\t\t\tspace = 0;\n+\t\t\tstrbuf_addch(sb, r[i]);\n+\t\t\tif (r[i] == '.')\n+\t\t\t\twhile (r[i+1] == '.')\n+\t\t\t\t\ti++;\n+\t\t} else\n+\t\t\tspace |= 1;\n+\t}\n+\tfree(r);\n+\n+\t/* trim any trailing '.' or '-' characters */\n+\ttrimlen = 0;\n+\twhile (sb->len - trimlen > start_len &&\n+\t\t(sb->buf[sb->len - 1 - trimlen] == '.'\n+\t\t|| sb->buf[sb->len - 1 - trimlen] == '-'))\n+\t\ttrimlen++;\n+\tstrbuf_remove(sb, sb->len - trimlen, trimlen);\n+}\ndiff --git a/format-support.h b/format-support.h\nnew file mode 100644\nindex 0000000000..c344ccbc33\n--- /dev/null\n+++ b/format-support.h\n@@ -0,0 +1,6 @@\n+#ifndef FORMAT_SUPPORT_H\n+#define FORMAT_SUPPORT_H\n+\n+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len);\n+\n+#endif /* FORMAT_SUPPORT_H */\ndiff --git a/pretty.c b/pretty.c\nindex 8d08e8278a..2de01b7115 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -12,6 +12,7 @@\n #include \"reflog-walk.h\"\n #include \"gpg-interface.h\"\n #include \"trailer.h\"\n+#include \"format-support.h\"\n \n static char *user_format;\n static struct cmt_fmt_map {\n@@ -833,45 +834,6 @@ static void parse_commit_header(struct format_commit_context *context)\n \tcontext->commit_header_parsed = 1;\n }\n \n-static int istitlechar(char c)\n-{\n-\treturn (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||\n-\t\t(c >= '0' && c <= '9') || c == '.' || c == '_';\n-}\n-\n-static void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n-{\n-\tchar *r = xmemdupz(msg, len);\n-\tsize_t trimlen;\n-\tsize_t start_len = sb->len;\n-\tint space = 2;\n-\tint i;\n-\n-\tfor (i = 0; i < len; i++) {\n-\t\tif (r[i] == '\\n')\n-\t\t\tr[i] = ' ';\n-\t\tif (istitlechar(r[i])) {\n-\t\t\tif (space == 1)\n-\t\t\t\tstrbuf_addch(sb, '-');\n-\t\t\tspace = 0;\n-\t\t\tstrbuf_addch(sb, r[i]);\n-\t\t\tif (r[i] == '.')\n-\t\t\t\twhile (r[i+1] == '.')\n-\t\t\t\t\ti++;\n-\t\t} else\n-\t\t\tspace |= 1;\n-\t}\n-\tfree(r);\n-\n-\t/* trim any trailing '.' or '-' characters */\n-\ttrimlen = 0;\n-\twhile (sb->len - trimlen > start_len &&\n-\t\t(sb->buf[sb->len - 1 - trimlen] == '.'\n-\t\t|| sb->buf[sb->len - 1 - trimlen] == '-'))\n-\t\ttrimlen++;\n-\tstrbuf_remove(sb, sb->len - trimlen, trimlen);\n-}\n-\n const char *format_subject(struct strbuf *sb, const char *msg,\n \t\t\t   const char *line_separator)\n {\n-- \ngitgitgadget\n\n"},{"id":"402985","messageId":"feace82752199aea87e728c340e9a7c9f0b2a2fa.1596664306.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"[PATCH v2 9/9] ref-filter: add `sanitize` option for 'subject' atom","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-05T21:51:45Z","receivedAt":"2020-08-05T21:52:08Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nCurrently, subject does not take any arguments. This commit introduce\n`sanitize` formatting option to 'subject' atom.\n\n`subject:sanitize` - print sanitized subject line, suitable for a filename.\n\ne.g.\n%(subject): \"the subject line\"\n%(subject:sanitize): \"the-subject-line\"\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 +++\n ref-filter.c                       | 24 ++++++++++++++++--------\n t/t6300-for-each-ref.sh            |  7 +++++++\n 3 files changed, 26 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex dd09763e7d..616ce46087 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -247,6 +247,9 @@ contents:subject::\n \tThe first paragraph of the message, which typically is a\n \tsingle line, is taken as the \"subject\" of the commit or the\n \ttag message.\n+\tInstead of `contents:subject`, field `subject` can also be used to\n+\tobtain same results. `:sanitize` can be appended to `subject` for\n+\tsubject line suitable for filename.\n \n contents:body::\n \tThe remainder of the commit or the tag message that follows\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 6d5bbb14a2..016a03ef20 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -23,6 +23,7 @@\n #include \"worktree.h\"\n #include \"hashmap.h\"\n #include \"argv-array.h\"\n+#include \"format-support.h\"\n \n static struct ref_msg {\n \tconst char *gone;\n@@ -127,8 +128,8 @@ static struct used_atom {\n \t\t\tunsigned int nobracket : 1, push : 1, push_remote : 1;\n \t\t} remote_ref;\n \t\tstruct {\n-\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH,\n-\t\t\t       C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n+\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH, C_LINES,\n+\t\t\t       C_SIG, C_SUB, C_SUB_SANITIZE, C_TRAILERS } option;\n \t\t\tstruct process_trailer_options trailer_opts;\n \t\t\tunsigned int nlines;\n \t\t} contents;\n@@ -301,9 +302,12 @@ static int body_atom_parser(const struct ref_format *format, struct used_atom *a\n static int subject_atom_parser(const struct ref_format *format, struct used_atom *atom,\n \t\t\t       const char *arg, struct strbuf *err)\n {\n-\tif (arg)\n-\t\treturn strbuf_addf_ret(err, -1, _(\"%%(subject) does not take arguments\"));\n-\tatom->u.contents.option = C_SUB;\n+\tif (!arg)\n+\t\tatom->u.contents.option = C_SUB;\n+\telse if (!strcmp(arg, \"sanitize\"))\n+\t\tatom->u.contents.option = C_SUB_SANITIZE;\n+\telse\n+\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized %%(subject) argument: %s\"), arg);\n \treturn 0;\n }\n \n@@ -1282,8 +1286,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n \t\t\tcontinue;\n \t\tif (deref)\n \t\t\tname++;\n-\t\tif (strcmp(name, \"subject\") &&\n-\t\t    strcmp(name, \"body\") &&\n+\t\tif (strcmp(name, \"body\") &&\n+\t\t    !starts_with(name, \"subject\") &&\n \t\t    !starts_with(name, \"trailers\") &&\n \t\t    !starts_with(name, \"contents\"))\n \t\t\tcontinue;\n@@ -1295,7 +1299,11 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n \n \t\tif (atom->u.contents.option == C_SUB)\n \t\t\tv->s = copy_subject(subpos, sublen);\n-\t\telse if (atom->u.contents.option == C_BODY_DEP)\n+\t\telse if (atom->u.contents.option == C_SUB_SANITIZE) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tformat_sanitized_subject(&sb, subpos, sublen);\n+\t\t\tv->s = strbuf_detach(&sb, NULL);\n+\t\t} else if (atom->u.contents.option == C_BODY_DEP)\n \t\t\tv->s = xmemdupz(bodypos, bodylen);\n \t\telse if (atom->u.contents.option == C_LENGTH)\n \t\t\tv->s = xstrfmt(\"%\"PRIuMAX, (uintmax_t)strlen(subpos));\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 79d5b29387..220ff5c3c2 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -150,6 +150,7 @@ test_atom head taggerdate ''\n test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n test_atom head subject 'Initial'\n+test_atom head subject:sanitize 'Initial'\n test_atom head contents:subject 'Initial'\n test_atom head body ''\n test_atom head contents:body ''\n@@ -207,6 +208,7 @@ test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag subject 'Tagging at 1151968727'\n+test_atom tag subject:sanitize 'Tagging-at-1151968727'\n test_atom tag contents:subject 'Tagging at 1151968727'\n test_atom tag body ''\n test_atom tag contents:body ''\n@@ -619,6 +621,7 @@ test_expect_success 'create tag with subject and body content' '\n \tgit tag -F msg subject-body\n '\n test_atom refs/tags/subject-body subject 'the subject line'\n+test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n test_atom refs/tags/subject-body body 'first body line\n second body line\n '\n@@ -639,6 +642,7 @@ test_expect_success 'create tag with multiline subject' '\n \tgit tag -F msg multiline\n '\n test_atom refs/tags/multiline subject 'first subject line second subject line'\n+test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n test_atom refs/tags/multiline body 'first body line\n second body line\n@@ -671,6 +675,7 @@ sig='-----BEGIN PGP SIGNATURE-----\n \n PREREQ=GPG\n test_atom refs/tags/signed-empty subject ''\n+test_atom refs/tags/signed-empty subject:sanitize ''\n test_atom refs/tags/signed-empty contents:subject ''\n test_atom refs/tags/signed-empty body \"$sig\"\n test_atom refs/tags/signed-empty contents:body ''\n@@ -678,6 +683,7 @@ test_atom refs/tags/signed-empty contents:signature \"$sig\"\n test_atom refs/tags/signed-empty contents \"$sig\"\n \n test_atom refs/tags/signed-short subject 'subject line'\n+test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n test_atom refs/tags/signed-short contents:subject 'subject line'\n test_atom refs/tags/signed-short body \"$sig\"\n test_atom refs/tags/signed-short contents:body ''\n@@ -686,6 +692,7 @@ test_atom refs/tags/signed-short contents \"subject line\n $sig\"\n \n test_atom refs/tags/signed-long subject 'subject line'\n+test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n test_atom refs/tags/signed-long contents:subject 'subject line'\n test_atom refs/tags/signed-long body \"body contents\n $sig\"\n-- \ngitgitgadget\n"},{"id":"402986","messageId":"95035765a00b4d553c2c43773bc1ab65c3c2ede9.1596664306.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"[PATCH v2 7/9] pretty: refactor `format_sanitized_subject()`","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-05T21:51:43Z","receivedAt":"2020-08-05T21:52:09Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nThe function 'format_sanitized_subject()' is responsible for\nsanitized subject line in pretty.c\ne.g.\nthe subject line\nthe-sanitized-subject-line\n\nIt would be a nice enhancement to `subject` atom to have the\nsame feature. So in the later commits, we plan to add this feature\nto ref-filter.\n\nRefactor `format_sanitized_subject()`, so it can be reused in\nref-filter.c for adding new modifier `sanitize` to \"subject\" atom.\n\nCurrently, the loop inside `format_sanitized_subject()` runs\nuntil `\\n` is found. But now, we stored the first occurrence\nof `\\n` in a variable `eol` and passed it in\n`format_sanitized_subject()`. And the loop runs upto `eol`.\n\nBut this change isn't sufficient to reuse this function in\nref-filter.c because there exist tags with multiline subject.\n\nIt's wise to replace `\\n` with ' ', if `format_sanitized_subject()`\nencounters `\\n` before end of subject line, just like `copy_subject()`.\nBecause we'll be only using `format_sanitized_subject()` for\n\"%(subject:sanitize)\", instead of `copy_subject()` and\n`format_sanitized_subject()` both. So, added the code:\n```\nif (char == '\\n') /* never true if called inside pretty.c */\n    char = ' ';\n```\n\nNow, it's ready to be reused in ref-filter.c\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n pretty.c | 24 +++++++++++++++---------\n 1 file changed, 15 insertions(+), 9 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 2a3d46bf42..8d08e8278a 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -839,24 +839,29 @@ static int istitlechar(char c)\n \t\t(c >= '0' && c <= '9') || c == '.' || c == '_';\n }\n \n-static void format_sanitized_subject(struct strbuf *sb, const char *msg)\n+static void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n {\n+\tchar *r = xmemdupz(msg, len);\n \tsize_t trimlen;\n \tsize_t start_len = sb->len;\n \tint space = 2;\n+\tint i;\n \n-\tfor (; *msg && *msg != '\\n'; msg++) {\n-\t\tif (istitlechar(*msg)) {\n+\tfor (i = 0; i < len; i++) {\n+\t\tif (r[i] == '\\n')\n+\t\t\tr[i] = ' ';\n+\t\tif (istitlechar(r[i])) {\n \t\t\tif (space == 1)\n \t\t\t\tstrbuf_addch(sb, '-');\n \t\t\tspace = 0;\n-\t\t\tstrbuf_addch(sb, *msg);\n-\t\t\tif (*msg == '.')\n-\t\t\t\twhile (*(msg+1) == '.')\n-\t\t\t\t\tmsg++;\n+\t\t\tstrbuf_addch(sb, r[i]);\n+\t\t\tif (r[i] == '.')\n+\t\t\t\twhile (r[i+1] == '.')\n+\t\t\t\t\ti++;\n \t\t} else\n \t\t\tspace |= 1;\n \t}\n+\tfree(r);\n \n \t/* trim any trailing '.' or '-' characters */\n \ttrimlen = 0;\n@@ -1155,7 +1160,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \tconst struct commit *commit = c->commit;\n \tconst char *msg = c->message;\n \tstruct commit_list *p;\n-\tconst char *arg;\n+\tconst char *arg, *eol;\n \tsize_t res;\n \tchar **slot;\n \n@@ -1405,7 +1410,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\tformat_subject(sb, msg + c->subject_off, \" \");\n \t\treturn 1;\n \tcase 'f':\t/* sanitized subject */\n-\t\tformat_sanitized_subject(sb, msg + c->subject_off);\n+\t\teol = strchrnul(msg + c->subject_off, '\\n');\n+\t\tformat_sanitized_subject(sb, msg + c->subject_off, eol - (msg + c->subject_off));\n \t\treturn 1;\n \tcase 'b':\t/* body */\n \t\tstrbuf_addstr(sb, msg + c->body_off);\n-- \ngitgitgadget\n\n"},{"id":"402987","messageId":"xmqqtuxgn408.fsf@gitster.c.googlers.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 0/9] [GSoC] Improvements to ref-filter","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-08-05T22:04:39Z","receivedAt":"2020-08-05T22:04:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Hariom Verma via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n>      -@@ ref-filter.c: static struct ref_to_worktree_map {\n>      - \tstruct worktree **worktrees;\n>      - } ref_to_worktree_map;\n>      +@@ ref-filter.c: static struct used_atom {\n>      + \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n>      + \t\t\tunsigned int length;\n>      + \t\t} objectname;\n>      ++\t\tstruct email_option {\n>      ++\t\t\tenum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n>      ++\t\t} email_option;\n>      + \t\tstruct refname_atom refname;\n>      + \t\tchar *head;\n>      + \t} u;\n\nI'll try to find enough time to read the body of the series sometime\nlater this week, but this interdiff alone smells that this is much\ncloser to being correct (no, I am not saying I spotted a bug, but it\ncertainly looks liek it is on the right track, relative to what I\nsaw the last time, to be right).\n\nA good test for this new feature may be to try using\n\n\t\"<%(authoremail:localpart)> <%(committeremail:trim)>\"\n\nas a format to make sure e-mail options are done per-atom.\n\nThanks.\n"},{"id":"403058","messageId":"CA+CkUQ-kRjBRTOKC-fuY7p6tOZFTaMddNseOjbZV_Mf6_F2mDA@mail.gmail.com","threadId":"53931","inReplyTo":"xmqqtuxgn408.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v2 0/9] [GSoC] Improvements to ref-filter","fromName":"Hariom verma","fromEmail":"hariom18599@gmail.com","sentAt":"2020-08-06T13:47:34Z","receivedAt":"2020-08-06T17:22:52Z","isPatch":true,"sender":{"key":"hariom18599@gmail.com","avatar":"https://avatars.githubusercontent.com/u/37576387?v=4"},"body":"Hi,\n\nOn Thu, Aug 6, 2020 at 3:34 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Hariom Verma via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> >      -@@ ref-filter.c: static struct ref_to_worktree_map {\n> >      -        struct worktree **worktrees;\n> >      - } ref_to_worktree_map;\n> >      +@@ ref-filter.c: static struct used_atom {\n> >      +                        enum { O_FULL, O_LENGTH, O_SHORT } option;\n> >      +                        unsigned int length;\n> >      +                } objectname;\n> >      ++               struct email_option {\n> >      ++                       enum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n> >      ++               } email_option;\n> >      +                struct refname_atom refname;\n> >      +                char *head;\n> >      +        } u;\n>\n> I'll try to find enough time to read the body of the series sometime\n> later this week, but this interdiff alone smells that this is much\n> closer to being correct (no, I am not saying I spotted a bug, but it\n> certainly looks liek it is on the right track, relative to what I\n> saw the last time, to be right).\n\nThanks. I'll wait for your review.\n\n> A good test for this new feature may be to try using\n>\n>         \"<%(authoremail:localpart)> <%(committeremail:trim)>\"\n>\n> as a format to make sure e-mail options are done per-atom.\n\nYeah. This test will surely make its way to t6300 in the next version\nof this patch series.\n\nThanks,\nHariom\n"},{"id":"403854","messageId":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v2.git.1596664305.gitgitgadget@gmail.com","subject":"[PATCH v3 0/9] [Resend][GSoC] Improvements to ref-filter","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T18:10:13Z","receivedAt":"2020-08-17T18:10:36Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"This is the first patch series that introduces some improvements and\nfeatures to file ref-filter.{c,h}. These changes are useful to ref-filter,\nbut in near future also will allow us to use ref-filter's logic in pretty.c\n\nI plan to add more to format-support.{c,h} in the upcoming patch series.\nThat will lead to more improved and feature-rich ref-filter.c\n\n\n----------------------------------------------------------------------------\n\nI just rebased the branch with master and fixed some merge conflicts. Only\nthe version number has been incremented, there are no changes in the\npatches. Original patch series: \nhttps://public-inbox.org/git/pull.684.v2.git.1596664305.gitgitgadget@gmail.com/#t\n\nHariom Verma (9):\n  ref-filter: support different email formats\n  ref-filter: refactor `grab_objectname()`\n  ref-filter: modify error messages in `grab_objectname()`\n  ref-filter: rename `objectname` related functions and fields\n  ref-filter: add `short` modifier to 'tree' atom\n  ref-filter: add `short` modifier to 'parent' atom\n  pretty: refactor `format_sanitized_subject()`\n  format-support: move `format_sanitized_subject()` from pretty\n  ref-filter: add `sanitize` option for 'subject' atom\n\n Documentation/git-for-each-ref.txt |  10 +-\n Makefile                           |   1 +\n format-support.c                   |  43 ++++++++\n format-support.h                   |   6 ++\n pretty.c                           |  40 +-------\n ref-filter.c                       | 159 +++++++++++++++++++----------\n t/t6300-for-each-ref.sh            |  35 +++++++\n 7 files changed, 202 insertions(+), 92 deletions(-)\n create mode 100644 format-support.c\n create mode 100644 format-support.h\n\n\nbase-commit: 878e727637ec5815ccb3301eb994a54df95b21b8\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-684%2Fharry-hov%2Fonly-rf6-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-684/harry-hov/only-rf6-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/684\n\nRange-diff vs v2:\n\n  1:  78e69032df =  1:  3e6fc66a46 ref-filter: support different email formats\n  2:  b6b6acab9a =  2:  5268b973da ref-filter: refactor `grab_objectname()`\n  3:  65fee332a3 =  3:  4a12ff8210 ref-filter: modify error messages in `grab_objectname()`\n  4:  976f2041a4 =  4:  d53ca56778 ref-filter: rename `objectname` related functions and fields\n  5:  dda7400b14 =  5:  fd4ed82e80 ref-filter: add `short` modifier to 'tree' atom\n  6:  764bb23b59 =  6:  7a039823de ref-filter: add `short` modifier to 'parent' atom\n  7:  95035765a0 =  7:  0ad22c7cdd pretty: refactor `format_sanitized_subject()`\n  8:  1c43f55d7c =  8:  7a64495f99 format-support: move `format_sanitized_subject()` from pretty\n  9:  feace82752 !  9:  1ab35e9251 ref-filter: add `sanitize` option for 'subject' atom\n     @@ ref-filter.c\n      @@\n       #include \"worktree.h\"\n       #include \"hashmap.h\"\n     - #include \"argv-array.h\"\n     + #include \"strvec.h\"\n      +#include \"format-support.h\"\n       \n       static struct ref_msg {\n\n-- \ngitgitgadget\n"},{"id":"403855","messageId":"5268b973dace3f453821a7800b5ec0dd0dfbe848.1597687822.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"[PATCH v3 2/9] ref-filter: refactor `grab_objectname()`","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T18:10:15Z","receivedAt":"2020-08-17T18:10:40Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nPrepares `grab_objectname()` for more generic usage.\nThis change will allow us to reuse `grab_objectname()` for\nthe `tree` and `parent` atoms in a following commit.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c | 36 +++++++++++++++++++++---------------\n 1 file changed, 21 insertions(+), 15 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex e60765f156..9bf92db6df 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -918,21 +918,27 @@ int verify_ref_format(struct ref_format *format)\n \treturn 0;\n }\n \n-static int grab_objectname(const char *name, const struct object_id *oid,\n+static const char *do_grab_objectname(const char *field, const struct object_id *oid,\n+\t\t\t\t      struct used_atom *atom)\n+{\n+\tswitch (atom->u.objectname.option) {\n+\tcase O_FULL:\n+\t\treturn oid_to_hex(oid);\n+\tcase O_LENGTH:\n+\t\treturn find_unique_abbrev(oid, atom->u.objectname.length);\n+\tcase O_SHORT:\n+\t\treturn find_unique_abbrev(oid, DEFAULT_ABBREV);\n+\tdefault:\n+\t\tBUG(\"unknown %%(%s) option\", field);\n+\t}\n+}\n+\n+static int grab_objectname(const char *name, const char *field, const struct object_id *oid,\n \t\t\t   struct atom_value *v, struct used_atom *atom)\n {\n-\tif (starts_with(name, \"objectname\")) {\n-\t\tif (atom->u.objectname.option == O_SHORT) {\n-\t\t\tv->s = xstrdup(find_unique_abbrev(oid, DEFAULT_ABBREV));\n-\t\t\treturn 1;\n-\t\t} else if (atom->u.objectname.option == O_FULL) {\n-\t\t\tv->s = xstrdup(oid_to_hex(oid));\n-\t\t\treturn 1;\n-\t\t} else if (atom->u.objectname.option == O_LENGTH) {\n-\t\t\tv->s = xstrdup(find_unique_abbrev(oid, atom->u.objectname.length));\n-\t\t\treturn 1;\n-\t\t} else\n-\t\t\tBUG(\"unknown %%(objectname) option\");\n+\tif (starts_with(name, field)) {\n+\t\tv->s = xstrdup(do_grab_objectname(field, oid, atom));\n+\t\treturn 1;\n \t}\n \treturn 0;\n }\n@@ -960,7 +966,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_\n \t\t} else if (!strcmp(name, \"deltabase\"))\n \t\t\tv->s = xstrdup(oid_to_hex(&oi->delta_base_oid));\n \t\telse if (deref)\n-\t\t\tgrab_objectname(name, &oi->oid, v, &used_atom[i]);\n+\t\t\tgrab_objectname(name, \"objectname\", &oi->oid, v, &used_atom[i]);\n \t}\n }\n \n@@ -1740,7 +1746,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)\n \t\t\t\tv->s = xstrdup(buf + 1);\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (!deref && grab_objectname(name, &ref->objectname, v, atom)) {\n+\t\t} else if (!deref && grab_objectname(name, \"objectname\", &ref->objectname, v, atom)) {\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"HEAD\")) {\n \t\t\tif (atom->u.head && !strcmp(ref->refname, atom->u.head))\n-- \ngitgitgadget\n\n"},{"id":"403856","messageId":"4a12ff821020ceab5195e406461e00cecbb8e1fb.1597687822.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"[PATCH v3 3/9] ref-filter: modify error messages in `grab_objectname()`","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T18:10:16Z","receivedAt":"2020-08-17T18:10:51Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nAs we plan to use `grab_objectname()` for `tree` and `parent` atom,\nit's better to parameterize the error messages in the function\n`grab_objectname()` where \"objectname\" is hard coded.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 9bf92db6df..4f4591cad0 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -372,11 +372,11 @@ static int objectname_atom_parser(const struct ref_format *format, struct used_a\n \t\tatom->u.objectname.option = O_LENGTH;\n \t\tif (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n \t\t    atom->u.objectname.length == 0)\n-\t\t\treturn strbuf_addf_ret(err, -1, _(\"positive value expected objectname:short=%s\"), arg);\n+\t\t\treturn strbuf_addf_ret(err, -1, _(\"positive value expected '%s' in %%(%s)\"), arg, atom->name);\n \t\tif (atom->u.objectname.length < MINIMUM_ABBREV)\n \t\t\tatom->u.objectname.length = MINIMUM_ABBREV;\n \t} else\n-\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized %%(objectname) argument: %s\"), arg);\n+\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized argument '%s' in %%(%s)\"), arg, atom->name);\n \treturn 0;\n }\n \n-- \ngitgitgadget\n\n"},{"id":"403857","messageId":"3e6fc66a4668a8abe1eefbbce352ccd30abc096b.1597687822.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"[PATCH v3 1/9] ref-filter: support different email formats","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T18:10:14Z","receivedAt":"2020-08-17T18:10:56Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nCurrently, ref-filter only supports printing email with angle brackets.\n\nLet's add support for two more email options.\n- trim : for email without angle brackets.\n- localpart : for the part before the @ sign out of trimmed email\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  5 ++-\n ref-filter.c                       | 54 +++++++++++++++++++++++++-----\n t/t6300-for-each-ref.sh            | 16 +++++++++\n 3 files changed, 65 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 2ea71c5f6c..e6ce8af612 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -230,7 +230,10 @@ These are intended for working on a mix of annotated and lightweight tags.\n \n Fields that have name-email-date tuple as its value (`author`,\n `committer`, and `tagger`) can be suffixed with `name`, `email`,\n-and `date` to extract the named component.\n+and `date` to extract the named component.  For email fields (`authoremail`,\n+`committeremail` and `taggeremail`), `:trim` can be appended to get the email\n+without angle brackets, and `:localpart` to get the part before the `@` symbol\n+out of the trimmed email.\n \n The message in a commit or a tag object is `contents`, from which\n `contents:<part>` can be used to extract various parts out of:\ndiff --git a/ref-filter.c b/ref-filter.c\nindex ba85869755..e60765f156 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -140,6 +140,9 @@ static struct used_atom {\n \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n \t\t\tunsigned int length;\n \t\t} objectname;\n+\t\tstruct email_option {\n+\t\t\tenum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n+\t\t} email_option;\n \t\tstruct refname_atom refname;\n \t\tchar *head;\n \t} u;\n@@ -377,6 +380,20 @@ static int objectname_atom_parser(const struct ref_format *format, struct used_a\n \treturn 0;\n }\n \n+static int person_email_atom_parser(const struct ref_format *format, struct used_atom *atom,\n+\t\t\t\t    const char *arg, struct strbuf *err)\n+{\n+\tif (!arg)\n+\t\tatom->u.email_option.option = EO_RAW;\n+\telse if (!strcmp(arg, \"trim\"))\n+\t\tatom->u.email_option.option = EO_TRIM;\n+\telse if (!strcmp(arg, \"localpart\"))\n+\t\tatom->u.email_option.option = EO_LOCALPART;\n+\telse\n+\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized email option: %s\"), arg);\n+\treturn 0;\n+}\n+\n static int refname_atom_parser(const struct ref_format *format, struct used_atom *atom,\n \t\t\t       const char *arg, struct strbuf *err)\n {\n@@ -488,15 +505,15 @@ static struct {\n \t{ \"tag\", SOURCE_OBJ },\n \t{ \"author\", SOURCE_OBJ },\n \t{ \"authorname\", SOURCE_OBJ },\n-\t{ \"authoremail\", SOURCE_OBJ },\n+\t{ \"authoremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n \t{ \"authordate\", SOURCE_OBJ, FIELD_TIME },\n \t{ \"committer\", SOURCE_OBJ },\n \t{ \"committername\", SOURCE_OBJ },\n-\t{ \"committeremail\", SOURCE_OBJ },\n+\t{ \"committeremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n \t{ \"committerdate\", SOURCE_OBJ, FIELD_TIME },\n \t{ \"tagger\", SOURCE_OBJ },\n \t{ \"taggername\", SOURCE_OBJ },\n-\t{ \"taggeremail\", SOURCE_OBJ },\n+\t{ \"taggeremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n \t{ \"taggerdate\", SOURCE_OBJ, FIELD_TIME },\n \t{ \"creator\", SOURCE_OBJ },\n \t{ \"creatordate\", SOURCE_OBJ, FIELD_TIME },\n@@ -1037,16 +1054,35 @@ static const char *copy_name(const char *buf)\n \treturn xstrdup(\"\");\n }\n \n-static const char *copy_email(const char *buf)\n+static const char *copy_email(const char *buf, struct used_atom *atom)\n {\n \tconst char *email = strchr(buf, '<');\n \tconst char *eoemail;\n \tif (!email)\n \t\treturn xstrdup(\"\");\n-\teoemail = strchr(email, '>');\n+\tswitch (atom->u.email_option.option) {\n+\tcase EO_RAW:\n+\t\teoemail = strchr(email, '>');\n+\t\tif (eoemail)\n+\t\t\teoemail++;\n+\t\tbreak;\n+\tcase EO_TRIM:\n+\t\temail++;\n+\t\teoemail = strchr(email, '>');\n+\t\tbreak;\n+\tcase EO_LOCALPART:\n+\t\temail++;\n+\t\teoemail = strchr(email, '@');\n+\t\tif (!eoemail)\n+\t\t\teoemail = strchr(email, '>');\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"unknown email option\");\n+\t}\n+\n \tif (!eoemail)\n \t\treturn xstrdup(\"\");\n-\treturn xmemdupz(email, eoemail + 1 - email);\n+\treturn xmemdupz(email, eoemail - email);\n }\n \n static char *copy_subject(const char *buf, unsigned long len)\n@@ -1116,7 +1152,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n \t\t\tcontinue;\n \t\tif (name[wholen] != 0 &&\n \t\t    strcmp(name + wholen, \"name\") &&\n-\t\t    strcmp(name + wholen, \"email\") &&\n+\t\t    !starts_with(name + wholen, \"email\") &&\n \t\t    !starts_with(name + wholen, \"date\"))\n \t\t\tcontinue;\n \t\tif (!wholine)\n@@ -1127,8 +1163,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n \t\t\tv->s = copy_line(wholine);\n \t\telse if (!strcmp(name + wholen, \"name\"))\n \t\t\tv->s = copy_name(wholine);\n-\t\telse if (!strcmp(name + wholen, \"email\"))\n-\t\t\tv->s = copy_email(wholine);\n+\t\telse if (starts_with(name + wholen, \"email\"))\n+\t\t\tv->s = copy_email(wholine, &used_atom[i]);\n \t\telse if (starts_with(name + wholen, \"date\"))\n \t\t\tgrab_date(wholine, v, name);\n \t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex a83579fbdf..64fbc91146 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -125,15 +125,21 @@ test_atom head '*objecttype' ''\n test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n test_atom head authorname 'A U Thor'\n test_atom head authoremail '<author@example.com>'\n+test_atom head authoremail:trim 'author@example.com'\n+test_atom head authoremail:localpart 'author'\n test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head committername 'C O Mitter'\n test_atom head committeremail '<committer@example.com>'\n+test_atom head committeremail:trim 'committer@example.com'\n+test_atom head committeremail:localpart 'committer'\n test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n test_atom head tag ''\n test_atom head tagger ''\n test_atom head taggername ''\n test_atom head taggeremail ''\n+test_atom head taggeremail:trim ''\n+test_atom head taggeremail:localpart ''\n test_atom head taggerdate ''\n test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n@@ -170,15 +176,21 @@ test_atom tag '*objecttype' 'commit'\n test_atom tag author ''\n test_atom tag authorname ''\n test_atom tag authoremail ''\n+test_atom tag authoremail:trim ''\n+test_atom tag authoremail:localpart ''\n test_atom tag authordate ''\n test_atom tag committer ''\n test_atom tag committername ''\n test_atom tag committeremail ''\n+test_atom tag committeremail:trim ''\n+test_atom tag committeremail:localpart ''\n test_atom tag committerdate ''\n test_atom tag tag 'testtag'\n test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag taggername 'C O Mitter'\n test_atom tag taggeremail '<committer@example.com>'\n+test_atom tag taggeremail:trim 'committer@example.com'\n+test_atom tag taggeremail:localpart 'committer'\n test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n@@ -564,10 +576,14 @@ test_atom refs/tags/taggerless tag 'taggerless'\n test_atom refs/tags/taggerless tagger ''\n test_atom refs/tags/taggerless taggername ''\n test_atom refs/tags/taggerless taggeremail ''\n+test_atom refs/tags/taggerless taggeremail:trim ''\n+test_atom refs/tags/taggerless taggeremail:localpart ''\n test_atom refs/tags/taggerless taggerdate ''\n test_atom refs/tags/taggerless committer ''\n test_atom refs/tags/taggerless committername ''\n test_atom refs/tags/taggerless committeremail ''\n+test_atom refs/tags/taggerless committeremail:trim ''\n+test_atom refs/tags/taggerless committeremail:localpart ''\n test_atom refs/tags/taggerless committerdate ''\n test_atom refs/tags/taggerless subject 'Broken tag'\n \n-- \ngitgitgadget\n\n"},{"id":"403858","messageId":"d53ca56778a789dfef59bd4f0e06122c065f40d2.1597687822.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"[PATCH v3 4/9] ref-filter: rename `objectname` related functions and fields","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T18:10:17Z","receivedAt":"2020-08-17T18:11:06Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nIn previous commits, we prepared some `objectname` related functions\nfor more generic usage, so that these functions can be used for `tree`\nand `parent` atom.\n\nBut the name of some functions and fields may mislead someone.\nFor ex: function `objectname_atom_parser()` implies that it is\nfor atom `objectname`.\n\nLet's rename all such functions and fields.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c | 40 ++++++++++++++++++++--------------------\n 1 file changed, 20 insertions(+), 20 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 4f4591cad0..066975b306 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -139,7 +139,7 @@ static struct used_atom {\n \t\tstruct {\n \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n \t\t\tunsigned int length;\n-\t\t} objectname;\n+\t\t} oid;\n \t\tstruct email_option {\n \t\t\tenum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n \t\t} email_option;\n@@ -361,20 +361,20 @@ static int contents_atom_parser(const struct ref_format *format, struct used_ato\n \treturn 0;\n }\n \n-static int objectname_atom_parser(const struct ref_format *format, struct used_atom *atom,\n-\t\t\t\t  const char *arg, struct strbuf *err)\n+static int oid_atom_parser(const struct ref_format *format, struct used_atom *atom,\n+\t\t\t   const char *arg, struct strbuf *err)\n {\n \tif (!arg)\n-\t\tatom->u.objectname.option = O_FULL;\n+\t\tatom->u.oid.option = O_FULL;\n \telse if (!strcmp(arg, \"short\"))\n-\t\tatom->u.objectname.option = O_SHORT;\n+\t\tatom->u.oid.option = O_SHORT;\n \telse if (skip_prefix(arg, \"short=\", &arg)) {\n-\t\tatom->u.objectname.option = O_LENGTH;\n-\t\tif (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n-\t\t    atom->u.objectname.length == 0)\n+\t\tatom->u.oid.option = O_LENGTH;\n+\t\tif (strtoul_ui(arg, 10, &atom->u.oid.length) ||\n+\t\t    atom->u.oid.length == 0)\n \t\t\treturn strbuf_addf_ret(err, -1, _(\"positive value expected '%s' in %%(%s)\"), arg, atom->name);\n-\t\tif (atom->u.objectname.length < MINIMUM_ABBREV)\n-\t\t\tatom->u.objectname.length = MINIMUM_ABBREV;\n+\t\tif (atom->u.oid.length < MINIMUM_ABBREV)\n+\t\t\tatom->u.oid.length = MINIMUM_ABBREV;\n \t} else\n \t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized argument '%s' in %%(%s)\"), arg, atom->name);\n \treturn 0;\n@@ -495,7 +495,7 @@ static struct {\n \t{ \"refname\", SOURCE_NONE, FIELD_STR, refname_atom_parser },\n \t{ \"objecttype\", SOURCE_OTHER, FIELD_STR, objecttype_atom_parser },\n \t{ \"objectsize\", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser },\n-\t{ \"objectname\", SOURCE_OTHER, FIELD_STR, objectname_atom_parser },\n+\t{ \"objectname\", SOURCE_OTHER, FIELD_STR, oid_atom_parser },\n \t{ \"deltabase\", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },\n \t{ \"tree\", SOURCE_OBJ },\n \t{ \"parent\", SOURCE_OBJ },\n@@ -918,14 +918,14 @@ int verify_ref_format(struct ref_format *format)\n \treturn 0;\n }\n \n-static const char *do_grab_objectname(const char *field, const struct object_id *oid,\n-\t\t\t\t      struct used_atom *atom)\n+static const char *do_grab_oid(const char *field, const struct object_id *oid,\n+\t\t\t       struct used_atom *atom)\n {\n-\tswitch (atom->u.objectname.option) {\n+\tswitch (atom->u.oid.option) {\n \tcase O_FULL:\n \t\treturn oid_to_hex(oid);\n \tcase O_LENGTH:\n-\t\treturn find_unique_abbrev(oid, atom->u.objectname.length);\n+\t\treturn find_unique_abbrev(oid, atom->u.oid.length);\n \tcase O_SHORT:\n \t\treturn find_unique_abbrev(oid, DEFAULT_ABBREV);\n \tdefault:\n@@ -933,11 +933,11 @@ static const char *do_grab_objectname(const char *field, const struct object_id\n \t}\n }\n \n-static int grab_objectname(const char *name, const char *field, const struct object_id *oid,\n-\t\t\t   struct atom_value *v, struct used_atom *atom)\n+static int grab_oid(const char *name, const char *field, const struct object_id *oid,\n+\t\t    struct atom_value *v, struct used_atom *atom)\n {\n \tif (starts_with(name, field)) {\n-\t\tv->s = xstrdup(do_grab_objectname(field, oid, atom));\n+\t\tv->s = xstrdup(do_grab_oid(field, oid, atom));\n \t\treturn 1;\n \t}\n \treturn 0;\n@@ -966,7 +966,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_\n \t\t} else if (!strcmp(name, \"deltabase\"))\n \t\t\tv->s = xstrdup(oid_to_hex(&oi->delta_base_oid));\n \t\telse if (deref)\n-\t\t\tgrab_objectname(name, \"objectname\", &oi->oid, v, &used_atom[i]);\n+\t\t\tgrab_oid(name, \"objectname\", &oi->oid, v, &used_atom[i]);\n \t}\n }\n \n@@ -1746,7 +1746,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)\n \t\t\t\tv->s = xstrdup(buf + 1);\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (!deref && grab_objectname(name, \"objectname\", &ref->objectname, v, atom)) {\n+\t\t} else if (!deref && grab_oid(name, \"objectname\", &ref->objectname, v, atom)) {\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"HEAD\")) {\n \t\t\tif (atom->u.head && !strcmp(ref->refname, atom->u.head))\n-- \ngitgitgadget\n\n"},{"id":"403859","messageId":"0ad22c7cdd3c692aa5b46444e64a3b76f1e87b4c.1597687822.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"[PATCH v3 7/9] pretty: refactor `format_sanitized_subject()`","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T18:10:20Z","receivedAt":"2020-08-17T18:11:23Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nThe function 'format_sanitized_subject()' is responsible for\nsanitized subject line in pretty.c\ne.g.\nthe subject line\nthe-sanitized-subject-line\n\nIt would be a nice enhancement to `subject` atom to have the\nsame feature. So in the later commits, we plan to add this feature\nto ref-filter.\n\nRefactor `format_sanitized_subject()`, so it can be reused in\nref-filter.c for adding new modifier `sanitize` to \"subject\" atom.\n\nCurrently, the loop inside `format_sanitized_subject()` runs\nuntil `\\n` is found. But now, we stored the first occurrence\nof `\\n` in a variable `eol` and passed it in\n`format_sanitized_subject()`. And the loop runs upto `eol`.\n\nBut this change isn't sufficient to reuse this function in\nref-filter.c because there exist tags with multiline subject.\n\nIt's wise to replace `\\n` with ' ', if `format_sanitized_subject()`\nencounters `\\n` before end of subject line, just like `copy_subject()`.\nBecause we'll be only using `format_sanitized_subject()` for\n\"%(subject:sanitize)\", instead of `copy_subject()` and\n`format_sanitized_subject()` both. So, added the code:\n```\nif (char == '\\n') /* never true if called inside pretty.c */\n    char = ' ';\n```\n\nNow, it's ready to be reused in ref-filter.c\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n pretty.c | 24 +++++++++++++++---------\n 1 file changed, 15 insertions(+), 9 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 2a3d46bf42..8d08e8278a 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -839,24 +839,29 @@ static int istitlechar(char c)\n \t\t(c >= '0' && c <= '9') || c == '.' || c == '_';\n }\n \n-static void format_sanitized_subject(struct strbuf *sb, const char *msg)\n+static void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n {\n+\tchar *r = xmemdupz(msg, len);\n \tsize_t trimlen;\n \tsize_t start_len = sb->len;\n \tint space = 2;\n+\tint i;\n \n-\tfor (; *msg && *msg != '\\n'; msg++) {\n-\t\tif (istitlechar(*msg)) {\n+\tfor (i = 0; i < len; i++) {\n+\t\tif (r[i] == '\\n')\n+\t\t\tr[i] = ' ';\n+\t\tif (istitlechar(r[i])) {\n \t\t\tif (space == 1)\n \t\t\t\tstrbuf_addch(sb, '-');\n \t\t\tspace = 0;\n-\t\t\tstrbuf_addch(sb, *msg);\n-\t\t\tif (*msg == '.')\n-\t\t\t\twhile (*(msg+1) == '.')\n-\t\t\t\t\tmsg++;\n+\t\t\tstrbuf_addch(sb, r[i]);\n+\t\t\tif (r[i] == '.')\n+\t\t\t\twhile (r[i+1] == '.')\n+\t\t\t\t\ti++;\n \t\t} else\n \t\t\tspace |= 1;\n \t}\n+\tfree(r);\n \n \t/* trim any trailing '.' or '-' characters */\n \ttrimlen = 0;\n@@ -1155,7 +1160,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \tconst struct commit *commit = c->commit;\n \tconst char *msg = c->message;\n \tstruct commit_list *p;\n-\tconst char *arg;\n+\tconst char *arg, *eol;\n \tsize_t res;\n \tchar **slot;\n \n@@ -1405,7 +1410,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\tformat_subject(sb, msg + c->subject_off, \" \");\n \t\treturn 1;\n \tcase 'f':\t/* sanitized subject */\n-\t\tformat_sanitized_subject(sb, msg + c->subject_off);\n+\t\teol = strchrnul(msg + c->subject_off, '\\n');\n+\t\tformat_sanitized_subject(sb, msg + c->subject_off, eol - (msg + c->subject_off));\n \t\treturn 1;\n \tcase 'b':\t/* body */\n \t\tstrbuf_addstr(sb, msg + c->body_off);\n-- \ngitgitgadget\n\n"},{"id":"403860","messageId":"7a039823de5a684e958241773b6a27c08199fc7c.1597687822.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"[PATCH v3 6/9] ref-filter: add `short` modifier to 'parent' atom","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T18:10:19Z","receivedAt":"2020-08-17T18:11:28Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nSometimes while using 'parent' atom, user might want to see abbrev hash\ninstead of full 40 character hash.\n\nJust like 'objectname', it might be convenient for users to have the\n`:short` and `:short=<length>` option for printing 'parent' hash.\n\nLet's introduce `short` option to 'parent' atom.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 2 +-\n ref-filter.c                       | 8 ++++----\n t/t6300-for-each-ref.sh            | 6 ++++++\n 3 files changed, 11 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 40ebdfcc41..dd09763e7d 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -222,7 +222,7 @@ worktreepath::\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\n-Field `tree` can also be used with modifier `:short` and\n+Fields `tree` and `parent` can also be used with modifier `:short` and\n `:short=<length>` just like `objectname`.\n \n For commit and tag objects, the special `creatordate` and `creator`\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 3449fe45d8..c7d81088e4 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -498,7 +498,7 @@ static struct {\n \t{ \"objectname\", SOURCE_OTHER, FIELD_STR, oid_atom_parser },\n \t{ \"deltabase\", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },\n \t{ \"tree\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n-\t{ \"parent\", SOURCE_OBJ },\n+\t{ \"parent\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n \t{ \"numparent\", SOURCE_OBJ, FIELD_ULONG },\n \t{ \"object\", SOURCE_OBJ },\n \t{ \"type\", SOURCE_OBJ },\n@@ -1011,14 +1011,14 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object\n \t\t\tv->value = commit_list_count(commit->parents);\n \t\t\tv->s = xstrfmt(\"%lu\", (unsigned long)v->value);\n \t\t}\n-\t\telse if (!strcmp(name, \"parent\")) {\n+\t\telse if (starts_with(name, \"parent\")) {\n \t\t\tstruct commit_list *parents;\n \t\t\tstruct strbuf s = STRBUF_INIT;\n \t\t\tfor (parents = commit->parents; parents; parents = parents->next) {\n-\t\t\t\tstruct commit *parent = parents->item;\n+\t\t\t\tstruct object_id *oid = &parents->item->object.oid;\n \t\t\t\tif (parents != commit->parents)\n \t\t\t\t\tstrbuf_addch(&s, ' ');\n-\t\t\t\tstrbuf_addstr(&s, oid_to_hex(&parent->object.oid));\n+\t\t\t\tstrbuf_addstr(&s, do_grab_oid(\"parent\", oid, &used_atom[i]));\n \t\t\t}\n \t\t\tv->s = strbuf_detach(&s, NULL);\n \t\t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex e30bbff6d9..79d5b29387 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -120,6 +120,9 @@ test_atom head tree:short $(git rev-parse --short refs/heads/master^{tree})\n test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/master^{tree})\n test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/master^{tree})\n test_atom head parent ''\n+test_atom head parent:short ''\n+test_atom head parent:short=1 ''\n+test_atom head parent:short=10 ''\n test_atom head numparent 0\n test_atom head object ''\n test_atom head type ''\n@@ -174,6 +177,9 @@ test_atom tag tree:short ''\n test_atom tag tree:short=1 ''\n test_atom tag tree:short=10 ''\n test_atom tag parent ''\n+test_atom tag parent:short ''\n+test_atom tag parent:short=1 ''\n+test_atom tag parent:short=10 ''\n test_atom tag numparent ''\n test_atom tag object $(git rev-parse refs/tags/testtag^0)\n test_atom tag type 'commit'\n-- \ngitgitgadget\n\n"},{"id":"403861","messageId":"fd4ed82e8067a81d5cbca6fd5711927108be236f.1597687822.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"[PATCH v3 5/9] ref-filter: add `short` modifier to 'tree' atom","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T18:10:18Z","receivedAt":"2020-08-17T18:11:38Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nSometimes while using 'tree' atom, user might want to see abbrev hash\ninstead of full 40 character hash.\n\nJust like 'objectname', it might be convenient for users to have the\n`:short` and `:short=<length>` option for printing 'tree' hash.\n\nLet's introduce `short` option to 'tree' atom.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 2 ++\n ref-filter.c                       | 9 ++++-----\n t/t6300-for-each-ref.sh            | 6 ++++++\n 3 files changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex e6ce8af612..40ebdfcc41 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -222,6 +222,8 @@ worktreepath::\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\n+Field `tree` can also be used with modifier `:short` and\n+`:short=<length>` just like `objectname`.\n \n For commit and tag objects, the special `creatordate` and `creator`\n fields will correspond to the appropriate date or name-email-date tuple\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 066975b306..3449fe45d8 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -497,7 +497,7 @@ static struct {\n \t{ \"objectsize\", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser },\n \t{ \"objectname\", SOURCE_OTHER, FIELD_STR, oid_atom_parser },\n \t{ \"deltabase\", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },\n-\t{ \"tree\", SOURCE_OBJ },\n+\t{ \"tree\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n \t{ \"parent\", SOURCE_OBJ },\n \t{ \"numparent\", SOURCE_OBJ, FIELD_ULONG },\n \t{ \"object\", SOURCE_OBJ },\n@@ -1005,10 +1005,9 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object\n \t\t\tcontinue;\n \t\tif (deref)\n \t\t\tname++;\n-\t\tif (!strcmp(name, \"tree\")) {\n-\t\t\tv->s = xstrdup(oid_to_hex(get_commit_tree_oid(commit)));\n-\t\t}\n-\t\telse if (!strcmp(name, \"numparent\")) {\n+\t\tif (grab_oid(name, \"tree\", get_commit_tree_oid(commit), v, &used_atom[i]))\n+\t\t\tcontinue;\n+\t\tif (!strcmp(name, \"numparent\")) {\n \t\t\tv->value = commit_list_count(commit->parents);\n \t\t\tv->s = xstrfmt(\"%lu\", (unsigned long)v->value);\n \t\t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 64fbc91146..e30bbff6d9 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -116,6 +116,9 @@ test_atom head objectname:short $(git rev-parse --short refs/heads/master)\n test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom head tree $(git rev-parse refs/heads/master^{tree})\n+test_atom head tree:short $(git rev-parse --short refs/heads/master^{tree})\n+test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/master^{tree})\n+test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/master^{tree})\n test_atom head parent ''\n test_atom head numparent 0\n test_atom head object ''\n@@ -167,6 +170,9 @@ test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom tag tree ''\n+test_atom tag tree:short ''\n+test_atom tag tree:short=1 ''\n+test_atom tag tree:short=10 ''\n test_atom tag parent ''\n test_atom tag numparent ''\n test_atom tag object $(git rev-parse refs/tags/testtag^0)\n-- \ngitgitgadget\n\n"},{"id":"403862","messageId":"7a64495f99ec97258687695d41d106e3f946d551.1597687822.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"[PATCH v3 8/9] format-support: move `format_sanitized_subject()` from pretty","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T18:10:21Z","receivedAt":"2020-08-17T18:11:49Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nIn hope of some new features in `subject` atom, move funtion\n`format_sanitized_subject()` and all the function it uses\nto new file format-support.{c,h}.\n\nConsider this new file as a common interface between functions that\npretty.c and ref-filter.c shares.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Makefile         |  1 +\n format-support.c | 43 +++++++++++++++++++++++++++++++++++++++++++\n format-support.h |  6 ++++++\n pretty.c         | 40 +---------------------------------------\n 4 files changed, 51 insertions(+), 39 deletions(-)\n create mode 100644 format-support.c\n create mode 100644 format-support.h\n\ndiff --git a/Makefile b/Makefile\nindex 65f8cfb236..4ead4a256c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -881,6 +881,7 @@ LIB_OBJS += exec-cmd.o\n LIB_OBJS += fetch-negotiator.o\n LIB_OBJS += fetch-pack.o\n LIB_OBJS += fmt-merge-msg.o\n+LIB_OBJS += format-support.o\n LIB_OBJS += fsck.o\n LIB_OBJS += fsmonitor.o\n LIB_OBJS += gettext.o\ndiff --git a/format-support.c b/format-support.c\nnew file mode 100644\nindex 0000000000..d693aa1744\n--- /dev/null\n+++ b/format-support.c\n@@ -0,0 +1,43 @@\n+#include \"diff.h\"\n+#include \"log-tree.h\"\n+#include \"color.h\"\n+#include \"format-support.h\"\n+\n+static int istitlechar(char c)\n+{\n+\treturn (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||\n+\t\t(c >= '0' && c <= '9') || c == '.' || c == '_';\n+}\n+\n+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n+{\n+\tchar *r = xmemdupz(msg, len);\n+\tsize_t trimlen;\n+\tsize_t start_len = sb->len;\n+\tint space = 2;\n+\tint i;\n+\n+\tfor (i = 0; i < len; i++) {\n+\t\tif (r[i] == '\\n')\n+\t\t\tr[i] = ' ';\n+\t\tif (istitlechar(r[i])) {\n+\t\t\tif (space == 1)\n+\t\t\t\tstrbuf_addch(sb, '-');\n+\t\t\tspace = 0;\n+\t\t\tstrbuf_addch(sb, r[i]);\n+\t\t\tif (r[i] == '.')\n+\t\t\t\twhile (r[i+1] == '.')\n+\t\t\t\t\ti++;\n+\t\t} else\n+\t\t\tspace |= 1;\n+\t}\n+\tfree(r);\n+\n+\t/* trim any trailing '.' or '-' characters */\n+\ttrimlen = 0;\n+\twhile (sb->len - trimlen > start_len &&\n+\t\t(sb->buf[sb->len - 1 - trimlen] == '.'\n+\t\t|| sb->buf[sb->len - 1 - trimlen] == '-'))\n+\t\ttrimlen++;\n+\tstrbuf_remove(sb, sb->len - trimlen, trimlen);\n+}\ndiff --git a/format-support.h b/format-support.h\nnew file mode 100644\nindex 0000000000..c344ccbc33\n--- /dev/null\n+++ b/format-support.h\n@@ -0,0 +1,6 @@\n+#ifndef FORMAT_SUPPORT_H\n+#define FORMAT_SUPPORT_H\n+\n+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len);\n+\n+#endif /* FORMAT_SUPPORT_H */\ndiff --git a/pretty.c b/pretty.c\nindex 8d08e8278a..2de01b7115 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -12,6 +12,7 @@\n #include \"reflog-walk.h\"\n #include \"gpg-interface.h\"\n #include \"trailer.h\"\n+#include \"format-support.h\"\n \n static char *user_format;\n static struct cmt_fmt_map {\n@@ -833,45 +834,6 @@ static void parse_commit_header(struct format_commit_context *context)\n \tcontext->commit_header_parsed = 1;\n }\n \n-static int istitlechar(char c)\n-{\n-\treturn (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||\n-\t\t(c >= '0' && c <= '9') || c == '.' || c == '_';\n-}\n-\n-static void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n-{\n-\tchar *r = xmemdupz(msg, len);\n-\tsize_t trimlen;\n-\tsize_t start_len = sb->len;\n-\tint space = 2;\n-\tint i;\n-\n-\tfor (i = 0; i < len; i++) {\n-\t\tif (r[i] == '\\n')\n-\t\t\tr[i] = ' ';\n-\t\tif (istitlechar(r[i])) {\n-\t\t\tif (space == 1)\n-\t\t\t\tstrbuf_addch(sb, '-');\n-\t\t\tspace = 0;\n-\t\t\tstrbuf_addch(sb, r[i]);\n-\t\t\tif (r[i] == '.')\n-\t\t\t\twhile (r[i+1] == '.')\n-\t\t\t\t\ti++;\n-\t\t} else\n-\t\t\tspace |= 1;\n-\t}\n-\tfree(r);\n-\n-\t/* trim any trailing '.' or '-' characters */\n-\ttrimlen = 0;\n-\twhile (sb->len - trimlen > start_len &&\n-\t\t(sb->buf[sb->len - 1 - trimlen] == '.'\n-\t\t|| sb->buf[sb->len - 1 - trimlen] == '-'))\n-\t\ttrimlen++;\n-\tstrbuf_remove(sb, sb->len - trimlen, trimlen);\n-}\n-\n const char *format_subject(struct strbuf *sb, const char *msg,\n \t\t\t   const char *line_separator)\n {\n-- \ngitgitgadget\n\n"},{"id":"403863","messageId":"1ab35e9251ab788d0334e1dc97be98dd3a2ecc1b.1597687822.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"[PATCH v3 9/9] ref-filter: add `sanitize` option for 'subject' atom","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T18:10:22Z","receivedAt":"2020-08-17T18:11:54Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nCurrently, subject does not take any arguments. This commit introduce\n`sanitize` formatting option to 'subject' atom.\n\n`subject:sanitize` - print sanitized subject line, suitable for a filename.\n\ne.g.\n%(subject): \"the subject line\"\n%(subject:sanitize): \"the-subject-line\"\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 +++\n ref-filter.c                       | 24 ++++++++++++++++--------\n t/t6300-for-each-ref.sh            |  7 +++++++\n 3 files changed, 26 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex dd09763e7d..616ce46087 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -247,6 +247,9 @@ contents:subject::\n \tThe first paragraph of the message, which typically is a\n \tsingle line, is taken as the \"subject\" of the commit or the\n \ttag message.\n+\tInstead of `contents:subject`, field `subject` can also be used to\n+\tobtain same results. `:sanitize` can be appended to `subject` for\n+\tsubject line suitable for filename.\n \n contents:body::\n \tThe remainder of the commit or the tag message that follows\ndiff --git a/ref-filter.c b/ref-filter.c\nindex c7d81088e4..7f03444767 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -23,6 +23,7 @@\n #include \"worktree.h\"\n #include \"hashmap.h\"\n #include \"strvec.h\"\n+#include \"format-support.h\"\n \n static struct ref_msg {\n \tconst char *gone;\n@@ -127,8 +128,8 @@ static struct used_atom {\n \t\t\tunsigned int nobracket : 1, push : 1, push_remote : 1;\n \t\t} remote_ref;\n \t\tstruct {\n-\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH,\n-\t\t\t       C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n+\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH, C_LINES,\n+\t\t\t       C_SIG, C_SUB, C_SUB_SANITIZE, C_TRAILERS } option;\n \t\t\tstruct process_trailer_options trailer_opts;\n \t\t\tunsigned int nlines;\n \t\t} contents;\n@@ -301,9 +302,12 @@ static int body_atom_parser(const struct ref_format *format, struct used_atom *a\n static int subject_atom_parser(const struct ref_format *format, struct used_atom *atom,\n \t\t\t       const char *arg, struct strbuf *err)\n {\n-\tif (arg)\n-\t\treturn strbuf_addf_ret(err, -1, _(\"%%(subject) does not take arguments\"));\n-\tatom->u.contents.option = C_SUB;\n+\tif (!arg)\n+\t\tatom->u.contents.option = C_SUB;\n+\telse if (!strcmp(arg, \"sanitize\"))\n+\t\tatom->u.contents.option = C_SUB_SANITIZE;\n+\telse\n+\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized %%(subject) argument: %s\"), arg);\n \treturn 0;\n }\n \n@@ -1282,8 +1286,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n \t\t\tcontinue;\n \t\tif (deref)\n \t\t\tname++;\n-\t\tif (strcmp(name, \"subject\") &&\n-\t\t    strcmp(name, \"body\") &&\n+\t\tif (strcmp(name, \"body\") &&\n+\t\t    !starts_with(name, \"subject\") &&\n \t\t    !starts_with(name, \"trailers\") &&\n \t\t    !starts_with(name, \"contents\"))\n \t\t\tcontinue;\n@@ -1295,7 +1299,11 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n \n \t\tif (atom->u.contents.option == C_SUB)\n \t\t\tv->s = copy_subject(subpos, sublen);\n-\t\telse if (atom->u.contents.option == C_BODY_DEP)\n+\t\telse if (atom->u.contents.option == C_SUB_SANITIZE) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tformat_sanitized_subject(&sb, subpos, sublen);\n+\t\t\tv->s = strbuf_detach(&sb, NULL);\n+\t\t} else if (atom->u.contents.option == C_BODY_DEP)\n \t\t\tv->s = xmemdupz(bodypos, bodylen);\n \t\telse if (atom->u.contents.option == C_LENGTH)\n \t\t\tv->s = xstrfmt(\"%\"PRIuMAX, (uintmax_t)strlen(subpos));\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 79d5b29387..220ff5c3c2 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -150,6 +150,7 @@ test_atom head taggerdate ''\n test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n test_atom head subject 'Initial'\n+test_atom head subject:sanitize 'Initial'\n test_atom head contents:subject 'Initial'\n test_atom head body ''\n test_atom head contents:body ''\n@@ -207,6 +208,7 @@ test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag subject 'Tagging at 1151968727'\n+test_atom tag subject:sanitize 'Tagging-at-1151968727'\n test_atom tag contents:subject 'Tagging at 1151968727'\n test_atom tag body ''\n test_atom tag contents:body ''\n@@ -619,6 +621,7 @@ test_expect_success 'create tag with subject and body content' '\n \tgit tag -F msg subject-body\n '\n test_atom refs/tags/subject-body subject 'the subject line'\n+test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n test_atom refs/tags/subject-body body 'first body line\n second body line\n '\n@@ -639,6 +642,7 @@ test_expect_success 'create tag with multiline subject' '\n \tgit tag -F msg multiline\n '\n test_atom refs/tags/multiline subject 'first subject line second subject line'\n+test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n test_atom refs/tags/multiline body 'first body line\n second body line\n@@ -671,6 +675,7 @@ sig='-----BEGIN PGP SIGNATURE-----\n \n PREREQ=GPG\n test_atom refs/tags/signed-empty subject ''\n+test_atom refs/tags/signed-empty subject:sanitize ''\n test_atom refs/tags/signed-empty contents:subject ''\n test_atom refs/tags/signed-empty body \"$sig\"\n test_atom refs/tags/signed-empty contents:body ''\n@@ -678,6 +683,7 @@ test_atom refs/tags/signed-empty contents:signature \"$sig\"\n test_atom refs/tags/signed-empty contents \"$sig\"\n \n test_atom refs/tags/signed-short subject 'subject line'\n+test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n test_atom refs/tags/signed-short contents:subject 'subject line'\n test_atom refs/tags/signed-short body \"$sig\"\n test_atom refs/tags/signed-short contents:body ''\n@@ -686,6 +692,7 @@ test_atom refs/tags/signed-short contents \"subject line\n $sig\"\n \n test_atom refs/tags/signed-long subject 'subject line'\n+test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n test_atom refs/tags/signed-long contents:subject 'subject line'\n test_atom refs/tags/signed-long body \"body contents\n $sig\"\n-- \ngitgitgadget\n"},{"id":"403873","messageId":"xmqqpn7p1373.fsf@gitster.c.googlers.com","threadId":"53931","inReplyTo":"0ad22c7cdd3c692aa5b46444e64a3b76f1e87b4c.1597687822.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 7/9] pretty: refactor `format_sanitized_subject()`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-08-17T19:29:20Z","receivedAt":"2020-08-17T19:29:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Hariom Verma via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> -static void format_sanitized_subject(struct strbuf *sb, const char *msg)\n> +static void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n>  {\n> +\tchar *r = xmemdupz(msg, len);\n>  \tsize_t trimlen;\n>  \tsize_t start_len = sb->len;\n>  \tint space = 2;\n> +\tint i;\n>  \n> -\tfor (; *msg && *msg != '\\n'; msg++) {\n> -\t\tif (istitlechar(*msg)) {\n> +\tfor (i = 0; i < len; i++) {\n> +\t\tif (r[i] == '\\n')\n> +\t\t\tr[i] = ' ';\n\nCopying the whole string only for this one looks very wasteful.\nCan't you do\n\n\tfor (i = 0; i < len; i++) {\n\t\tchar r = msg[i];\n\t\tif (isspace(r))\n\t\t\tr = ' ';\n\t\tif (istitlechar(r)) {\n\t\t\t...\n\t}\n\nor something like that instead?  \n\n> +\t\tif (istitlechar(r[i])) {\n>  \t\t\tif (space == 1)\n>  \t\t\t\tstrbuf_addch(sb, '-');\n>  \t\t\tspace = 0;\n> -\t\t\tstrbuf_addch(sb, *msg);\n> -\t\t\tif (*msg == '.')\n> -\t\t\t\twhile (*(msg+1) == '.')\n> -\t\t\t\t\tmsg++;\n> +\t\t\tstrbuf_addch(sb, r[i]);\n> +\t\t\tif (r[i] == '.')\n> +\t\t\t\twhile (r[i+1] == '.')\n> +\t\t\t\t\ti++;\n>  \t\t} else\n>  \t\t\tspace |= 1;\n>  \t}\n> +\tfree(r);\n\nAlso, because neither LF or SP is a titlechar(), wouldn't the \"if\nr[i] is LF, replace it with SP\" a no-op wrt what will be in sb at\nthe end?\n\n\n>  \tcase 'f':\t/* sanitized subject */\n> -\t\tformat_sanitized_subject(sb, msg + c->subject_off);\n> +\t\teol = strchrnul(msg + c->subject_off, '\\n');\n> +\t\tformat_sanitized_subject(sb, msg + c->subject_off, eol - (msg + c->subject_off));\n\nThis original caller expected the helper to stop reading at the end\nof the first line, but the updated helper needs to be told where to\nstop, so we do so with some extra computation.  Makes sense.\n\n"},{"id":"403874","messageId":"xmqqlfid1305.fsf@gitster.c.googlers.com","threadId":"53931","inReplyTo":"7a64495f99ec97258687695d41d106e3f946d551.1597687822.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 8/9] format-support: move `format_sanitized_subject()` from pretty","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-08-17T19:33:30Z","receivedAt":"2020-08-17T19:33:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Hariom Verma via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Hariom Verma <hariom18599@gmail.com>\n>\n> In hope of some new features in `subject` atom, move funtion\n> `format_sanitized_subject()` and all the function it uses\n> to new file format-support.{c,h}.\n>\n> Consider this new file as a common interface between functions that\n> pretty.c and ref-filter.c shares.\n\nSorry, I do not see a point.  Let's not do this and keep or add them\nin pretty.[ch] if you are making some of the static ones public.\n\nThanks.\n\n"},{"id":"403875","messageId":"xmqqh7t112tn.fsf@gitster.c.googlers.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 0/9] [Resend][GSoC] Improvements to ref-filter","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-08-17T19:37:24Z","receivedAt":"2020-08-17T19:37:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Hariom Verma via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> This is the first patch series that introduces some improvements and\n> features to file ref-filter.{c,h}. These changes are useful to ref-filter,\n> but in near future also will allow us to use ref-filter's logic in pretty.c\n\nOverall the series was a pleasant read, except for some places I\nfound small questionable things.\n\nThanks, will queue.\n\n"},{"id":"404008","messageId":"CA+CkUQ9tkwXmrHq_ZV+RCgwoFHZ0M4dEhBkjUd97Xi+3shB-WQ@mail.gmail.com","threadId":"53931","inReplyTo":"xmqqpn7p1373.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v3 7/9] pretty: refactor `format_sanitized_subject()`","fromName":"Hariom verma","fromEmail":"hariom18599@gmail.com","sentAt":"2020-08-19T13:36:59Z","receivedAt":"2020-08-19T13:37:42Z","isPatch":true,"sender":{"key":"hariom18599@gmail.com","avatar":"https://avatars.githubusercontent.com/u/37576387?v=4"},"body":"Hi Junio,\n\nOn Tue, Aug 18, 2020 at 12:59 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Hariom Verma via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> > -static void format_sanitized_subject(struct strbuf *sb, const char *msg)\n> > +static void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n> >  {\n> > +     char *r = xmemdupz(msg, len);\n> >       size_t trimlen;\n> >       size_t start_len = sb->len;\n> >       int space = 2;\n> > +     int i;\n> >\n> > -     for (; *msg && *msg != '\\n'; msg++) {\n> > -             if (istitlechar(*msg)) {\n> > +     for (i = 0; i < len; i++) {\n> > +             if (r[i] == '\\n')\n> > +                     r[i] = ' ';\n>\n> Copying the whole string only for this one looks very wasteful.\n> Can't you do\n>\n>         for (i = 0; i < len; i++) {\n>                 char r = msg[i];\n>                 if (isspace(r))\n>                         r = ' ';\n>                 if (istitlechar(r)) {\n>                         ...\n>         }\n>\n> or something like that instead?\n\nOk, that sounds better. Noted for the next version.\n\n> > +             if (istitlechar(r[i])) {\n> >                       if (space == 1)\n> >                               strbuf_addch(sb, '-');\n> >                       space = 0;\n> > -                     strbuf_addch(sb, *msg);\n> > -                     if (*msg == '.')\n> > -                             while (*(msg+1) == '.')\n> > -                                     msg++;\n> > +                     strbuf_addch(sb, r[i]);\n> > +                     if (r[i] == '.')\n> > +                             while (r[i+1] == '.')\n> > +                                     i++;\n> >               } else\n> >                       space |= 1;\n> >       }\n> > +     free(r);\n>\n> Also, because neither LF or SP is a titlechar(), wouldn't the \"if\n> r[i] is LF, replace it with SP\" a no-op wrt what will be in sb at\n> the end?\n\nMaybe its better to directly replace LF with hyphen? [Instead of first\nreplacing LF with SP and then replacing SP with '-'.]\n\n> >       case 'f':       /* sanitized subject */\n> > -             format_sanitized_subject(sb, msg + c->subject_off);\n> > +             eol = strchrnul(msg + c->subject_off, '\\n');\n> > +             format_sanitized_subject(sb, msg + c->subject_off, eol - (msg + c->subject_off));\n>\n> This original caller expected the helper to stop reading at the end\n> of the first line, but the updated helper needs to be told where to\n> stop, so we do so with some extra computation.  Makes sense.\n\nYeah.\n\nThanks,\nHariom\n"},{"id":"404030","messageId":"xmqqimdevd4l.fsf@gitster.c.googlers.com","threadId":"53931","inReplyTo":"CA+CkUQ9tkwXmrHq_ZV+RCgwoFHZ0M4dEhBkjUd97Xi+3shB-WQ@mail.gmail.com","subject":"Re: [PATCH v3 7/9] pretty: refactor `format_sanitized_subject()`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-08-19T16:01:14Z","receivedAt":"2020-08-19T16:01:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Hariom verma <hariom18599@gmail.com> writes:\n\n>> Also, because neither LF or SP is a titlechar(), wouldn't the \"if\n>> r[i] is LF, replace it with SP\" a no-op wrt what will be in sb at\n>> the end?\n>\n> Maybe its better to directly replace LF with hyphen? [Instead of first\n> replacing LF with SP and then replacing SP with '-'.]\n\nWhy do you think LF is so special?\n\nEverything other than titlechar() including HT, '#', '*', SP is\ntreated in the same way as the body of that loop.  It does not\ndirectly contribute to the final contents of sb, but just leaves\nthe marker in the variable \"space\" the fact that when adding the\nnext titlechar() to the resulting sb, we need a SP to wordbreak.\n\nLF now happens to be in the set due to the way you extended the\nfunction (it wasn't fed to this function by its sole caller), but\nother than that, it is no more special than HT, SP or '*'.  And they\nare not replaced with SP or replaced with '-'.\n\nSo it would be the most sensible to just drop 'if LF, replace it\nwith SP before doing anything else' you added.  The existing 'if\ntitlechar, add it to sb but if we saw non-title, add a SP before\ndoing so to wordbreak, and if not titlechar, just remember the fact\nthat we saw one' should work fine as-is without special casing LF at\nall.\n\nOr am I missing something subtle?\n"},{"id":"404031","messageId":"xmqqeeo2vcsl.fsf@gitster.c.googlers.com","threadId":"53931","inReplyTo":"xmqqimdevd4l.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v3 7/9] pretty: refactor `format_sanitized_subject()`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-08-19T16:08:26Z","receivedAt":"2020-08-19T16:08:33Z","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> Hariom verma <hariom18599@gmail.com> writes:\n>\n>>> Also, because neither LF or SP is a titlechar(), wouldn't the \"if\n>>> r[i] is LF, replace it with SP\" a no-op wrt what will be in sb at\n>>> the end?\n>>\n>> Maybe its better to directly replace LF with hyphen? [Instead of first\n>> replacing LF with SP and then replacing SP with '-'.]\n>\n> Why do you think LF is so special?\n>\n> Everything other than titlechar() including HT, '#', '*', SP is\n> treated in the same way as the body of that loop.  It does not\n> directly contribute to the final contents of sb, but just leaves\n> the marker in the variable \"space\" the fact that when adding the\n> next titlechar() to the resulting sb, we need a SP to wordbreak.\n\nI was undecided between mentioning and not mentioning the variable\nname \"space\" here.  On one hand, one _could_ argue that \"space\" is\nused to remember we saw \"space and the like\" and if it were named\n\"seen_non_title_char\", then such a confusion to treat LF so\nspecially might not have occurred.  But on the other hand, \"space\"\nis what the variable exactly keeps track of; it is just the need for\nspace on the output side, i.e. we remember that \"space needed before\nthe next output\" with that variable.\n\nI am inclined not to suggest renaming \"space\" at all, but it won't\nbe the end of the world if it were renamed to \"need_space\" (before\nthe next output), or \"seen_nontitle\".  If we were to actually\nrename, I have moderately strong preference to the \"need_space\" over\n\"seen_nontitle\", as it won't have to be renamed again when the logic\nto require a space before the next output has to be updated to\ninclude cases other than just \"we saw a nontitle character\".\n\nThanks.\n"},{"id":"404103","messageId":"CA+CkUQ8=ej=NuG=FjsE6oqT+n8YUpGeVVNyhhs3FZ7UwZ5pG_g@mail.gmail.com","threadId":"53931","inReplyTo":"xmqqimdevd4l.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v3 7/9] pretty: refactor `format_sanitized_subject()`","fromName":"Hariom verma","fromEmail":"hariom18599@gmail.com","sentAt":"2020-08-20T17:27:03Z","receivedAt":"2020-08-20T17:27:22Z","isPatch":true,"sender":{"key":"hariom18599@gmail.com","avatar":"https://avatars.githubusercontent.com/u/37576387?v=4"},"body":"Hi,\n\nOn Wed, Aug 19, 2020 at 9:31 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Hariom verma <hariom18599@gmail.com> writes:\n>\n> >> Also, because neither LF or SP is a titlechar(), wouldn't the \"if\n> >> r[i] is LF, replace it with SP\" a no-op wrt what will be in sb at\n> >> the end?\n> >\n> > Maybe its better to directly replace LF with hyphen? [Instead of first\n> > replacing LF with SP and then replacing SP with '-'.]\n>\n> Why do you think LF is so special?\n>\n> Everything other than titlechar() including HT, '#', '*', SP is\n> treated in the same way as the body of that loop.  It does not\n> directly contribute to the final contents of sb, but just leaves\n> the marker in the variable \"space\" the fact that when adding the\n> next titlechar() to the resulting sb, we need a SP to wordbreak.\n>\n> LF now happens to be in the set due to the way you extended the\n> function (it wasn't fed to this function by its sole caller), but\n> other than that, it is no more special than HT, SP or '*'.  And they\n> are not replaced with SP or replaced with '-'.\n>\n> So it would be the most sensible to just drop 'if LF, replace it\n> with SP before doing anything else' you added.  The existing 'if\n> titlechar, add it to sb but if we saw non-title, add a SP before\n> doing so to wordbreak, and if not titlechar, just remember the fact\n> that we saw one' should work fine as-is without special casing LF at\n> all.\n>\n> Or am I missing something subtle?\n\nYou actually got it all right. Thanks for the insight.\n\nNow, I got it. There is no need to give special attention to LF. I\nmissed to see that `titlechar()` is already taking care of everything.\n\nThanks,\nHariom\n"},{"id":"404104","messageId":"CA+CkUQ8tkKM6SKrpJJ9-+9Nj+4Ly3FWHKUp1BQgJEG0-XyWENw@mail.gmail.com","threadId":"53931","inReplyTo":"xmqqeeo2vcsl.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v3 7/9] pretty: refactor `format_sanitized_subject()`","fromName":"Hariom verma","fromEmail":"hariom18599@gmail.com","sentAt":"2020-08-20T17:33:21Z","receivedAt":"2020-08-20T17:33:38Z","isPatch":true,"sender":{"key":"hariom18599@gmail.com","avatar":"https://avatars.githubusercontent.com/u/37576387?v=4"},"body":"Hi,\n\nOn Wed, Aug 19, 2020 at 9:38 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n> > Hariom verma <hariom18599@gmail.com> writes:\n> >\n> >>> Also, because neither LF or SP is a titlechar(), wouldn't the \"if\n> >>> r[i] is LF, replace it with SP\" a no-op wrt what will be in sb at\n> >>> the end?\n> >>\n> >> Maybe its better to directly replace LF with hyphen? [Instead of first\n> >> replacing LF with SP and then replacing SP with '-'.]\n> >\n> > Why do you think LF is so special?\n> >\n> > Everything other than titlechar() including HT, '#', '*', SP is\n> > treated in the same way as the body of that loop.  It does not\n> > directly contribute to the final contents of sb, but just leaves\n> > the marker in the variable \"space\" the fact that when adding the\n> > next titlechar() to the resulting sb, we need a SP to wordbreak.\n>\n> I was undecided between mentioning and not mentioning the variable\n> name \"space\" here.  On one hand, one _could_ argue that \"space\" is\n> used to remember we saw \"space and the like\" and if it were named\n> \"seen_non_title_char\", then such a confusion to treat LF so\n> specially might not have occurred.  But on the other hand, \"space\"\n> is what the variable exactly keeps track of; it is just the need for\n> space on the output side, i.e. we remember that \"space needed before\n> the next output\" with that variable.\n>\n> I am inclined not to suggest renaming \"space\" at all, but it won't\n> be the end of the world if it were renamed to \"need_space\" (before\n> the next output), or \"seen_nontitle\".  If we were to actually\n> rename, I have moderately strong preference to the \"need_space\" over\n> \"seen_nontitle\", as it won't have to be renamed again when the logic\n> to require a space before the next output has to be updated to\n> include cases other than just \"we saw a nontitle character\".\n\nYeah, if it was named \"seen_non_title_char\", I might not get confused.\nBut now as you have already explained its working pretty well, \"space\"\nmakes more sense to me.\nWell, I'm okay with both \"space\" and \"need_space\".\n\nI wonder what others have to say on this? \"space\" or \"need_space\"?\n\nThanks,\nHariom\n"},{"id":"404223","messageId":"pull.684.v4.git.1598046110.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v3.git.1597687822.gitgitgadget@gmail.com","subject":"[PATCH v4 0/8] [GSoC] Improvements to ref-filter","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-21T21:41:42Z","receivedAt":"2020-08-21T21:41:56Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"This is the first patch series that introduces some improvements and\nfeatures to file ref-filter.{c,h}. These changes are useful to ref-filter,\nbut in near future also will allow us to use ref-filter's logic in pretty.c\n\nI plan to add more to format-support.{c,h} in the upcoming patch series.\nThat will lead to more improved and feature-rich ref-filter.c\n\nHariom Verma (8):\n  ref-filter: support different email formats\n  ref-filter: refactor `grab_objectname()`\n  ref-filter: modify error messages in `grab_objectname()`\n  ref-filter: rename `objectname` related functions and fields\n  ref-filter: add `short` modifier to 'tree' atom\n  ref-filter: add `short` modifier to 'parent' atom\n  pretty: refactor `format_sanitized_subject()`\n  ref-filter: add `sanitize` option for 'subject' atom\n\n Documentation/git-for-each-ref.txt |  10 +-\n pretty.c                           |  20 ++--\n pretty.h                           |   3 +\n ref-filter.c                       | 158 +++++++++++++++++++----------\n t/t6300-for-each-ref.sh            |  35 +++++++\n 5 files changed, 161 insertions(+), 65 deletions(-)\n\n\nbase-commit: 675a4aaf3b226c0089108221b96559e0baae5de9\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-684%2Fharry-hov%2Fonly-rf6-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-684/harry-hov/only-rf6-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/684\n\nRange-diff vs v3:\n\n  1:  3e6fc66a46 =  1:  55618fe4c1 ref-filter: support different email formats\n  2:  5268b973da =  2:  c508c96eb8 ref-filter: refactor `grab_objectname()`\n  3:  4a12ff8210 =  3:  582f00ace6 ref-filter: modify error messages in `grab_objectname()`\n  4:  d53ca56778 =  4:  503a1874ce ref-filter: rename `objectname` related functions and fields\n  5:  fd4ed82e80 =  5:  6b97166796 ref-filter: add `short` modifier to 'tree' atom\n  6:  7a039823de =  6:  5ed5ac259d ref-filter: add `short` modifier to 'parent' atom\n  7:  0ad22c7cdd !  7:  6105046d96 pretty: refactor `format_sanitized_subject()`\n     @@ Commit message\n          of `\\n` in a variable `eol` and passed it in\n          `format_sanitized_subject()`. And the loop runs upto `eol`.\n      \n     -    But this change isn't sufficient to reuse this function in\n     -    ref-filter.c because there exist tags with multiline subject.\n     -\n     -    It's wise to replace `\\n` with ' ', if `format_sanitized_subject()`\n     -    encounters `\\n` before end of subject line, just like `copy_subject()`.\n     -    Because we'll be only using `format_sanitized_subject()` for\n     -    \"%(subject:sanitize)\", instead of `copy_subject()` and\n     -    `format_sanitized_subject()` both. So, added the code:\n     -    ```\n     -    if (char == '\\n') /* never true if called inside pretty.c */\n     -        char = ' ';\n     -    ```\n     -\n     -    Now, it's ready to be reused in ref-filter.c\n     -\n          Mentored-by: Christian Couder <chriscool@tuxfamily.org>\n          Mentored-by: Heba Waly <heba.waly@gmail.com>\n          Signed-off-by: Hariom Verma <hariom18599@gmail.com>\n     @@ pretty.c: static int istitlechar(char c)\n       }\n       \n      -static void format_sanitized_subject(struct strbuf *sb, const char *msg)\n     -+static void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n     ++void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n       {\n     -+\tchar *r = xmemdupz(msg, len);\n       \tsize_t trimlen;\n       \tsize_t start_len = sb->len;\n       \tint space = 2;\n     @@ pretty.c: static int istitlechar(char c)\n      -\tfor (; *msg && *msg != '\\n'; msg++) {\n      -\t\tif (istitlechar(*msg)) {\n      +\tfor (i = 0; i < len; i++) {\n     -+\t\tif (r[i] == '\\n')\n     -+\t\t\tr[i] = ' ';\n     -+\t\tif (istitlechar(r[i])) {\n     ++\t\tif (istitlechar(msg[i])) {\n       \t\t\tif (space == 1)\n       \t\t\t\tstrbuf_addch(sb, '-');\n       \t\t\tspace = 0;\n     @@ pretty.c: static int istitlechar(char c)\n      -\t\t\tif (*msg == '.')\n      -\t\t\t\twhile (*(msg+1) == '.')\n      -\t\t\t\t\tmsg++;\n     -+\t\t\tstrbuf_addch(sb, r[i]);\n     -+\t\t\tif (r[i] == '.')\n     -+\t\t\t\twhile (r[i+1] == '.')\n     ++\t\t\tstrbuf_addch(sb, msg[i]);\n     ++\t\t\tif (msg[i] == '.')\n     ++\t\t\t\twhile (msg[i+1] == '.')\n      +\t\t\t\t\ti++;\n       \t\t} else\n       \t\t\tspace |= 1;\n       \t}\n     -+\tfree(r);\n     - \n     - \t/* trim any trailing '.' or '-' characters */\n     - \ttrimlen = 0;\n      @@ pretty.c: static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n       \tconst struct commit *commit = c->commit;\n       \tconst char *msg = c->message;\n     @@ pretty.c: static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n       \t\treturn 1;\n       \tcase 'b':\t/* body */\n       \t\tstrbuf_addstr(sb, msg + c->body_off);\n     +\n     + ## pretty.h ##\n     +@@ pretty.h: const char *format_subject(struct strbuf *sb, const char *msg,\n     + /* Check if \"cmit_fmt\" will produce an empty output. */\n     + int commit_format_is_empty(enum cmit_fmt);\n     + \n     ++/* Make subject of commit message suitable for filename */\n     ++void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len);\n     ++\n     + #endif /* PRETTY_H */\n  8:  7a64495f99 <  -:  ---------- format-support: move `format_sanitized_subject()` from pretty\n  9:  1ab35e9251 !  8:  7cba8d7a28 ref-filter: add `sanitize` option for 'subject' atom\n     @@ Documentation/git-for-each-ref.txt: contents:subject::\n       \tThe remainder of the commit or the tag message that follows\n      \n       ## ref-filter.c ##\n     -@@\n     - #include \"worktree.h\"\n     - #include \"hashmap.h\"\n     - #include \"strvec.h\"\n     -+#include \"format-support.h\"\n     - \n     - static struct ref_msg {\n     - \tconst char *gone;\n      @@ ref-filter.c: static struct used_atom {\n       \t\t\tunsigned int nobracket : 1, push : 1, push_remote : 1;\n       \t\t} remote_ref;\n\n-- \ngitgitgadget\n"},{"id":"404224","messageId":"55618fe4c1ea96fb0c2734121f8a54a921fb0aff.1598046110.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v4.git.1598046110.gitgitgadget@gmail.com","subject":"[PATCH v4 1/8] ref-filter: support different email formats","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-21T21:41:43Z","receivedAt":"2020-08-21T21:42:01Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nCurrently, ref-filter only supports printing email with angle brackets.\n\nLet's add support for two more email options.\n- trim : for email without angle brackets.\n- localpart : for the part before the @ sign out of trimmed email\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  5 ++-\n ref-filter.c                       | 54 +++++++++++++++++++++++++-----\n t/t6300-for-each-ref.sh            | 16 +++++++++\n 3 files changed, 65 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 2ea71c5f6c..e6ce8af612 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -230,7 +230,10 @@ These are intended for working on a mix of annotated and lightweight tags.\n \n Fields that have name-email-date tuple as its value (`author`,\n `committer`, and `tagger`) can be suffixed with `name`, `email`,\n-and `date` to extract the named component.\n+and `date` to extract the named component.  For email fields (`authoremail`,\n+`committeremail` and `taggeremail`), `:trim` can be appended to get the email\n+without angle brackets, and `:localpart` to get the part before the `@` symbol\n+out of the trimmed email.\n \n The message in a commit or a tag object is `contents`, from which\n `contents:<part>` can be used to extract various parts out of:\ndiff --git a/ref-filter.c b/ref-filter.c\nindex ba85869755..e60765f156 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -140,6 +140,9 @@ static struct used_atom {\n \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n \t\t\tunsigned int length;\n \t\t} objectname;\n+\t\tstruct email_option {\n+\t\t\tenum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n+\t\t} email_option;\n \t\tstruct refname_atom refname;\n \t\tchar *head;\n \t} u;\n@@ -377,6 +380,20 @@ static int objectname_atom_parser(const struct ref_format *format, struct used_a\n \treturn 0;\n }\n \n+static int person_email_atom_parser(const struct ref_format *format, struct used_atom *atom,\n+\t\t\t\t    const char *arg, struct strbuf *err)\n+{\n+\tif (!arg)\n+\t\tatom->u.email_option.option = EO_RAW;\n+\telse if (!strcmp(arg, \"trim\"))\n+\t\tatom->u.email_option.option = EO_TRIM;\n+\telse if (!strcmp(arg, \"localpart\"))\n+\t\tatom->u.email_option.option = EO_LOCALPART;\n+\telse\n+\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized email option: %s\"), arg);\n+\treturn 0;\n+}\n+\n static int refname_atom_parser(const struct ref_format *format, struct used_atom *atom,\n \t\t\t       const char *arg, struct strbuf *err)\n {\n@@ -488,15 +505,15 @@ static struct {\n \t{ \"tag\", SOURCE_OBJ },\n \t{ \"author\", SOURCE_OBJ },\n \t{ \"authorname\", SOURCE_OBJ },\n-\t{ \"authoremail\", SOURCE_OBJ },\n+\t{ \"authoremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n \t{ \"authordate\", SOURCE_OBJ, FIELD_TIME },\n \t{ \"committer\", SOURCE_OBJ },\n \t{ \"committername\", SOURCE_OBJ },\n-\t{ \"committeremail\", SOURCE_OBJ },\n+\t{ \"committeremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n \t{ \"committerdate\", SOURCE_OBJ, FIELD_TIME },\n \t{ \"tagger\", SOURCE_OBJ },\n \t{ \"taggername\", SOURCE_OBJ },\n-\t{ \"taggeremail\", SOURCE_OBJ },\n+\t{ \"taggeremail\", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },\n \t{ \"taggerdate\", SOURCE_OBJ, FIELD_TIME },\n \t{ \"creator\", SOURCE_OBJ },\n \t{ \"creatordate\", SOURCE_OBJ, FIELD_TIME },\n@@ -1037,16 +1054,35 @@ static const char *copy_name(const char *buf)\n \treturn xstrdup(\"\");\n }\n \n-static const char *copy_email(const char *buf)\n+static const char *copy_email(const char *buf, struct used_atom *atom)\n {\n \tconst char *email = strchr(buf, '<');\n \tconst char *eoemail;\n \tif (!email)\n \t\treturn xstrdup(\"\");\n-\teoemail = strchr(email, '>');\n+\tswitch (atom->u.email_option.option) {\n+\tcase EO_RAW:\n+\t\teoemail = strchr(email, '>');\n+\t\tif (eoemail)\n+\t\t\teoemail++;\n+\t\tbreak;\n+\tcase EO_TRIM:\n+\t\temail++;\n+\t\teoemail = strchr(email, '>');\n+\t\tbreak;\n+\tcase EO_LOCALPART:\n+\t\temail++;\n+\t\teoemail = strchr(email, '@');\n+\t\tif (!eoemail)\n+\t\t\teoemail = strchr(email, '>');\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"unknown email option\");\n+\t}\n+\n \tif (!eoemail)\n \t\treturn xstrdup(\"\");\n-\treturn xmemdupz(email, eoemail + 1 - email);\n+\treturn xmemdupz(email, eoemail - email);\n }\n \n static char *copy_subject(const char *buf, unsigned long len)\n@@ -1116,7 +1152,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n \t\t\tcontinue;\n \t\tif (name[wholen] != 0 &&\n \t\t    strcmp(name + wholen, \"name\") &&\n-\t\t    strcmp(name + wholen, \"email\") &&\n+\t\t    !starts_with(name + wholen, \"email\") &&\n \t\t    !starts_with(name + wholen, \"date\"))\n \t\t\tcontinue;\n \t\tif (!wholine)\n@@ -1127,8 +1163,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void\n \t\t\tv->s = copy_line(wholine);\n \t\telse if (!strcmp(name + wholen, \"name\"))\n \t\t\tv->s = copy_name(wholine);\n-\t\telse if (!strcmp(name + wholen, \"email\"))\n-\t\t\tv->s = copy_email(wholine);\n+\t\telse if (starts_with(name + wholen, \"email\"))\n+\t\t\tv->s = copy_email(wholine, &used_atom[i]);\n \t\telse if (starts_with(name + wholen, \"date\"))\n \t\t\tgrab_date(wholine, v, name);\n \t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex a83579fbdf..64fbc91146 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -125,15 +125,21 @@ test_atom head '*objecttype' ''\n test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n test_atom head authorname 'A U Thor'\n test_atom head authoremail '<author@example.com>'\n+test_atom head authoremail:trim 'author@example.com'\n+test_atom head authoremail:localpart 'author'\n test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head committername 'C O Mitter'\n test_atom head committeremail '<committer@example.com>'\n+test_atom head committeremail:trim 'committer@example.com'\n+test_atom head committeremail:localpart 'committer'\n test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n test_atom head tag ''\n test_atom head tagger ''\n test_atom head taggername ''\n test_atom head taggeremail ''\n+test_atom head taggeremail:trim ''\n+test_atom head taggeremail:localpart ''\n test_atom head taggerdate ''\n test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n@@ -170,15 +176,21 @@ test_atom tag '*objecttype' 'commit'\n test_atom tag author ''\n test_atom tag authorname ''\n test_atom tag authoremail ''\n+test_atom tag authoremail:trim ''\n+test_atom tag authoremail:localpart ''\n test_atom tag authordate ''\n test_atom tag committer ''\n test_atom tag committername ''\n test_atom tag committeremail ''\n+test_atom tag committeremail:trim ''\n+test_atom tag committeremail:localpart ''\n test_atom tag committerdate ''\n test_atom tag tag 'testtag'\n test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag taggername 'C O Mitter'\n test_atom tag taggeremail '<committer@example.com>'\n+test_atom tag taggeremail:trim 'committer@example.com'\n+test_atom tag taggeremail:localpart 'committer'\n test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n@@ -564,10 +576,14 @@ test_atom refs/tags/taggerless tag 'taggerless'\n test_atom refs/tags/taggerless tagger ''\n test_atom refs/tags/taggerless taggername ''\n test_atom refs/tags/taggerless taggeremail ''\n+test_atom refs/tags/taggerless taggeremail:trim ''\n+test_atom refs/tags/taggerless taggeremail:localpart ''\n test_atom refs/tags/taggerless taggerdate ''\n test_atom refs/tags/taggerless committer ''\n test_atom refs/tags/taggerless committername ''\n test_atom refs/tags/taggerless committeremail ''\n+test_atom refs/tags/taggerless committeremail:trim ''\n+test_atom refs/tags/taggerless committeremail:localpart ''\n test_atom refs/tags/taggerless committerdate ''\n test_atom refs/tags/taggerless subject 'Broken tag'\n \n-- \ngitgitgadget\n\n"},{"id":"404225","messageId":"c508c96eb88f7e11b0ac6303c30fa78dd7585ec0.1598046110.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v4.git.1598046110.gitgitgadget@gmail.com","subject":"[PATCH v4 2/8] ref-filter: refactor `grab_objectname()`","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-21T21:41:44Z","receivedAt":"2020-08-21T21:42:07Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nPrepares `grab_objectname()` for more generic usage.\nThis change will allow us to reuse `grab_objectname()` for\nthe `tree` and `parent` atoms in a following commit.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c | 36 +++++++++++++++++++++---------------\n 1 file changed, 21 insertions(+), 15 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex e60765f156..9bf92db6df 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -918,21 +918,27 @@ int verify_ref_format(struct ref_format *format)\n \treturn 0;\n }\n \n-static int grab_objectname(const char *name, const struct object_id *oid,\n+static const char *do_grab_objectname(const char *field, const struct object_id *oid,\n+\t\t\t\t      struct used_atom *atom)\n+{\n+\tswitch (atom->u.objectname.option) {\n+\tcase O_FULL:\n+\t\treturn oid_to_hex(oid);\n+\tcase O_LENGTH:\n+\t\treturn find_unique_abbrev(oid, atom->u.objectname.length);\n+\tcase O_SHORT:\n+\t\treturn find_unique_abbrev(oid, DEFAULT_ABBREV);\n+\tdefault:\n+\t\tBUG(\"unknown %%(%s) option\", field);\n+\t}\n+}\n+\n+static int grab_objectname(const char *name, const char *field, const struct object_id *oid,\n \t\t\t   struct atom_value *v, struct used_atom *atom)\n {\n-\tif (starts_with(name, \"objectname\")) {\n-\t\tif (atom->u.objectname.option == O_SHORT) {\n-\t\t\tv->s = xstrdup(find_unique_abbrev(oid, DEFAULT_ABBREV));\n-\t\t\treturn 1;\n-\t\t} else if (atom->u.objectname.option == O_FULL) {\n-\t\t\tv->s = xstrdup(oid_to_hex(oid));\n-\t\t\treturn 1;\n-\t\t} else if (atom->u.objectname.option == O_LENGTH) {\n-\t\t\tv->s = xstrdup(find_unique_abbrev(oid, atom->u.objectname.length));\n-\t\t\treturn 1;\n-\t\t} else\n-\t\t\tBUG(\"unknown %%(objectname) option\");\n+\tif (starts_with(name, field)) {\n+\t\tv->s = xstrdup(do_grab_objectname(field, oid, atom));\n+\t\treturn 1;\n \t}\n \treturn 0;\n }\n@@ -960,7 +966,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_\n \t\t} else if (!strcmp(name, \"deltabase\"))\n \t\t\tv->s = xstrdup(oid_to_hex(&oi->delta_base_oid));\n \t\telse if (deref)\n-\t\t\tgrab_objectname(name, &oi->oid, v, &used_atom[i]);\n+\t\t\tgrab_objectname(name, \"objectname\", &oi->oid, v, &used_atom[i]);\n \t}\n }\n \n@@ -1740,7 +1746,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)\n \t\t\t\tv->s = xstrdup(buf + 1);\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (!deref && grab_objectname(name, &ref->objectname, v, atom)) {\n+\t\t} else if (!deref && grab_objectname(name, \"objectname\", &ref->objectname, v, atom)) {\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"HEAD\")) {\n \t\t\tif (atom->u.head && !strcmp(ref->refname, atom->u.head))\n-- \ngitgitgadget\n\n"},{"id":"404226","messageId":"582f00ace6b3173cfebb3f6e5d859f471ea01ab7.1598046110.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v4.git.1598046110.gitgitgadget@gmail.com","subject":"[PATCH v4 3/8] ref-filter: modify error messages in `grab_objectname()`","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-21T21:41:45Z","receivedAt":"2020-08-21T21:42:08Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nAs we plan to use `grab_objectname()` for `tree` and `parent` atom,\nit's better to parameterize the error messages in the function\n`grab_objectname()` where \"objectname\" is hard coded.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 9bf92db6df..4f4591cad0 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -372,11 +372,11 @@ static int objectname_atom_parser(const struct ref_format *format, struct used_a\n \t\tatom->u.objectname.option = O_LENGTH;\n \t\tif (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n \t\t    atom->u.objectname.length == 0)\n-\t\t\treturn strbuf_addf_ret(err, -1, _(\"positive value expected objectname:short=%s\"), arg);\n+\t\t\treturn strbuf_addf_ret(err, -1, _(\"positive value expected '%s' in %%(%s)\"), arg, atom->name);\n \t\tif (atom->u.objectname.length < MINIMUM_ABBREV)\n \t\t\tatom->u.objectname.length = MINIMUM_ABBREV;\n \t} else\n-\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized %%(objectname) argument: %s\"), arg);\n+\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized argument '%s' in %%(%s)\"), arg, atom->name);\n \treturn 0;\n }\n \n-- \ngitgitgadget\n\n"},{"id":"404227","messageId":"6b971667960549fb5fbd293e1880ec34169e5aea.1598046110.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v4.git.1598046110.gitgitgadget@gmail.com","subject":"[PATCH v4 5/8] ref-filter: add `short` modifier to 'tree' atom","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-21T21:41:47Z","receivedAt":"2020-08-21T21:42:09Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nSometimes while using 'tree' atom, user might want to see abbrev hash\ninstead of full 40 character hash.\n\nJust like 'objectname', it might be convenient for users to have the\n`:short` and `:short=<length>` option for printing 'tree' hash.\n\nLet's introduce `short` option to 'tree' atom.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 2 ++\n ref-filter.c                       | 9 ++++-----\n t/t6300-for-each-ref.sh            | 6 ++++++\n 3 files changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex e6ce8af612..40ebdfcc41 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -222,6 +222,8 @@ worktreepath::\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\n+Field `tree` can also be used with modifier `:short` and\n+`:short=<length>` just like `objectname`.\n \n For commit and tag objects, the special `creatordate` and `creator`\n fields will correspond to the appropriate date or name-email-date tuple\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 066975b306..3449fe45d8 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -497,7 +497,7 @@ static struct {\n \t{ \"objectsize\", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser },\n \t{ \"objectname\", SOURCE_OTHER, FIELD_STR, oid_atom_parser },\n \t{ \"deltabase\", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },\n-\t{ \"tree\", SOURCE_OBJ },\n+\t{ \"tree\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n \t{ \"parent\", SOURCE_OBJ },\n \t{ \"numparent\", SOURCE_OBJ, FIELD_ULONG },\n \t{ \"object\", SOURCE_OBJ },\n@@ -1005,10 +1005,9 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object\n \t\t\tcontinue;\n \t\tif (deref)\n \t\t\tname++;\n-\t\tif (!strcmp(name, \"tree\")) {\n-\t\t\tv->s = xstrdup(oid_to_hex(get_commit_tree_oid(commit)));\n-\t\t}\n-\t\telse if (!strcmp(name, \"numparent\")) {\n+\t\tif (grab_oid(name, \"tree\", get_commit_tree_oid(commit), v, &used_atom[i]))\n+\t\t\tcontinue;\n+\t\tif (!strcmp(name, \"numparent\")) {\n \t\t\tv->value = commit_list_count(commit->parents);\n \t\t\tv->s = xstrfmt(\"%lu\", (unsigned long)v->value);\n \t\t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 64fbc91146..e30bbff6d9 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -116,6 +116,9 @@ test_atom head objectname:short $(git rev-parse --short refs/heads/master)\n test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom head tree $(git rev-parse refs/heads/master^{tree})\n+test_atom head tree:short $(git rev-parse --short refs/heads/master^{tree})\n+test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/master^{tree})\n+test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/master^{tree})\n test_atom head parent ''\n test_atom head numparent 0\n test_atom head object ''\n@@ -167,6 +170,9 @@ test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom tag tree ''\n+test_atom tag tree:short ''\n+test_atom tag tree:short=1 ''\n+test_atom tag tree:short=10 ''\n test_atom tag parent ''\n test_atom tag numparent ''\n test_atom tag object $(git rev-parse refs/tags/testtag^0)\n-- \ngitgitgadget\n\n"},{"id":"404228","messageId":"5ed5ac259d59b855fc5c956bc8af80fecfc7c971.1598046110.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v4.git.1598046110.gitgitgadget@gmail.com","subject":"[PATCH v4 6/8] ref-filter: add `short` modifier to 'parent' atom","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-21T21:41:48Z","receivedAt":"2020-08-21T21:42:11Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nSometimes while using 'parent' atom, user might want to see abbrev hash\ninstead of full 40 character hash.\n\nJust like 'objectname', it might be convenient for users to have the\n`:short` and `:short=<length>` option for printing 'parent' hash.\n\nLet's introduce `short` option to 'parent' atom.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 2 +-\n ref-filter.c                       | 8 ++++----\n t/t6300-for-each-ref.sh            | 6 ++++++\n 3 files changed, 11 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 40ebdfcc41..dd09763e7d 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -222,7 +222,7 @@ worktreepath::\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\n-Field `tree` can also be used with modifier `:short` and\n+Fields `tree` and `parent` can also be used with modifier `:short` and\n `:short=<length>` just like `objectname`.\n \n For commit and tag objects, the special `creatordate` and `creator`\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 3449fe45d8..c7d81088e4 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -498,7 +498,7 @@ static struct {\n \t{ \"objectname\", SOURCE_OTHER, FIELD_STR, oid_atom_parser },\n \t{ \"deltabase\", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },\n \t{ \"tree\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n-\t{ \"parent\", SOURCE_OBJ },\n+\t{ \"parent\", SOURCE_OBJ, FIELD_STR, oid_atom_parser },\n \t{ \"numparent\", SOURCE_OBJ, FIELD_ULONG },\n \t{ \"object\", SOURCE_OBJ },\n \t{ \"type\", SOURCE_OBJ },\n@@ -1011,14 +1011,14 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object\n \t\t\tv->value = commit_list_count(commit->parents);\n \t\t\tv->s = xstrfmt(\"%lu\", (unsigned long)v->value);\n \t\t}\n-\t\telse if (!strcmp(name, \"parent\")) {\n+\t\telse if (starts_with(name, \"parent\")) {\n \t\t\tstruct commit_list *parents;\n \t\t\tstruct strbuf s = STRBUF_INIT;\n \t\t\tfor (parents = commit->parents; parents; parents = parents->next) {\n-\t\t\t\tstruct commit *parent = parents->item;\n+\t\t\t\tstruct object_id *oid = &parents->item->object.oid;\n \t\t\t\tif (parents != commit->parents)\n \t\t\t\t\tstrbuf_addch(&s, ' ');\n-\t\t\t\tstrbuf_addstr(&s, oid_to_hex(&parent->object.oid));\n+\t\t\t\tstrbuf_addstr(&s, do_grab_oid(\"parent\", oid, &used_atom[i]));\n \t\t\t}\n \t\t\tv->s = strbuf_detach(&s, NULL);\n \t\t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex e30bbff6d9..79d5b29387 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -120,6 +120,9 @@ test_atom head tree:short $(git rev-parse --short refs/heads/master^{tree})\n test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/master^{tree})\n test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/master^{tree})\n test_atom head parent ''\n+test_atom head parent:short ''\n+test_atom head parent:short=1 ''\n+test_atom head parent:short=10 ''\n test_atom head numparent 0\n test_atom head object ''\n test_atom head type ''\n@@ -174,6 +177,9 @@ test_atom tag tree:short ''\n test_atom tag tree:short=1 ''\n test_atom tag tree:short=10 ''\n test_atom tag parent ''\n+test_atom tag parent:short ''\n+test_atom tag parent:short=1 ''\n+test_atom tag parent:short=10 ''\n test_atom tag numparent ''\n test_atom tag object $(git rev-parse refs/tags/testtag^0)\n test_atom tag type 'commit'\n-- \ngitgitgadget\n\n"},{"id":"404229","messageId":"7cba8d7a2881055e89976ca420392da2eaa596b8.1598046110.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v4.git.1598046110.gitgitgadget@gmail.com","subject":"[PATCH v4 8/8] ref-filter: add `sanitize` option for 'subject' atom","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-21T21:41:50Z","receivedAt":"2020-08-21T21:42:12Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nCurrently, subject does not take any arguments. This commit introduce\n`sanitize` formatting option to 'subject' atom.\n\n`subject:sanitize` - print sanitized subject line, suitable for a filename.\n\ne.g.\n%(subject): \"the subject line\"\n%(subject:sanitize): \"the-subject-line\"\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 +++\n ref-filter.c                       | 23 +++++++++++++++--------\n t/t6300-for-each-ref.sh            |  7 +++++++\n 3 files changed, 25 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex dd09763e7d..616ce46087 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -247,6 +247,9 @@ contents:subject::\n \tThe first paragraph of the message, which typically is a\n \tsingle line, is taken as the \"subject\" of the commit or the\n \ttag message.\n+\tInstead of `contents:subject`, field `subject` can also be used to\n+\tobtain same results. `:sanitize` can be appended to `subject` for\n+\tsubject line suitable for filename.\n \n contents:body::\n \tThe remainder of the commit or the tag message that follows\ndiff --git a/ref-filter.c b/ref-filter.c\nindex c7d81088e4..12bb78ce06 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -127,8 +127,8 @@ static struct used_atom {\n \t\t\tunsigned int nobracket : 1, push : 1, push_remote : 1;\n \t\t} remote_ref;\n \t\tstruct {\n-\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH,\n-\t\t\t       C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n+\t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH, C_LINES,\n+\t\t\t       C_SIG, C_SUB, C_SUB_SANITIZE, C_TRAILERS } option;\n \t\t\tstruct process_trailer_options trailer_opts;\n \t\t\tunsigned int nlines;\n \t\t} contents;\n@@ -301,9 +301,12 @@ static int body_atom_parser(const struct ref_format *format, struct used_atom *a\n static int subject_atom_parser(const struct ref_format *format, struct used_atom *atom,\n \t\t\t       const char *arg, struct strbuf *err)\n {\n-\tif (arg)\n-\t\treturn strbuf_addf_ret(err, -1, _(\"%%(subject) does not take arguments\"));\n-\tatom->u.contents.option = C_SUB;\n+\tif (!arg)\n+\t\tatom->u.contents.option = C_SUB;\n+\telse if (!strcmp(arg, \"sanitize\"))\n+\t\tatom->u.contents.option = C_SUB_SANITIZE;\n+\telse\n+\t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized %%(subject) argument: %s\"), arg);\n \treturn 0;\n }\n \n@@ -1282,8 +1285,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n \t\t\tcontinue;\n \t\tif (deref)\n \t\t\tname++;\n-\t\tif (strcmp(name, \"subject\") &&\n-\t\t    strcmp(name, \"body\") &&\n+\t\tif (strcmp(name, \"body\") &&\n+\t\t    !starts_with(name, \"subject\") &&\n \t\t    !starts_with(name, \"trailers\") &&\n \t\t    !starts_with(name, \"contents\"))\n \t\t\tcontinue;\n@@ -1295,7 +1298,11 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)\n \n \t\tif (atom->u.contents.option == C_SUB)\n \t\t\tv->s = copy_subject(subpos, sublen);\n-\t\telse if (atom->u.contents.option == C_BODY_DEP)\n+\t\telse if (atom->u.contents.option == C_SUB_SANITIZE) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tformat_sanitized_subject(&sb, subpos, sublen);\n+\t\t\tv->s = strbuf_detach(&sb, NULL);\n+\t\t} else if (atom->u.contents.option == C_BODY_DEP)\n \t\t\tv->s = xmemdupz(bodypos, bodylen);\n \t\telse if (atom->u.contents.option == C_LENGTH)\n \t\t\tv->s = xstrfmt(\"%\"PRIuMAX, (uintmax_t)strlen(subpos));\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 79d5b29387..220ff5c3c2 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -150,6 +150,7 @@ test_atom head taggerdate ''\n test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n test_atom head subject 'Initial'\n+test_atom head subject:sanitize 'Initial'\n test_atom head contents:subject 'Initial'\n test_atom head body ''\n test_atom head contents:body ''\n@@ -207,6 +208,7 @@ test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n test_atom tag subject 'Tagging at 1151968727'\n+test_atom tag subject:sanitize 'Tagging-at-1151968727'\n test_atom tag contents:subject 'Tagging at 1151968727'\n test_atom tag body ''\n test_atom tag contents:body ''\n@@ -619,6 +621,7 @@ test_expect_success 'create tag with subject and body content' '\n \tgit tag -F msg subject-body\n '\n test_atom refs/tags/subject-body subject 'the subject line'\n+test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n test_atom refs/tags/subject-body body 'first body line\n second body line\n '\n@@ -639,6 +642,7 @@ test_expect_success 'create tag with multiline subject' '\n \tgit tag -F msg multiline\n '\n test_atom refs/tags/multiline subject 'first subject line second subject line'\n+test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n test_atom refs/tags/multiline body 'first body line\n second body line\n@@ -671,6 +675,7 @@ sig='-----BEGIN PGP SIGNATURE-----\n \n PREREQ=GPG\n test_atom refs/tags/signed-empty subject ''\n+test_atom refs/tags/signed-empty subject:sanitize ''\n test_atom refs/tags/signed-empty contents:subject ''\n test_atom refs/tags/signed-empty body \"$sig\"\n test_atom refs/tags/signed-empty contents:body ''\n@@ -678,6 +683,7 @@ test_atom refs/tags/signed-empty contents:signature \"$sig\"\n test_atom refs/tags/signed-empty contents \"$sig\"\n \n test_atom refs/tags/signed-short subject 'subject line'\n+test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n test_atom refs/tags/signed-short contents:subject 'subject line'\n test_atom refs/tags/signed-short body \"$sig\"\n test_atom refs/tags/signed-short contents:body ''\n@@ -686,6 +692,7 @@ test_atom refs/tags/signed-short contents \"subject line\n $sig\"\n \n test_atom refs/tags/signed-long subject 'subject line'\n+test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n test_atom refs/tags/signed-long contents:subject 'subject line'\n test_atom refs/tags/signed-long body \"body contents\n $sig\"\n-- \ngitgitgadget\n"},{"id":"404230","messageId":"503a1874ce48192eba27d7fe3d61ffb6d44e4ee0.1598046110.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v4.git.1598046110.gitgitgadget@gmail.com","subject":"[PATCH v4 4/8] ref-filter: rename `objectname` related functions and fields","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-21T21:41:46Z","receivedAt":"2020-08-21T21:42:13Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nIn previous commits, we prepared some `objectname` related functions\nfor more generic usage, so that these functions can be used for `tree`\nand `parent` atom.\n\nBut the name of some functions and fields may mislead someone.\nFor ex: function `objectname_atom_parser()` implies that it is\nfor atom `objectname`.\n\nLet's rename all such functions and fields.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n ref-filter.c | 40 ++++++++++++++++++++--------------------\n 1 file changed, 20 insertions(+), 20 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 4f4591cad0..066975b306 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -139,7 +139,7 @@ static struct used_atom {\n \t\tstruct {\n \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n \t\t\tunsigned int length;\n-\t\t} objectname;\n+\t\t} oid;\n \t\tstruct email_option {\n \t\t\tenum { EO_RAW, EO_TRIM, EO_LOCALPART } option;\n \t\t} email_option;\n@@ -361,20 +361,20 @@ static int contents_atom_parser(const struct ref_format *format, struct used_ato\n \treturn 0;\n }\n \n-static int objectname_atom_parser(const struct ref_format *format, struct used_atom *atom,\n-\t\t\t\t  const char *arg, struct strbuf *err)\n+static int oid_atom_parser(const struct ref_format *format, struct used_atom *atom,\n+\t\t\t   const char *arg, struct strbuf *err)\n {\n \tif (!arg)\n-\t\tatom->u.objectname.option = O_FULL;\n+\t\tatom->u.oid.option = O_FULL;\n \telse if (!strcmp(arg, \"short\"))\n-\t\tatom->u.objectname.option = O_SHORT;\n+\t\tatom->u.oid.option = O_SHORT;\n \telse if (skip_prefix(arg, \"short=\", &arg)) {\n-\t\tatom->u.objectname.option = O_LENGTH;\n-\t\tif (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n-\t\t    atom->u.objectname.length == 0)\n+\t\tatom->u.oid.option = O_LENGTH;\n+\t\tif (strtoul_ui(arg, 10, &atom->u.oid.length) ||\n+\t\t    atom->u.oid.length == 0)\n \t\t\treturn strbuf_addf_ret(err, -1, _(\"positive value expected '%s' in %%(%s)\"), arg, atom->name);\n-\t\tif (atom->u.objectname.length < MINIMUM_ABBREV)\n-\t\t\tatom->u.objectname.length = MINIMUM_ABBREV;\n+\t\tif (atom->u.oid.length < MINIMUM_ABBREV)\n+\t\t\tatom->u.oid.length = MINIMUM_ABBREV;\n \t} else\n \t\treturn strbuf_addf_ret(err, -1, _(\"unrecognized argument '%s' in %%(%s)\"), arg, atom->name);\n \treturn 0;\n@@ -495,7 +495,7 @@ static struct {\n \t{ \"refname\", SOURCE_NONE, FIELD_STR, refname_atom_parser },\n \t{ \"objecttype\", SOURCE_OTHER, FIELD_STR, objecttype_atom_parser },\n \t{ \"objectsize\", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser },\n-\t{ \"objectname\", SOURCE_OTHER, FIELD_STR, objectname_atom_parser },\n+\t{ \"objectname\", SOURCE_OTHER, FIELD_STR, oid_atom_parser },\n \t{ \"deltabase\", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },\n \t{ \"tree\", SOURCE_OBJ },\n \t{ \"parent\", SOURCE_OBJ },\n@@ -918,14 +918,14 @@ int verify_ref_format(struct ref_format *format)\n \treturn 0;\n }\n \n-static const char *do_grab_objectname(const char *field, const struct object_id *oid,\n-\t\t\t\t      struct used_atom *atom)\n+static const char *do_grab_oid(const char *field, const struct object_id *oid,\n+\t\t\t       struct used_atom *atom)\n {\n-\tswitch (atom->u.objectname.option) {\n+\tswitch (atom->u.oid.option) {\n \tcase O_FULL:\n \t\treturn oid_to_hex(oid);\n \tcase O_LENGTH:\n-\t\treturn find_unique_abbrev(oid, atom->u.objectname.length);\n+\t\treturn find_unique_abbrev(oid, atom->u.oid.length);\n \tcase O_SHORT:\n \t\treturn find_unique_abbrev(oid, DEFAULT_ABBREV);\n \tdefault:\n@@ -933,11 +933,11 @@ static const char *do_grab_objectname(const char *field, const struct object_id\n \t}\n }\n \n-static int grab_objectname(const char *name, const char *field, const struct object_id *oid,\n-\t\t\t   struct atom_value *v, struct used_atom *atom)\n+static int grab_oid(const char *name, const char *field, const struct object_id *oid,\n+\t\t    struct atom_value *v, struct used_atom *atom)\n {\n \tif (starts_with(name, field)) {\n-\t\tv->s = xstrdup(do_grab_objectname(field, oid, atom));\n+\t\tv->s = xstrdup(do_grab_oid(field, oid, atom));\n \t\treturn 1;\n \t}\n \treturn 0;\n@@ -966,7 +966,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_\n \t\t} else if (!strcmp(name, \"deltabase\"))\n \t\t\tv->s = xstrdup(oid_to_hex(&oi->delta_base_oid));\n \t\telse if (deref)\n-\t\t\tgrab_objectname(name, \"objectname\", &oi->oid, v, &used_atom[i]);\n+\t\t\tgrab_oid(name, \"objectname\", &oi->oid, v, &used_atom[i]);\n \t}\n }\n \n@@ -1746,7 +1746,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)\n \t\t\t\tv->s = xstrdup(buf + 1);\n \t\t\t}\n \t\t\tcontinue;\n-\t\t} else if (!deref && grab_objectname(name, \"objectname\", &ref->objectname, v, atom)) {\n+\t\t} else if (!deref && grab_oid(name, \"objectname\", &ref->objectname, v, atom)) {\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"HEAD\")) {\n \t\t\tif (atom->u.head && !strcmp(ref->refname, atom->u.head))\n-- \ngitgitgadget\n\n"},{"id":"404231","messageId":"6105046d96223bda40ab0f0177e4f0281376ba53.1598046110.git.gitgitgadget@gmail.com","threadId":"53931","inReplyTo":"pull.684.v4.git.1598046110.gitgitgadget@gmail.com","subject":"[PATCH v4 7/8] pretty: refactor `format_sanitized_subject()`","fromName":"Hariom Verma via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-21T21:41:49Z","receivedAt":"2020-08-21T21:42:18Z","isPatch":true,"sender":{"key":"name:Hariom Verma","avatar":null},"body":"From: Hariom Verma <hariom18599@gmail.com>\n\nThe function 'format_sanitized_subject()' is responsible for\nsanitized subject line in pretty.c\ne.g.\nthe subject line\nthe-sanitized-subject-line\n\nIt would be a nice enhancement to `subject` atom to have the\nsame feature. So in the later commits, we plan to add this feature\nto ref-filter.\n\nRefactor `format_sanitized_subject()`, so it can be reused in\nref-filter.c for adding new modifier `sanitize` to \"subject\" atom.\n\nCurrently, the loop inside `format_sanitized_subject()` runs\nuntil `\\n` is found. But now, we stored the first occurrence\nof `\\n` in a variable `eol` and passed it in\n`format_sanitized_subject()`. And the loop runs upto `eol`.\n\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Heba Waly <heba.waly@gmail.com>\nSigned-off-by: Hariom Verma <hariom18599@gmail.com>\n---\n pretty.c | 20 +++++++++++---------\n pretty.h |  3 +++\n 2 files changed, 14 insertions(+), 9 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 2a3d46bf42..7a7708a0ea 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -839,21 +839,22 @@ static int istitlechar(char c)\n \t\t(c >= '0' && c <= '9') || c == '.' || c == '_';\n }\n \n-static void format_sanitized_subject(struct strbuf *sb, const char *msg)\n+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)\n {\n \tsize_t trimlen;\n \tsize_t start_len = sb->len;\n \tint space = 2;\n+\tint i;\n \n-\tfor (; *msg && *msg != '\\n'; msg++) {\n-\t\tif (istitlechar(*msg)) {\n+\tfor (i = 0; i < len; i++) {\n+\t\tif (istitlechar(msg[i])) {\n \t\t\tif (space == 1)\n \t\t\t\tstrbuf_addch(sb, '-');\n \t\t\tspace = 0;\n-\t\t\tstrbuf_addch(sb, *msg);\n-\t\t\tif (*msg == '.')\n-\t\t\t\twhile (*(msg+1) == '.')\n-\t\t\t\t\tmsg++;\n+\t\t\tstrbuf_addch(sb, msg[i]);\n+\t\t\tif (msg[i] == '.')\n+\t\t\t\twhile (msg[i+1] == '.')\n+\t\t\t\t\ti++;\n \t\t} else\n \t\t\tspace |= 1;\n \t}\n@@ -1155,7 +1156,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \tconst struct commit *commit = c->commit;\n \tconst char *msg = c->message;\n \tstruct commit_list *p;\n-\tconst char *arg;\n+\tconst char *arg, *eol;\n \tsize_t res;\n \tchar **slot;\n \n@@ -1405,7 +1406,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\tformat_subject(sb, msg + c->subject_off, \" \");\n \t\treturn 1;\n \tcase 'f':\t/* sanitized subject */\n-\t\tformat_sanitized_subject(sb, msg + c->subject_off);\n+\t\teol = strchrnul(msg + c->subject_off, '\\n');\n+\t\tformat_sanitized_subject(sb, msg + c->subject_off, eol - (msg + c->subject_off));\n \t\treturn 1;\n \tcase 'b':\t/* body */\n \t\tstrbuf_addstr(sb, msg + c->body_off);\ndiff --git a/pretty.h b/pretty.h\nindex 071f2fb8e4..7ce6c0b437 100644\n--- a/pretty.h\n+++ b/pretty.h\n@@ -139,4 +139,7 @@ const char *format_subject(struct strbuf *sb, const char *msg,\n /* Check if \"cmit_fmt\" will produce an empty output. */\n int commit_format_is_empty(enum cmit_fmt);\n \n+/* Make subject of commit message suitable for filename */\n+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len);\n+\n #endif /* PRETTY_H */\n-- \ngitgitgadget\n\n"}]}