{"thread":{"id":"34387","subject":"[RESEND][PATCH 00/15] Towards a more awesome git branch","startedAt":"2013-07-09T10:32:11Z","lastAt":"2013-07-13T12:31:39Z","messageCount":17,"participants":["Ramkumar Ramachandra","Duy Nguyen"],"isPatch":true,"patchVersion":1,"patchTotal":15},"messages":[{"id":"222897","messageId":"1373365946-9230-1-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":null,"subject":"[RESEND][PATCH 00/15] Towards a more awesome git branch","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:11Z","receivedAt":"2013-07-09T10:32:11Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nI'm sending this out in the hope of attracting some reviews.  It's an\nunedited resend, and there were zero conflicts from the rebase.\n\nThanks.\n\nNguyễn Thái Ngọc Duy (8):\n  for-each-ref, quote: convert *_quote_print -> *_quote_buf\n  for-each-ref: don't print out elements directly\n  pretty: extend pretty_print_context with callback\n  pretty: allow passing NULL commit to format_commit_message()\n  for-each-ref: get --pretty using format_commit_message()\n  for-each-ref: teach verify_format() about pretty's syntax\n  for-each-ref: introduce format specifier %>(*) and %<(*)\n  for-each-ref: improve responsiveness of %(upstream:track)\n\nRamkumar Ramachandra (7):\n  tar-tree: remove dependency on sq_quote_print()\n  quote: remove sq_quote_print()\n  pretty: limit recursion in format_commit_one()\n  for-each-ref: introduce %(HEAD) marker\n  for-each-ref: introduce %(upstream:track[short])\n  pretty: introduce get_pretty_userformat\n  for-each-ref: use get_pretty_userformat in --pretty\n\n Documentation/git-for-each-ref.txt |  43 +++++-\n builtin/for-each-ref.c             | 279 ++++++++++++++++++++++++++++++-------\n builtin/tar-tree.c                 |  11 +-\n commit.h                           |   9 ++\n pretty.c                           |  77 +++++++++-\n quote.c                            |  61 +++-----\n quote.h                            |   8 +-\n t/t6300-for-each-ref.sh            | 143 +++++++++++++++++++\n 8 files changed, 521 insertions(+), 110 deletions(-)\n\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222899","messageId":"1373365946-9230-2-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 01/15] for-each-ref, quote: convert *_quote_print -> *_quote_buf","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:12Z","receivedAt":"2013-07-09T10:32:12Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nfor-each-ref.c:print_value() currently prints values to stdout\nimmediately using {sq|perl|python|tcl}_quote_print, giving us no\nopportunity to do any further processing.  In preparation for getting\nprint_value() to accept an additional strbuf argument to write to,\nconvert the *_quote_print functions and callers to *_quote_buf.\n\n[rr: commit message, minor modifications]\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/for-each-ref.c | 13 +++++++++----\n quote.c                | 44 ++++++++++++++++++++++----------------------\n quote.h                |  6 +++---\n 3 files changed, 34 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 7f059c3..1d4083c 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -867,24 +867,29 @@ static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs\n static void print_value(struct refinfo *ref, int atom, int quote_style)\n {\n \tstruct atom_value *v;\n+\tstruct strbuf sb = STRBUF_INIT;\n \tget_value(ref, atom, &v);\n \tswitch (quote_style) {\n \tcase QUOTE_NONE:\n \t\tfputs(v->s, stdout);\n \t\tbreak;\n \tcase QUOTE_SHELL:\n-\t\tsq_quote_print(stdout, v->s);\n+\t\tsq_quote_buf(&sb, v->s);\n \t\tbreak;\n \tcase QUOTE_PERL:\n-\t\tperl_quote_print(stdout, v->s);\n+\t\tperl_quote_buf(&sb, v->s);\n \t\tbreak;\n \tcase QUOTE_PYTHON:\n-\t\tpython_quote_print(stdout, v->s);\n+\t\tpython_quote_buf(&sb, v->s);\n \t\tbreak;\n \tcase QUOTE_TCL:\n-\t\ttcl_quote_print(stdout, v->s);\n+\t\ttcl_quote_buf(&sb, v->s);\n \t\tbreak;\n \t}\n+\tif (quote_style != QUOTE_NONE) {\n+\t\tfputs(sb.buf, stdout);\n+\t\tstrbuf_release(&sb);\n+\t}\n }\n \n static int hex1(char ch)\ndiff --git a/quote.c b/quote.c\nindex 911229f..8c294df 100644\n--- a/quote.c\n+++ b/quote.c\n@@ -463,72 +463,72 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)\n \n /* quoting as a string literal for other languages */\n \n-void perl_quote_print(FILE *stream, const char *src)\n+void perl_quote_buf(struct strbuf *sb, const char *src)\n {\n \tconst char sq = '\\'';\n \tconst char bq = '\\\\';\n \tchar c;\n \n-\tfputc(sq, stream);\n+\tstrbuf_addch(sb, sq);\n \twhile ((c = *src++)) {\n \t\tif (c == sq || c == bq)\n-\t\t\tfputc(bq, stream);\n-\t\tfputc(c, stream);\n+\t\t\tstrbuf_addch(sb, bq);\n+\t\tstrbuf_addch(sb, c);\n \t}\n-\tfputc(sq, stream);\n+\tstrbuf_addch(sb, sq);\n }\n \n-void python_quote_print(FILE *stream, const char *src)\n+void python_quote_buf(struct strbuf *sb, const char *src)\n {\n \tconst char sq = '\\'';\n \tconst char bq = '\\\\';\n \tconst char nl = '\\n';\n \tchar c;\n \n-\tfputc(sq, stream);\n+\tstrbuf_addch(sb, sq);\n \twhile ((c = *src++)) {\n \t\tif (c == nl) {\n-\t\t\tfputc(bq, stream);\n-\t\t\tfputc('n', stream);\n+\t\t\tstrbuf_addch(sb, bq);\n+\t\t\tstrbuf_addch(sb, 'n');\n \t\t\tcontinue;\n \t\t}\n \t\tif (c == sq || c == bq)\n-\t\t\tfputc(bq, stream);\n-\t\tfputc(c, stream);\n+\t\t\tstrbuf_addch(sb, bq);\n+\t\tstrbuf_addch(sb, c);\n \t}\n-\tfputc(sq, stream);\n+\tstrbuf_addch(sb, sq);\n }\n \n-void tcl_quote_print(FILE *stream, const char *src)\n+void tcl_quote_buf(struct strbuf *sb, const char *src)\n {\n \tchar c;\n \n-\tfputc('\"', stream);\n+\tstrbuf_addch(sb, '\"');\n \twhile ((c = *src++)) {\n \t\tswitch (c) {\n \t\tcase '[': case ']':\n \t\tcase '{': case '}':\n \t\tcase '$': case '\\\\': case '\"':\n-\t\t\tfputc('\\\\', stream);\n+\t\t\tstrbuf_addch(sb, '\\\\');\n \t\tdefault:\n-\t\t\tfputc(c, stream);\n+\t\t\tstrbuf_addch(sb, c);\n \t\t\tbreak;\n \t\tcase '\\f':\n-\t\t\tfputs(\"\\\\f\", stream);\n+\t\t\tstrbuf_addstr(sb, \"\\\\f\");\n \t\t\tbreak;\n \t\tcase '\\r':\n-\t\t\tfputs(\"\\\\r\", stream);\n+\t\t\tstrbuf_addstr(sb, \"\\\\r\");\n \t\t\tbreak;\n \t\tcase '\\n':\n-\t\t\tfputs(\"\\\\n\", stream);\n+\t\t\tstrbuf_addstr(sb, \"\\\\n\");\n \t\t\tbreak;\n \t\tcase '\\t':\n-\t\t\tfputs(\"\\\\t\", stream);\n+\t\t\tstrbuf_addstr(sb, \"\\\\t\");\n \t\t\tbreak;\n \t\tcase '\\v':\n-\t\t\tfputs(\"\\\\v\", stream);\n+\t\t\tstrbuf_addstr(sb, \"\\\\v\");\n \t\t\tbreak;\n \t\t}\n \t}\n-\tfputc('\"', stream);\n+\tstrbuf_addch(sb, '\"');\n }\ndiff --git a/quote.h b/quote.h\nindex 133155a..ed06df5 100644\n--- a/quote.h\n+++ b/quote.h\n@@ -69,8 +69,8 @@ extern char *quote_path_relative(const char *in, int len,\n \t\t\t  struct strbuf *out, const char *prefix);\n \n /* quoting as a string literal for other languages */\n-extern void perl_quote_print(FILE *stream, const char *src);\n-extern void python_quote_print(FILE *stream, const char *src);\n-extern void tcl_quote_print(FILE *stream, const char *src);\n+extern void perl_quote_buf(struct strbuf *sb, const char *src);\n+extern void python_quote_buf(struct strbuf *sb, const char *src);\n+extern void tcl_quote_buf(struct strbuf *sb, const char *src);\n \n #endif\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222898","messageId":"1373365946-9230-3-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 02/15] for-each-ref: don't print out elements directly","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:13Z","receivedAt":"2013-07-09T10:32:13Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nCurrently, the entire callchain starting from show_ref() parses and\nprints immediately.  This inflexibility limits our ability to extend the\nparser.  So, convert the entire callchain to accept a strbuf argument to\nwrite to.  Also introduce a show_refs() helper that calls show_ref() in\na loop to avoid cluttering up cmd_for_each_ref() with the task of\ninitializing/freeing the strbuf.\n\n[rr: commit message]\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/for-each-ref.c | 55 ++++++++++++++++++++++++++++++++------------------\n 1 file changed, 35 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 1d4083c..e2d6c5a 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -864,31 +864,31 @@ static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs\n \tqsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);\n }\n \n-static void print_value(struct refinfo *ref, int atom, int quote_style)\n+static void print_value(struct strbuf *sb, struct refinfo *ref,\n+\t\t\tint atom, int quote_style)\n {\n \tstruct atom_value *v;\n-\tstruct strbuf sb = STRBUF_INIT;\n \tget_value(ref, atom, &v);\n \tswitch (quote_style) {\n \tcase QUOTE_NONE:\n-\t\tfputs(v->s, stdout);\n+\t\tstrbuf_addstr(sb, v->s);\n \t\tbreak;\n \tcase QUOTE_SHELL:\n-\t\tsq_quote_buf(&sb, v->s);\n+\t\tsq_quote_buf(sb, v->s);\n \t\tbreak;\n \tcase QUOTE_PERL:\n-\t\tperl_quote_buf(&sb, v->s);\n+\t\tperl_quote_buf(sb, v->s);\n \t\tbreak;\n \tcase QUOTE_PYTHON:\n-\t\tpython_quote_buf(&sb, v->s);\n+\t\tpython_quote_buf(sb, v->s);\n \t\tbreak;\n \tcase QUOTE_TCL:\n-\t\ttcl_quote_buf(&sb, v->s);\n+\t\ttcl_quote_buf(sb, v->s);\n \t\tbreak;\n \t}\n \tif (quote_style != QUOTE_NONE) {\n-\t\tfputs(sb.buf, stdout);\n-\t\tstrbuf_release(&sb);\n+\t\tfputs(sb->buf, stdout);\n+\t\tstrbuf_release(sb);\n \t}\n }\n \n@@ -910,7 +910,7 @@ static int hex2(const char *cp)\n \t\treturn -1;\n }\n \n-static void emit(const char *cp, const char *ep)\n+static void emit(struct strbuf *sb, const char *cp, const char *ep)\n {\n \twhile (*cp && (!ep || cp < ep)) {\n \t\tif (*cp == '%') {\n@@ -919,32 +919,47 @@ static void emit(const char *cp, const char *ep)\n \t\t\telse {\n \t\t\t\tint ch = hex2(cp + 1);\n \t\t\t\tif (0 <= ch) {\n-\t\t\t\t\tputchar(ch);\n+\t\t\t\t\tstrbuf_addch(sb, ch);\n \t\t\t\t\tcp += 3;\n \t\t\t\t\tcontinue;\n \t\t\t\t}\n \t\t\t}\n \t\t}\n-\t\tputchar(*cp);\n+\t\tstrbuf_addch(sb, *cp);\n \t\tcp++;\n \t}\n }\n \n-static void show_ref(struct refinfo *info, const char *format, int quote_style)\n+static void show_ref(struct strbuf *sb, struct refinfo *info,\n+\t\t     const char *format, int quote_style)\n {\n \tconst char *cp, *sp, *ep;\n \n \tfor (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n \t\tep = strchr(sp, ')');\n \t\tif (cp < sp)\n-\t\t\temit(cp, sp);\n-\t\tprint_value(info, parse_atom(sp + 2, ep), quote_style);\n+\t\t\temit(sb, cp, sp);\n+\t\tprint_value(sb, info, parse_atom(sp + 2, ep), quote_style);\n \t}\n \tif (*cp) {\n \t\tsp = cp + strlen(cp);\n-\t\temit(cp, sp);\n+\t\temit(sb, cp, sp);\n \t}\n-\tputchar('\\n');\n+\tstrbuf_addch(sb, '\\n');\n+}\n+\n+static void show_refs(struct refinfo **refs, int maxcount,\n+\t\t      const char *format, int quote_style)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint i;\n+\n+\tfor (i = 0; i < maxcount; i++) {\n+\t\tstrbuf_reset(&sb);\n+\t\tshow_ref(&sb, refs[i], format, quote_style);\n+\t\tfputs(sb.buf, stdout);\n+\t}\n+\tstrbuf_release(&sb);\n }\n \n static struct ref_sort *default_sort(void)\n@@ -987,7 +1002,7 @@ static char const * const for_each_ref_usage[] = {\n \n int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n {\n-\tint i, num_refs;\n+\tint num_refs;\n \tconst char *format = \"%(objectname) %(objecttype)\\t%(refname)\";\n \tstruct ref_sort *sort = NULL, **sort_tail = &sort;\n \tint maxcount = 0, quote_style = 0;\n@@ -1041,7 +1056,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \n \tif (!maxcount || num_refs < maxcount)\n \t\tmaxcount = num_refs;\n-\tfor (i = 0; i < maxcount; i++)\n-\t\tshow_ref(refs[i], format, quote_style);\n+\n+\tshow_refs(refs, maxcount, format, quote_style);\n \treturn 0;\n }\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222901","messageId":"1373365946-9230-4-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 03/15] tar-tree: remove dependency on sq_quote_print()","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:14Z","receivedAt":"2013-07-09T10:32:14Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Currently, there is exactly one caller of sq_quote_print(), namely\ncmd_tar_tree().  In the interest of removing sq_quote_print() and\nsimplification, replace it with an equivalent call to sq_quote_argv().\nNo functional changes intended.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/tar-tree.c | 11 +++++------\n 1 file changed, 5 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/tar-tree.c b/builtin/tar-tree.c\nindex 3f1e701..ba3ffe6 100644\n--- a/builtin/tar-tree.c\n+++ b/builtin/tar-tree.c\n@@ -26,8 +26,8 @@ int cmd_tar_tree(int argc, const char **argv, const char *prefix)\n \t * $0 tree-ish basedir ==>\n \t * \tgit archive --format-tar --prefix=basedir tree-ish\n \t */\n-\tint i;\n \tconst char **nargv = xcalloc(sizeof(*nargv), argc + 3);\n+\tstruct strbuf sb = STRBUF_INIT;\n \tchar *basedir_arg;\n \tint nargc = 0;\n \n@@ -65,11 +65,10 @@ int cmd_tar_tree(int argc, const char **argv, const char *prefix)\n \tfprintf(stderr,\n \t\t\"*** \\\"git tar-tree\\\" is now deprecated.\\n\"\n \t\t\"*** Running \\\"git archive\\\" instead.\\n***\");\n-\tfor (i = 0; i < nargc; i++) {\n-\t\tfputc(' ', stderr);\n-\t\tsq_quote_print(stderr, nargv[i]);\n-\t}\n-\tfputc('\\n', stderr);\n+\tsq_quote_argv(&sb, nargv, 0);\n+\tstrbuf_addch(&sb, '\\n');\n+\tfputs(sb.buf, stderr);\n+\tstrbuf_release(&sb);\n \treturn cmd_archive(nargc, nargv, prefix);\n }\n \n-- \n1.8.3.2.736.g869de25\n"},{"id":"222900","messageId":"1373365946-9230-5-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 04/15] quote: remove sq_quote_print()","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:15Z","receivedAt":"2013-07-09T10:32:15Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Remove sq_quote_print() since it has no callers.  A nicer alternative\nsq_quote_buf() exists: its callers aren't forced to print immediately.\n\nFor historical context, sq_quote_print() was first introduced in\n575ba9d6 (GIT_TRACE: show which built-in/external commands are executed,\n2006-06-25) for the purpose of printing argv for $GIT_TRACE.  Today, we\nachieve this using trace_argv_printf() -> sq_quote_argv() ->\nsq_quote_buf(), which ultimately fills in a strbuf.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n quote.c | 17 -----------------\n quote.h |  2 --\n 2 files changed, 19 deletions(-)\n\ndiff --git a/quote.c b/quote.c\nindex 8c294df..778b39a 100644\n--- a/quote.c\n+++ b/quote.c\n@@ -42,23 +42,6 @@ void sq_quote_buf(struct strbuf *dst, const char *src)\n \tfree(to_free);\n }\n \n-void sq_quote_print(FILE *stream, const char *src)\n-{\n-\tchar c;\n-\n-\tfputc('\\'', stream);\n-\twhile ((c = *src++)) {\n-\t\tif (need_bs_quote(c)) {\n-\t\t\tfputs(\"'\\\\\", stream);\n-\t\t\tfputc(c, stream);\n-\t\t\tfputc('\\'', stream);\n-\t\t} else {\n-\t\t\tfputc(c, stream);\n-\t\t}\n-\t}\n-\tfputc('\\'', stream);\n-}\n-\n void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)\n {\n \tint i;\ndiff --git a/quote.h b/quote.h\nindex ed06df5..251f3cc 100644\n--- a/quote.h\n+++ b/quote.h\n@@ -27,8 +27,6 @@ struct strbuf;\n  * excluding the final null regardless of the buffer size.\n  */\n \n-extern void sq_quote_print(FILE *stream, const char *src);\n-\n extern void sq_quote_buf(struct strbuf *, const char *src);\n extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);\n \n-- \n1.8.3.2.736.g869de25\n"},{"id":"222902","messageId":"1373365946-9230-6-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 05/15] pretty: extend pretty_print_context with callback","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:16Z","receivedAt":"2013-07-09T10:32:16Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nThe struct pretty_print_context contains the context in which the\nplaceholders in format_commit_one() should be parsed.  Although\nformat_commit_one() primarily acts as a parser, there is no way for a\ncaller to plug in custom callbacks.  Now, callers can:\n\n1. Parse a custom placeholder that is not supported by\n   format_commit_one(), and act on it independently of the pretty\n   machinery.\n\n2. Parse a custom placeholder to substitute the custom placeholder with\n   a placeholder that format_commit_one() understands.  This is\n   especially useful for supporting %>(*), where * is substituted with a\n   length computed by the caller.\n\nTo support these two usecases, the interface for the function looks\nlike:\n\ntypedef size_t (*format_message_fn)(struct strbuf *sb,\n\t\t\t\tconst char *placeholder,\n\t\t\t\tvoid *format_context,\n\t\t\t\tvoid *user_data,\n\t\t\t\tstruct strbuf *placeholder_subst)\n\nIt is exactly like format_commit_one(), except that there are two\nadditional fields: user_data (to pass custom data to the callback), and\nplaceholder_subst (to set the substitution).  The callback should return\nthe length of the original string parsed, and optionally set\nplaceholder_subst.\n\n[rr: commit message, minor modifications]\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n commit.h |  8 ++++++++\n pretty.c | 25 +++++++++++++++++++++++++\n 2 files changed, 33 insertions(+)\n\ndiff --git a/commit.h b/commit.h\nindex 4d452dc..ced7100 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -78,6 +78,12 @@ enum cmit_fmt {\n \tCMIT_FMT_UNSPECIFIED\n };\n \n+typedef size_t (*format_message_fn)(struct strbuf *sb,\n+\t\t\t\tconst char *placeholder,\n+\t\t\t\tvoid *format_context,\n+\t\t\t\tvoid *user_data,\n+\t\t\t\tstruct strbuf *placeholder_subst);\n+\n struct pretty_print_context {\n \tenum cmit_fmt fmt;\n \tint abbrev;\n@@ -92,6 +98,8 @@ struct pretty_print_context {\n \tconst char *output_encoding;\n \tstruct string_list *mailmap;\n \tint color;\n+\tformat_message_fn format;\n+\tvoid *user_data;\n };\n \n struct userformat_want {\ndiff --git a/pretty.c b/pretty.c\nindex 9e43154..095e5ba 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -1069,6 +1069,31 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \tstruct commit_list *p;\n \tint h1, h2;\n \n+\tif (c->pretty_ctx->format) {\n+\t\tstruct strbuf subst = STRBUF_INIT;\n+\t\tint ret = c->pretty_ctx->format(sb, placeholder, context,\n+\t\t\t\t\t\tc->pretty_ctx->user_data,\n+\t\t\t\t\t\t&subst);\n+\t\tif (ret && subst.len) {\n+\t\t\t/*\n+\t\t\t * Something was parsed by format(), and a\n+\t\t\t * placeholder-substitution was set.\n+\t\t\t * Recursion is required to override the\n+\t\t\t * return value of format_commit_one() with\n+\t\t\t * ret: the length of the original string\n+\t\t\t * before substitution.\n+\t\t\t */\n+\t\t\tret = format_commit_one(sb, subst.buf, context) ? ret : 0;\n+\t\t\tstrbuf_release(&subst);\n+\t\t\treturn ret;\n+\t\t} else if (ret)\n+\t\t\t/*\n+\t\t\t * Something was parsed by format(), but\n+\t\t\t * no placeholder-substitution was set.\n+\t\t\t */\n+\t\t\treturn ret;\n+\t}\n+\n \t/* these are independent of the commit */\n \tswitch (placeholder[0]) {\n \tcase 'C':\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222903","messageId":"1373365946-9230-7-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 06/15] pretty: limit recursion in format_commit_one()","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:17Z","receivedAt":"2013-07-09T10:32:17Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"To make sure that a pretty_ctx->format substitution doesn't result in an\ninfinite recursion, change the prototype of format_commit_one() to\naccept one last argument: no_recurse.  So, a single substitution by\nformat() must yield a result that can be parsed by format_commit_one()\nwithout the help of format().\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c | 11 ++++++-----\n 1 file changed, 6 insertions(+), 5 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 095e5ba..0063f2d 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -1061,7 +1061,8 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \n static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\t\t\tconst char *placeholder,\n-\t\t\t\tvoid *context)\n+\t\t\t\tvoid *context,\n+\t\t\t\tint no_recurse)\n {\n \tstruct format_commit_context *c = context;\n \tconst struct commit *commit = c->commit;\n@@ -1069,7 +1070,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \tstruct commit_list *p;\n \tint h1, h2;\n \n-\tif (c->pretty_ctx->format) {\n+\tif (!no_recurse && c->pretty_ctx->format) {\n \t\tstruct strbuf subst = STRBUF_INIT;\n \t\tint ret = c->pretty_ctx->format(sb, placeholder, context,\n \t\t\t\t\t\tc->pretty_ctx->user_data,\n@@ -1083,7 +1084,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\t\t * ret: the length of the original string\n \t\t\t * before substitution.\n \t\t\t */\n-\t\t\tret = format_commit_one(sb, subst.buf, context) ? ret : 0;\n+\t\t\tret = format_commit_one(sb, subst.buf, context, 1) ? ret : 0;\n \t\t\tstrbuf_release(&subst);\n \t\t\treturn ret;\n \t\t} else if (ret)\n@@ -1332,7 +1333,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t}\n \twhile (1) {\n \t\tint modifier = *placeholder == 'C';\n-\t\tint consumed = format_commit_one(&local_sb, placeholder, c);\n+\t\tint consumed = format_commit_one(&local_sb, placeholder, c, 0);\n \t\ttotal_consumed += consumed;\n \n \t\tif (!modifier)\n@@ -1452,7 +1453,7 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \tif (((struct format_commit_context *)context)->flush_type != no_flush)\n \t\tconsumed = format_and_pad_commit(sb, placeholder, context);\n \telse\n-\t\tconsumed = format_commit_one(sb, placeholder, context);\n+\t\tconsumed = format_commit_one(sb, placeholder, context, 0);\n \tif (magic == NO_MAGIC)\n \t\treturn consumed;\n \n-- \n1.8.3.2.736.g869de25\n"},{"id":"222904","messageId":"1373365946-9230-8-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 07/15] pretty: allow passing NULL commit to format_commit_message()","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:18Z","receivedAt":"2013-07-09T10:32:18Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nThe new formatter, for-each-ref, may use non-commit placeholders only.\nWhile it could audit the format line and warn/exclude commit\nplaceholders, that's a lot more work than simply ignore them.\nUnrecognized placeholders are displayed as-is, pretty obvious that they\nare not handled.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n pretty.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 0063f2d..816aa32 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -1156,6 +1156,9 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t}\n \n \t/* these depend on the commit */\n+\tif (!commit)\n+\t\treturn 0;\n+\n \tif (!commit->object.parsed)\n \t\tparse_object(commit->object.sha1);\n \n@@ -1276,6 +1279,9 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t}\n \n \n+\tif (!c->message)\n+\t\treturn 0;\n+\n \t/* For the rest we have to parse the commit header. */\n \tif (!c->commit_header_parsed)\n \t\tparse_commit_header(c);\n@@ -1510,9 +1516,10 @@ void format_commit_message(const struct commit *commit,\n \tcontext.commit = commit;\n \tcontext.pretty_ctx = pretty_ctx;\n \tcontext.wrap_start = sb->len;\n-\tcontext.message = logmsg_reencode(commit,\n-\t\t\t\t\t  &context.commit_encoding,\n-\t\t\t\t\t  output_enc);\n+\tif (commit)\n+\t\tcontext.message = logmsg_reencode(commit,\n+\t\t\t\t\t\t  &context.commit_encoding,\n+\t\t\t\t\t\t  output_enc);\n \n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n@@ -1535,7 +1542,8 @@ void format_commit_message(const struct commit *commit,\n \t}\n \n \tfree(context.commit_encoding);\n-\tlogmsg_free(context.message, commit);\n+\tif (commit)\n+\t\tlogmsg_free(context.message, commit);\n \tfree(context.signature_check.gpg_output);\n \tfree(context.signature_check.signer);\n }\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222905","messageId":"1373365946-9230-9-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 08/15] for-each-ref: get --pretty using format_commit_message()","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:19Z","receivedAt":"2013-07-09T10:32:19Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\n--format is very limited in its capabilities.  Introduce --pretty, which\nextends the existing --format with pretty-formats.  In --pretty:\n\n - Existing --format %(atom) is available. They also accept some pretty\n   magic.  For example, you can use \"% (atom)\" to only display a leading\n   space if the atom produces something.\n\n - %ab to display a hex character 0xab is not available as it may\n   conflict with other pretty's placeholders.  Use %xab instead.\n\n - Many pretty placeholders are designed to work on commits.  While some\n   of them should work on tags too, they don't (yet).\n\n - Unsupported atoms cause for-each-ref to exit early and report.\n   Unsupported pretty placeholders are displayed as-is.\n\n - Pretty placeholders can not be used as a sorting criteria.\n\n--format is considered deprecated. If the user hits a bug specific in\n--format code, they are advised to migrate to --pretty.\n\n[rr: documentation]\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  23 ++++++-\n builtin/for-each-ref.c             |  72 +++++++++++++++++++++-\n t/t6300-for-each-ref.sh            | 123 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 214 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex f2e08d1..d8ad758 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -9,7 +9,8 @@ SYNOPSIS\n --------\n [verse]\n 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]\n-\t\t   [(--sort=<key>)...] [--format=<format>] [<pattern>...]\n+\t\t   [(--sort=<key>)...] [--format=<format>|--pretty=<pretty>]\n+\t\t   [<pattern>...]\n \n DESCRIPTION\n -----------\n@@ -47,6 +48,26 @@ OPTIONS\n \t`xx`; for example `%00` interpolates to `\\0` (NUL),\n \t`%09` to `\\t` (TAB) and `%0a` to `\\n` (LF).\n \n+<pretty>::\n+\tA format string with supporting placeholders described in the\n+\t\"PRETTY FORMATS\" section in linkgit:git-log[1].  Additionally\n+\tsupports placeholders from `<format>`\n+\t(i.e. `%[*](fieldname)`).\n++\n+Caveats:\n+\n+1. Many of the placeholders in \"PRETTY FORMATS\" are designed to work\n+   specifically on commit objects: when non-commit objects are\n+   supplied, those placeholders won't work (i.e. they will be emitted\n+   literally).\n+\n+2. Does not interpolate `%ab` (where `ab` are hex digits) with the\n+   corresponding hex code.  To print a byte from a hex code, use\n+   `%xab` (from pretty-formats) instead.\n+\n+3. Only the placeholders inherited from `<format>` will respect\n+   quoting settings.\n+\n <pattern>...::\n \tIf one or more patterns are given, only refs are shown that\n \tmatch against at least one pattern, either using fnmatch(3) or\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex e2d6c5a..8611777 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -962,6 +962,63 @@ static void show_refs(struct refinfo **refs, int maxcount,\n \tstrbuf_release(&sb);\n }\n \n+struct format_one_atom_context {\n+\tstruct refinfo *info;\n+\tint quote_style;\n+};\n+\n+static size_t format_one_atom(struct strbuf *sb, const char *placeholder,\n+\t\t\t      void *format_context, void *user_data,\n+\t\t\t      struct strbuf *subst)\n+{\n+\tstruct format_one_atom_context *ctx = user_data;\n+\tconst char *ep;\n+\n+\tif (*placeholder == '%') {\n+\t\tstrbuf_addch(sb, '%');\n+\t\treturn 1;\n+\t}\n+\n+\tif (*placeholder != '(')\n+\t\treturn 0;\n+\n+\tep = strchr(placeholder + 1, ')');\n+\tif (!ep)\n+\t\treturn 0;\n+\tprint_value(sb, ctx->info, parse_atom(placeholder + 1, ep),\n+\t\t    ctx->quote_style);\n+\treturn ep + 1 - placeholder;\n+}\n+\n+static void show_pretty_refs(struct refinfo **refs, int maxcount,\n+\t\t\t     const char *format, int quote_style)\n+{\n+\tstruct pretty_print_context ctx = {0};\n+\tstruct format_one_atom_context fctx;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint i;\n+\n+\t/*\n+\t * FIXME: add --date= for %ad, --decorate for %d and --color\n+\t * for %C\n+\t */\n+\tctx.abbrev = DEFAULT_ABBREV;\n+\tctx.format = format_one_atom;\n+\tctx.user_data = &fctx;\n+\tfctx.quote_style = quote_style;\n+\tfor (i = 0; i < maxcount; i++) {\n+\t\tstruct commit *commit = NULL;\n+\t\tfctx.info = refs[i];\n+\t\tif (sha1_object_info(refs[i]->objectname, NULL) == OBJ_COMMIT)\n+\t\t\tcommit = lookup_commit(refs[i]->objectname);\n+\t\tstrbuf_reset(&sb);\n+\t\tformat_commit_message(commit, format, &sb, &ctx);\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t\tfputs(sb.buf, stdout);\n+\t}\n+\tstrbuf_release(&sb);\n+}\n+\n static struct ref_sort *default_sort(void)\n {\n \tstatic const char cstr_name[] = \"refname\";\n@@ -1003,7 +1060,9 @@ static char const * const for_each_ref_usage[] = {\n int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n {\n \tint num_refs;\n-\tconst char *format = \"%(objectname) %(objecttype)\\t%(refname)\";\n+\tconst char *default_format = \"%(objectname) %(objecttype)\\t%(refname)\";\n+\tconst char *format = default_format;\n+\tconst char *pretty = NULL;\n \tstruct ref_sort *sort = NULL, **sort_tail = &sort;\n \tint maxcount = 0, quote_style = 0;\n \tstruct refinfo **refs;\n@@ -1022,6 +1081,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\tOPT_GROUP(\"\"),\n \t\tOPT_INTEGER( 0 , \"count\", &maxcount, N_(\"show only <n> matched refs\")),\n \t\tOPT_STRING(  0 , \"format\", &format, N_(\"format\"), N_(\"format to use for the output\")),\n+\t\tOPT_STRING(  0 , \"pretty\", &pretty, N_(\"format\"), N_(\"alternative format to use for the output\")),\n \t\tOPT_CALLBACK(0 , \"sort\", sort_tail, N_(\"key\"),\n \t\t\t    N_(\"field name to sort on\"), &opt_parse_sort),\n \t\tOPT_END(),\n@@ -1036,7 +1096,10 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\terror(\"more than one quoting style?\");\n \t\tusage_with_options(for_each_ref_usage, opts);\n \t}\n-\tif (verify_format(format))\n+\tif (format != default_format && pretty)\n+\t\tdie(\"--format and --pretty cannot be used together\");\n+\tif ((pretty && verify_format(pretty)) ||\n+\t    (!pretty && verify_format(format)))\n \t\tusage_with_options(for_each_ref_usage, opts);\n \n \tif (!sort)\n@@ -1057,6 +1120,9 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \tif (!maxcount || num_refs < maxcount)\n \t\tmaxcount = num_refs;\n \n-\tshow_refs(refs, maxcount, format, quote_style);\n+\tif (pretty)\n+\t\tshow_pretty_refs(refs, maxcount, pretty, quote_style);\n+\telse\n+\t\tshow_refs(refs, maxcount, format, quote_style);\n \treturn 0;\n }\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 752f5cb..d39e0b4 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -48,6 +48,20 @@ test_atom() {\n \t\"\n }\n \n+test_pretty() {\n+\tcase \"$1\" in\n+\t\thead) ref=refs/heads/master ;;\n+\t\t tag) ref=refs/tags/testtag ;;\n+\t\t   *) ref=$1 ;;\n+\tesac\n+\tprintf '%s\\n' \"$3\" >expected\n+\ttest_expect_${4:-success} $PREREQ \"basic pretty: $1 $2\" \"\n+\t\tgit for-each-ref --pretty='$2' $ref >actual &&\n+\t\tsanitize_pgp <actual >actual.clean &&\n+\t\ttest_cmp expected actual.clean\n+\t\"\n+}\n+\n test_atom head refname refs/heads/master\n test_atom head upstream refs/remotes/origin/master\n test_atom head objecttype commit\n@@ -114,6 +128,115 @@ test_atom tag contents:signature ''\n test_atom tag contents 'Tagging at 1151939927\n '\n \n+echo \"Mailmap'd <map@example.com> <author@example.com>\" > $HOME/.mailmap\n+\n+test_pretty head '%(refname)' refs/heads/master\n+test_pretty head '%(upstream)' refs/remotes/origin/master\n+test_pretty head '%(objecttype)' commit\n+test_pretty head '%(objectsize)' 171\n+test_pretty head '%(objectname)' 67a36f10722846e891fbada1ba48ed035de75581\n+test_pretty head '%H' 67a36f10722846e891fbada1ba48ed035de75581\n+test_pretty head '%h' 67a36f1\n+test_pretty head '%(tree)' 0e51c00fcb93dffc755546f27593d511e1bdb46f\n+test_pretty head '%T' 0e51c00fcb93dffc755546f27593d511e1bdb46f\n+test_pretty head '%t' 0e51c00\n+test_pretty head '%(parent)' ''\n+test_pretty head '%P' ''\n+test_pretty head '%(numparent)' 0\n+test_pretty head '%(object)' ''\n+test_pretty head '%(type)' ''\n+test_pretty head '%(author)' 'A U Thor <author@example.com> 1151939924 +0200'\n+test_pretty head '%(authorname)' 'A U Thor'\n+test_pretty head '%an' 'A U Thor'\n+test_pretty head '%aN' \"Mailmap'd\"\n+test_pretty head '%(authoremail)' '<author@example.com>'\n+test_pretty head '%ae' 'author@example.com'\n+test_pretty head '%aE' 'map@example.com'\n+test_pretty head '%(authordate)' 'Mon Jul 3 17:18:44 2006 +0200'\n+test_pretty head '%aD' 'Mon, 3 Jul 2006 17:18:44 +0200'\n+test_pretty head '%(committer)' 'C O Mitter <committer@example.com> 1151939923 +0200'\n+test_pretty head '%(committername)' 'C O Mitter'\n+test_pretty head '%cn' 'C O Mitter'\n+test_pretty head '%(committeremail)' '<committer@example.com>'\n+test_pretty head '%ce' 'committer@example.com'\n+test_pretty head '%(committerdate)' 'Mon Jul 3 17:18:43 2006 +0200'\n+test_pretty head '%cD' 'Mon, 3 Jul 2006 17:18:43 +0200'\n+test_pretty head '%(tag)' ''\n+test_pretty head '%(tagger)' ''\n+test_pretty head '%(taggername)' ''\n+test_pretty head '%(taggeremail)' ''\n+test_pretty head '%(taggerdate)' ''\n+test_pretty head '%(creator)' 'C O Mitter <committer@example.com> 1151939923 +0200'\n+test_pretty head '%(creatordate)' 'Mon Jul 3 17:18:43 2006 +0200'\n+test_pretty head '%(subject)' 'Initial'\n+test_pretty head '%(contents:subject)' 'Initial'\n+test_pretty head '%(body)' ''\n+test_pretty head '%(contents:body)' ''\n+test_pretty head '%(contents:signature)' ''\n+test_pretty head '%(contents)' 'Initial\n+'\n+\n+test_pretty head '%d' ' (HEAD, tag: testtag, origin/master, master)'\n+test_pretty head '%x20' ' '\n+test_pretty head '%g' '%g'\n+test_pretty head '%unknown' '%unknown'\n+test_pretty head '% (parent)' ''\n+test_pretty head '% P' ''\n+test_pretty head '% (tree)' ' 0e51c00fcb93dffc755546f27593d511e1bdb46f'\n+test_pretty head '% T' ' 0e51c00fcb93dffc755546f27593d511e1bdb46f'\n+\n+test_expect_success '% (unknown)' '\n+\ttest_must_fail git for-each-ref --pretty=\"% (unknown)\" refs/heads/master\n+'\n+\n+test_pretty head '%<(20)%cn end' 'C O Mitter           end'\n+test_pretty head '%>(20)%cn end' '          C O Mitter end'\n+test_pretty head '%><(20)%cn end' '     C O Mitter      end'\n+test_pretty head '%<(20)%(committername) end' 'C O Mitter           end'\n+test_pretty head '%>(20)%(committername) end' '          C O Mitter end'\n+test_pretty head '%><(20)%(committername) end' '     C O Mitter      end'\n+\n+test_pretty tag '%(refname)' refs/tags/testtag\n+test_pretty tag '%(upstream)' ''\n+test_pretty tag '%(objecttype)' tag\n+test_pretty tag '%(objectsize)' 154\n+test_pretty tag '%(objectname)' 98b46b1d36e5b07909de1b3886224e3e81e87322\n+test_pretty tag '%(tree)' ''\n+test_pretty tag '%(parent)' ''\n+test_pretty tag '%(numparent)' ''\n+test_pretty tag '%(object)' '67a36f10722846e891fbada1ba48ed035de75581'\n+test_pretty tag '%(type)' 'commit'\n+test_pretty tag '%(author)' ''\n+test_pretty tag '%(authorname)' ''\n+test_pretty tag '%(authoremail)' ''\n+test_pretty tag '%(authordate)' ''\n+test_pretty tag '%(committer)' ''\n+test_pretty tag '%(committername)' ''\n+test_pretty tag '%(committeremail)' ''\n+test_pretty tag '%(committerdate)' ''\n+test_pretty tag '%(tag)' 'testtag'\n+test_pretty tag '%(tagger)' 'C O Mitter <committer@example.com> 1151939925 +0200'\n+test_pretty tag '%(taggername)' 'C O Mitter'\n+test_pretty tag '%(taggeremail)' '<committer@example.com>'\n+test_pretty tag '%(taggerdate)' 'Mon Jul 3 17:18:45 2006 +0200'\n+test_pretty tag '%(creator)' 'C O Mitter <committer@example.com> 1151939925 +0200'\n+test_pretty tag '%(creatordate)' 'Mon Jul 3 17:18:45 2006 +0200'\n+test_pretty tag '%(subject)' 'Tagging at 1151939927'\n+test_pretty tag '%(contents:subject)' 'Tagging at 1151939927'\n+test_pretty tag '%(body)' ''\n+test_pretty tag '%(contents:body)' ''\n+test_pretty tag '%(contents:signature)' ''\n+test_pretty tag '%(contents)' 'Tagging at 1151939927\n+'\n+\n+# make sure we don't segfault when non-commits are passed in\n+# format_commit_message. Should be fixed so that some of these\n+# placeholders produce something useful for non-commits.\n+test_pretty tag '%H' '%H'\n+test_pretty tag '%h' '%h'\n+test_pretty tag '%T' '%T'\n+test_pretty tag '%t' '%t'\n+\n test_expect_success 'Check invalid atoms names are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n '\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222906","messageId":"1373365946-9230-10-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 09/15] for-each-ref: teach verify_format() about pretty's syntax","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:20Z","receivedAt":"2013-07-09T10:32:20Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nPretty format accepts either ' ', '+' or '-' after '%' and before the\nplaceholder name to modify certain behaviors. Teach verify_format()\nabout this so that it finds atom \"upstream\" in, for example,\n'% (upstream)'. This is important because verify_format populates\nused_atom, which get_value() and populate_value() later rely on.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/for-each-ref.c | 15 +++++++++------\n pretty.c               |  4 ++++\n 2 files changed, 13 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 8611777..39454fb 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -150,7 +150,7 @@ static int parse_atom(const char *atom, const char *ep)\n /*\n  * In a format string, find the next occurrence of %(atom).\n  */\n-static const char *find_next(const char *cp)\n+static const char *find_next(const char *cp, int pretty)\n {\n \twhile (*cp) {\n \t\tif (*cp == '%') {\n@@ -160,6 +160,9 @@ static const char *find_next(const char *cp)\n \t\t\t */\n \t\t\tif (cp[1] == '(')\n \t\t\t\treturn cp;\n+\t\t\telse if (pretty && cp[1] && cp[2] == '(' &&\n+\t\t\t\t strchr(\" +-\", cp[1])) /* see format_commit_item() */\n+\t\t\t\treturn cp + 1;\n \t\t\telse if (cp[1] == '%')\n \t\t\t\tcp++; /* skip over two % */\n \t\t\t/* otherwise this is a singleton, literal % */\n@@ -173,10 +176,10 @@ static const char *find_next(const char *cp)\n  * Make sure the format string is well formed, and parse out\n  * the used atoms.\n  */\n-static int verify_format(const char *format)\n+static int verify_format(const char *format, int pretty)\n {\n \tconst char *cp, *sp;\n-\tfor (cp = format; *cp && (sp = find_next(cp)); ) {\n+\tfor (cp = format; *cp && (sp = find_next(cp, pretty)); ) {\n \t\tconst char *ep = strchr(sp, ')');\n \t\tif (!ep)\n \t\t\treturn error(\"malformed format string %s\", sp);\n@@ -935,7 +938,7 @@ static void show_ref(struct strbuf *sb, struct refinfo *info,\n {\n \tconst char *cp, *sp, *ep;\n \n-\tfor (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n+\tfor (cp = format; *cp && (sp = find_next(cp, 0)); cp = ep + 1) {\n \t\tep = strchr(sp, ')');\n \t\tif (cp < sp)\n \t\t\temit(sb, cp, sp);\n@@ -1098,8 +1101,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t}\n \tif (format != default_format && pretty)\n \t\tdie(\"--format and --pretty cannot be used together\");\n-\tif ((pretty && verify_format(pretty)) ||\n-\t    (!pretty && verify_format(format)))\n+\tif ((pretty && verify_format(pretty, 1)) ||\n+\t    (!pretty && verify_format(format, 0)))\n \t\tusage_with_options(for_each_ref_usage, opts);\n \n \tif (!sort)\ndiff --git a/pretty.c b/pretty.c\nindex 816aa32..28c0a72 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -1439,6 +1439,10 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\tADD_SP_BEFORE_NON_EMPTY\n \t} magic = NO_MAGIC;\n \n+\t/*\n+\t * Note: any modification in what placeholder[0] contains\n+\t * should be redone in builtin/for-each-ref.c:find_next().\n+\t */\n \tswitch (placeholder[0]) {\n \tcase '-':\n \t\tmagic = DEL_LF_BEFORE_EMPTY;\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222907","messageId":"1373365946-9230-11-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 10/15] for-each-ref: introduce format specifier %>(*) and %<(*)","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:21Z","receivedAt":"2013-07-09T10:32:21Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nPretty placeholders %>(N) and %<(N) require a user provided width N,\nwhich makes sense because the commit chain could be really long and the\nuser only needs to look at a few at the top, going to the end just to\ncalculate the best width wastes CPU cycles.\n\nfor-each-ref is different; the display set is small, and we display them\nall at once. We even support sorting, which goes through all display\nitems anyway.  This patch introduces new %>(*) and %<(*), which are\nsupposed to be followed immediately by %(fieldname) (i.e. original\nfor-each-ref specifiers, not ones coming from pretty.c). They calculate\nthe best width for the %(fieldname), ignoring ansi escape sequences if\nany.\n\n[rr: documentation]\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  7 +++++++\n builtin/for-each-ref.c             | 38 ++++++++++++++++++++++++++++++++++++++\n t/t6300-for-each-ref.sh            | 20 ++++++++++++++++++++\n 3 files changed, 65 insertions(+)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex d8ad758..8cbc08c 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -47,6 +47,10 @@ OPTIONS\n \tare hex digits interpolates to character with hex code\n \t`xx`; for example `%00` interpolates to `\\0` (NUL),\n \t`%09` to `\\t` (TAB) and `%0a` to `\\n` (LF).\n++\n+Placeholders `%<(*)` and `%>(*)` work like `%<(<N>)` and `%>(<N>)`\n+respectively, except that the width of the next placeholder is\n+calculated.\n \n <pretty>::\n \tA format string with supporting placeholders described in the\n@@ -68,6 +72,9 @@ Caveats:\n 3. Only the placeholders inherited from `<format>` will respect\n    quoting settings.\n \n+3. Only the placeholders inherited from `<format>` will work with the\n+   alignment placeholders `%<(*)` and '%>(*)`.\n+\n <pattern>...::\n \tIf one or more patterns are given, only refs are shown that\n \tmatch against at least one pattern, either using fnmatch(3) or\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 39454fb..da479d1 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -9,6 +9,7 @@\n #include \"quote.h\"\n #include \"parse-options.h\"\n #include \"remote.h\"\n+#include \"utf8.h\"\n \n /* Quoting styles */\n #define QUOTE_NONE 0\n@@ -966,10 +967,30 @@ static void show_refs(struct refinfo **refs, int maxcount,\n }\n \n struct format_one_atom_context {\n+\tstruct refinfo **refs;\n+\tint maxcount;\n+\n \tstruct refinfo *info;\n \tint quote_style;\n };\n \n+static unsigned int get_atom_width(struct format_one_atom_context *ctx,\n+\t\t\t\t   const char *start, const char *end)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint i, atom = parse_atom(start, end);\n+\tunsigned int len = 0, sb_len;\n+\tfor (i = 0; i < ctx->maxcount; i++) {\n+\t\tprint_value(&sb, ctx->refs[i], atom, ctx->quote_style);\n+\t\tsb_len = utf8_strnwidth(sb.buf, sb.len, 1);\n+\t\tif (sb_len > len)\n+\t\t\tlen = sb_len;\n+\t\tstrbuf_reset(&sb);\n+\t}\n+\tstrbuf_release(&sb);\n+\treturn len;\n+}\n+\n static size_t format_one_atom(struct strbuf *sb, const char *placeholder,\n \t\t\t      void *format_context, void *user_data,\n \t\t\t      struct strbuf *subst)\n@@ -982,6 +1003,21 @@ static size_t format_one_atom(struct strbuf *sb, const char *placeholder,\n \t\treturn 1;\n \t}\n \n+\t/*\n+\t * Substitute %>(*)%(atom) and friends with real width.\n+\t */\n+\tif (*placeholder == '>' || *placeholder == '<') {\n+\t\tconst char *star = placeholder + 1;\n+\t\tif (!prefixcmp(star, \"(*)%(\") &&\n+\t\t    ((ep = strchr(star + strlen(\"(*)%(\"), ')')) != NULL)) {\n+\t\t\tstar++;\n+\t\t\tstrbuf_addf(subst, \"%c(%u)\",\n+\t\t\t\t    *placeholder,\n+\t\t\t\t    get_atom_width(ctx, star + strlen(\"*)%(\"), ep));\n+\t\t\treturn 1 + strlen(\"(*)\");\n+\t\t}\n+\t}\n+\n \tif (*placeholder != '(')\n \t\treturn 0;\n \n@@ -1008,6 +1044,8 @@ static void show_pretty_refs(struct refinfo **refs, int maxcount,\n \tctx.abbrev = DEFAULT_ABBREV;\n \tctx.format = format_one_atom;\n \tctx.user_data = &fctx;\n+\tfctx.refs = refs;\n+\tfctx.maxcount = maxcount;\n \tfctx.quote_style = quote_style;\n \tfor (i = 0; i < maxcount; i++) {\n \t\tstruct commit *commit = NULL;\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex d39e0b4..160018c 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -196,6 +196,26 @@ test_pretty head '%<(20)%(committername) end' 'C O Mitter           end'\n test_pretty head '%>(20)%(committername) end' '          C O Mitter end'\n test_pretty head '%><(20)%(committername) end' '     C O Mitter      end'\n \n+test_expect_success '%<(*)%(refname) A' '\n+\tgit for-each-ref --pretty=\"%<(*)%(refname) A\" >actual &&\n+\tcat >expected <<EOF &&\n+refs/heads/master          A\n+refs/remotes/origin/master A\n+refs/tags/testtag          A\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '%>(*)%(refname) A' '\n+\tgit for-each-ref --pretty=\"%>(*)%(refname) A\" >actual &&\n+\tqz_to_tab_space >expected <<EOF &&\n+Z        refs/heads/master A\n+refs/remotes/origin/master A\n+Z        refs/tags/testtag A\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n test_pretty tag '%(refname)' refs/tags/testtag\n test_pretty tag '%(upstream)' ''\n test_pretty tag '%(objecttype)' tag\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222909","messageId":"1373365946-9230-12-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 11/15] for-each-ref: introduce %(HEAD) marker","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:22Z","receivedAt":"2013-07-09T10:32:22Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"'git branch' shows which branch you are currently on with an '*', but\n'git for-each-ref' misses this feature.  So, extend the format with\n%(HEAD) to do exactly the same thing.\n\nNow you can use the following format in for-each-ref:\n\n  %C(red)%(HEAD)%C(reset) %C(green)%(refname:short)%C(reset)\n\nto display a red asterisk next to the current ref.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  4 ++++\n builtin/for-each-ref.c             | 13 +++++++++++--\n 2 files changed, 15 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 8cbc08c..8d982e3 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -121,6 +121,10 @@ upstream::\n \tfrom the displayed ref. Respects `:short` in the same way as\n \t`refname` above.\n \n+HEAD::\n+\tUseful to indicate the currently checked out branch.  Is '*'\n+\tif HEAD points to the current ref, and ' ' otherwise.\n+\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.\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex da479d1..3d357a9 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -76,6 +76,7 @@ static struct {\n \t{ \"upstream\" },\n \t{ \"symref\" },\n \t{ \"flag\" },\n+\t{ \"HEAD\" },\n };\n \n /*\n@@ -679,8 +680,16 @@ static void populate_value(struct refinfo *ref)\n \t\t\t\tv->s = xstrdup(buf + 1);\n \t\t\t}\n \t\t\tcontinue;\n-\t\t}\n-\t\telse\n+\t\t} else if (!strcmp(name, \"HEAD\")) {\n+\t\t\tconst char *head;\n+\t\t\tunsigned char sha1[20];\n+\t\t\thead = resolve_ref_unsafe(\"HEAD\", sha1, 1, NULL);\n+\t\t\tif (!strcmp(ref->refname, head))\n+\t\t\t\tv->s = \"*\";\n+\t\t\telse\n+\t\t\t\tv->s = \" \";\n+\t\t\tcontinue;\n+\t\t} else\n \t\t\tcontinue;\n \n \t\tformatp = strchr(name, ':');\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222908","messageId":"1373365946-9230-13-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 12/15] for-each-ref: introduce %(upstream:track[short])","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:23Z","receivedAt":"2013-07-09T10:32:23Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Introduce %(upstream:track) to display \"[ahead M, behind N]\" and\n%(upstream:trackshort) to display \"=\", \">\", \"<\", or \"<>\"\nappropriately (inspired by the contrib/completion/git-prompt.sh).\n\nNow you can use the following format in for-each-ref:\n\n  %C(green)%(refname:short)%C(reset)%(upstream:trackshort)\n\nto display refs with terse tracking information.\n\nNote that :track and :trackshort only work with upstream, and error out\nwhen used with anything else.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  6 +++++-\n builtin/for-each-ref.c             | 42 ++++++++++++++++++++++++++++++++++++--\n 2 files changed, 45 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 8d982e3..d666ebd 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -119,7 +119,11 @@ objectname::\n upstream::\n \tThe name of a local ref which can be considered ``upstream''\n \tfrom the displayed ref. Respects `:short` in the same way as\n-\t`refname` above.\n+\t`refname` above.  Additionally respects `:track` to show\n+\t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n+\tversion (like the prompt) \">\", \"<\", \"<>\", or \"=\".  Has no\n+\teffect if the ref does not have tracking information\n+\tassociated with it.\n \n HEAD::\n \tUseful to indicate the currently checked out branch.  Is '*'\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 3d357a9..72b33ee 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -628,6 +628,7 @@ static void populate_value(struct refinfo *ref)\n \tint eaten, i;\n \tunsigned long size;\n \tconst unsigned char *tagged;\n+\tint upstream_present = 0;\n \n \tref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);\n \n@@ -645,6 +646,7 @@ static void populate_value(struct refinfo *ref)\n \t\tint deref = 0;\n \t\tconst char *refname;\n \t\tconst char *formatp;\n+\t\tstruct branch *branch;\n \n \t\tif (*name == '*') {\n \t\t\tderef = 1;\n@@ -656,7 +658,6 @@ static void populate_value(struct refinfo *ref)\n \t\telse if (!prefixcmp(name, \"symref\"))\n \t\t\trefname = ref->symref ? ref->symref : \"\";\n \t\telse if (!prefixcmp(name, \"upstream\")) {\n-\t\t\tstruct branch *branch;\n \t\t\t/* only local branches may have an upstream */\n \t\t\tif (prefixcmp(ref->refname, \"refs/heads/\"))\n \t\t\t\tcontinue;\n@@ -666,6 +667,7 @@ static void populate_value(struct refinfo *ref)\n \t\t\t    !branch->merge[0]->dst)\n \t\t\t\tcontinue;\n \t\t\trefname = branch->merge[0]->dst;\n+\t\t\tupstream_present = 1;\n \t\t}\n \t\telse if (!strcmp(name, \"flag\")) {\n \t\t\tchar buf[256], *cp = buf;\n@@ -683,6 +685,7 @@ static void populate_value(struct refinfo *ref)\n \t\t} else if (!strcmp(name, \"HEAD\")) {\n \t\t\tconst char *head;\n \t\t\tunsigned char sha1[20];\n+\n \t\t\thead = resolve_ref_unsafe(\"HEAD\", sha1, 1, NULL);\n \t\t\tif (!strcmp(ref->refname, head))\n \t\t\t\tv->s = \"*\";\n@@ -695,11 +698,46 @@ static void populate_value(struct refinfo *ref)\n \t\tformatp = strchr(name, ':');\n \t\t/* look for \"short\" refname format */\n \t\tif (formatp) {\n+\t\t\tint num_ours, num_theirs;\n+\n \t\t\tformatp++;\n \t\t\tif (!strcmp(formatp, \"short\"))\n \t\t\t\trefname = shorten_unambiguous_ref(refname,\n \t\t\t\t\t\t      warn_ambiguous_refs);\n-\t\t\telse\n+\t\t\telse if (!strcmp(formatp, \"track\") &&\n+\t\t\t\t!prefixcmp(name, \"upstream\")) {\n+\t\t\t\tchar buf[40];\n+\n+\t\t\t\tif (!upstream_present)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tif (!stat_tracking_info(branch, &num_ours, &num_theirs))\n+\t\t\t\t\tv->s = \"\";\n+\t\t\t\telse if (!num_ours) {\n+\t\t\t\t\tsprintf(buf, \"[behind %d]\", num_theirs);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t} else if (!num_theirs) {\n+\t\t\t\t\tsprintf(buf, \"[ahead %d]\", num_ours);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t} else {\n+\t\t\t\t\tsprintf(buf, \"[ahead %d, behind %d]\",\n+\t\t\t\t\t\tnum_ours, num_theirs);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t}\n+\t\t\t\tcontinue;\n+\t\t\t} else if (!strcmp(formatp, \"trackshort\") &&\n+\t\t\t\t!prefixcmp(name, \"upstream\")) {\n+\t\t\t\tif (!upstream_present)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tif (!stat_tracking_info(branch, &num_ours, &num_theirs))\n+\t\t\t\t\tv->s = \"=\";\n+\t\t\t\telse if (!num_ours)\n+\t\t\t\t\tv->s = \"<\";\n+\t\t\t\telse if (!num_theirs)\n+\t\t\t\t\tv->s = \">\";\n+\t\t\t\telse\n+\t\t\t\t\tv->s = \"<>\";\n+\t\t\t\tcontinue;\n+\t\t\t} else\n \t\t\t\tdie(\"unknown %.*s format %s\",\n \t\t\t\t    (int)(formatp - name), name, formatp);\n \t\t}\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222911","messageId":"1373365946-9230-14-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 13/15] for-each-ref: improve responsiveness of %(upstream:track)","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:24Z","receivedAt":"2013-07-09T10:32:24Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nBefore anything is printed, for-each-ref sorts all refs first.  As\npart of the sorting, populate_value() is called to fill the values in\nfor all atoms/placeholders per entry. By the time sort_refs() is done,\npretty much all data is already retrieved.\n\nThis works fine when data can be cheaply retrieved before\n%(upstream:track) comes into the picture. It may take a noticeable\namount of time to process %(upstream:track) for each entry. All\nentries add up and make --format='%(refname)%(upstream:track)' seem\nhung for a few seconds, then display everything at once.\n\nImprove the responsiveness by only processing the one atom (*) at a\ntime so that processing one atom for all entries (e.g. sorting) won't\ncause much delay (unless you choose a \"heavy\" atom to process).\n\n(*) This is not entirely correct. If you sort by an atom that needs\nobject database access, then it will fill all atoms that need odb.\nWhich is not a bad thing. We don't want to access odb once at sorting\nphase and again at display phase.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/for-each-ref.c | 49 +++++++++++++++++++++++++------------------------\n 1 file changed, 25 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 72b33ee..25764aa 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -565,20 +565,6 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj\n }\n \n /*\n- * We want to have empty print-string for field requests\n- * that do not apply (e.g. \"authordate\" for a tag object)\n- */\n-static void fill_missing_values(struct atom_value *val)\n-{\n-\tint i;\n-\tfor (i = 0; i < used_atom_cnt; i++) {\n-\t\tstruct atom_value *v = &val[i];\n-\t\tif (v->s == NULL)\n-\t\t\tv->s = \"\";\n-\t}\n-}\n-\n-/*\n  * val is a list of atom_value to hold returned values.  Extract\n  * the values for atoms in used_atom array out of (obj, buf, sz).\n  * when deref is false, (obj, buf, sz) is the object that is\n@@ -621,7 +607,7 @@ static inline char *copy_advance(char *dst, const char *src)\n /*\n  * Parse the object referred by ref, and grab needed value.\n  */\n-static void populate_value(struct refinfo *ref)\n+static void populate_value(struct refinfo *ref, int only_this_atom)\n {\n \tvoid *buf;\n \tstruct object *obj;\n@@ -630,13 +616,15 @@ static void populate_value(struct refinfo *ref)\n \tconst unsigned char *tagged;\n \tint upstream_present = 0;\n \n-\tref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);\n+\tif (!ref->value) {\n+\t\tref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);\n \n-\tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n-\t\tunsigned char unused1[20];\n-\t\tref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);\n-\t\tif (!ref->symref)\n-\t\t\tref->symref = \"\";\n+\t\tif (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {\n+\t\t\tunsigned char unused1[20];\n+\t\t\tref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);\n+\t\t\tif (!ref->symref)\n+\t\t\t\tref->symref = \"\";\n+\t\t}\n \t}\n \n \t/* Fill in specials first */\n@@ -648,6 +636,9 @@ static void populate_value(struct refinfo *ref)\n \t\tconst char *formatp;\n \t\tstruct branch *branch;\n \n+\t\tif (only_this_atom != -1 && only_this_atom != i)\n+\t\t\tcontinue;\n+\n \t\tif (*name == '*') {\n \t\t\tderef = 1;\n \t\t\tname++;\n@@ -754,6 +745,10 @@ static void populate_value(struct refinfo *ref)\n \n \tfor (i = 0; i < used_atom_cnt; i++) {\n \t\tstruct atom_value *v = &ref->value[i];\n+\n+\t\tif (only_this_atom != -1 && only_this_atom != i)\n+\t\t\tcontinue;\n+\n \t\tif (v->s == NULL)\n \t\t\tgoto need_obj;\n \t}\n@@ -809,9 +804,15 @@ static void populate_value(struct refinfo *ref)\n  */\n static void get_value(struct refinfo *ref, int atom, struct atom_value **v)\n {\n-\tif (!ref->value) {\n-\t\tpopulate_value(ref);\n-\t\tfill_missing_values(ref->value);\n+\tif (!ref->value || !ref->value[atom].s) {\n+\t\tpopulate_value(ref, atom);\n+\t\t/*\n+\t\t * We want to have empty print-string for field\n+\t\t * requests that do not apply (e.g. \"authordate\" for a\n+\t\t * tag object)\n+\t\t */\n+\t\tif (!ref->value[atom].s)\n+\t\t\tref->value[atom].s = \"\";\n \t}\n \t*v = &ref->value[atom];\n }\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222910","messageId":"1373365946-9230-15-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 14/15] pretty: introduce get_pretty_userformat","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:25Z","receivedAt":"2013-07-09T10:32:25Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"This helper function is intended to be used by callers implementing\n--pretty themselves; it parses pretty.* configuration variables\nrecursively and hands the user-defined format back to the caller.  No\nbuiltins are supported, as CMT_FMT_* are really only useful when\ndisplaying commits.  Callers might like to define their own builtins in\nthe future.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n commit.h |  1 +\n pretty.c | 25 +++++++++++++++++++++++++\n 2 files changed, 26 insertions(+)\n\ndiff --git a/commit.h b/commit.h\nindex ced7100..331a2db 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -113,6 +113,7 @@ extern char *logmsg_reencode(const struct commit *commit,\n \t\t\t     const char *output_encoding);\n extern void logmsg_free(char *msg, const struct commit *commit);\n extern void get_commit_format(const char *arg, struct rev_info *);\n+extern const char *get_pretty_userformat(const char *arg);\n extern const char *format_subject(struct strbuf *sb, const char *msg,\n \t\t\t\t  const char *line_separator);\n extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);\ndiff --git a/pretty.c b/pretty.c\nindex 28c0a72..70e4e44 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -174,6 +174,31 @@ void get_commit_format(const char *arg, struct rev_info *rev)\n }\n \n /*\n+ * Function to parse --pretty string, lookup pretty.* configuration\n+ * variables and return the format string, assuming no builtin\n+ * formats.  Not limited to commits, unlike get_commit_format().\n+ */\n+const char *get_pretty_userformat(const char *arg)\n+{\n+\tstruct cmt_fmt_map *commit_format;\n+\n+\tif (!arg || !*arg)\n+\t\treturn NULL;\n+\n+\tif (!prefixcmp(arg, \"format:\") || !prefixcmp(arg, \"tformat:\"))\n+\t\treturn xstrdup(strchr(arg, ':' + 1));\n+\n+\tif (strchr(arg, '%'))\n+\t\treturn xstrdup(arg);\n+\n+\tcommit_format = find_commit_format(arg);\n+\tif (!commit_format || commit_format->format != CMIT_FMT_USERFORMAT)\n+\t\tdie(\"invalid --pretty format: %s\", arg);\n+\n+\treturn xstrdup(commit_format->user_format);\n+}\n+\n+/*\n  * Generic support for pretty-printing the header\n  */\n static int get_one_line(const char *msg)\n-- \n1.8.3.2.736.g869de25\n"},{"id":"222912","messageId":"1373365946-9230-16-git-send-email-artagnon@gmail.com","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 15/15] for-each-ref: use get_pretty_userformat in --pretty","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-07-09T10:32:26Z","receivedAt":"2013-07-09T10:32:26Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Use get_pretty_userformat() to interpret the --pretty string.  This\nmeans that you can now reference a format specified in a pretty.*\nconfiguration variable as an argument to 'git for-each-ref --pretty='.\nThere are two caveats:\n\n1. A leading \"format:\" or \"tformat:\" is automatically stripped and\n   ignored.  Separator semantics are not configurable (yet).\n\n2. No built-in formats are available.  The ones specified in\n   pretty-formats (oneline, short etc) don't make sense when displaying\n   refs anyway.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 +++\n builtin/for-each-ref.c             | 16 +++++++++-------\n 2 files changed, 12 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex d666ebd..ef39f2a 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -60,6 +60,9 @@ calculated.\n +\n Caveats:\n \n+0. No built-in formats from PRETTY FORMATS (like oneline, short) are\n+   available.\n+\n 1. Many of the placeholders in \"PRETTY FORMATS\" are designed to work\n    specifically on commit objects: when non-commit objects are\n    supplied, those placeholders won't work (i.e. they will be emitted\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 25764aa..ed7bd7d 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -1151,7 +1151,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \tint num_refs;\n \tconst char *default_format = \"%(objectname) %(objecttype)\\t%(refname)\";\n \tconst char *format = default_format;\n-\tconst char *pretty = NULL;\n+\tconst char *pretty_raw = NULL, *pretty_userformat = NULL;\n \tstruct ref_sort *sort = NULL, **sort_tail = &sort;\n \tint maxcount = 0, quote_style = 0;\n \tstruct refinfo **refs;\n@@ -1170,13 +1170,15 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\tOPT_GROUP(\"\"),\n \t\tOPT_INTEGER( 0 , \"count\", &maxcount, N_(\"show only <n> matched refs\")),\n \t\tOPT_STRING(  0 , \"format\", &format, N_(\"format\"), N_(\"format to use for the output\")),\n-\t\tOPT_STRING(  0 , \"pretty\", &pretty, N_(\"format\"), N_(\"alternative format to use for the output\")),\n+\t\tOPT_STRING(  0 , \"pretty\", &pretty_raw, N_(\"format\"), N_(\"alternative format to use for the output\")),\n \t\tOPT_CALLBACK(0 , \"sort\", sort_tail, N_(\"key\"),\n \t\t\t    N_(\"field name to sort on\"), &opt_parse_sort),\n \t\tOPT_END(),\n \t};\n \n \tparse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);\n+\tif (pretty_raw)\n+\t\tpretty_userformat = get_pretty_userformat(pretty_raw);\n \tif (maxcount < 0) {\n \t\terror(\"invalid --count argument: `%d'\", maxcount);\n \t\tusage_with_options(for_each_ref_usage, opts);\n@@ -1185,10 +1187,10 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \t\terror(\"more than one quoting style?\");\n \t\tusage_with_options(for_each_ref_usage, opts);\n \t}\n-\tif (format != default_format && pretty)\n+\tif (format != default_format && pretty_userformat)\n \t\tdie(\"--format and --pretty cannot be used together\");\n-\tif ((pretty && verify_format(pretty, 1)) ||\n-\t    (!pretty && verify_format(format, 0)))\n+\tif ((pretty_userformat && verify_format(pretty_userformat, 1)) ||\n+\t    (!pretty_userformat && verify_format(format, 0)))\n \t\tusage_with_options(for_each_ref_usage, opts);\n \n \tif (!sort)\n@@ -1209,8 +1211,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \tif (!maxcount || num_refs < maxcount)\n \t\tmaxcount = num_refs;\n \n-\tif (pretty)\n-\t\tshow_pretty_refs(refs, maxcount, pretty, quote_style);\n+\tif (pretty_userformat)\n+\t\tshow_pretty_refs(refs, maxcount, pretty_userformat, quote_style);\n \telse\n \t\tshow_refs(refs, maxcount, format, quote_style);\n \treturn 0;\n-- \n1.8.3.2.736.g869de25\n"},{"id":"223244","messageId":"20130713123139.GA19512@lanh","threadId":"34387","inReplyTo":"1373365946-9230-1-git-send-email-artagnon@gmail.com","subject":"Re: [RESEND][PATCH 00/15] Towards a more awesome git branch","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-07-13T12:31:39Z","receivedAt":"2013-07-13T12:31:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Jul 09, 2013 at 04:02:11PM +0530, Ramkumar Ramachandra wrote:\n> Hi,\n> \n> I'm sending this out in the hope of attracting some reviews.  It's an\n> unedited resend, and there were zero conflicts from the rebase.\n\nI still don't like my callback idea. How about this? It's refactoring\na bit so that you can now extract the color parsing and alignment code\nfrom pretty.c and add them to for-each-ref as new atoms in --format.\nAs this is simply extending --format, the syntax unification idea is\nput back on the shelf and won't hinder your work.\n\nparse_color() can be used to generate color escape sequences. For\nalignment, you can do something like this:\n\npad_an_item();\nprocess_following_atom_after_the_alignment_atom();\nalign_an_item();\n\nI'm afraid it needs a bit more work on the for-each-ref side and may\neven need some fixups (I tested t4205 only and it passed, but I may\nhave left some bugs)\n\n-- 8< --\ndiff --git a/pretty.c b/pretty.c\nindex 9e43154..40a30a4 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -766,19 +766,23 @@ enum trunc_type {\n \ttrunc_right\n };\n \n+struct alignment {\n+\tenum flush_type flush_type;\n+\tenum trunc_type truncate;\n+\tint padding;\n+};\n+\n struct format_commit_context {\n \tconst struct commit *commit;\n \tconst struct pretty_print_context *pretty_ctx;\n \tunsigned commit_header_parsed:1;\n \tunsigned commit_message_parsed:1;\n \tstruct signature_check signature_check;\n-\tenum flush_type flush_type;\n-\tenum trunc_type truncate;\n \tchar *message;\n \tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n \tint auto_color;\n-\tint padding;\n+\tstruct alignment alignment;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -957,7 +961,7 @@ static int format_reflog_person(struct strbuf *sb,\n \n static size_t parse_color(struct strbuf *sb, /* in UTF-8 */\n \t\t\t  const char *placeholder,\n-\t\t\t  struct format_commit_context *c)\n+\t\t\t  int color_setting)\n {\n \tif (placeholder[1] == '(') {\n \t\tconst char *begin = placeholder + 2;\n@@ -967,7 +971,7 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */\n \t\tif (!end)\n \t\t\treturn 0;\n \t\tif (!prefixcmp(begin, \"auto,\")) {\n-\t\t\tif (!want_color(c->pretty_ctx->color))\n+\t\t\tif (!want_color(color_setting))\n \t\t\t\treturn end - placeholder + 1;\n \t\t\tbegin += 5;\n \t\t}\n@@ -995,7 +999,7 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */\n \n static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\t\t\t\tconst char *placeholder,\n-\t\t\t\t\tstruct format_commit_context *c)\n+\t\t\t\t\tstruct alignment *c)\n {\n \tconst char *ch = placeholder;\n \tenum flush_type flush_type;\n@@ -1076,7 +1080,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\t\tc->auto_color = 1;\n \t\t\treturn 7; /* consumed 7 bytes, \"C(auto)\" */\n \t\t} else {\n-\t\t\tint ret = parse_color(sb, placeholder, c);\n+\t\t\tint ret = parse_color(sb, placeholder,\n+\t\t\t\t\t      c->pretty_ctx->color);\n \t\t\tif (ret)\n \t\t\t\tc->auto_color = 0;\n \t\t\t/*\n@@ -1126,7 +1131,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \n \tcase '<':\n \tcase '>':\n-\t\treturn parse_padding_placeholder(sb, placeholder, c);\n+\t\treturn parse_padding_placeholder(sb, placeholder, &c->alignment);\n \t}\n \n \t/* these depend on the commit */\n@@ -1291,12 +1296,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \treturn 0;\t/* unknown placeholder */\n }\n \n-static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n-\t\t\t\t    const char *placeholder,\n-\t\t\t\t    struct format_commit_context *c)\n+static int pad_an_item(struct strbuf *sb, int padding)\n {\n-\tstruct strbuf local_sb = STRBUF_INIT;\n-\tint total_consumed = 0, len, padding = c->padding;\n \tif (padding < 0) {\n \t\tconst char *start = strrchr(sb->buf, '\\n');\n \t\tint occupied;\n@@ -1305,9 +1306,17 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\toccupied = utf8_strnwidth(start, -1, 1);\n \t\tpadding = (-padding) - occupied;\n \t}\n+\treturn padding;\n+}\n+\n+static int format_an_item(const char *placeholder,\n+\t\t\t  struct strbuf *local_sb,\n+\t\t\t  struct format_commit_context *c)\n+{\n+\tint total_consumed = 0;\n \twhile (1) {\n \t\tint modifier = *placeholder == 'C';\n-\t\tint consumed = format_commit_one(&local_sb, placeholder, c);\n+\t\tint consumed = format_commit_one(local_sb, placeholder, c);\n \t\ttotal_consumed += consumed;\n \n \t\tif (!modifier)\n@@ -1319,9 +1328,19 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\tplaceholder++;\n \t\ttotal_consumed++;\n \t}\n-\tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\treturn total_consumed;\n+}\n \n-\tif (c->flush_type == flush_left_and_steal) {\n+static void align_an_item(struct strbuf *sb, /* in UTF-8 */\n+\t\t\t  struct strbuf *item,\n+\t\t\t  enum flush_type flush_type,\n+\t\t\t  enum trunc_type truncate,\n+\t\t\t  int padding)\n+{\n+\tint len;\n+\n+\tlen = utf8_strnwidth(item->buf, -1, 1);\n+\tif (flush_type == flush_left_and_steal) {\n \t\tconst char *ch = sb->buf + sb->len - 1;\n \t\twhile (len > padding && ch > sb->buf) {\n \t\t\tconst char *p;\n@@ -1343,55 +1362,68 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\t\t * got a good ansi sequence, put it back to\n \t\t\t * local_sb as we're cutting sb\n \t\t\t */\n-\t\t\tstrbuf_insert(&local_sb, 0, p, ch + 1 - p);\n+\t\t\tstrbuf_insert(item, 0, p, ch + 1 - p);\n \t\t\tch = p - 1;\n \t\t}\n \t\tstrbuf_setlen(sb, ch + 1 - sb->buf);\n-\t\tc->flush_type = flush_left;\n+\t\tflush_type = flush_left;\n \t}\n \n \tif (len > padding) {\n-\t\tswitch (c->truncate) {\n+\t\tswitch (truncate) {\n \t\tcase trunc_left:\n-\t\t\tstrbuf_utf8_replace(&local_sb,\n+\t\t\tstrbuf_utf8_replace(item,\n \t\t\t\t\t    0, len - (padding - 2),\n \t\t\t\t\t    \"..\");\n \t\t\tbreak;\n \t\tcase trunc_middle:\n-\t\t\tstrbuf_utf8_replace(&local_sb,\n+\t\t\tstrbuf_utf8_replace(item,\n \t\t\t\t\t    padding / 2 - 1,\n \t\t\t\t\t    len - (padding - 2),\n \t\t\t\t\t    \"..\");\n \t\t\tbreak;\n \t\tcase trunc_right:\n-\t\t\tstrbuf_utf8_replace(&local_sb,\n+\t\t\tstrbuf_utf8_replace(item,\n \t\t\t\t\t    padding - 2, len - (padding - 2),\n \t\t\t\t\t    \"..\");\n \t\t\tbreak;\n \t\tcase trunc_none:\n \t\t\tbreak;\n \t\t}\n-\t\tstrbuf_addstr(sb, local_sb.buf);\n+\t\tstrbuf_addstr(sb, item->buf);\n \t} else {\n \t\tint sb_len = sb->len, offset = 0;\n-\t\tif (c->flush_type == flush_left)\n+\t\tif (flush_type == flush_left)\n \t\t\toffset = padding - len;\n-\t\telse if (c->flush_type == flush_both)\n+\t\telse if (flush_type == flush_both)\n \t\t\toffset = (padding - len) / 2;\n \t\t/*\n \t\t * we calculate padding in columns, now\n \t\t * convert it back to chars\n \t\t */\n-\t\tpadding = padding - len + local_sb.len;\n+\t\tpadding = padding - len + item->len;\n \t\tstrbuf_grow(sb, padding);\n \t\tstrbuf_setlen(sb, sb_len + padding);\n \t\tmemset(sb->buf + sb_len, ' ', sb->len - sb_len);\n-\t\tmemcpy(sb->buf + sb_len + offset, local_sb.buf,\n-\t\t       local_sb.len);\n+\t\tmemcpy(sb->buf + sb_len + offset, item->buf,\n+\t\t       item->len);\n \t}\n+}\n+\n+static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n+\t\t\t\t    const char *placeholder,\n+\t\t\t\t    struct format_commit_context *c)\n+{\n+\tint consumed, padding;\n+\tstruct strbuf local_sb = STRBUF_INIT;\n+\n+\tpadding = pad_an_item(sb, c->alignment.padding);\n+\tconsumed = format_an_item(placeholder, &local_sb, c);\n+\talign_an_item(sb, &local_sb, c->alignment.flush_type,\n+\t\t      c->alignment.truncate, padding);\n \tstrbuf_release(&local_sb);\n-\tc->flush_type = no_flush;\n-\treturn total_consumed;\n+\tc->alignment.flush_type = no_flush;\n+\treturn consumed;\n }\n \n static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n@@ -1424,7 +1456,7 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\tplaceholder++;\n \n \torig_len = sb->len;\n-\tif (((struct format_commit_context *)context)->flush_type != no_flush)\n+\tif (((struct format_commit_context *)context)->alignment.flush_type != no_flush)\n \t\tconsumed = format_and_pad_commit(sb, placeholder, context);\n \telse\n \t\tconsumed = format_commit_one(sb, placeholder, context);\n-- 8< --\n"}]}