{"thread":{"id":"33195","subject":"[PATCH 01/12] pretty-formats.txt: wrap long lines","startedAt":"2013-03-16T02:24:31Z","lastAt":"2013-04-18T23:08:52Z","messageCount":83,"participants":["Nguyễn Thái Ngọc Duy","Paul Campbell","Eric Sunshine","Duy Nguyen","Torsten Bögershausen","Junio C Hamano","Jakub Narębski"],"isPatch":true,"patchVersion":1,"patchTotal":12},"messages":[{"id":"211440","messageId":"1363400683-14813-1-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":null,"subject":"[PATCH 00/12] Layout control placeholders for pretty format","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:31Z","receivedAt":"2013-03-16T02:24:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This series was ejected out of pu some time ago due to gcc warnings. I\ndid not have time to look at it (and it worked ok for me so it was not\na pressing matter). This re-roll should fix that.\n\nFor demonstration, try\n\ngit log --pretty='format:%C(auto)%h %<(80,trunc)%s%>>(10,ltrunc)%C(auto)%d%>(15,mtrunc)% an'\n\nNguyễn Thái Ngọc Duy (12):\n  pretty-formats.txt: wrap long lines\n  pretty: share code between format_decoration and show_decorations\n  utf8.c: move display_mode_esc_sequence_len() for use by other functions\n  utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences\n  pretty: save commit encoding from logmsg_reencode if the caller needs it\n  pretty: get the correct encoding for --pretty:format=%e\n  utf8: keep NULs in reencode_string()\n  pretty: two phase conversion for non utf-8 commits\n  pretty: add %C(auto) for auto-coloring on the next placeholder\n  pretty: support padding placeholders, %< %> and %><\n  pretty: support truncating in %>, %< and %><\n  pretty: support %>> that steal trailing spaces\n\n Documentation/pretty-formats.txt |  34 ++++-\n builtin/blame.c                  |   2 +-\n builtin/commit.c                 |   2 +-\n builtin/fast-export.c            |   3 +-\n builtin/mailinfo.c               |   3 +-\n commit.h                         |   1 +\n compat/precompose_utf8.c         |   2 +-\n log-tree.c                       |  60 +++++----\n log-tree.h                       |   3 +\n notes.c                          |   4 +-\n pretty.c                         | 282 ++++++++++++++++++++++++++++++++++-----\n revision.c                       |   2 +-\n sequencer.c                      |   5 +-\n t/t4207-log-decoration-colors.sh |   8 +-\n t/t6006-rev-list-format.sh       |  12 +-\n utf8.c                           | 104 +++++++++++----\n utf8.h                           |  14 +-\n 17 files changed, 434 insertions(+), 107 deletions(-)\n\n-- \n1.8.2.83.gc99314b\n"},{"id":"211428","messageId":"1363400683-14813-2-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 01/12] pretty-formats.txt: wrap long lines","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:32Z","receivedAt":"2013-03-16T02:24:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt | 18 ++++++++++++------\n 1 file changed, 12 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 105f18a..66345d1 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -106,18 +106,22 @@ The placeholders are:\n - '%P': parent hashes\n - '%p': abbreviated parent hashes\n - '%an': author name\n-- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1]\n+  or linkgit:git-blame[1])\n - '%ae': author email\n-- '%aE': author email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%aE': author email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ad': author date (format respects --date= option)\n - '%aD': author date, RFC2822 style\n - '%ar': author date, relative\n - '%at': author date, UNIX timestamp\n - '%ai': author date, ISO 8601 format\n - '%cn': committer name\n-- '%cN': committer name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%cN': committer name (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ce': committer email\n-- '%cE': committer email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%cE': committer email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%cd': committer date\n - '%cD': committer date, RFC2822 style\n - '%cr': committer date, relative\n@@ -136,9 +140,11 @@ The placeholders are:\n - '%gD': reflog selector, e.g., `refs/stash@{1}`\n - '%gd': shortened reflog selector, e.g., `stash@{1}`\n - '%gn': reflog identity name\n-- '%gN': reflog identity name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%gN': reflog identity name (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ge': reflog identity email\n-- '%gE': reflog identity email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%gE': reflog identity email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%gs': reflog subject\n - '%Cred': switch color to red\n - '%Cgreen': switch color to green\n-- \n1.8.2.83.gc99314b\n"},{"id":"211429","messageId":"1363400683-14813-3-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 02/12] pretty: share code between format_decoration and show_decorations","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:33Z","receivedAt":"2013-03-16T02:24:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This also adds color support to format_decoration()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n log-tree.c                       | 60 +++++++++++++++++++++++++---------------\n log-tree.h                       |  3 ++\n pretty.c                         | 19 +------------\n t/t4207-log-decoration-colors.sh |  8 +++---\n 4 files changed, 45 insertions(+), 45 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex 5dc45c4..7467a1d 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -174,36 +174,50 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n \t}\n }\n \n-void show_decorations(struct rev_info *opt, struct commit *commit)\n+void format_decoration(struct strbuf *sb,\n+\t\t       const struct commit *commit,\n+\t\t       int use_color)\n {\n-\tconst char *prefix;\n-\tstruct name_decoration *decoration;\n+\tconst char *prefix = \" (\";\n+\tstruct name_decoration *d;\n \tconst char *color_commit =\n-\t\tdiff_get_color_opt(&opt->diffopt, DIFF_COMMIT);\n+\t\tdiff_get_color(use_color, DIFF_COMMIT);\n \tconst char *color_reset =\n-\t\tdecorate_get_color_opt(&opt->diffopt, DECORATION_NONE);\n+\t\tdecorate_get_color(use_color, DECORATION_NONE);\n+\n+\tload_ref_decorations(DECORATE_SHORT_REFS);\n+\td = lookup_decoration(&name_decoration, &commit->object);\n+\tif (!d)\n+\t\treturn;\n+\twhile (d) {\n+\t\tstrbuf_addstr(sb, color_commit);\n+\t\tstrbuf_addstr(sb, prefix);\n+\t\tstrbuf_addstr(sb, decorate_get_color(use_color, d->type));\n+\t\tif (d->type == DECORATION_REF_TAG)\n+\t\t\tstrbuf_addstr(sb, \"tag: \");\n+\t\tstrbuf_addstr(sb, d->name);\n+\t\tstrbuf_addstr(sb, color_reset);\n+\t\tprefix = \", \";\n+\t\td = d->next;\n+\t}\n+\tif (prefix[0] == ',') {\n+\t\tstrbuf_addstr(sb, color_commit);\n+\t\tstrbuf_addch(sb, ')');\n+\t\tstrbuf_addstr(sb, color_reset);\n+\t}\n+}\n+\n+void show_decorations(struct rev_info *opt, struct commit *commit)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tif (opt->show_source && commit->util)\n \t\tprintf(\"\\t%s\", (char *) commit->util);\n \tif (!opt->show_decorations)\n \t\treturn;\n-\tdecoration = lookup_decoration(&name_decoration, &commit->object);\n-\tif (!decoration)\n-\t\treturn;\n-\tprefix = \" (\";\n-\twhile (decoration) {\n-\t\tprintf(\"%s\", prefix);\n-\t\tfputs(decorate_get_color_opt(&opt->diffopt, decoration->type),\n-\t\t      stdout);\n-\t\tif (decoration->type == DECORATION_REF_TAG)\n-\t\t\tfputs(\"tag: \", stdout);\n-\t\tprintf(\"%s\", decoration->name);\n-\t\tfputs(color_reset, stdout);\n-\t\tfputs(color_commit, stdout);\n-\t\tprefix = \", \";\n-\t\tdecoration = decoration->next;\n-\t}\n-\tputchar(')');\n+\tformat_decoration(&sb, commit, opt->diffopt.use_color);\n+\tfputs(sb.buf, stdout);\n+\tstrbuf_release(&sb);\n }\n \n /*\n@@ -625,8 +639,8 @@ void show_log(struct rev_info *opt)\n \t\t\tprintf(\" (from %s)\",\n \t\t\t       find_unique_abbrev(parent->object.sha1,\n \t\t\t\t\t\t  abbrev_commit));\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n \t\tshow_decorations(opt, commit);\n-\t\tprintf(\"%s\", diff_get_color_opt(&opt->diffopt, DIFF_RESET));\n \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n \t\t\tputchar(' ');\n \t\t} else {\ndiff --git a/log-tree.h b/log-tree.h\nindex 9140f48..e6a2da5 100644\n--- a/log-tree.h\n+++ b/log-tree.h\n@@ -13,6 +13,9 @@ int log_tree_diff_flush(struct rev_info *);\n int log_tree_commit(struct rev_info *, struct commit *);\n int log_tree_opt_parse(struct rev_info *, const char **, int);\n void show_log(struct rev_info *opt);\n+void format_decoration(struct strbuf *sb,\n+\t\t       const struct commit *commit,\n+\t\t       int use_color);\n void show_decorations(struct rev_info *opt, struct commit *commit);\n void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\t\t     const char **subject_p,\ndiff --git a/pretty.c b/pretty.c\nindex eae57ad..79784be 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -898,23 +898,6 @@ static void parse_commit_message(struct format_commit_context *c)\n \tc->commit_message_parsed = 1;\n }\n \n-static void format_decoration(struct strbuf *sb, const struct commit *commit)\n-{\n-\tstruct name_decoration *d;\n-\tconst char *prefix = \" (\";\n-\n-\tload_ref_decorations(DECORATE_SHORT_REFS);\n-\td = lookup_decoration(&name_decoration, &commit->object);\n-\twhile (d) {\n-\t\tstrbuf_addstr(sb, prefix);\n-\t\tprefix = \", \";\n-\t\tstrbuf_addstr(sb, d->name);\n-\t\td = d->next;\n-\t}\n-\tif (prefix[0] == ',')\n-\t\tstrbuf_addch(sb, ')');\n-}\n-\n static void strbuf_wrap(struct strbuf *sb, size_t pos,\n \t\t\tsize_t width, size_t indent1, size_t indent2)\n {\n@@ -1146,7 +1129,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\tstrbuf_addstr(sb, get_revision_mark(NULL, commit));\n \t\treturn 1;\n \tcase 'd':\n-\t\tformat_decoration(sb, commit);\n+\t\tformat_decoration(sb, commit, 0);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex bbde31b..925f577 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -44,15 +44,15 @@ test_expect_success setup '\n '\n \n cat >expected <<EOF\n-${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_HEAD}HEAD${c_reset}${c_commit},\\\n  ${c_tag}tag: v1.0${c_reset}${c_commit},\\\n  ${c_tag}tag: B${c_reset}${c_commit},\\\n  ${c_branch}master${c_reset}${c_commit})${c_reset} B\n-${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A1${c_reset}${c_commit},\\\n  ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1\n-${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n  On master: Changes to A.t\n-${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n EOF\n \n # We want log to show all, but the second parent to refs/stash is irrelevant\n-- \n1.8.2.83.gc99314b\n"},{"id":"211430","messageId":"1363400683-14813-4-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 03/12] utf8.c: move display_mode_esc_sequence_len() for use by other functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:34Z","receivedAt":"2013-03-16T02:24:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex 1087870..82c2ddf 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,6 +9,20 @@ struct interval {\n   int last;\n };\n \n+static size_t display_mode_esc_sequence_len(const char *s)\n+{\n+\tconst char *p = s;\n+\tif (*p++ != '\\033')\n+\t\treturn 0;\n+\tif (*p++ != '[')\n+\t\treturn 0;\n+\twhile (isdigit(*p) || *p == ';')\n+\t\tp++;\n+\tif (*p++ != 'm')\n+\t\treturn 0;\n+\treturn p - s;\n+}\n+\n /* auxiliary function for binary search in interval table */\n static int bisearch(ucs_char_t ucs, const struct interval *table, int max)\n {\n@@ -303,20 +317,6 @@ static void strbuf_add_indented_text(struct strbuf *buf, const char *text,\n \t}\n }\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n-{\n-\tconst char *p = s;\n-\tif (*p++ != '\\033')\n-\t\treturn 0;\n-\tif (*p++ != '[')\n-\t\treturn 0;\n-\twhile (isdigit(*p) || *p == ';')\n-\t\tp++;\n-\tif (*p++ != 'm')\n-\t\treturn 0;\n-\treturn p - s;\n-}\n-\n /*\n  * Wrap the text, if necessary. The variable indent is the indent for the\n  * first line, indent2 is the indent for all other lines.\n-- \n1.8.2.83.gc99314b\n"},{"id":"211431","messageId":"1363400683-14813-5-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 04/12] utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:35Z","receivedAt":"2013-03-16T02:24:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 20 ++++++++++++++------\n utf8.h |  1 +\n 2 files changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex 82c2ddf..38322a1 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -266,18 +266,26 @@ int utf8_width(const char **start, size_t *remainder_p)\n  * string, assuming that the string is utf8.  Returns strlen() instead\n  * if the string does not look like a valid utf8 string.\n  */\n-int utf8_strwidth(const char *string)\n+int utf8_strnwidth(const char *string, int len, int skip_ansi)\n {\n \tint width = 0;\n \tconst char *orig = string;\n \n-\twhile (1) {\n-\t\tif (!string)\n-\t\t\treturn strlen(orig);\n-\t\tif (!*string)\n-\t\t\treturn width;\n+\tif (len == -1)\n+\t\tlen = strlen(string);\n+\twhile (string && string < orig + len) {\n+\t\tint skip;\n+\t\twhile (skip_ansi &&\n+\t\t       (skip = display_mode_esc_sequence_len(string)))\n+\t\t\tstring += skip;\n \t\twidth += utf8_width(&string, NULL);\n \t}\n+\treturn string ? width : len;\n+}\n+\n+int utf8_strwidth(const char *string)\n+{\n+\treturn utf8_strnwidth(string, -1, 0);\n }\n \n int is_utf8(const char *text)\ndiff --git a/utf8.h b/utf8.h\nindex 501b2bd..a556932 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -4,6 +4,7 @@\n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n int utf8_width(const char **start, size_t *remainder_p);\n+int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n int is_utf8(const char *text);\n int is_encoding_utf8(const char *name);\n-- \n1.8.2.83.gc99314b\n"},{"id":"211437","messageId":"1363400683-14813-6-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 05/12] pretty: save commit encoding from logmsg_reencode if the caller needs it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:36Z","receivedAt":"2013-03-16T02:24:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The commit encoding is parsed by logmsg_reencode, there's no need for\nthe caller to re-parse it again. The reencoded message now have the\nnew encoding, not the original one. The caller would need to read\ncommit object again before parsing.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/blame.c  |  2 +-\n builtin/commit.c |  2 +-\n commit.h         |  1 +\n pretty.c         | 16 ++++++++++++----\n revision.c       |  2 +-\n 5 files changed, 16 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 86100e9..104a948 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1425,7 +1425,7 @@ static void get_commit_info(struct commit *commit,\n \tcommit_info_init(ret);\n \n \tencoding = get_log_output_encoding();\n-\tmessage = logmsg_reencode(commit, encoding);\n+\tmessage = logmsg_reencode(commit, NULL, encoding);\n \tget_ac_line(message, \"\\nauthor \",\n \t\t    &ret->author, &ret->author_mail,\n \t\t    &ret->author_time, &ret->author_tz);\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 3348aa1..beead44 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -953,7 +953,7 @@ static const char *read_commit_message(const char *name)\n \tif (!commit)\n \t\tdie(_(\"could not lookup commit %s\"), name);\n \tout_enc = get_commit_output_encoding();\n-\treturn logmsg_reencode(commit, out_enc);\n+\treturn logmsg_reencode(commit, NULL, out_enc);\n }\n \n static int parse_and_validate_options(int argc, const char *argv[],\ndiff --git a/commit.h b/commit.h\nindex 4138bb4..085349a 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -100,6 +100,7 @@ struct userformat_want {\n extern int has_non_ascii(const char *text);\n struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */\n extern char *logmsg_reencode(const struct commit *commit,\n+\t\t\t     char **commit_encoding,\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 *);\ndiff --git a/pretty.c b/pretty.c\nindex 79784be..ab5d70f 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -584,6 +584,7 @@ static char *replace_encoding_header(char *buf, const char *encoding)\n }\n \n char *logmsg_reencode(const struct commit *commit,\n+\t\t      char **commit_encoding,\n \t\t      const char *output_encoding)\n {\n \tstatic const char *utf8 = \"UTF-8\";\n@@ -605,9 +606,15 @@ char *logmsg_reencode(const struct commit *commit,\n \t\t\t    sha1_to_hex(commit->object.sha1), typename(type));\n \t}\n \n-\tif (!output_encoding || !*output_encoding)\n+\tif (!output_encoding || !*output_encoding) {\n+\t\tif (commit_encoding)\n+\t\t\t*commit_encoding =\n+\t\t\t\tget_header(commit, msg, \"encoding\");\n \t\treturn msg;\n+\t}\n \tencoding = get_header(commit, msg, \"encoding\");\n+\tif (commit_encoding)\n+\t\t*commit_encoding = encoding;\n \tuse_encoding = encoding ? encoding : utf8;\n \tif (same_encoding(use_encoding, output_encoding)) {\n \t\t/*\n@@ -648,7 +655,8 @@ char *logmsg_reencode(const struct commit *commit,\n \tif (out)\n \t\tout = replace_encoding_header(out, output_encoding);\n \n-\tfree(encoding);\n+\tif (!commit_encoding)\n+\t\tfree(encoding);\n \t/*\n \t * If the re-encoding failed, out might be NULL here; in that\n \t * case we just return the commit message verbatim.\n@@ -1313,7 +1321,7 @@ 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, output_enc);\n+\tcontext.message = logmsg_reencode(commit, NULL, output_enc);\n \n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n@@ -1476,7 +1484,7 @@ void pretty_print_commit(const struct pretty_print_context *pp,\n \t}\n \n \tencoding = get_log_output_encoding();\n-\tmsg = reencoded = logmsg_reencode(commit, encoding);\n+\tmsg = reencoded = logmsg_reencode(commit, NULL, encoding);\n \n \tif (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)\n \t\tindent = 0;\ndiff --git a/revision.c b/revision.c\nindex ef60205..c6ff560 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2290,7 +2290,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)\n \t * in it.\n \t */\n \tencoding = get_log_output_encoding();\n-\tmessage = logmsg_reencode(commit, encoding);\n+\tmessage = logmsg_reencode(commit, NULL, encoding);\n \n \t/* Copy the commit to temporary if we are using \"fake\" headers */\n \tif (buf.len)\n-- \n1.8.2.83.gc99314b\n"},{"id":"211432","messageId":"1363400683-14813-7-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 06/12] pretty: get the correct encoding for --pretty:format=%e","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:37Z","receivedAt":"2013-03-16T02:24:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"parse_commit_header() provides the commit encoding for '%e' and it\nreads it from the re-encoded message, which contains the new encoding,\nnot the original one in the commit object.\n\nGet the commit encoding from logmsg_reencode() instead.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c | 13 +++++++------\n 1 file changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex ab5d70f..e2241e5 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -771,12 +771,12 @@ struct format_commit_context {\n \t\tchar *signer;\n \t} signature;\n \tchar *message;\n+\tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n \tstruct chunk committer;\n-\tstruct chunk encoding;\n \tsize_t message_off;\n \tsize_t subject_off;\n \tsize_t body_off;\n@@ -823,9 +823,6 @@ static void parse_commit_header(struct format_commit_context *context)\n \t\t} else if (!prefixcmp(msg + i, \"committer \")) {\n \t\t\tcontext->committer.off = i + 10;\n \t\t\tcontext->committer.len = eol - i - 10;\n-\t\t} else if (!prefixcmp(msg + i, \"encoding \")) {\n-\t\t\tcontext->encoding.off = i + 9;\n-\t\t\tcontext->encoding.len = eol - i - 9;\n \t\t}\n \t\ti = eol;\n \t}\n@@ -1210,7 +1207,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t\t\t   msg + c->committer.off, c->committer.len,\n \t\t\t\t   c->pretty_ctx->date_mode);\n \tcase 'e':\t/* encoding */\n-\t\tstrbuf_add(sb, msg + c->encoding.off, c->encoding.len);\n+\t\tif (c->commit_encoding)\n+\t\t\tstrbuf_addstr(sb, c->commit_encoding);\n \t\treturn 1;\n \tcase 'B':\t/* raw body */\n \t\t/* message_off is always left at the initial newline */\n@@ -1321,11 +1319,14 @@ 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, NULL, output_enc);\n+\tcontext.message = logmsg_reencode(commit,\n+\t\t\t\t\t  &context.commit_encoding,\n+\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 \n+\tfree(context.commit_encoding);\n \tlogmsg_free(context.message, commit);\n \tfree(context.signature.gpg_output);\n \tfree(context.signature.signer);\n-- \n1.8.2.83.gc99314b\n"},{"id":"211439","messageId":"1363400683-14813-8-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 07/12] utf8: keep NULs in reencode_string()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:38Z","receivedAt":"2013-03-16T02:24:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"reencode_string() will be used in the next patch for re-encoding\npretty output, which can contain NULs.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fast-export.c    |  3 ++-\n builtin/mailinfo.c       |  3 ++-\n compat/precompose_utf8.c |  2 +-\n notes.c                  |  4 +++-\n pretty.c                 |  3 ++-\n sequencer.c              |  5 +++--\n utf8.c                   | 10 +++++++---\n utf8.h                   | 10 +++++++---\n 8 files changed, 27 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 77dffd1..7ba9f3b 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -316,7 +316,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)\n \n \tmark_next_object(&commit->object);\n \tif (!is_encoding_utf8(encoding))\n-\t\treencoded = reencode_string(message, \"UTF-8\", encoding);\n+\t\treencoded = reencode_string(message, strlen(message),\n+\t\t\t\t\t    \"UTF-8\", encoding, NULL);\n \tif (!commit->parents)\n \t\tprintf(\"reset %s\\n\", (const char*)commit->util);\n \tprintf(\"commit %s\\nmark :%\"PRIu32\"\\n%.*s\\n%.*s\\ndata %u\\n%s\",\ndiff --git a/builtin/mailinfo.c b/builtin/mailinfo.c\nindex 24a772d..129e7dc 100644\n--- a/builtin/mailinfo.c\n+++ b/builtin/mailinfo.c\n@@ -486,7 +486,8 @@ static void convert_to_utf8(struct strbuf *line, const char *charset)\n \n \tif (same_encoding(metainfo_charset, charset))\n \t\treturn;\n-\tout = reencode_string(line->buf, metainfo_charset, charset);\n+\tout = reencode_string(line->buf, line->len,\n+\t\t\t      metainfo_charset, charset, NULL);\n \tif (!out)\n \t\tdie(\"cannot convert from %s to %s\",\n \t\t    charset, metainfo_charset);\ndiff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\nindex 8cf5955..d9203d0 100644\n--- a/compat/precompose_utf8.c\n+++ b/compat/precompose_utf8.c\n@@ -78,7 +78,7 @@ void precompose_argv(int argc, const char **argv)\n \t\tsize_t namelen;\n \t\toldarg = argv[i];\n \t\tif (has_non_ascii(oldarg, (size_t)-1, &namelen)) {\n-\t\t\tnewarg = reencode_string_iconv(oldarg, namelen, ic_precompose);\n+\t\t\tnewarg = reencode_string_iconv(oldarg, namelen, ic_precompose, NULL);\n \t\t\tif (newarg)\n \t\t\t\targv[i] = newarg;\n \t\t}\ndiff --git a/notes.c b/notes.c\nindex f63fd57..4ae3b25 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1222,7 +1222,9 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \n \tif (output_encoding && *output_encoding &&\n \t    !is_encoding_utf8(output_encoding)) {\n-\t\tchar *reencoded = reencode_string(msg, output_encoding, utf8);\n+\t\tchar *reencoded = reencode_string(msg, strlen(msg),\n+\t\t\t\t\t\t  output_encoding, utf8,\n+\t\t\t\t\t\t  NULL);\n \t\tif (reencoded) {\n \t\t\tfree(msg);\n \t\t\tmsg = reencoded;\ndiff --git a/pretty.c b/pretty.c\nindex e2241e5..092dd1d 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -643,7 +643,8 @@ char *logmsg_reencode(const struct commit *commit,\n \t\t * this point, we are done with msg. If we allocated a fresh\n \t\t * copy, we can free it.\n \t\t */\n-\t\tout = reencode_string(msg, output_encoding, use_encoding);\n+\t\tout = reencode_string(msg, strlen(msg),\n+\t\t\t\t      output_encoding, use_encoding, NULL);\n \t\tif (out && msg != commit->buffer)\n \t\t\tfree(msg);\n \t}\ndiff --git a/sequencer.c b/sequencer.c\nindex aef5e8a..bf15531 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -61,8 +61,9 @@ static int get_message(struct commit *commit, struct commit_message *out)\n \tout->reencoded_message = NULL;\n \tout->message = commit->buffer;\n \tif (same_encoding(encoding, git_commit_encoding))\n-\t\tout->reencoded_message = reencode_string(commit->buffer,\n-\t\t\t\t\tgit_commit_encoding, encoding);\n+\t\tout->reencoded_message =\n+\t\t\treencode_string(commit->buffer, strlen(commit->buffer),\n+\t\t\t\t\tgit_commit_encoding, encoding, NULL);\n \tif (out->reencoded_message)\n \t\tout->message = out->reencoded_message;\n \ndiff --git a/utf8.c b/utf8.c\nindex 38322a1..9d98043 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -468,7 +468,7 @@ int utf8_fprintf(FILE *stream, const char *format, ...)\n #else\n \ttypedef char * iconv_ibp;\n #endif\n-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)\n+char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, int *outsz_p)\n {\n \tsize_t outsz, outalloc;\n \tchar *out, *outpos;\n@@ -502,13 +502,17 @@ char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)\n \t\t}\n \t\telse {\n \t\t\t*outpos = '\\0';\n+\t\t\tif (outsz_p)\n+\t\t\t\t*outsz_p = outpos - out;\n \t\t\tbreak;\n \t\t}\n \t}\n \treturn out;\n }\n \n-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding)\n+char *reencode_string(const char *in, int insz,\n+\t\t      const char *out_encoding, const char *in_encoding,\n+\t\t      int *outsz)\n {\n \ticonv_t conv;\n \tchar *out;\n@@ -518,7 +522,7 @@ char *reencode_string(const char *in, const char *out_encoding, const char *in_e\n \tconv = iconv_open(out_encoding, in_encoding);\n \tif (conv == (iconv_t) -1)\n \t\treturn NULL;\n-\tout = reencode_string_iconv(in, strlen(in), conv);\n+\tout = reencode_string_iconv(in, insz, conv, outsz);\n \ticonv_close(conv);\n \treturn out;\n }\ndiff --git a/utf8.h b/utf8.h\nindex a556932..99db3e0 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -17,10 +17,14 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \t\t\t     int indent, int indent2, int width);\n \n #ifndef NO_ICONV\n-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv);\n-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);\n+char *reencode_string_iconv(const char *in, size_t insz,\n+\t\t\t    iconv_t conv, int *outsz);\n+char *reencode_string(const char *in, int insz,\n+\t\t      const char *out_encoding,\n+\t\t      const char *in_encoding,\n+\t\t      int *outsz);\n #else\n-#define reencode_string(a,b,c) NULL\n+#define reencode_string(a,b,c,d) NULL\n #endif\n \n #endif\n-- \n1.8.2.83.gc99314b\n"},{"id":"211438","messageId":"1363400683-14813-9-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 08/12] pretty: two phase conversion for non utf-8 commits","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:39Z","receivedAt":"2013-03-16T02:24:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Always assume format_commit_item() takes an utf-8 string for string\nhandling simplicity (we can handle utf-8 strings, but can't with other\nencodings).\n\nIf commit message is in non-utf8, or output encoding is not, then the\ncommit is first converted to utf-8, processed, then output converted\nto output encoding. This of course only works with encodings that are\ncompatible with Unicode.\n\nThis also fixes the iso8859-1 test in t6006. It's supposed to create\nan iso8859-1 commit, but the commit content in t6006 is in UTF-8.\nt6006 is now converted back in UTF-8 (the downside is we can't put\nutf-8 strings there anymore).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c                   | 24 ++++++++++++++++++++++--\n t/t6006-rev-list-format.sh | 12 ++++++------\n 2 files changed, 28 insertions(+), 8 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 092dd1d..3f4809a 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -1003,7 +1003,8 @@ static int format_reflog_person(struct strbuf *sb,\n \treturn format_person_part(sb, part, ident, strlen(ident), dmode);\n }\n \n-static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\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 {\n \tstruct format_commit_context *c = context;\n@@ -1235,7 +1236,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \treturn 0;\t/* unknown placeholder */\n }\n \n-static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n+static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n+\t\t\t\t const char *placeholder,\n \t\t\t\t void *context)\n {\n \tint consumed;\n@@ -1315,6 +1317,7 @@ void format_commit_message(const struct commit *commit,\n {\n \tstruct format_commit_context context;\n \tconst char *output_enc = pretty_ctx->output_encoding;\n+\tconst char *utf8 = \"UTF-8\";\n \n \tmemset(&context, 0, sizeof(context));\n \tcontext.commit = commit;\n@@ -1327,6 +1330,23 @@ void format_commit_message(const struct commit *commit,\n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n \n+\tif (output_enc) {\n+\t\tif (same_encoding(utf8, output_enc))\n+\t\t\toutput_enc = NULL;\n+\t} else {\n+\t\tif (context.commit_encoding &&\n+\t\t    !same_encoding(context.commit_encoding, utf8))\n+\t\t\toutput_enc = context.commit_encoding;\n+\t}\n+\n+\tif (output_enc) {\n+\t\tint outsz;\n+\t\tchar *out = reencode_string(sb->buf, sb->len,\n+\t\t\t\t\t    output_enc, utf8, &outsz);\n+\t\tif (out)\n+\t\t\tstrbuf_attach(sb, out, outsz, outsz + 1);\n+\t}\n+\n \tfree(context.commit_encoding);\n \tlogmsg_free(context.message, commit);\n \tfree(context.signature.gpg_output);\ndiff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh\nindex 3fc3b74..0393c9f 100755\n--- a/t/t6006-rev-list-format.sh\n+++ b/t/t6006-rev-list-format.sh\n@@ -184,7 +184,7 @@ Test printing of complex bodies\n \n This commit message is much longer than the others,\n and it will be encoded in iso8859-1. We should therefore\n-include an iso8859 character: ¡bueno!\n+include an iso8859 character: �bueno!\n EOF\n test_expect_success 'setup complex body' '\n git config i18n.commitencoding iso8859-1 &&\n@@ -192,14 +192,14 @@ git config i18n.commitencoding iso8859-1 &&\n '\n \n test_format complex-encoding %e <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n iso8859-1\n commit 131a310eb913d107dd3c09a65d1651175898735d\n commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n EOF\n \n test_format complex-subject %s <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n Test printing of complex bodies\n commit 131a310eb913d107dd3c09a65d1651175898735d\n changed foo\n@@ -208,17 +208,17 @@ added foo\n EOF\n \n test_format complex-body %b <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n This commit message is much longer than the others,\n and it will be encoded in iso8859-1. We should therefore\n-include an iso8859 character: ¡bueno!\n+include an iso8859 character: �bueno!\n \n commit 131a310eb913d107dd3c09a65d1651175898735d\n commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n EOF\n \n test_expect_success '%x00 shows NUL' '\n-\techo  >expect commit f58db70b055c5718631e5c61528b28b12090cdea &&\n+\techo  >expect commit 1ed88da4a5b5ed8c449114ac131efc62178734c3 &&\n \techo >>expect fooQbar &&\n \tgit rev-list -1 --format=foo%x00bar HEAD >actual.nul &&\n \tnul_to_q <actual.nul >actual &&\n-- \n1.8.2.83.gc99314b\n"},{"id":"211433","messageId":"1363400683-14813-10-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 09/12] pretty: add %C(auto) for auto-coloring on the next placeholder","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:40Z","receivedAt":"2013-03-16T02:24:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is not simply convenient over $C(auto,xxx). Some placeholders\n(actually only one, %d) do multi coloring and we can't emit a multiple\ncolors with %C(auto,xxx).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  3 ++-\n pretty.c                         | 15 +++++++++++++--\n 2 files changed, 15 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 66345d1..8734224 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -154,7 +154,8 @@ The placeholders are:\n   adding `auto,` at the beginning will emit color only when colors are\n   enabled for log output (by `color.diff`, `color.ui`, or `--color`, and\n   respecting the `auto` settings of the former if we are going to a\n-  terminal)\n+  terminal). `auto` alone (i.e. `%C(auto)`) will turn on auto coloring\n+  on the following placeholder.\n - '%m': left, right or boundary mark\n - '%n': newline\n - '%%': a raw '%'\ndiff --git a/pretty.c b/pretty.c\nindex 3f4809a..c333fd6 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -774,6 +774,7 @@ struct format_commit_context {\n \tchar *message;\n \tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n+\tint auto_color;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -1011,7 +1012,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-\tint h1, h2;\n+\tint h1, h2, use_color;\n \n \t/* these are independent of the commit */\n \tswitch (placeholder[0]) {\n@@ -1023,6 +1024,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \n \t\t\tif (!end)\n \t\t\t\treturn 0;\n+\t\t\tif (!prefixcmp(begin, \"auto)\")) {\n+\t\t\t\tc->auto_color = 1;\n+\t\t\t\treturn end - placeholder + 1;\n+\t\t\t}\n \t\t\tif (!prefixcmp(begin, \"auto,\")) {\n \t\t\t\tif (!want_color(c->pretty_ctx->color))\n \t\t\t\t\treturn end - placeholder + 1;\n@@ -1090,16 +1095,22 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t/* these depend on the commit */\n \tif (!commit->object.parsed)\n \t\tparse_object(commit->object.sha1);\n+\tuse_color = c->auto_color;\n+\tc->auto_color = 0;\n \n \tswitch (placeholder[0]) {\n \tcase 'H':\t\t/* commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(use_color, DIFF_COMMIT));\n \t\tstrbuf_addstr(sb, sha1_to_hex(commit->object.sha1));\n+\t\tstrbuf_addstr(sb, diff_get_color(use_color, DIFF_RESET));\n \t\treturn 1;\n \tcase 'h':\t\t/* abbreviated commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(use_color, DIFF_COMMIT));\n \t\tif (add_again(sb, &c->abbrev_commit_hash))\n \t\t\treturn 1;\n \t\tstrbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,\n \t\t\t\t\t\t     c->pretty_ctx->abbrev));\n+\t\tstrbuf_addstr(sb, diff_get_color(use_color, DIFF_RESET));\n \t\tc->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;\n \t\treturn 1;\n \tcase 'T':\t\t/* tree hash */\n@@ -1136,7 +1147,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\tstrbuf_addstr(sb, get_revision_mark(NULL, commit));\n \t\treturn 1;\n \tcase 'd':\n-\t\tformat_decoration(sb, commit, 0);\n+\t\tformat_decoration(sb, commit, use_color);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\n-- \n1.8.2.83.gc99314b\n"},{"id":"211434","messageId":"1363400683-14813-11-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 10/12] pretty: support padding placeholders, %< %> and %><","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:41Z","receivedAt":"2013-03-16T02:24:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Either %<, %> or %<> standing before a placeholder specifies how many\ncolumns (at least as the placeholder can exceed it) it takes. Each\ndiffers on how spaces are padded:\n\n  %< pads on the right (aka left alignment)\n  %> pads on the left (aka right alignment)\n  %>< pads both ways equally (aka centered)\n\nThe (<N>) follows them, e.g. `%<(100)', to specify the number of\ncolumns the next placeholder takes.\n\nHowever, if '|' stands before (<N>), e.g. `%>|(100)', then the number\nof columns is calculated so that it reaches the Nth column on screen.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |   8 +++\n pretty.c                         | 117 ++++++++++++++++++++++++++++++++++++++-\n 2 files changed, 124 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 8734224..87ca2c4 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -162,6 +162,14 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n+- '%<(<N>)': make the next placeholder take at least N columns,\n+  padding spaces on the right if necessary\n+- '%<|(<N>)': make the next placeholder take at least until Nth\n+  columns, padding spaces on the right if necessary\n+- '%>(<N>)', '%>|(<N>)': similar to '%<(<N<)', '%<|(<N<)'\n+  respectively, but padding spaces on the left\n+- '%><(<N>)', '%><|(<N>)': similar to '%<(<N<)', '%<|(<N<)'\n+  respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\n revision traversal engine. For example, the `%g*` reflog options will\ndiff --git a/pretty.c b/pretty.c\nindex c333fd6..233d69c 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -760,12 +760,20 @@ struct chunk {\n \tsize_t len;\n };\n \n+enum flush_type {\n+\tno_flush,\n+\tflush_right,\n+\tflush_left,\n+\tflush_both\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 \tunsigned commit_signature_parsed:1;\n+\tenum flush_type flush_type;\n \tstruct {\n \t\tchar *gpg_output;\n \t\tchar good_bad;\n@@ -775,6 +783,7 @@ struct format_commit_context {\n \tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n \tint auto_color;\n+\tint padding;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -1004,6 +1013,52 @@ static int format_reflog_person(struct strbuf *sb,\n \treturn format_person_part(sb, part, ident, strlen(ident), dmode);\n }\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+{\n+\tconst char *ch = placeholder;\n+\tenum flush_type flush_type;\n+\tint to_column = 0;\n+\n+\tswitch (*ch++) {\n+\tcase '<':\n+\t\tflush_type = flush_right;\n+\t\tbreak;\n+\tcase '>':\n+\t\tif (*ch == '<') {\n+\t\t\tflush_type = flush_both;\n+\t\t\tch++;\n+\t\t} else\n+\t\t\tflush_type = flush_left;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\t/* the next value means \"wide enough to that column\" */\n+\tif (*ch == '|') {\n+\t\tto_column = 1;\n+\t\tch++;\n+\t}\n+\n+\tif (*ch == '(') {\n+\t\tconst char *start = ch + 1;\n+\t\tconst char *end = strchr(start, ')');\n+\t\tchar *next;\n+\t\tint width;\n+\t\tif (!end || end == start)\n+\t\t\treturn 0;\n+\t\twidth = strtoul(start, &next, 10);\n+\t\tif (next == start || width == 0)\n+\t\t\treturn 0;\n+\t\tc->padding = to_column ? -width : width;\n+\t\tc->flush_type = flush_type;\n+\t\treturn end - placeholder + 1;\n+\t}\n+\treturn 0;\n+}\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@@ -1090,6 +1145,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\t\treturn end - placeholder + 1;\n \t\t} else\n \t\t\treturn 0;\n+\n+\tcase '<':\n+\tcase '>':\n+\t\treturn parse_padding_placeholder(sb, placeholder, c);\n \t}\n \n \t/* these depend on the commit */\n@@ -1247,6 +1306,59 @@ 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+{\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+\t\tif (!start)\n+\t\t\tstart = sb->buf;\n+\t\toccupied = utf8_strnwidth(start, -1, 1);\n+\t\tpadding = (-padding) - occupied;\n+\t}\n+\twhile (1) {\n+\t\tint modifier = *placeholder == 'C';\n+\t\tint consumed = format_commit_one(&local_sb, placeholder, c);\n+\t\ttotal_consumed += consumed;\n+\n+\t\tif (!modifier)\n+\t\t\tbreak;\n+\n+\t\tplaceholder += consumed;\n+\t\tif (*placeholder != '%')\n+\t\t\tbreak;\n+\t\tplaceholder++;\n+\t\ttotal_consumed++;\n+\t}\n+\tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\tif (len > padding)\n+\t\tstrbuf_addstr(sb, local_sb.buf);\n+\telse {\n+\t\tint sb_len = sb->len, offset = 0;\n+\t\tif (c->flush_type == flush_left)\n+\t\t\toffset = padding - len;\n+\t\telse if (c->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\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}\n+\tstrbuf_release(&local_sb);\n+\tc->flush_type = no_flush;\n+\treturn total_consumed;\n+}\n+\n static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\t\t\t const char *placeholder,\n \t\t\t\t void *context)\n@@ -1277,7 +1389,10 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\tplaceholder++;\n \n \torig_len = sb->len;\n-\tconsumed = format_commit_one(sb, placeholder, context);\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 \tif (magic == NO_MAGIC)\n \t\treturn consumed;\n \n-- \n1.8.2.83.gc99314b\n"},{"id":"211435","messageId":"1363400683-14813-12-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 11/12] pretty: support truncating in %>, %< and %><","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:42Z","receivedAt":"2013-03-16T02:24:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"%>(N,trunc) truncates the righ part after N columns and replace the\nlast two letters with \"..\". ltrunc does the same on the left. mtrunc\ncuts the middle out.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  6 +++--\n pretty.c                         | 51 +++++++++++++++++++++++++++++++++++++---\n utf8.c                           | 46 ++++++++++++++++++++++++++++++++++++\n utf8.h                           |  2 ++\n 4 files changed, 100 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 87ca2c4..17f82f4 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -162,8 +162,10 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n-- '%<(<N>)': make the next placeholder take at least N columns,\n-  padding spaces on the right if necessary\n+- '%<(<N>[,trunc|ltrunc|mtrunc])': make the next placeholder take at\n+  least N columns, padding spaces on the right if necessary.\n+  Optionally truncate at the beginning (ltrunc), the middle (mtrunc)\n+  or the end (trunc) if the output is longer than N columns.\n - '%<|(<N>)': make the next placeholder take at least until Nth\n   columns, padding spaces on the right if necessary\n - '%>(<N>)', '%>|(<N>)': similar to '%<(<N<)', '%<|(<N<)'\ndiff --git a/pretty.c b/pretty.c\nindex 233d69c..29384b5 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -767,6 +767,13 @@ enum flush_type {\n \tflush_both\n };\n \n+enum trunc_type {\n+\ttrunc_none,\n+\ttrunc_left,\n+\ttrunc_middle,\n+\ttrunc_right\n+};\n+\n struct format_commit_context {\n \tconst struct commit *commit;\n \tconst struct pretty_print_context *pretty_ctx;\n@@ -774,6 +781,7 @@ struct format_commit_context {\n \tunsigned commit_message_parsed:1;\n \tunsigned commit_signature_parsed:1;\n \tenum flush_type flush_type;\n+\tenum trunc_type truncate;\n \tstruct {\n \t\tchar *gpg_output;\n \t\tchar good_bad;\n@@ -1044,7 +1052,7 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \n \tif (*ch == '(') {\n \t\tconst char *start = ch + 1;\n-\t\tconst char *end = strchr(start, ')');\n+\t\tconst char *end = start + strcspn(start, \",)\");\n \t\tchar *next;\n \t\tint width;\n \t\tif (!end || end == start)\n@@ -1054,6 +1062,23 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\t\treturn 0;\n \t\tc->padding = to_column ? -width : width;\n \t\tc->flush_type = flush_type;\n+\n+\t\tif (*end == ',') {\n+\t\t\tstart = end + 1;\n+\t\t\tend = strchr(start, ')');\n+\t\t\tif (!end || end == start)\n+\t\t\t\treturn 0;\n+\t\t\tif (!prefixcmp(start, \"trunc)\"))\n+\t\t\t\tc->truncate = trunc_right;\n+\t\t\telse if (!prefixcmp(start, \"ltrunc)\"))\n+\t\t\t\tc->truncate = trunc_left;\n+\t\t\telse if (!prefixcmp(start, \"mtrunc)\"))\n+\t\t\t\tc->truncate = trunc_middle;\n+\t\t\telse\n+\t\t\t\treturn 0;\n+\t\t} else\n+\t\t\tc->truncate = trunc_none;\n+\n \t\treturn end - placeholder + 1;\n \t}\n \treturn 0;\n@@ -1335,9 +1360,29 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\ttotal_consumed++;\n \t}\n \tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n-\tif (len > padding)\n+\tif (len > padding) {\n+\t\tswitch (c->truncate) {\n+\t\tcase trunc_left:\n+\t\t\tstrbuf_utf8_replace(&local_sb,\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\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\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-\telse {\n+\t} else {\n \t\tint sb_len = sb->len, offset = 0;\n \t\tif (c->flush_type == flush_left)\n \t\t\toffset = padding - len;\ndiff --git a/utf8.c b/utf8.c\nindex 9d98043..766df80 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -421,6 +421,52 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \tfree(tmp);\n }\n \n+void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,\n+\t\t\t const char *subst)\n+{\n+\tstruct strbuf sb_dst = STRBUF_INIT;\n+\tchar *src = sb_src->buf;\n+\tchar *end = src + sb_src->len;\n+\tchar *dst;\n+\tint w = 0, subst_len = 0;\n+\n+\tif (subst)\n+\t\tsubst_len = strlen(subst);\n+\tstrbuf_grow(&sb_dst, sb_src->len + subst_len);\n+\tdst = sb_dst.buf;\n+\n+\twhile (src < end) {\n+\t\tchar *old;\n+\t\tsize_t n;\n+\n+\t\twhile ((n = display_mode_esc_sequence_len(src))) {\n+\t\t\tmemcpy(dst, src, n);\n+\t\t\tsrc += n;\n+\t\t\tdst += n;\n+\t\t}\n+\n+\t\told = src;\n+\t\tn = utf8_width((const char**)&src, NULL);\n+\t\tif (!src) \t/* broken utf-8, do nothing */\n+\t\t\treturn;\n+\t\tif (n && w >= pos && w < pos + width) {\n+\t\t\tif (subst) {\n+\t\t\t\tmemcpy(dst, subst, subst_len);\n+\t\t\t\tdst += subst_len;\n+\t\t\t\tsubst = NULL;\n+\t\t\t}\n+\t\t\tw += n;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tmemcpy(dst, old, src - old);\n+\t\tdst += src - old;\n+\t\tw += n;\n+\t}\n+\tstrbuf_setlen(&sb_dst, dst - sb_dst.buf);\n+\tstrbuf_attach(sb_src, strbuf_detach(&sb_dst, NULL),\n+\t\t      sb_dst.len, sb_dst.alloc);\n+}\n+\n int is_encoding_utf8(const char *name)\n {\n \tif (!name)\ndiff --git a/utf8.h b/utf8.h\nindex 99db3e0..faf2f91 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -15,6 +15,8 @@ void strbuf_add_wrapped_text(struct strbuf *buf,\n \t\tconst char *text, int indent, int indent2, int width);\n void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \t\t\t     int indent, int indent2, int width);\n+void strbuf_utf8_replace(struct strbuf *sb, int pos, int width,\n+\t\t\t const char *subst);\n \n #ifndef NO_ICONV\n char *reencode_string_iconv(const char *in, size_t insz,\n-- \n1.8.2.83.gc99314b\n"},{"id":"211436","messageId":"1363400683-14813-13-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 12/12] pretty: support %>> that steal trailing spaces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:24:43Z","receivedAt":"2013-03-16T02:24:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is pretty useful in `%<(100)%s%Cred%>(20)% an' where %s does not\nuse up all 100 columns and %an needs more than 20 columns. By\nreplacing %>(20) with %>>(20), %an can steal spaces from %s.\n\n%>> understands escape sequences, so %Cred does not stop it from\nstealing spaces in %<(100).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  5 ++++-\n pretty.c                         | 34 ++++++++++++++++++++++++++++++++++\n utf8.c                           |  2 +-\n utf8.h                           |  1 +\n 4 files changed, 40 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 17f82f4..036c14a 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -170,7 +170,10 @@ The placeholders are:\n   columns, padding spaces on the right if necessary\n - '%>(<N>)', '%>|(<N>)': similar to '%<(<N<)', '%<|(<N<)'\n   respectively, but padding spaces on the left\n-- '%><(<N>)', '%><|(<N>)': similar to '%<(<N<)', '%<|(<N<)'\n+- '%>>(<N>)', '%>>|(<N>)': similar to '%>(<N<)', '%>|(<N<)'\n+  respectively, except that if the next placeholder takes more spaces\n+  than given and there are spaces on its left, use those spaces\n+- '%><(<N>)', '%><|(<N>)': similar to '% <(<N<)', '%<|(<N<)'\n   respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\ndiff --git a/pretty.c b/pretty.c\nindex 29384b5..892274d 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -764,6 +764,7 @@ enum flush_type {\n \tno_flush,\n \tflush_right,\n \tflush_left,\n+\tflush_left_and_steal,\n \tflush_both\n };\n \n@@ -1037,6 +1038,9 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\tif (*ch == '<') {\n \t\t\tflush_type = flush_both;\n \t\t\tch++;\n+\t\t} else if (*ch == '>') {\n+\t\t\tflush_type = flush_left_and_steal;\n+\t\t\tch++;\n \t\t} else\n \t\t\tflush_type = flush_left;\n \t\tbreak;\n@@ -1360,6 +1364,36 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\ttotal_consumed++;\n \t}\n \tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\n+\tif (c->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+\t\t\tif (*ch == ' ') {\n+\t\t\t\tch--;\n+\t\t\t\tpadding++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\t/* check for trailing ansi sequences */\n+\t\t\tif (*ch != 'm')\n+\t\t\t\tbreak;\n+\t\t\tp = ch - 1;\n+\t\t\twhile (ch - p < 10 && *p != '\\033')\n+\t\t\t\tp--;\n+\t\t\tif (*p != '\\033' ||\n+\t\t\t    ch + 1 - p != display_mode_esc_sequence_len(p))\n+\t\t\t\tbreak;\n+\t\t\t/*\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\tch = p - 1;\n+\t\t}\n+\t\tstrbuf_setlen(sb, ch + 1 - sb->buf);\n+\t\tc->flush_type = flush_left;\n+\t}\n+\n \tif (len > padding) {\n \t\tswitch (c->truncate) {\n \t\tcase trunc_left:\ndiff --git a/utf8.c b/utf8.c\nindex 766df80..414ae49 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,7 +9,7 @@ struct interval {\n   int last;\n };\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n+size_t display_mode_esc_sequence_len(const char *s)\n {\n \tconst char *p = s;\n \tif (*p++ != '\\033')\ndiff --git a/utf8.h b/utf8.h\nindex faf2f91..e913edb 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -3,6 +3,7 @@\n \n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n+size_t display_mode_esc_sequence_len(const char *s);\n int utf8_width(const char **start, size_t *remainder_p);\n int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n-- \n1.8.2.83.gc99314b\n"},{"id":"211446","messageId":"CALeLG_=eWXzi0aUje7J2BCx-_f58Jgn_YWxvRhuVZBk3GvFD-A@mail.gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-12-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 11/12] pretty: support truncating in %>, %< and %><","fromName":"Paul Campbell","fromEmail":"pcampbell@kemitix.net","sentAt":"2013-03-16T09:04:55Z","receivedAt":"2013-03-16T09:04:55Z","isPatch":true,"sender":{"key":"pcampbell@kemitix.net","avatar":"https://gravatar.com/avatar/57584e05501b694929004e43fcd7308f4ad64df2eb0474cd8c7e5f93662bb0f1?d=mp&s=160"},"body":"On Sat, Mar 16, 2013 at 2:24 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> %>(N,trunc) truncates the righ part after N columns and replace the\n> last two letters with \"..\". ltrunc does the same on the left. mtrunc\n> cuts the middle out.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\ns/righ/right/\n\n>  Documentation/pretty-formats.txt |  6 +++--\n>  pretty.c                         | 51 +++++++++++++++++++++++++++++++++++++---\n>  utf8.c                           | 46 ++++++++++++++++++++++++++++++++++++\n>  utf8.h                           |  2 ++\n>  4 files changed, 100 insertions(+), 5 deletions(-)\n>\n> diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\n> index 87ca2c4..17f82f4 100644\n> --- a/Documentation/pretty-formats.txt\n> +++ b/Documentation/pretty-formats.txt\n> @@ -162,8 +162,10 @@ The placeholders are:\n>  - '%x00': print a byte from a hex code\n>  - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n>    linkgit:git-shortlog[1].\n> -- '%<(<N>)': make the next placeholder take at least N columns,\n> -  padding spaces on the right if necessary\n> +- '%<(<N>[,trunc|ltrunc|mtrunc])': make the next placeholder take at\n> +  least N columns, padding spaces on the right if necessary.\n> +  Optionally truncate at the beginning (ltrunc), the middle (mtrunc)\n> +  or the end (trunc) if the output is longer than N columns.\n>  - '%<|(<N>)': make the next placeholder take at least until Nth\n>    columns, padding spaces on the right if necessary\n>  - '%>(<N>)', '%>|(<N>)': similar to '%<(<N<)', '%<|(<N<)'\n> diff --git a/pretty.c b/pretty.c\n> index 233d69c..29384b5 100644\n> --- a/pretty.c\n> +++ b/pretty.c\n> @@ -767,6 +767,13 @@ enum flush_type {\n>         flush_both\n>  };\n>\n> +enum trunc_type {\n> +       trunc_none,\n> +       trunc_left,\n> +       trunc_middle,\n> +       trunc_right\n> +};\n> +\n>  struct format_commit_context {\n>         const struct commit *commit;\n>         const struct pretty_print_context *pretty_ctx;\n> @@ -774,6 +781,7 @@ struct format_commit_context {\n>         unsigned commit_message_parsed:1;\n>         unsigned commit_signature_parsed:1;\n>         enum flush_type flush_type;\n> +       enum trunc_type truncate;\n>         struct {\n>                 char *gpg_output;\n>                 char good_bad;\n> @@ -1044,7 +1052,7 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n>\n>         if (*ch == '(') {\n>                 const char *start = ch + 1;\n> -               const char *end = strchr(start, ')');\n> +               const char *end = start + strcspn(start, \",)\");\n>                 char *next;\n>                 int width;\n>                 if (!end || end == start)\n> @@ -1054,6 +1062,23 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n>                         return 0;\n>                 c->padding = to_column ? -width : width;\n>                 c->flush_type = flush_type;\n> +\n> +               if (*end == ',') {\n> +                       start = end + 1;\n> +                       end = strchr(start, ')');\n> +                       if (!end || end == start)\n> +                               return 0;\n> +                       if (!prefixcmp(start, \"trunc)\"))\n> +                               c->truncate = trunc_right;\n> +                       else if (!prefixcmp(start, \"ltrunc)\"))\n> +                               c->truncate = trunc_left;\n> +                       else if (!prefixcmp(start, \"mtrunc)\"))\n> +                               c->truncate = trunc_middle;\n> +                       else\n> +                               return 0;\n> +               } else\n> +                       c->truncate = trunc_none;\n> +\n>                 return end - placeholder + 1;\n>         }\n>         return 0;\n> @@ -1335,9 +1360,29 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n>                 total_consumed++;\n>         }\n>         len = utf8_strnwidth(local_sb.buf, -1, 1);\n> -       if (len > padding)\n> +       if (len > padding) {\n> +               switch (c->truncate) {\n> +               case trunc_left:\n> +                       strbuf_utf8_replace(&local_sb,\n> +                                           0, len - (padding - 2),\n> +                                           \"..\");\n> +                       break;\n> +               case trunc_middle:\n> +                       strbuf_utf8_replace(&local_sb,\n> +                                           padding / 2 - 1,\n> +                                           len - (padding - 2),\n> +                                           \"..\");\n> +                       break;\n> +               case trunc_right:\n> +                       strbuf_utf8_replace(&local_sb,\n> +                                           padding - 2, len - (padding - 2),\n> +                                           \"..\");\n> +                       break;\n> +               case trunc_none:\n> +                       break;\n> +               }\n>                 strbuf_addstr(sb, local_sb.buf);\n> -       else {\n> +       } else {\n>                 int sb_len = sb->len, offset = 0;\n>                 if (c->flush_type == flush_left)\n>                         offset = padding - len;\n> diff --git a/utf8.c b/utf8.c\n> index 9d98043..766df80 100644\n> --- a/utf8.c\n> +++ b/utf8.c\n> @@ -421,6 +421,52 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n>         free(tmp);\n>  }\n>\n> +void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,\n> +                        const char *subst)\n> +{\n> +       struct strbuf sb_dst = STRBUF_INIT;\n> +       char *src = sb_src->buf;\n> +       char *end = src + sb_src->len;\n> +       char *dst;\n> +       int w = 0, subst_len = 0;\n> +\n> +       if (subst)\n> +               subst_len = strlen(subst);\n> +       strbuf_grow(&sb_dst, sb_src->len + subst_len);\n> +       dst = sb_dst.buf;\n> +\n> +       while (src < end) {\n> +               char *old;\n> +               size_t n;\n> +\n> +               while ((n = display_mode_esc_sequence_len(src))) {\n> +                       memcpy(dst, src, n);\n> +                       src += n;\n> +                       dst += n;\n> +               }\n> +\n> +               old = src;\n> +               n = utf8_width((const char**)&src, NULL);\n> +               if (!src)       /* broken utf-8, do nothing */\n> +                       return;\n> +               if (n && w >= pos && w < pos + width) {\n> +                       if (subst) {\n> +                               memcpy(dst, subst, subst_len);\n> +                               dst += subst_len;\n> +                               subst = NULL;\n> +                       }\n> +                       w += n;\n> +                       continue;\n> +               }\n> +               memcpy(dst, old, src - old);\n> +               dst += src - old;\n> +               w += n;\n> +       }\n> +       strbuf_setlen(&sb_dst, dst - sb_dst.buf);\n> +       strbuf_attach(sb_src, strbuf_detach(&sb_dst, NULL),\n> +                     sb_dst.len, sb_dst.alloc);\n> +}\n> +\n>  int is_encoding_utf8(const char *name)\n>  {\n>         if (!name)\n> diff --git a/utf8.h b/utf8.h\n> index 99db3e0..faf2f91 100644\n> --- a/utf8.h\n> +++ b/utf8.h\n> @@ -15,6 +15,8 @@ void strbuf_add_wrapped_text(struct strbuf *buf,\n>                 const char *text, int indent, int indent2, int width);\n>  void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n>                              int indent, int indent2, int width);\n> +void strbuf_utf8_replace(struct strbuf *sb, int pos, int width,\n> +                        const char *subst);\n>\n>  #ifndef NO_ICONV\n>  char *reencode_string_iconv(const char *in, size_t insz,\n> --\n> 1.8.2.83.gc99314b\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n\n\n\n-- \nPaul [W] Campbell\n"},{"id":"211510","messageId":"CAPig+cQ52J=21Z5JVq2ifZKVAiDSHBYr=_Db7L6k+Lr2tLgHBQ@mail.gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 05/12] pretty: save commit encoding from logmsg_reencode if the caller needs it","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-03-17T08:57:43Z","receivedAt":"2013-03-17T08:57:43Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Mar 15, 2013 at 10:24 PM, Nguyễn Thái Ngọc Duy\n<pclouds@gmail.com> wrote:\n> The commit encoding is parsed by logmsg_reencode, there's no need for\n> the caller to re-parse it again. The reencoded message now have the\n\ns/have/has/\n\n> new encoding, not the original one. The caller would need to read\n> commit object again before parsing.\n"},{"id":"211511","messageId":"CAPig+cTsq82zaEn8779vc3StD_-Ms2jgfGc4nhiB+EBTHA=+2w@mail.gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-10-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 09/12] pretty: add %C(auto) for auto-coloring on the next placeholder","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-03-17T08:59:49Z","receivedAt":"2013-03-17T08:59:49Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Mar 15, 2013 at 10:24 PM, Nguyễn Thái Ngọc Duy\n<pclouds@gmail.com> wrote:\n> This is not simply convenient over $C(auto,xxx). Some placeholders\n\ns/\\$/%/\n\n> (actually only one, %d) do multi coloring and we can't emit a multiple\n> colors with %C(auto,xxx).\n>\n> diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\n> index 66345d1..8734224 100644\n> --- a/Documentation/pretty-formats.txt\n> +++ b/Documentation/pretty-formats.txt\n> @@ -154,7 +154,8 @@ The placeholders are:\n>    adding `auto,` at the beginning will emit color only when colors are\n>    enabled for log output (by `color.diff`, `color.ui`, or `--color`, and\n>    respecting the `auto` settings of the former if we are going to a\n> -  terminal)\n> +  terminal). `auto` alone (i.e. `%C(auto)`) will turn on auto coloring\n\ns/auto coloring/auto-coloring/\n\n> +  on the following placeholder.\n"},{"id":"211512","messageId":"CAPig+cSdoPad=fuhNOp+ChXyh+c8fUD2ewxoaarQQRhJ19SjjA@mail.gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-11-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 10/12] pretty: support padding placeholders, %< %> and %><","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-03-17T09:03:23Z","receivedAt":"2013-03-17T09:03:23Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Mar 15, 2013 at 10:24 PM, Nguyễn Thái Ngọc Duy\n<pclouds@gmail.com> wrote:\n> Either %<, %> or %<> standing before a placeholder specifies how many\n\ns/%<>/%></\n\n> diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\n> index 8734224..87ca2c4 100644\n> --- a/Documentation/pretty-formats.txt\n> +++ b/Documentation/pretty-formats.txt\n> @@ -162,6 +162,14 @@ The placeholders are:\n>  - '%x00': print a byte from a hex code\n>  - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n>    linkgit:git-shortlog[1].\n> +- '%<(<N>)': make the next placeholder take at least N columns,\n> +  padding spaces on the right if necessary\n> +- '%<|(<N>)': make the next placeholder take at least until Nth\n> +  columns, padding spaces on the right if necessary\n> +- '%>(<N>)', '%>|(<N>)': similar to '%<(<N<)', '%<|(<N<)'\n\ns/<N</<N>/g\n\n> +  respectively, but padding spaces on the left\n> +- '%><(<N>)', '%><|(<N>)': similar to '%<(<N<)', '%<|(<N<)'\n\nDitto: s/<N</<N>/g\n\n> +  respectively, but padding both sides (i.e. the text is centered)\n"},{"id":"211513","messageId":"CAPig+cSzHuSnT2+e+U+zJtOUtReUo=3JWZDQcCbMKS4ibZtjvA@mail.gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-13-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 12/12] pretty: support %>> that steal trailing spaces","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-03-17T09:06:31Z","receivedAt":"2013-03-17T09:06:31Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Mar 15, 2013 at 10:24 PM, Nguyễn Thái Ngọc Duy\n<pclouds@gmail.com> wrote:\n> This is pretty useful in `%<(100)%s%Cred%>(20)% an' where %s does not\n\ns/% an/%an/\n\n> use up all 100 columns and %an needs more than 20 columns. By\n> replacing %>(20) with %>>(20), %an can steal spaces from %s.\n>\n> diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\n> index 17f82f4..036c14a 100644\n> --- a/Documentation/pretty-formats.txt\n> +++ b/Documentation/pretty-formats.txt\n> @@ -170,7 +170,10 @@ The placeholders are:\n>    columns, padding spaces on the right if necessary\n>  - '%>(<N>)', '%>|(<N>)': similar to '%<(<N<)', '%<|(<N<)'\n>    respectively, but padding spaces on the left\n> -- '%><(<N>)', '%><|(<N>)': similar to '%<(<N<)', '%<|(<N<)'\n> +- '%>>(<N>)', '%>>|(<N>)': similar to '%>(<N<)', '%>|(<N<)'\n\ns/<N</<N>/g\n\n> +  respectively, except that if the next placeholder takes more spaces\n> +  than given and there are spaces on its left, use those spaces\n> +- '%><(<N>)', '%><|(<N>)': similar to '% <(<N<)', '%<|(<N<)'\n\nDitto: s/<N</<N>/g\n\n>    respectively, but padding both sides (i.e. the text is centered)\n"},{"id":"212681","messageId":"CACsJy8Bd7duZzTmhHM6H_e1tvkGM=socofDps8dMeQbtj5_5-Q@mail.gmail.com","threadId":"33195","inReplyTo":"CAPig+cSzHuSnT2+e+U+zJtOUtReUo=3JWZDQcCbMKS4ibZtjvA@mail.gmail.com","subject":"Re: [PATCH 12/12] pretty: support %>> that steal trailing spaces","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:31:12Z","receivedAt":"2013-03-30T09:31:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Mar 17, 2013 at 4:06 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Fri, Mar 15, 2013 at 10:24 PM, Nguyễn Thái Ngọc Duy\n> <pclouds@gmail.com> wrote:\n>> This is pretty useful in `%<(100)%s%Cred%>(20)% an' where %s does not\n>\n> s/% an/%an/\n\nNo. The space after '%' is intentional. And thanks for proofreading in\nall my patches.\n--\nDuy\n"},{"id":"212682","messageId":"1364636112-15065-1-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1363400683-14813-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 00/12] Layout control placeholders for pretty format","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:00Z","receivedAt":"2013-03-30T09:35:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The only difference in v2 is typo fixes (mostly in commit messages,\nbut some in pretty-formats.txt). Also resend as a reminder to Junio if\nhe forgot to pick it up.\n\nNguyễn Thái Ngọc Duy (12):\n  pretty-formats.txt: wrap long lines\n  pretty: share code between format_decoration and show_decorations\n  utf8.c: move display_mode_esc_sequence_len() for use by other\n    functions\n  utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences\n  pretty: save commit encoding from logmsg_reencode if the caller needs\n    it\n  pretty: get the correct encoding for --pretty:format=%e\n  utf8: keep NULs in reencode_string()\n  pretty: two phase conversion for non utf-8 commits\n  pretty: add %C(auto) for auto-coloring on the next placeholder\n  pretty: support padding placeholders, %< %> and %><\n  pretty: support truncating in %>, %< and %><\n  pretty: support %>> that steal trailing spaces\n\n Documentation/pretty-formats.txt |  34 ++++-\n builtin/blame.c                  |   2 +-\n builtin/commit.c                 |   2 +-\n builtin/fast-export.c            |   3 +-\n builtin/mailinfo.c               |   3 +-\n commit.h                         |   1 +\n compat/precompose_utf8.c         |   2 +-\n log-tree.c                       |  60 +++++----\n log-tree.h                       |   3 +\n notes.c                          |   4 +-\n pretty.c                         | 282 ++++++++++++++++++++++++++++++++++-----\n revision.c                       |   2 +-\n sequencer.c                      |   5 +-\n t/t4207-log-decoration-colors.sh |   8 +-\n t/t6006-rev-list-format.sh       |  12 +-\n utf8.c                           | 104 +++++++++++----\n utf8.h                           |  14 +-\n 17 files changed, 434 insertions(+), 107 deletions(-)\n\n-- \n1.8.2.83.gc99314b\n"},{"id":"212683","messageId":"1364636112-15065-2-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 01/12] pretty-formats.txt: wrap long lines","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:01Z","receivedAt":"2013-03-30T09:35:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt | 18 ++++++++++++------\n 1 file changed, 12 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 105f18a..66345d1 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -106,18 +106,22 @@ The placeholders are:\n - '%P': parent hashes\n - '%p': abbreviated parent hashes\n - '%an': author name\n-- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1]\n+  or linkgit:git-blame[1])\n - '%ae': author email\n-- '%aE': author email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%aE': author email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ad': author date (format respects --date= option)\n - '%aD': author date, RFC2822 style\n - '%ar': author date, relative\n - '%at': author date, UNIX timestamp\n - '%ai': author date, ISO 8601 format\n - '%cn': committer name\n-- '%cN': committer name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%cN': committer name (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ce': committer email\n-- '%cE': committer email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%cE': committer email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%cd': committer date\n - '%cD': committer date, RFC2822 style\n - '%cr': committer date, relative\n@@ -136,9 +140,11 @@ The placeholders are:\n - '%gD': reflog selector, e.g., `refs/stash@{1}`\n - '%gd': shortened reflog selector, e.g., `stash@{1}`\n - '%gn': reflog identity name\n-- '%gN': reflog identity name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%gN': reflog identity name (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ge': reflog identity email\n-- '%gE': reflog identity email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%gE': reflog identity email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%gs': reflog subject\n - '%Cred': switch color to red\n - '%Cgreen': switch color to green\n-- \n1.8.2.83.gc99314b\n"},{"id":"212684","messageId":"1364636112-15065-3-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 02/12] pretty: share code between format_decoration and show_decorations","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:02Z","receivedAt":"2013-03-30T09:35:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This also adds color support to format_decoration()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n log-tree.c                       | 60 +++++++++++++++++++++++++---------------\n log-tree.h                       |  3 ++\n pretty.c                         | 19 +------------\n t/t4207-log-decoration-colors.sh |  8 +++---\n 4 files changed, 45 insertions(+), 45 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex 5dc45c4..7467a1d 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -174,36 +174,50 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n \t}\n }\n \n-void show_decorations(struct rev_info *opt, struct commit *commit)\n+void format_decoration(struct strbuf *sb,\n+\t\t       const struct commit *commit,\n+\t\t       int use_color)\n {\n-\tconst char *prefix;\n-\tstruct name_decoration *decoration;\n+\tconst char *prefix = \" (\";\n+\tstruct name_decoration *d;\n \tconst char *color_commit =\n-\t\tdiff_get_color_opt(&opt->diffopt, DIFF_COMMIT);\n+\t\tdiff_get_color(use_color, DIFF_COMMIT);\n \tconst char *color_reset =\n-\t\tdecorate_get_color_opt(&opt->diffopt, DECORATION_NONE);\n+\t\tdecorate_get_color(use_color, DECORATION_NONE);\n+\n+\tload_ref_decorations(DECORATE_SHORT_REFS);\n+\td = lookup_decoration(&name_decoration, &commit->object);\n+\tif (!d)\n+\t\treturn;\n+\twhile (d) {\n+\t\tstrbuf_addstr(sb, color_commit);\n+\t\tstrbuf_addstr(sb, prefix);\n+\t\tstrbuf_addstr(sb, decorate_get_color(use_color, d->type));\n+\t\tif (d->type == DECORATION_REF_TAG)\n+\t\t\tstrbuf_addstr(sb, \"tag: \");\n+\t\tstrbuf_addstr(sb, d->name);\n+\t\tstrbuf_addstr(sb, color_reset);\n+\t\tprefix = \", \";\n+\t\td = d->next;\n+\t}\n+\tif (prefix[0] == ',') {\n+\t\tstrbuf_addstr(sb, color_commit);\n+\t\tstrbuf_addch(sb, ')');\n+\t\tstrbuf_addstr(sb, color_reset);\n+\t}\n+}\n+\n+void show_decorations(struct rev_info *opt, struct commit *commit)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n \n \tif (opt->show_source && commit->util)\n \t\tprintf(\"\\t%s\", (char *) commit->util);\n \tif (!opt->show_decorations)\n \t\treturn;\n-\tdecoration = lookup_decoration(&name_decoration, &commit->object);\n-\tif (!decoration)\n-\t\treturn;\n-\tprefix = \" (\";\n-\twhile (decoration) {\n-\t\tprintf(\"%s\", prefix);\n-\t\tfputs(decorate_get_color_opt(&opt->diffopt, decoration->type),\n-\t\t      stdout);\n-\t\tif (decoration->type == DECORATION_REF_TAG)\n-\t\t\tfputs(\"tag: \", stdout);\n-\t\tprintf(\"%s\", decoration->name);\n-\t\tfputs(color_reset, stdout);\n-\t\tfputs(color_commit, stdout);\n-\t\tprefix = \", \";\n-\t\tdecoration = decoration->next;\n-\t}\n-\tputchar(')');\n+\tformat_decoration(&sb, commit, opt->diffopt.use_color);\n+\tfputs(sb.buf, stdout);\n+\tstrbuf_release(&sb);\n }\n \n /*\n@@ -625,8 +639,8 @@ void show_log(struct rev_info *opt)\n \t\t\tprintf(\" (from %s)\",\n \t\t\t       find_unique_abbrev(parent->object.sha1,\n \t\t\t\t\t\t  abbrev_commit));\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n \t\tshow_decorations(opt, commit);\n-\t\tprintf(\"%s\", diff_get_color_opt(&opt->diffopt, DIFF_RESET));\n \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n \t\t\tputchar(' ');\n \t\t} else {\ndiff --git a/log-tree.h b/log-tree.h\nindex 9140f48..e6a2da5 100644\n--- a/log-tree.h\n+++ b/log-tree.h\n@@ -13,6 +13,9 @@ int log_tree_diff_flush(struct rev_info *);\n int log_tree_commit(struct rev_info *, struct commit *);\n int log_tree_opt_parse(struct rev_info *, const char **, int);\n void show_log(struct rev_info *opt);\n+void format_decoration(struct strbuf *sb,\n+\t\t       const struct commit *commit,\n+\t\t       int use_color);\n void show_decorations(struct rev_info *opt, struct commit *commit);\n void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\t\t     const char **subject_p,\ndiff --git a/pretty.c b/pretty.c\nindex eae57ad..79784be 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -898,23 +898,6 @@ static void parse_commit_message(struct format_commit_context *c)\n \tc->commit_message_parsed = 1;\n }\n \n-static void format_decoration(struct strbuf *sb, const struct commit *commit)\n-{\n-\tstruct name_decoration *d;\n-\tconst char *prefix = \" (\";\n-\n-\tload_ref_decorations(DECORATE_SHORT_REFS);\n-\td = lookup_decoration(&name_decoration, &commit->object);\n-\twhile (d) {\n-\t\tstrbuf_addstr(sb, prefix);\n-\t\tprefix = \", \";\n-\t\tstrbuf_addstr(sb, d->name);\n-\t\td = d->next;\n-\t}\n-\tif (prefix[0] == ',')\n-\t\tstrbuf_addch(sb, ')');\n-}\n-\n static void strbuf_wrap(struct strbuf *sb, size_t pos,\n \t\t\tsize_t width, size_t indent1, size_t indent2)\n {\n@@ -1146,7 +1129,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\tstrbuf_addstr(sb, get_revision_mark(NULL, commit));\n \t\treturn 1;\n \tcase 'd':\n-\t\tformat_decoration(sb, commit);\n+\t\tformat_decoration(sb, commit, 0);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex bbde31b..925f577 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -44,15 +44,15 @@ test_expect_success setup '\n '\n \n cat >expected <<EOF\n-${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_HEAD}HEAD${c_reset}${c_commit},\\\n  ${c_tag}tag: v1.0${c_reset}${c_commit},\\\n  ${c_tag}tag: B${c_reset}${c_commit},\\\n  ${c_branch}master${c_reset}${c_commit})${c_reset} B\n-${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A1${c_reset}${c_commit},\\\n  ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1\n-${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n  On master: Changes to A.t\n-${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n EOF\n \n # We want log to show all, but the second parent to refs/stash is irrelevant\n-- \n1.8.2.83.gc99314b\n"},{"id":"212685","messageId":"1364636112-15065-4-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 03/12] utf8.c: move display_mode_esc_sequence_len() for use by other functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:03Z","receivedAt":"2013-03-30T09:35:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex 1087870..82c2ddf 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,6 +9,20 @@ struct interval {\n   int last;\n };\n \n+static size_t display_mode_esc_sequence_len(const char *s)\n+{\n+\tconst char *p = s;\n+\tif (*p++ != '\\033')\n+\t\treturn 0;\n+\tif (*p++ != '[')\n+\t\treturn 0;\n+\twhile (isdigit(*p) || *p == ';')\n+\t\tp++;\n+\tif (*p++ != 'm')\n+\t\treturn 0;\n+\treturn p - s;\n+}\n+\n /* auxiliary function for binary search in interval table */\n static int bisearch(ucs_char_t ucs, const struct interval *table, int max)\n {\n@@ -303,20 +317,6 @@ static void strbuf_add_indented_text(struct strbuf *buf, const char *text,\n \t}\n }\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n-{\n-\tconst char *p = s;\n-\tif (*p++ != '\\033')\n-\t\treturn 0;\n-\tif (*p++ != '[')\n-\t\treturn 0;\n-\twhile (isdigit(*p) || *p == ';')\n-\t\tp++;\n-\tif (*p++ != 'm')\n-\t\treturn 0;\n-\treturn p - s;\n-}\n-\n /*\n  * Wrap the text, if necessary. The variable indent is the indent for the\n  * first line, indent2 is the indent for all other lines.\n-- \n1.8.2.83.gc99314b\n"},{"id":"212686","messageId":"1364636112-15065-5-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 04/12] utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:04Z","receivedAt":"2013-03-30T09:35:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 20 ++++++++++++++------\n utf8.h |  1 +\n 2 files changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex 82c2ddf..38322a1 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -266,18 +266,26 @@ int utf8_width(const char **start, size_t *remainder_p)\n  * string, assuming that the string is utf8.  Returns strlen() instead\n  * if the string does not look like a valid utf8 string.\n  */\n-int utf8_strwidth(const char *string)\n+int utf8_strnwidth(const char *string, int len, int skip_ansi)\n {\n \tint width = 0;\n \tconst char *orig = string;\n \n-\twhile (1) {\n-\t\tif (!string)\n-\t\t\treturn strlen(orig);\n-\t\tif (!*string)\n-\t\t\treturn width;\n+\tif (len == -1)\n+\t\tlen = strlen(string);\n+\twhile (string && string < orig + len) {\n+\t\tint skip;\n+\t\twhile (skip_ansi &&\n+\t\t       (skip = display_mode_esc_sequence_len(string)))\n+\t\t\tstring += skip;\n \t\twidth += utf8_width(&string, NULL);\n \t}\n+\treturn string ? width : len;\n+}\n+\n+int utf8_strwidth(const char *string)\n+{\n+\treturn utf8_strnwidth(string, -1, 0);\n }\n \n int is_utf8(const char *text)\ndiff --git a/utf8.h b/utf8.h\nindex 501b2bd..a556932 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -4,6 +4,7 @@\n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n int utf8_width(const char **start, size_t *remainder_p);\n+int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n int is_utf8(const char *text);\n int is_encoding_utf8(const char *name);\n-- \n1.8.2.83.gc99314b\n"},{"id":"212687","messageId":"1364636112-15065-6-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 05/12] pretty: save commit encoding from logmsg_reencode if the caller needs it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:05Z","receivedAt":"2013-03-30T09:35:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The commit encoding is parsed by logmsg_reencode, there's no need for\nthe caller to re-parse it again. The reencoded message now has the new\nencoding, not the original one. The caller would need to read commit\nobject again before parsing.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/blame.c  |  2 +-\n builtin/commit.c |  2 +-\n commit.h         |  1 +\n pretty.c         | 16 ++++++++++++----\n revision.c       |  2 +-\n 5 files changed, 16 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 86100e9..104a948 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1425,7 +1425,7 @@ static void get_commit_info(struct commit *commit,\n \tcommit_info_init(ret);\n \n \tencoding = get_log_output_encoding();\n-\tmessage = logmsg_reencode(commit, encoding);\n+\tmessage = logmsg_reencode(commit, NULL, encoding);\n \tget_ac_line(message, \"\\nauthor \",\n \t\t    &ret->author, &ret->author_mail,\n \t\t    &ret->author_time, &ret->author_tz);\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 3348aa1..beead44 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -953,7 +953,7 @@ static const char *read_commit_message(const char *name)\n \tif (!commit)\n \t\tdie(_(\"could not lookup commit %s\"), name);\n \tout_enc = get_commit_output_encoding();\n-\treturn logmsg_reencode(commit, out_enc);\n+\treturn logmsg_reencode(commit, NULL, out_enc);\n }\n \n static int parse_and_validate_options(int argc, const char *argv[],\ndiff --git a/commit.h b/commit.h\nindex 4138bb4..085349a 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -100,6 +100,7 @@ struct userformat_want {\n extern int has_non_ascii(const char *text);\n struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */\n extern char *logmsg_reencode(const struct commit *commit,\n+\t\t\t     char **commit_encoding,\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 *);\ndiff --git a/pretty.c b/pretty.c\nindex 79784be..ab5d70f 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -584,6 +584,7 @@ static char *replace_encoding_header(char *buf, const char *encoding)\n }\n \n char *logmsg_reencode(const struct commit *commit,\n+\t\t      char **commit_encoding,\n \t\t      const char *output_encoding)\n {\n \tstatic const char *utf8 = \"UTF-8\";\n@@ -605,9 +606,15 @@ char *logmsg_reencode(const struct commit *commit,\n \t\t\t    sha1_to_hex(commit->object.sha1), typename(type));\n \t}\n \n-\tif (!output_encoding || !*output_encoding)\n+\tif (!output_encoding || !*output_encoding) {\n+\t\tif (commit_encoding)\n+\t\t\t*commit_encoding =\n+\t\t\t\tget_header(commit, msg, \"encoding\");\n \t\treturn msg;\n+\t}\n \tencoding = get_header(commit, msg, \"encoding\");\n+\tif (commit_encoding)\n+\t\t*commit_encoding = encoding;\n \tuse_encoding = encoding ? encoding : utf8;\n \tif (same_encoding(use_encoding, output_encoding)) {\n \t\t/*\n@@ -648,7 +655,8 @@ char *logmsg_reencode(const struct commit *commit,\n \tif (out)\n \t\tout = replace_encoding_header(out, output_encoding);\n \n-\tfree(encoding);\n+\tif (!commit_encoding)\n+\t\tfree(encoding);\n \t/*\n \t * If the re-encoding failed, out might be NULL here; in that\n \t * case we just return the commit message verbatim.\n@@ -1313,7 +1321,7 @@ 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, output_enc);\n+\tcontext.message = logmsg_reencode(commit, NULL, output_enc);\n \n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n@@ -1476,7 +1484,7 @@ void pretty_print_commit(const struct pretty_print_context *pp,\n \t}\n \n \tencoding = get_log_output_encoding();\n-\tmsg = reencoded = logmsg_reencode(commit, encoding);\n+\tmsg = reencoded = logmsg_reencode(commit, NULL, encoding);\n \n \tif (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)\n \t\tindent = 0;\ndiff --git a/revision.c b/revision.c\nindex ef60205..c6ff560 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2290,7 +2290,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)\n \t * in it.\n \t */\n \tencoding = get_log_output_encoding();\n-\tmessage = logmsg_reencode(commit, encoding);\n+\tmessage = logmsg_reencode(commit, NULL, encoding);\n \n \t/* Copy the commit to temporary if we are using \"fake\" headers */\n \tif (buf.len)\n-- \n1.8.2.83.gc99314b\n"},{"id":"212688","messageId":"1364636112-15065-7-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 06/12] pretty: get the correct encoding for --pretty:format=%e","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:06Z","receivedAt":"2013-03-30T09:35:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"parse_commit_header() provides the commit encoding for '%e' and it\nreads it from the re-encoded message, which contains the new encoding,\nnot the original one in the commit object.\n\nGet the commit encoding from logmsg_reencode() instead.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c | 13 +++++++------\n 1 file changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex ab5d70f..e2241e5 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -771,12 +771,12 @@ struct format_commit_context {\n \t\tchar *signer;\n \t} signature;\n \tchar *message;\n+\tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n \tstruct chunk committer;\n-\tstruct chunk encoding;\n \tsize_t message_off;\n \tsize_t subject_off;\n \tsize_t body_off;\n@@ -823,9 +823,6 @@ static void parse_commit_header(struct format_commit_context *context)\n \t\t} else if (!prefixcmp(msg + i, \"committer \")) {\n \t\t\tcontext->committer.off = i + 10;\n \t\t\tcontext->committer.len = eol - i - 10;\n-\t\t} else if (!prefixcmp(msg + i, \"encoding \")) {\n-\t\t\tcontext->encoding.off = i + 9;\n-\t\t\tcontext->encoding.len = eol - i - 9;\n \t\t}\n \t\ti = eol;\n \t}\n@@ -1210,7 +1207,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t\t\t   msg + c->committer.off, c->committer.len,\n \t\t\t\t   c->pretty_ctx->date_mode);\n \tcase 'e':\t/* encoding */\n-\t\tstrbuf_add(sb, msg + c->encoding.off, c->encoding.len);\n+\t\tif (c->commit_encoding)\n+\t\t\tstrbuf_addstr(sb, c->commit_encoding);\n \t\treturn 1;\n \tcase 'B':\t/* raw body */\n \t\t/* message_off is always left at the initial newline */\n@@ -1321,11 +1319,14 @@ 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, NULL, output_enc);\n+\tcontext.message = logmsg_reencode(commit,\n+\t\t\t\t\t  &context.commit_encoding,\n+\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 \n+\tfree(context.commit_encoding);\n \tlogmsg_free(context.message, commit);\n \tfree(context.signature.gpg_output);\n \tfree(context.signature.signer);\n-- \n1.8.2.83.gc99314b\n"},{"id":"212690","messageId":"1364636112-15065-8-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 07/12] utf8: keep NULs in reencode_string()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:07Z","receivedAt":"2013-03-30T09:35:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"reencode_string() will be used in the next patch for re-encoding\npretty output, which can contain NULs.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fast-export.c    |  3 ++-\n builtin/mailinfo.c       |  3 ++-\n compat/precompose_utf8.c |  2 +-\n notes.c                  |  4 +++-\n pretty.c                 |  3 ++-\n sequencer.c              |  5 +++--\n utf8.c                   | 10 +++++++---\n utf8.h                   | 10 +++++++---\n 8 files changed, 27 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 77dffd1..7ba9f3b 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -316,7 +316,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)\n \n \tmark_next_object(&commit->object);\n \tif (!is_encoding_utf8(encoding))\n-\t\treencoded = reencode_string(message, \"UTF-8\", encoding);\n+\t\treencoded = reencode_string(message, strlen(message),\n+\t\t\t\t\t    \"UTF-8\", encoding, NULL);\n \tif (!commit->parents)\n \t\tprintf(\"reset %s\\n\", (const char*)commit->util);\n \tprintf(\"commit %s\\nmark :%\"PRIu32\"\\n%.*s\\n%.*s\\ndata %u\\n%s\",\ndiff --git a/builtin/mailinfo.c b/builtin/mailinfo.c\nindex 24a772d..129e7dc 100644\n--- a/builtin/mailinfo.c\n+++ b/builtin/mailinfo.c\n@@ -486,7 +486,8 @@ static void convert_to_utf8(struct strbuf *line, const char *charset)\n \n \tif (same_encoding(metainfo_charset, charset))\n \t\treturn;\n-\tout = reencode_string(line->buf, metainfo_charset, charset);\n+\tout = reencode_string(line->buf, line->len,\n+\t\t\t      metainfo_charset, charset, NULL);\n \tif (!out)\n \t\tdie(\"cannot convert from %s to %s\",\n \t\t    charset, metainfo_charset);\ndiff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\nindex 8cf5955..d9203d0 100644\n--- a/compat/precompose_utf8.c\n+++ b/compat/precompose_utf8.c\n@@ -78,7 +78,7 @@ void precompose_argv(int argc, const char **argv)\n \t\tsize_t namelen;\n \t\toldarg = argv[i];\n \t\tif (has_non_ascii(oldarg, (size_t)-1, &namelen)) {\n-\t\t\tnewarg = reencode_string_iconv(oldarg, namelen, ic_precompose);\n+\t\t\tnewarg = reencode_string_iconv(oldarg, namelen, ic_precompose, NULL);\n \t\t\tif (newarg)\n \t\t\t\targv[i] = newarg;\n \t\t}\ndiff --git a/notes.c b/notes.c\nindex f63fd57..4ae3b25 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1222,7 +1222,9 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1,\n \n \tif (output_encoding && *output_encoding &&\n \t    !is_encoding_utf8(output_encoding)) {\n-\t\tchar *reencoded = reencode_string(msg, output_encoding, utf8);\n+\t\tchar *reencoded = reencode_string(msg, strlen(msg),\n+\t\t\t\t\t\t  output_encoding, utf8,\n+\t\t\t\t\t\t  NULL);\n \t\tif (reencoded) {\n \t\t\tfree(msg);\n \t\t\tmsg = reencoded;\ndiff --git a/pretty.c b/pretty.c\nindex e2241e5..092dd1d 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -643,7 +643,8 @@ char *logmsg_reencode(const struct commit *commit,\n \t\t * this point, we are done with msg. If we allocated a fresh\n \t\t * copy, we can free it.\n \t\t */\n-\t\tout = reencode_string(msg, output_encoding, use_encoding);\n+\t\tout = reencode_string(msg, strlen(msg),\n+\t\t\t\t      output_encoding, use_encoding, NULL);\n \t\tif (out && msg != commit->buffer)\n \t\t\tfree(msg);\n \t}\ndiff --git a/sequencer.c b/sequencer.c\nindex aef5e8a..bf15531 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -61,8 +61,9 @@ static int get_message(struct commit *commit, struct commit_message *out)\n \tout->reencoded_message = NULL;\n \tout->message = commit->buffer;\n \tif (same_encoding(encoding, git_commit_encoding))\n-\t\tout->reencoded_message = reencode_string(commit->buffer,\n-\t\t\t\t\tgit_commit_encoding, encoding);\n+\t\tout->reencoded_message =\n+\t\t\treencode_string(commit->buffer, strlen(commit->buffer),\n+\t\t\t\t\tgit_commit_encoding, encoding, NULL);\n \tif (out->reencoded_message)\n \t\tout->message = out->reencoded_message;\n \ndiff --git a/utf8.c b/utf8.c\nindex 38322a1..9d98043 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -468,7 +468,7 @@ int utf8_fprintf(FILE *stream, const char *format, ...)\n #else\n \ttypedef char * iconv_ibp;\n #endif\n-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)\n+char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, int *outsz_p)\n {\n \tsize_t outsz, outalloc;\n \tchar *out, *outpos;\n@@ -502,13 +502,17 @@ char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)\n \t\t}\n \t\telse {\n \t\t\t*outpos = '\\0';\n+\t\t\tif (outsz_p)\n+\t\t\t\t*outsz_p = outpos - out;\n \t\t\tbreak;\n \t\t}\n \t}\n \treturn out;\n }\n \n-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding)\n+char *reencode_string(const char *in, int insz,\n+\t\t      const char *out_encoding, const char *in_encoding,\n+\t\t      int *outsz)\n {\n \ticonv_t conv;\n \tchar *out;\n@@ -518,7 +522,7 @@ char *reencode_string(const char *in, const char *out_encoding, const char *in_e\n \tconv = iconv_open(out_encoding, in_encoding);\n \tif (conv == (iconv_t) -1)\n \t\treturn NULL;\n-\tout = reencode_string_iconv(in, strlen(in), conv);\n+\tout = reencode_string_iconv(in, insz, conv, outsz);\n \ticonv_close(conv);\n \treturn out;\n }\ndiff --git a/utf8.h b/utf8.h\nindex a556932..99db3e0 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -17,10 +17,14 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \t\t\t     int indent, int indent2, int width);\n \n #ifndef NO_ICONV\n-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv);\n-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);\n+char *reencode_string_iconv(const char *in, size_t insz,\n+\t\t\t    iconv_t conv, int *outsz);\n+char *reencode_string(const char *in, int insz,\n+\t\t      const char *out_encoding,\n+\t\t      const char *in_encoding,\n+\t\t      int *outsz);\n #else\n-#define reencode_string(a,b,c) NULL\n+#define reencode_string(a,b,c,d) NULL\n #endif\n \n #endif\n-- \n1.8.2.83.gc99314b\n"},{"id":"212689","messageId":"1364636112-15065-9-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 08/12] pretty: two phase conversion for non utf-8 commits","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:08Z","receivedAt":"2013-03-30T09:35:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Always assume format_commit_item() takes an utf-8 string for string\nhandling simplicity (we can handle utf-8 strings, but can't with other\nencodings).\n\nIf commit message is in non-utf8, or output encoding is not, then the\ncommit is first converted to utf-8, processed, then output converted\nto output encoding. This of course only works with encodings that are\ncompatible with Unicode.\n\nThis also fixes the iso8859-1 test in t6006. It's supposed to create\nan iso8859-1 commit, but the commit content in t6006 is in UTF-8.\nt6006 is now converted back in UTF-8 (the downside is we can't put\nutf-8 strings there anymore).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c                   | 24 ++++++++++++++++++++++--\n t/t6006-rev-list-format.sh | 12 ++++++------\n 2 files changed, 28 insertions(+), 8 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 092dd1d..3f4809a 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -1003,7 +1003,8 @@ static int format_reflog_person(struct strbuf *sb,\n \treturn format_person_part(sb, part, ident, strlen(ident), dmode);\n }\n \n-static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\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 {\n \tstruct format_commit_context *c = context;\n@@ -1235,7 +1236,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \treturn 0;\t/* unknown placeholder */\n }\n \n-static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n+static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n+\t\t\t\t const char *placeholder,\n \t\t\t\t void *context)\n {\n \tint consumed;\n@@ -1315,6 +1317,7 @@ void format_commit_message(const struct commit *commit,\n {\n \tstruct format_commit_context context;\n \tconst char *output_enc = pretty_ctx->output_encoding;\n+\tconst char *utf8 = \"UTF-8\";\n \n \tmemset(&context, 0, sizeof(context));\n \tcontext.commit = commit;\n@@ -1327,6 +1330,23 @@ void format_commit_message(const struct commit *commit,\n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n \n+\tif (output_enc) {\n+\t\tif (same_encoding(utf8, output_enc))\n+\t\t\toutput_enc = NULL;\n+\t} else {\n+\t\tif (context.commit_encoding &&\n+\t\t    !same_encoding(context.commit_encoding, utf8))\n+\t\t\toutput_enc = context.commit_encoding;\n+\t}\n+\n+\tif (output_enc) {\n+\t\tint outsz;\n+\t\tchar *out = reencode_string(sb->buf, sb->len,\n+\t\t\t\t\t    output_enc, utf8, &outsz);\n+\t\tif (out)\n+\t\t\tstrbuf_attach(sb, out, outsz, outsz + 1);\n+\t}\n+\n \tfree(context.commit_encoding);\n \tlogmsg_free(context.message, commit);\n \tfree(context.signature.gpg_output);\ndiff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh\nindex 3fc3b74..0393c9f 100755\n--- a/t/t6006-rev-list-format.sh\n+++ b/t/t6006-rev-list-format.sh\n@@ -184,7 +184,7 @@ Test printing of complex bodies\n \n This commit message is much longer than the others,\n and it will be encoded in iso8859-1. We should therefore\n-include an iso8859 character: ¡bueno!\n+include an iso8859 character: �bueno!\n EOF\n test_expect_success 'setup complex body' '\n git config i18n.commitencoding iso8859-1 &&\n@@ -192,14 +192,14 @@ git config i18n.commitencoding iso8859-1 &&\n '\n \n test_format complex-encoding %e <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n iso8859-1\n commit 131a310eb913d107dd3c09a65d1651175898735d\n commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n EOF\n \n test_format complex-subject %s <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n Test printing of complex bodies\n commit 131a310eb913d107dd3c09a65d1651175898735d\n changed foo\n@@ -208,17 +208,17 @@ added foo\n EOF\n \n test_format complex-body %b <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n This commit message is much longer than the others,\n and it will be encoded in iso8859-1. We should therefore\n-include an iso8859 character: ¡bueno!\n+include an iso8859 character: �bueno!\n \n commit 131a310eb913d107dd3c09a65d1651175898735d\n commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n EOF\n \n test_expect_success '%x00 shows NUL' '\n-\techo  >expect commit f58db70b055c5718631e5c61528b28b12090cdea &&\n+\techo  >expect commit 1ed88da4a5b5ed8c449114ac131efc62178734c3 &&\n \techo >>expect fooQbar &&\n \tgit rev-list -1 --format=foo%x00bar HEAD >actual.nul &&\n \tnul_to_q <actual.nul >actual &&\n-- \n1.8.2.83.gc99314b\n"},{"id":"212691","messageId":"1364636112-15065-10-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 09/12] pretty: add %C(auto) for auto-coloring on the next placeholder","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:09Z","receivedAt":"2013-03-30T09:35:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is not simply convenient over %C(auto,xxx). Some placeholders\n(actually only one, %d) do multi coloring and we can't emit a multiple\ncolors with %C(auto,xxx).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  3 ++-\n pretty.c                         | 15 +++++++++++++--\n 2 files changed, 15 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 66345d1..8734224 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -154,7 +154,8 @@ The placeholders are:\n   adding `auto,` at the beginning will emit color only when colors are\n   enabled for log output (by `color.diff`, `color.ui`, or `--color`, and\n   respecting the `auto` settings of the former if we are going to a\n-  terminal)\n+  terminal). `auto` alone (i.e. `%C(auto)`) will turn on auto coloring\n+  on the following placeholder.\n - '%m': left, right or boundary mark\n - '%n': newline\n - '%%': a raw '%'\ndiff --git a/pretty.c b/pretty.c\nindex 3f4809a..c333fd6 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -774,6 +774,7 @@ struct format_commit_context {\n \tchar *message;\n \tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n+\tint auto_color;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -1011,7 +1012,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-\tint h1, h2;\n+\tint h1, h2, use_color;\n \n \t/* these are independent of the commit */\n \tswitch (placeholder[0]) {\n@@ -1023,6 +1024,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \n \t\t\tif (!end)\n \t\t\t\treturn 0;\n+\t\t\tif (!prefixcmp(begin, \"auto)\")) {\n+\t\t\t\tc->auto_color = 1;\n+\t\t\t\treturn end - placeholder + 1;\n+\t\t\t}\n \t\t\tif (!prefixcmp(begin, \"auto,\")) {\n \t\t\t\tif (!want_color(c->pretty_ctx->color))\n \t\t\t\t\treturn end - placeholder + 1;\n@@ -1090,16 +1095,22 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t/* these depend on the commit */\n \tif (!commit->object.parsed)\n \t\tparse_object(commit->object.sha1);\n+\tuse_color = c->auto_color;\n+\tc->auto_color = 0;\n \n \tswitch (placeholder[0]) {\n \tcase 'H':\t\t/* commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(use_color, DIFF_COMMIT));\n \t\tstrbuf_addstr(sb, sha1_to_hex(commit->object.sha1));\n+\t\tstrbuf_addstr(sb, diff_get_color(use_color, DIFF_RESET));\n \t\treturn 1;\n \tcase 'h':\t\t/* abbreviated commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(use_color, DIFF_COMMIT));\n \t\tif (add_again(sb, &c->abbrev_commit_hash))\n \t\t\treturn 1;\n \t\tstrbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,\n \t\t\t\t\t\t     c->pretty_ctx->abbrev));\n+\t\tstrbuf_addstr(sb, diff_get_color(use_color, DIFF_RESET));\n \t\tc->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;\n \t\treturn 1;\n \tcase 'T':\t\t/* tree hash */\n@@ -1136,7 +1147,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\tstrbuf_addstr(sb, get_revision_mark(NULL, commit));\n \t\treturn 1;\n \tcase 'd':\n-\t\tformat_decoration(sb, commit, 0);\n+\t\tformat_decoration(sb, commit, use_color);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\n-- \n1.8.2.83.gc99314b\n"},{"id":"212692","messageId":"1364636112-15065-11-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 10/12] pretty: support padding placeholders, %< %> and %><","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:10Z","receivedAt":"2013-03-30T09:35:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Either %<, %> or %>< standing before a placeholder specifies how many\ncolumns (at least as the placeholder can exceed it) it takes. Each\ndiffers on how spaces are padded:\n\n  %< pads on the right (aka left alignment)\n  %> pads on the left (aka right alignment)\n  %>< pads both ways equally (aka centered)\n\nThe (<N>) follows them, e.g. `%<(100)', to specify the number of\ncolumns the next placeholder takes.\n\nHowever, if '|' stands before (<N>), e.g. `%>|(100)', then the number\nof columns is calculated so that it reaches the Nth column on screen.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |   8 +++\n pretty.c                         | 117 ++++++++++++++++++++++++++++++++++++++-\n 2 files changed, 124 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 8734224..0ab1233 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -162,6 +162,14 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n+- '%<(<N>)': make the next placeholder take at least N columns,\n+  padding spaces on the right if necessary\n+- '%<|(<N>)': make the next placeholder take at least until Nth\n+  columns, padding spaces on the right if necessary\n+- '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n+  respectively, but padding spaces on the left\n+- '%><(<N>)', '%><|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n+  respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\n revision traversal engine. For example, the `%g*` reflog options will\ndiff --git a/pretty.c b/pretty.c\nindex c333fd6..233d69c 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -760,12 +760,20 @@ struct chunk {\n \tsize_t len;\n };\n \n+enum flush_type {\n+\tno_flush,\n+\tflush_right,\n+\tflush_left,\n+\tflush_both\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 \tunsigned commit_signature_parsed:1;\n+\tenum flush_type flush_type;\n \tstruct {\n \t\tchar *gpg_output;\n \t\tchar good_bad;\n@@ -775,6 +783,7 @@ struct format_commit_context {\n \tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n \tint auto_color;\n+\tint padding;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -1004,6 +1013,52 @@ static int format_reflog_person(struct strbuf *sb,\n \treturn format_person_part(sb, part, ident, strlen(ident), dmode);\n }\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+{\n+\tconst char *ch = placeholder;\n+\tenum flush_type flush_type;\n+\tint to_column = 0;\n+\n+\tswitch (*ch++) {\n+\tcase '<':\n+\t\tflush_type = flush_right;\n+\t\tbreak;\n+\tcase '>':\n+\t\tif (*ch == '<') {\n+\t\t\tflush_type = flush_both;\n+\t\t\tch++;\n+\t\t} else\n+\t\t\tflush_type = flush_left;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\t/* the next value means \"wide enough to that column\" */\n+\tif (*ch == '|') {\n+\t\tto_column = 1;\n+\t\tch++;\n+\t}\n+\n+\tif (*ch == '(') {\n+\t\tconst char *start = ch + 1;\n+\t\tconst char *end = strchr(start, ')');\n+\t\tchar *next;\n+\t\tint width;\n+\t\tif (!end || end == start)\n+\t\t\treturn 0;\n+\t\twidth = strtoul(start, &next, 10);\n+\t\tif (next == start || width == 0)\n+\t\t\treturn 0;\n+\t\tc->padding = to_column ? -width : width;\n+\t\tc->flush_type = flush_type;\n+\t\treturn end - placeholder + 1;\n+\t}\n+\treturn 0;\n+}\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@@ -1090,6 +1145,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\t\treturn end - placeholder + 1;\n \t\t} else\n \t\t\treturn 0;\n+\n+\tcase '<':\n+\tcase '>':\n+\t\treturn parse_padding_placeholder(sb, placeholder, c);\n \t}\n \n \t/* these depend on the commit */\n@@ -1247,6 +1306,59 @@ 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+{\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+\t\tif (!start)\n+\t\t\tstart = sb->buf;\n+\t\toccupied = utf8_strnwidth(start, -1, 1);\n+\t\tpadding = (-padding) - occupied;\n+\t}\n+\twhile (1) {\n+\t\tint modifier = *placeholder == 'C';\n+\t\tint consumed = format_commit_one(&local_sb, placeholder, c);\n+\t\ttotal_consumed += consumed;\n+\n+\t\tif (!modifier)\n+\t\t\tbreak;\n+\n+\t\tplaceholder += consumed;\n+\t\tif (*placeholder != '%')\n+\t\t\tbreak;\n+\t\tplaceholder++;\n+\t\ttotal_consumed++;\n+\t}\n+\tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\tif (len > padding)\n+\t\tstrbuf_addstr(sb, local_sb.buf);\n+\telse {\n+\t\tint sb_len = sb->len, offset = 0;\n+\t\tif (c->flush_type == flush_left)\n+\t\t\toffset = padding - len;\n+\t\telse if (c->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\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}\n+\tstrbuf_release(&local_sb);\n+\tc->flush_type = no_flush;\n+\treturn total_consumed;\n+}\n+\n static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\t\t\t const char *placeholder,\n \t\t\t\t void *context)\n@@ -1277,7 +1389,10 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\tplaceholder++;\n \n \torig_len = sb->len;\n-\tconsumed = format_commit_one(sb, placeholder, context);\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 \tif (magic == NO_MAGIC)\n \t\treturn consumed;\n \n-- \n1.8.2.83.gc99314b\n"},{"id":"212693","messageId":"1364636112-15065-12-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 11/12] pretty: support truncating in %>, %< and %><","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:11Z","receivedAt":"2013-03-30T09:35:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"%>(N,trunc) truncates the right part after N columns and replace the\nlast two letters with \"..\". ltrunc does the same on the left. mtrunc\ncuts the middle out.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  6 +++--\n pretty.c                         | 51 +++++++++++++++++++++++++++++++++++++---\n utf8.c                           | 46 ++++++++++++++++++++++++++++++++++++\n utf8.h                           |  2 ++\n 4 files changed, 100 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 0ab1233..f2df941 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -162,8 +162,10 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n-- '%<(<N>)': make the next placeholder take at least N columns,\n-  padding spaces on the right if necessary\n+- '%<(<N>[,trunc|ltrunc|mtrunc])': make the next placeholder take at\n+  least N columns, padding spaces on the right if necessary.\n+  Optionally truncate at the beginning (ltrunc), the middle (mtrunc)\n+  or the end (trunc) if the output is longer than N columns.\n - '%<|(<N>)': make the next placeholder take at least until Nth\n   columns, padding spaces on the right if necessary\n - '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\ndiff --git a/pretty.c b/pretty.c\nindex 233d69c..29384b5 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -767,6 +767,13 @@ enum flush_type {\n \tflush_both\n };\n \n+enum trunc_type {\n+\ttrunc_none,\n+\ttrunc_left,\n+\ttrunc_middle,\n+\ttrunc_right\n+};\n+\n struct format_commit_context {\n \tconst struct commit *commit;\n \tconst struct pretty_print_context *pretty_ctx;\n@@ -774,6 +781,7 @@ struct format_commit_context {\n \tunsigned commit_message_parsed:1;\n \tunsigned commit_signature_parsed:1;\n \tenum flush_type flush_type;\n+\tenum trunc_type truncate;\n \tstruct {\n \t\tchar *gpg_output;\n \t\tchar good_bad;\n@@ -1044,7 +1052,7 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \n \tif (*ch == '(') {\n \t\tconst char *start = ch + 1;\n-\t\tconst char *end = strchr(start, ')');\n+\t\tconst char *end = start + strcspn(start, \",)\");\n \t\tchar *next;\n \t\tint width;\n \t\tif (!end || end == start)\n@@ -1054,6 +1062,23 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\t\treturn 0;\n \t\tc->padding = to_column ? -width : width;\n \t\tc->flush_type = flush_type;\n+\n+\t\tif (*end == ',') {\n+\t\t\tstart = end + 1;\n+\t\t\tend = strchr(start, ')');\n+\t\t\tif (!end || end == start)\n+\t\t\t\treturn 0;\n+\t\t\tif (!prefixcmp(start, \"trunc)\"))\n+\t\t\t\tc->truncate = trunc_right;\n+\t\t\telse if (!prefixcmp(start, \"ltrunc)\"))\n+\t\t\t\tc->truncate = trunc_left;\n+\t\t\telse if (!prefixcmp(start, \"mtrunc)\"))\n+\t\t\t\tc->truncate = trunc_middle;\n+\t\t\telse\n+\t\t\t\treturn 0;\n+\t\t} else\n+\t\t\tc->truncate = trunc_none;\n+\n \t\treturn end - placeholder + 1;\n \t}\n \treturn 0;\n@@ -1335,9 +1360,29 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\ttotal_consumed++;\n \t}\n \tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n-\tif (len > padding)\n+\tif (len > padding) {\n+\t\tswitch (c->truncate) {\n+\t\tcase trunc_left:\n+\t\t\tstrbuf_utf8_replace(&local_sb,\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\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\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-\telse {\n+\t} else {\n \t\tint sb_len = sb->len, offset = 0;\n \t\tif (c->flush_type == flush_left)\n \t\t\toffset = padding - len;\ndiff --git a/utf8.c b/utf8.c\nindex 9d98043..766df80 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -421,6 +421,52 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \tfree(tmp);\n }\n \n+void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,\n+\t\t\t const char *subst)\n+{\n+\tstruct strbuf sb_dst = STRBUF_INIT;\n+\tchar *src = sb_src->buf;\n+\tchar *end = src + sb_src->len;\n+\tchar *dst;\n+\tint w = 0, subst_len = 0;\n+\n+\tif (subst)\n+\t\tsubst_len = strlen(subst);\n+\tstrbuf_grow(&sb_dst, sb_src->len + subst_len);\n+\tdst = sb_dst.buf;\n+\n+\twhile (src < end) {\n+\t\tchar *old;\n+\t\tsize_t n;\n+\n+\t\twhile ((n = display_mode_esc_sequence_len(src))) {\n+\t\t\tmemcpy(dst, src, n);\n+\t\t\tsrc += n;\n+\t\t\tdst += n;\n+\t\t}\n+\n+\t\told = src;\n+\t\tn = utf8_width((const char**)&src, NULL);\n+\t\tif (!src) \t/* broken utf-8, do nothing */\n+\t\t\treturn;\n+\t\tif (n && w >= pos && w < pos + width) {\n+\t\t\tif (subst) {\n+\t\t\t\tmemcpy(dst, subst, subst_len);\n+\t\t\t\tdst += subst_len;\n+\t\t\t\tsubst = NULL;\n+\t\t\t}\n+\t\t\tw += n;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tmemcpy(dst, old, src - old);\n+\t\tdst += src - old;\n+\t\tw += n;\n+\t}\n+\tstrbuf_setlen(&sb_dst, dst - sb_dst.buf);\n+\tstrbuf_attach(sb_src, strbuf_detach(&sb_dst, NULL),\n+\t\t      sb_dst.len, sb_dst.alloc);\n+}\n+\n int is_encoding_utf8(const char *name)\n {\n \tif (!name)\ndiff --git a/utf8.h b/utf8.h\nindex 99db3e0..faf2f91 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -15,6 +15,8 @@ void strbuf_add_wrapped_text(struct strbuf *buf,\n \t\tconst char *text, int indent, int indent2, int width);\n void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \t\t\t     int indent, int indent2, int width);\n+void strbuf_utf8_replace(struct strbuf *sb, int pos, int width,\n+\t\t\t const char *subst);\n \n #ifndef NO_ICONV\n char *reencode_string_iconv(const char *in, size_t insz,\n-- \n1.8.2.83.gc99314b\n"},{"id":"212694","messageId":"1364636112-15065-13-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 12/12] pretty: support %>> that steal trailing spaces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-30T09:35:12Z","receivedAt":"2013-03-30T09:35:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is pretty useful in `%<(100)%s%Cred%>(20)% an' where %s does not\nuse up all 100 columns and %an needs more than 20 columns. By\nreplacing %>(20) with %>>(20), %an can steal spaces from %s.\n\n%>> understands escape sequences, so %Cred does not stop it from\nstealing spaces in %<(100).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  5 ++++-\n pretty.c                         | 34 ++++++++++++++++++++++++++++++++++\n utf8.c                           |  2 +-\n utf8.h                           |  1 +\n 4 files changed, 40 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex f2df941..73ae4d6 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -170,7 +170,10 @@ The placeholders are:\n   columns, padding spaces on the right if necessary\n - '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n   respectively, but padding spaces on the left\n-- '%><(<N>)', '%><|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n+- '%>>(<N>)', '%>>|(<N>)': similar to '%>(<N>)', '%>|(<N>)'\n+  respectively, except that if the next placeholder takes more spaces\n+  than given and there are spaces on its left, use those spaces\n+- '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'\n   respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\ndiff --git a/pretty.c b/pretty.c\nindex 29384b5..892274d 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -764,6 +764,7 @@ enum flush_type {\n \tno_flush,\n \tflush_right,\n \tflush_left,\n+\tflush_left_and_steal,\n \tflush_both\n };\n \n@@ -1037,6 +1038,9 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\tif (*ch == '<') {\n \t\t\tflush_type = flush_both;\n \t\t\tch++;\n+\t\t} else if (*ch == '>') {\n+\t\t\tflush_type = flush_left_and_steal;\n+\t\t\tch++;\n \t\t} else\n \t\t\tflush_type = flush_left;\n \t\tbreak;\n@@ -1360,6 +1364,36 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\ttotal_consumed++;\n \t}\n \tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\n+\tif (c->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+\t\t\tif (*ch == ' ') {\n+\t\t\t\tch--;\n+\t\t\t\tpadding++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\t/* check for trailing ansi sequences */\n+\t\t\tif (*ch != 'm')\n+\t\t\t\tbreak;\n+\t\t\tp = ch - 1;\n+\t\t\twhile (ch - p < 10 && *p != '\\033')\n+\t\t\t\tp--;\n+\t\t\tif (*p != '\\033' ||\n+\t\t\t    ch + 1 - p != display_mode_esc_sequence_len(p))\n+\t\t\t\tbreak;\n+\t\t\t/*\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\tch = p - 1;\n+\t\t}\n+\t\tstrbuf_setlen(sb, ch + 1 - sb->buf);\n+\t\tc->flush_type = flush_left;\n+\t}\n+\n \tif (len > padding) {\n \t\tswitch (c->truncate) {\n \t\tcase trunc_left:\ndiff --git a/utf8.c b/utf8.c\nindex 766df80..414ae49 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,7 +9,7 @@ struct interval {\n   int last;\n };\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n+size_t display_mode_esc_sequence_len(const char *s)\n {\n \tconst char *p = s;\n \tif (*p++ != '\\033')\ndiff --git a/utf8.h b/utf8.h\nindex faf2f91..e913edb 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -3,6 +3,7 @@\n \n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n+size_t display_mode_esc_sequence_len(const char *s);\n int utf8_width(const char **start, size_t *remainder_p);\n int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n-- \n1.8.2.83.gc99314b\n"},{"id":"212706","messageId":"51571B92.80303@web.de","threadId":"33195","inReplyTo":"1364636112-15065-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 07/12] utf8: keep NULs in reencode_string()","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2013-03-30T17:06:26Z","receivedAt":"2013-03-30T17:06:26Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 30.03.13 10:35, Nguyễn Thái Ngọc Duy wrote:\n[...]\nThe short version of a review:\nWould it make sense to leave  reencode_string() as it is,\nand add a new function reencode_string_len()\n\n+char *reencode_string_len(const char *in, int insz,\n+\t\t      const char *out_encoding, const char *in_encoding,\n+\t\t      int *outsz)\n\nAnd I didn't manage to apply the patch on master (631bc94e67383b66da190550866566f09d32)\nis there a specific commitID it should be applied on ?\n\n/Torsten\n\n\n\n\n> pretty output, which can contain NULs.\n> -\t\treencoded = reencode_string(message, \"UTF-8\", encoding);\n> +\t\treencoded = reencode_string(message, strlen(message),\n> +\t\t\t\t\t    \"UTF-8\", encoding, NULL);\n\n> -\tout = reencode_string(line->buf, metainfo_charset, charset);\n> +\tout = reencode_string(line->buf, line->len,\n> +\t\t\t      metainfo_charset, charset, NULL);\n\n> -\t\t\tnewarg = reencode_string_iconv(oldarg, namelen, ic_precompose);\n> +\t\t\tnewarg = reencode_string_iconv(oldarg, namelen, ic_precompose, NULL);\n> -\t\tchar *reencoded = reencode_string(msg, output_encoding, utf8);\n\n> +\t\tchar *reencoded = reencode_string(msg, strlen(msg),\n> +\t\t\t\t\t\t  output_encoding, utf8,\n> +\t\t\t\t\t\t  NULL);\n\n> -\t\tout = reencode_string(msg, output_encoding, use_encoding);\n> +\t\tout = reencode_string(msg, strlen(msg),\n> +\t\t\t\t      output_encoding, use_encoding, NULL);\n\n> -\t\tout->reencoded_message = reencode_string(commit->buffer,\n> -\t\t\t\t\tgit_commit_encoding, encoding);\n> +\t\tout->reencoded_message =\n> +\t\t\treencode_string(commit->buffer, strlen(commit->buffer),\n> +\t\t\t\t\tgit_commit_encoding, encoding, NULL);\n\n>  \n> +char *reencode_string_len(const char *in, int insz,\n> +\t\t      const char *out_encoding, const char *in_encoding,\n> +\t\t      int *outsz)\n>  \n"},{"id":"212711","messageId":"CACsJy8DGPtfeAGAGXPZ0XaVNPYr=_upcR3rZojYXE2QD4cxvUA@mail.gmail.com","threadId":"33195","inReplyTo":"51571B92.80303@web.de","subject":"Re: [PATCH v2 07/12] utf8: keep NULs in reencode_string()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-31T00:23:02Z","receivedAt":"2013-03-31T00:23:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Mar 31, 2013 at 12:06 AM, Torsten Bögershausen <tboegi@web.de> wrote:\n> On 30.03.13 10:35, Nguyễn Thái Ngọc Duy wrote:\n> [...]\n> The short version of a review:\n> Would it make sense to leave  reencode_string() as it is,\n> and add a new function reencode_string_len()\n\nHmm.. yeah.\n\n> +char *reencode_string_len(const char *in, int insz,\n> +                     const char *out_encoding, const char *in_encoding,\n> +                     int *outsz)\n>\n> And I didn't manage to apply the patch on master (631bc94e67383b66da190550866566f09d32)\n> is there a specific commitID it should be applied on ?\n\nv1.8.2\n"},{"id":"212804","messageId":"7vd2uejfxr.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"1364636112-15065-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 02/12] pretty: share code between format_decoration and show_decorations","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-01T17:53:20Z","receivedAt":"2013-04-01T17:53:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This also adds color support to format_decoration()\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  log-tree.c                       | 60 +++++++++++++++++++++++++---------------\n>  log-tree.h                       |  3 ++\n>  pretty.c                         | 19 +------------\n>  t/t4207-log-decoration-colors.sh |  8 +++---\n>  4 files changed, 45 insertions(+), 45 deletions(-)\n>\n> diff --git a/log-tree.c b/log-tree.c\n> index 5dc45c4..7467a1d 100644\n> --- a/log-tree.c\n> +++ b/log-tree.c\n> @@ -174,36 +174,50 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n>  \t}\n>  }\n>  \n> -void show_decorations(struct rev_info *opt, struct commit *commit)\n> +void format_decoration(struct strbuf *sb,\n> +\t\t       const struct commit *commit,\n> +\t\t       int use_color)\n>  {\n> -\tconst char *prefix;\n> -\tstruct name_decoration *decoration;\n> +\tconst char *prefix = \" (\";\n> +\tstruct name_decoration *d;\n\nThis renaming of variable from decoration to d seems to make the\npatched result unnecessarily different from the original in\nshow_decorations, making it harder to compare.  Intentional?\n\n>  \tconst char *color_commit =\n> -\t\tdiff_get_color_opt(&opt->diffopt, DIFF_COMMIT);\n> +\t\tdiff_get_color(use_color, DIFF_COMMIT);\n>  \tconst char *color_reset =\n> -\t\tdecorate_get_color_opt(&opt->diffopt, DECORATION_NONE);\n> +\t\tdecorate_get_color(use_color, DECORATION_NONE);\n> +\n> +\tload_ref_decorations(DECORATE_SHORT_REFS);\n\nIn cmd_log_init_finish(), we have loaded decorations with specified\ndecoration_style already.  Why is this needed (and with a hardcoded\nstyle that may be different from what the user specified)?\n\n> +\td = lookup_decoration(&name_decoration, &commit->object);\n> +\tif (!d)\n> +\t\treturn;\n> +\twhile (d) {\n> +\t\tstrbuf_addstr(sb, color_commit);\n> +\t\tstrbuf_addstr(sb, prefix);\n> +\t\tstrbuf_addstr(sb, decorate_get_color(use_color, d->type));\n> +\t\tif (d->type == DECORATION_REF_TAG)\n> +\t\t\tstrbuf_addstr(sb, \"tag: \");\n> +\t\tstrbuf_addstr(sb, d->name);\n> +\t\tstrbuf_addstr(sb, color_reset);\n> +\t\tprefix = \", \";\n> +\t\td = d->next;\n> +\t}\n> +\tif (prefix[0] == ',') {\n> +\t\tstrbuf_addstr(sb, color_commit);\n> +\t\tstrbuf_addch(sb, ')');\n> +\t\tstrbuf_addstr(sb, color_reset);\n> +\t}\n\nWas this change to conditionally close ' (' mentioned in the log\nmessage?  It is in line with the version taken from pretty.c, and I\nthink it may be an improvement, but I do not think the check is\nnecessary in the context of this function.  You will never see\nprefix[0] != ',' after the loop, because \"while (d)\" above runs at\nleast once; otherwise the \"if (!d) return\" would have returned from\nthe function early, no?\n\n> +}\n> +\n> +void show_decorations(struct rev_info *opt, struct commit *commit)\n> +{\n> +\tstruct strbuf sb = STRBUF_INIT;\n>  \n>  \tif (opt->show_source && commit->util)\n>  \t\tprintf(\"\\t%s\", (char *) commit->util);\n>  \tif (!opt->show_decorations)\n>  \t\treturn;\n> -\tdecoration = lookup_decoration(&name_decoration, &commit->object);\n> -\tif (!decoration)\n> -\t\treturn;\n> -\tprefix = \" (\";\n> -\twhile (decoration) {\n> -\t\tprintf(\"%s\", prefix);\n> -\t\tfputs(decorate_get_color_opt(&opt->diffopt, decoration->type),\n> -\t\t      stdout);\n> -\t\tif (decoration->type == DECORATION_REF_TAG)\n> -\t\t\tfputs(\"tag: \", stdout);\n> -\t\tprintf(\"%s\", decoration->name);\n> -\t\tfputs(color_reset, stdout);\n> -\t\tfputs(color_commit, stdout);\n> -\t\tprefix = \", \";\n> -\t\tdecoration = decoration->next;\n> -\t}\n> -\tputchar(')');\n> +\tformat_decoration(&sb, commit, opt->diffopt.use_color);\n> +\tfputs(sb.buf, stdout);\n> +\tstrbuf_release(&sb);\n>  }\n>  \n>  /*\n> @@ -625,8 +639,8 @@ void show_log(struct rev_info *opt)\n>  \t\t\tprintf(\" (from %s)\",\n>  \t\t\t       find_unique_abbrev(parent->object.sha1,\n>  \t\t\t\t\t\t  abbrev_commit));\n> +\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n>  \t\tshow_decorations(opt, commit);\n> -\t\tprintf(\"%s\", diff_get_color_opt(&opt->diffopt, DIFF_RESET));\n\nWe used to show and then reset.  I can see the updated\nshow_decorations() to format_decoration() callchain always reset at\nthe end, so the loss of the final reset here is very sane, but is\nthere a need to reset beforehand?  What is the calling convention\nfor the updated show_decorations()?  The caller should make sure\nthere is no funny colors in effect before calling, and the caller\ncan rest assured that there is no funny colors when the function\nreturns?\n\n> diff --git a/log-tree.h b/log-tree.h\n> index 9140f48..e6a2da5 100644\n> --- a/log-tree.h\n> +++ b/log-tree.h\n> @@ -13,6 +13,9 @@ int log_tree_diff_flush(struct rev_info *);\n>  int log_tree_commit(struct rev_info *, struct commit *);\n>  int log_tree_opt_parse(struct rev_info *, const char **, int);\n>  void show_log(struct rev_info *opt);\n> +void format_decoration(struct strbuf *sb,\n> +\t\t       const struct commit *commit,\n> +\t\t       int use_color);\n\nI think you can fit these on a single line, especially if you drop\nthe unused variable names (they help when there are more than one\nparameter of the same type to document the order of the arguments,\nbut that does not apply here).  That would help people who run\n\"grep\" on the header files without using CTAGS/ETAGS.\n\nWouldn't it be \"format_decorations()\", or does it handle only one?\n"},{"id":"212805","messageId":"7v8v52jff3.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"1364636112-15065-5-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 04/12] utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-01T18:04:32Z","receivedAt":"2013-04-01T18:04:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  utf8.c | 20 ++++++++++++++------\n>  utf8.h |  1 +\n>  2 files changed, 15 insertions(+), 6 deletions(-)\n>\n> diff --git a/utf8.c b/utf8.c\n> index 82c2ddf..38322a1 100644\n> --- a/utf8.c\n> +++ b/utf8.c\n> @@ -266,18 +266,26 @@ int utf8_width(const char **start, size_t *remainder_p)\n>   * string, assuming that the string is utf8.  Returns strlen() instead\n>   * if the string does not look like a valid utf8 string.\n>   */\n> -int utf8_strwidth(const char *string)\n> +int utf8_strnwidth(const char *string, int len, int skip_ansi)\n>  {\n>  \tint width = 0;\n>  \tconst char *orig = string;\n>  \n> -\twhile (1) {\n> -\t\tif (!string)\n> -\t\t\treturn strlen(orig);\n> -\t\tif (!*string)\n> -\t\t\treturn width;\n> +\tif (len == -1)\n> +\t\tlen = strlen(string);\n> +\twhile (string && string < orig + len) {\n> +\t\tint skip;\n> +\t\twhile (skip_ansi &&\n> +\t\t       (skip = display_mode_esc_sequence_len(string)))\n\nWe prefer to avoid assignment in conditionals; please write the\nsecond as something like:\n\n\t((var = func(...)) != 0)\n\nto clarify that it is not a misspelled comparison.\n\n> +\t\t\tstring += skip;\n>  \t\twidth += utf8_width(&string, NULL);\n>  \t}\n> +\treturn string ? width : len;\n> +}\n> +\n> +int utf8_strwidth(const char *string)\n> +{\n> +\treturn utf8_strnwidth(string, -1, 0);\n>  }\n>  \n>  int is_utf8(const char *text)\n> diff --git a/utf8.h b/utf8.h\n> index 501b2bd..a556932 100644\n> --- a/utf8.h\n> +++ b/utf8.h\n> @@ -4,6 +4,7 @@\n>  typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n>  \n>  int utf8_width(const char **start, size_t *remainder_p);\n> +int utf8_strnwidth(const char *string, int len, int skip_ansi);\n>  int utf8_strwidth(const char *string);\n>  int is_utf8(const char *text);\n>  int is_encoding_utf8(const char *name);\n"},{"id":"212806","messageId":"7v4nfqjf5p.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"1364636112-15065-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 05/12] pretty: save commit encoding from logmsg_reencode if the caller needs it","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-01T18:10:10Z","receivedAt":"2013-04-01T18:10:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> The commit encoding is parsed by logmsg_reencode, there's no need for\n> the caller to re-parse it again. The reencoded message now has the new\n> encoding, not the original one. The caller would need to read commit\n> object again before parsing.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nThis and 06/12 look like a fix independent from the remainder of the\nseries.  Can they be moved a lot earlier in the series?\n"},{"id":"212807","messageId":"7vzjxihztj.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"1364636112-15065-10-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 09/12] pretty: add %C(auto) for auto-coloring on the next placeholder","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-01T18:26:48Z","receivedAt":"2013-04-01T18:26:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This is not simply convenient over %C(auto,xxx). Some placeholders\n> (actually only one, %d) do multi coloring and we can't emit a multiple\n> colors with %C(auto,xxx).\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/pretty-formats.txt |  3 ++-\n>  pretty.c                         | 15 +++++++++++++--\n>  2 files changed, 15 insertions(+), 3 deletions(-)\n>\n> diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\n> index 66345d1..8734224 100644\n> --- a/Documentation/pretty-formats.txt\n> +++ b/Documentation/pretty-formats.txt\n> @@ -154,7 +154,8 @@ The placeholders are:\n>    adding `auto,` at the beginning will emit color only when colors are\n>    enabled for log output (by `color.diff`, `color.ui`, or `--color`, and\n>    respecting the `auto` settings of the former if we are going to a\n> -  terminal)\n> +  terminal). `auto` alone (i.e. `%C(auto)`) will turn on auto coloring\n> +  on the following placeholder.\n>  - '%m': left, right or boundary mark\n>  - '%n': newline\n>  - '%%': a raw '%'\n\nI like this at the conceptual level.\n\nIf you say \"%C(auto)%C(red)Text%C(auto)%C(reset)\", does it do the\nright thing when the output is not capable of color?\n\nI am a bit worried if the placement of the \"grab c->auto_color to\ndecide if we paint for this round and reset it\" is optimial and will\nstay optimal as we enhance format_commit_one() later.  Is there a\nreason why we do not do that at the beginning of the function,\nbefore \"these are independent of the commit\" comment?\n\nSide note.  Should the new field called \"auto_color_next\" or\nsomething?\n"},{"id":"212809","messageId":"7vvc86hz8x.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"1364636112-15065-13-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 12/12] pretty: support %>> that steal trailing spaces","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-01T18:39:10Z","receivedAt":"2013-04-01T18:39:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The last three patches are a lot of fun.  It would be nice to have\nsome test vectors for them.\n\nThanks.\n"},{"id":"213228","messageId":"CACsJy8C0wb92QOhh=e27Cqd=e5yJYnQWYkYi4uPd+vPXGqVbdQ@mail.gmail.com","threadId":"33195","inReplyTo":"7vzjxihztj.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 09/12] pretty: add %C(auto) for auto-coloring on the next placeholder","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-05T02:21:42Z","receivedAt":"2013-04-05T02:21:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Apr 2, 2013 at 5:26 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> This is not simply convenient over %C(auto,xxx). Some placeholders\n>> (actually only one, %d) do multi coloring and we can't emit a multiple\n>> colors with %C(auto,xxx).\n>>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  Documentation/pretty-formats.txt |  3 ++-\n>>  pretty.c                         | 15 +++++++++++++--\n>>  2 files changed, 15 insertions(+), 3 deletions(-)\n>>\n>> diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\n>> index 66345d1..8734224 100644\n>> --- a/Documentation/pretty-formats.txt\n>> +++ b/Documentation/pretty-formats.txt\n>> @@ -154,7 +154,8 @@ The placeholders are:\n>>    adding `auto,` at the beginning will emit color only when colors are\n>>    enabled for log output (by `color.diff`, `color.ui`, or `--color`, and\n>>    respecting the `auto` settings of the former if we are going to a\n>> -  terminal)\n>> +  terminal). `auto` alone (i.e. `%C(auto)`) will turn on auto coloring\n>> +  on the following placeholder.\n>>  - '%m': left, right or boundary mark\n>>  - '%n': newline\n>>  - '%%': a raw '%'\n>\n> I like this at the conceptual level.\n>\n> If you say \"%C(auto)%C(red)Text%C(auto)%C(reset)\", does it do the\n> right thing when the output is not capable of color?\n\nThe above should have written \"will turn on auto coloring on the\nfollowing _textual_ placeholder\". I didn't intend %C(auto) to be\nfollowed by %C(color) as it's already covered by %C(auto,red). But of\ncourse we could make it work too.\n\n> I am a bit worried if the placement of the \"grab c->auto_color to\n> decide if we paint for this round and reset it\" is optimial and will\n> stay optimal as we enhance format_commit_one() later.  Is there a\n> reason why we do not do that at the beginning of the function,\n> before \"these are independent of the commit\" comment?\n\nNo reason. Will move.\n\n> Side note.  Should the new field called \"auto_color_next\" or\n> something?\n\nYep, sounds better than my variable name.\n--\nDuy\n"},{"id":"213252","messageId":"515E83EF.3070609@gmail.com","threadId":"33195","inReplyTo":"7vd2uejfxr.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 02/12] pretty: share code between format_decoration and show_decorations","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2013-04-05T07:57:35Z","receivedAt":"2013-04-05T07:57:35Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n>> diff --git a/log-tree.h b/log-tree.h\n>> index 9140f48..e6a2da5 100644\n>> --- a/log-tree.h\n>> +++ b/log-tree.h\n>> @@ -13,6 +13,9 @@ int log_tree_diff_flush(struct rev_info *);\n>>  int log_tree_commit(struct rev_info *, struct commit *);\n>>  int log_tree_opt_parse(struct rev_info *, const char **, int);\n>>  void show_log(struct rev_info *opt);\n>> +void format_decoration(struct strbuf *sb,\n>> +\t\t       const struct commit *commit,\n>> +\t\t       int use_color);\n> \n> I think you can fit these on a single line, especially if you drop\n> the unused variable names (they help when there are more than one\n> parameter of the same type to document the order of the arguments,\n> but that does not apply here).  That would help people who run\n> \"grep\" on the header files without using CTAGS/ETAGS.\n\nWell, I think \"int use_color\" should be left with variable name,\ndon't you think?\n\n-- \nJakub Narębski\n"},{"id":"213275","messageId":"7v8v4wdhpd.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"CACsJy8C0wb92QOhh=e27Cqd=e5yJYnQWYkYi4uPd+vPXGqVbdQ@mail.gmail.com","subject":"Re: [PATCH v2 09/12] pretty: add %C(auto) for auto-coloring on the next placeholder","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-05T17:13:02Z","receivedAt":"2013-04-05T17:13:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> The above should have written \"will turn on auto coloring on the\n> following _textual_ placeholder\". I didn't intend %C(auto) to be\n> followed by %C(color) as it's already covered by %C(auto,red). But of\n> course we could make it work too.\n\nYou are right that there is no need to say \"%C(auto)%C(red)\", it is\n\"%C(auto,red)\", but that misses the point.\n\nIf %C(auto) applies to some %<placeholder> but not to some others,\nthe user needs to learn which %<placeholder> will eat the \"auto\" (so\nit no longer applies to the next one) and which one will not even\nlook at \"auto\" (so the next %<placeholder> is affected by the auto,\ni.e. making the effect of auto skip a %<placeholder>).  If the rule\nwere \"%C(auto) applies to -next- placeholder\", then the user does\nnot have to worry about which ones are what you call textual and\nwhich ones are not (and there is no textual placeholder defined in\nthe glossary).\n\nThat would make it harder to learn. It would be much easier to\nexplain if you said \"%C(auto) affects the next %-placeholder and\nthen resets\".\n\nI wonder if \"Everything after %C(auto) will not be coloured if the\noutput is not going to the terminal.\", i.e. not resetting once\ncolouring decision is made, makes more sense, though...\n"},{"id":"214114","messageId":"CACsJy8BnWg2JPmGUtLdA+XG9=UX65deYv9moefOHL-O7Kd3kww@mail.gmail.com","threadId":"33195","inReplyTo":"7vd2uejfxr.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 02/12] pretty: share code between format_decoration and show_decorations","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-12T23:34:01Z","receivedAt":"2013-04-12T23:34:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Sorry for this late reply. I've been quite busy lately..\n\nOn Tue, Apr 2, 2013 at 4:53 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> -void show_decorations(struct rev_info *opt, struct commit *commit)\n>> +void format_decoration(struct strbuf *sb,\n>> +                    const struct commit *commit,\n>> +                    int use_color)\n>>  {\n>> -     const char *prefix;\n>> -     struct name_decoration *decoration;\n>> +     const char *prefix = \" (\";\n>> +     struct name_decoration *d;\n>\n> This renaming of variable from decoration to d seems to make the\n> patched result unnecessarily different from the original in\n> show_decorations, making it harder to compare.  Intentional?\n\nI think I just happened to reuse the style of the old\nformat_decoration(). Will reuse the name \"decoration\" instead.\n\n>>       const char *color_commit =\n>> -             diff_get_color_opt(&opt->diffopt, DIFF_COMMIT);\n>> +             diff_get_color(use_color, DIFF_COMMIT);\n>>       const char *color_reset =\n>> -             decorate_get_color_opt(&opt->diffopt, DECORATION_NONE);\n>> +             decorate_get_color(use_color, DECORATION_NONE);\n>> +\n>> +     load_ref_decorations(DECORATE_SHORT_REFS);\n>\n> In cmd_log_init_finish(), we have loaded decorations with specified\n> decoration_style already.  Why is this needed (and with a hardcoded\n> style that may be different from what the user specified)?\n\nlegacy from pretty.c:format_decoration(). Will move it to the caller\nformat_commit_one.\n\n>\n>> +     d = lookup_decoration(&name_decoration, &commit->object);\n>> +     if (!d)\n>> +             return;\n>> +     while (d) {\n>> +             strbuf_addstr(sb, color_commit);\n>> +             strbuf_addstr(sb, prefix);\n>> +             strbuf_addstr(sb, decorate_get_color(use_color, d->type));\n>> +             if (d->type == DECORATION_REF_TAG)\n>> +                     strbuf_addstr(sb, \"tag: \");\n>> +             strbuf_addstr(sb, d->name);\n>> +             strbuf_addstr(sb, color_reset);\n>> +             prefix = \", \";\n>> +             d = d->next;\n>> +     }\n>> +     if (prefix[0] == ',') {\n>> +             strbuf_addstr(sb, color_commit);\n>> +             strbuf_addch(sb, ')');\n>> +             strbuf_addstr(sb, color_reset);\n>> +     }\n>\n> Was this change to conditionally close ' (' mentioned in the log\n> message?  It is in line with the version taken from pretty.c, and I\n> think it may be an improvement, but I do not think the check is\n> necessary in the context of this function.  You will never see\n> prefix[0] != ',' after the loop, because \"while (d)\" above runs at\n> least once; otherwise the \"if (!d) return\" would have returned from\n> the function early, no?\n\nYes, your eyeballs have really good quality ;)\n\n>> @@ -625,8 +639,8 @@ void show_log(struct rev_info *opt)\n>>                       printf(\" (from %s)\",\n>>                              find_unique_abbrev(parent->object.sha1,\n>>                                                 abbrev_commit));\n>> +             fputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n>>               show_decorations(opt, commit);\n>> -             printf(\"%s\", diff_get_color_opt(&opt->diffopt, DIFF_RESET));\n>\n> We used to show and then reset.  I can see the updated\n> show_decorations() to format_decoration() callchain always reset at\n> the end, so the loss of the final reset here is very sane, but is\n> there a need to reset beforehand?  What is the calling convention\n> for the updated show_decorations()?  The caller should make sure\n> there is no funny colors in effect before calling, and the caller\n> can rest assured that there is no funny colors when the function\n> returns?\n\nI think it's a sane convention, unless we want a some background color\ngoing through show_decorations.\n\n>> +void format_decoration(struct strbuf *sb,\n>> +                    const struct commit *commit,\n>> +                    int use_color);\n>\n> I think you can fit these on a single line, especially if you drop\n> the unused variable names (they help when there are more than one\n> parameter of the same type to document the order of the arguments,\n> but that does not apply here).  That would help people who run\n> \"grep\" on the header files without using CTAGS/ETAGS.\n\nNo problem.\n\n> Wouldn't it be \"format_decorations()\", or does it handle only one?\n\nAll in one, apparently. Will rename.\n--\nDuy\n"},{"id":"214115","messageId":"CACsJy8CmzuDxCPebMaEkmF=u3xA4wXq2ydRMiU_rNsy1Pr411w@mail.gmail.com","threadId":"33195","inReplyTo":"515E83EF.3070609@gmail.com","subject":"Re: [PATCH v2 02/12] pretty: share code between format_decoration and show_decorations","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-12T23:36:02Z","receivedAt":"2013-04-12T23:36:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Apr 5, 2013 at 6:57 PM, Jakub Narębski <jnareb@gmail.com> wrote:\n>>> +void format_decoration(struct strbuf *sb,\n>>> +                   const struct commit *commit,\n>>> +                   int use_color);\n>>\n>> I think you can fit these on a single line, especially if you drop\n>> the unused variable names (they help when there are more than one\n>> parameter of the same type to document the order of the arguments,\n>> but that does not apply here).  That would help people who run\n>> \"grep\" on the header files without using CTAGS/ETAGS.\n>\n> Well, I think \"int use_color\" should be left with variable name,\n> don't you think?\n\nI don't care too much about this. If Junio does not respond, I'll\nleave the names in place (in one long line).\n--\nDuy\n"},{"id":"214290","messageId":"CACsJy8A6vMxZLn8RXaxxMAg2yx-Pmfriip_u_DSSYeyjwY2aHg@mail.gmail.com","threadId":"33195","inReplyTo":"7v8v4wdhpd.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 09/12] pretty: add %C(auto) for auto-coloring on the next placeholder","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-15T09:54:44Z","receivedAt":"2013-04-15T09:54:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Apr 6, 2013 at 4:13 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> If %C(auto) applies to some %<placeholder> but not to some others,\n> the user needs to learn which %<placeholder> will eat the \"auto\" (so\n> it no longer applies to the next one) and which one will not even\n> look at \"auto\" (so the next %<placeholder> is affected by the auto,\n> i.e. making the effect of auto skip a %<placeholder>).  If the rule\n> were \"%C(auto) applies to -next- placeholder\", then the user does\n> not have to worry about which ones are what you call textual and\n> which ones are not (and there is no textual placeholder defined in\n> the glossary).\n>\n> That would make it harder to learn. It would be much easier to\n> explain if you said \"%C(auto) affects the next %-placeholder and\n> then resets\".\n\nSo far (after this series, that is), we have two modifiers: %C(auto)\nand %< (and friends). Both can be used to modify the \"next\"\nplaceholder, so either of them must learn to ignore the next\nnon-textual placeholder...\n\n> I wonder if \"Everything after %C(auto) will not be coloured if the\n> output is not going to the terminal.\", i.e. not resetting once\n> colouring decision is made, makes more sense, though...\n\n.. or we do this, which makes %< and friends the only placeholders\nthat care about the next one. Thanks for the idea.\n--\nDuy\n"},{"id":"214449","messageId":"1366100702-31745-1-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1364636112-15065-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 00/13] nd/pretty-formats","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:49Z","receivedAt":"2013-04-16T08:24:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I've been updating this series on and off over a long span of time, I\ndon't think I remember exactly what changes I've made. But basically\nI think I have covered all comments from v2. The only semantics change\nis %C(auto) now turns auto coloring on for all following placeholders\nuntil another valid %C is encountered.\n\nNguyễn Thái Ngọc Duy (13):\n  pretty: save commit encoding from logmsg_reencode if the caller needs it\n  pretty: get the correct encoding for --pretty:format=%e\n  pretty-formats.txt: wrap long lines\n  pretty: share code between format_decoration and show_decorations\n  utf8.c: move display_mode_esc_sequence_len() for use by other functions\n  utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences\n  utf8.c: add reencode_string_len() that can handle NULs in string\n  pretty: two phase conversion for non utf-8 commits\n  pretty: split color parsing into a separate function\n  pretty: add %C(auto) for auto-coloring\n  pretty: support padding placeholders, %< %> and %><\n  pretty: support truncating in %>, %< and %><\n  pretty: support %>> that steal trailing spaces\n\n Documentation/pretty-formats.txt |  35 +++-\n builtin/blame.c                  |   2 +-\n builtin/commit.c                 |   2 +-\n commit.h                         |   1 +\n compat/precompose_utf8.c         |   2 +-\n log-tree.c                       |  48 ++++--\n log-tree.h                       |   1 +\n pretty.c                         | 349 ++++++++++++++++++++++++++++++++-------\n revision.c                       |   2 +-\n t/t4205-log-pretty-formats.sh    | 179 ++++++++++++++++++++\n t/t4207-log-decoration-colors.sh |   8 +-\n t/t6006-rev-list-format.sh       |  12 +-\n utf8.c                           | 104 +++++++++---\n utf8.h                           |  23 ++-\n 14 files changed, 644 insertions(+), 124 deletions(-)\n\n-- \n1.8.2.82.gc24b958\n"},{"id":"214450","messageId":"1366100702-31745-2-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 01/13] pretty: save commit encoding from logmsg_reencode if the caller needs it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:50Z","receivedAt":"2013-04-16T08:24:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The commit encoding is parsed by logmsg_reencode, there's no need for\nthe caller to re-parse it again. The reencoded message now has the new\nencoding, not the original one. The caller would need to read commit\nobject again before parsing.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/blame.c  |  2 +-\n builtin/commit.c |  2 +-\n commit.h         |  1 +\n pretty.c         | 16 ++++++++++++----\n revision.c       |  2 +-\n 5 files changed, 16 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 86100e9..104a948 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1425,7 +1425,7 @@ static void get_commit_info(struct commit *commit,\n \tcommit_info_init(ret);\n \n \tencoding = get_log_output_encoding();\n-\tmessage = logmsg_reencode(commit, encoding);\n+\tmessage = logmsg_reencode(commit, NULL, encoding);\n \tget_ac_line(message, \"\\nauthor \",\n \t\t    &ret->author, &ret->author_mail,\n \t\t    &ret->author_time, &ret->author_tz);\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 4620437..d2f30d9 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -955,7 +955,7 @@ static const char *read_commit_message(const char *name)\n \tif (!commit)\n \t\tdie(_(\"could not lookup commit %s\"), name);\n \tout_enc = get_commit_output_encoding();\n-\treturn logmsg_reencode(commit, out_enc);\n+\treturn logmsg_reencode(commit, NULL, out_enc);\n }\n \n static int parse_and_validate_options(int argc, const char *argv[],\ndiff --git a/commit.h b/commit.h\nindex 87b4b6c..ad55213 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -101,6 +101,7 @@ struct userformat_want {\n extern int has_non_ascii(const char *text);\n struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */\n extern char *logmsg_reencode(const struct commit *commit,\n+\t\t\t     char **commit_encoding,\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 *);\ndiff --git a/pretty.c b/pretty.c\nindex d3a82d2..c361b9b 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -594,6 +594,7 @@ static char *replace_encoding_header(char *buf, const char *encoding)\n }\n \n char *logmsg_reencode(const struct commit *commit,\n+\t\t      char **commit_encoding,\n \t\t      const char *output_encoding)\n {\n \tstatic const char *utf8 = \"UTF-8\";\n@@ -615,9 +616,15 @@ char *logmsg_reencode(const struct commit *commit,\n \t\t\t    sha1_to_hex(commit->object.sha1), typename(type));\n \t}\n \n-\tif (!output_encoding || !*output_encoding)\n+\tif (!output_encoding || !*output_encoding) {\n+\t\tif (commit_encoding)\n+\t\t\t*commit_encoding =\n+\t\t\t\tget_header(commit, msg, \"encoding\");\n \t\treturn msg;\n+\t}\n \tencoding = get_header(commit, msg, \"encoding\");\n+\tif (commit_encoding)\n+\t\t*commit_encoding = encoding;\n \tuse_encoding = encoding ? encoding : utf8;\n \tif (same_encoding(use_encoding, output_encoding)) {\n \t\t/*\n@@ -658,7 +665,8 @@ char *logmsg_reencode(const struct commit *commit,\n \tif (out)\n \t\tout = replace_encoding_header(out, output_encoding);\n \n-\tfree(encoding);\n+\tif (!commit_encoding)\n+\t\tfree(encoding);\n \t/*\n \t * If the re-encoding failed, out might be NULL here; in that\n \t * case we just return the commit message verbatim.\n@@ -1288,7 +1296,7 @@ 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, output_enc);\n+\tcontext.message = logmsg_reencode(commit, NULL, output_enc);\n \n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n@@ -1451,7 +1459,7 @@ void pretty_print_commit(const struct pretty_print_context *pp,\n \t}\n \n \tencoding = get_log_output_encoding();\n-\tmsg = reencoded = logmsg_reencode(commit, encoding);\n+\tmsg = reencoded = logmsg_reencode(commit, NULL, encoding);\n \n \tif (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)\n \t\tindent = 0;\ndiff --git a/revision.c b/revision.c\nindex 71e62d8..2e77397 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2314,7 +2314,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)\n \t * in it.\n \t */\n \tencoding = get_log_output_encoding();\n-\tmessage = logmsg_reencode(commit, encoding);\n+\tmessage = logmsg_reencode(commit, NULL, encoding);\n \n \t/* Copy the commit to temporary if we are using \"fake\" headers */\n \tif (buf.len)\n-- \n1.8.2.82.gc24b958\n"},{"id":"214451","messageId":"1366100702-31745-3-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 02/13] pretty: get the correct encoding for --pretty:format=%e","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:51Z","receivedAt":"2013-04-16T08:24:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"parse_commit_header() provides the commit encoding for '%e' and it\nreads it from the re-encoded message, which contains the new encoding,\nnot the original one in the commit object. This never happens because\n--pretty=format:xxx never respects i18n.logoutputencoding. But that's\na different story.\n\nGet the commit encoding from logmsg_reencode() instead.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c | 13 +++++++------\n 1 file changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex c361b9b..e59688b 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -776,12 +776,12 @@ struct format_commit_context {\n \tunsigned commit_message_parsed:1;\n \tstruct signature_check signature_check;\n \tchar *message;\n+\tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n \tstruct chunk committer;\n-\tstruct chunk encoding;\n \tsize_t message_off;\n \tsize_t subject_off;\n \tsize_t body_off;\n@@ -828,9 +828,6 @@ static void parse_commit_header(struct format_commit_context *context)\n \t\t} else if (!prefixcmp(msg + i, \"committer \")) {\n \t\t\tcontext->committer.off = i + 10;\n \t\t\tcontext->committer.len = eol - i - 10;\n-\t\t} else if (!prefixcmp(msg + i, \"encoding \")) {\n-\t\t\tcontext->encoding.off = i + 9;\n-\t\t\tcontext->encoding.len = eol - i - 9;\n \t\t}\n \t\ti = eol;\n \t}\n@@ -1185,7 +1182,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t\t\t   msg + c->committer.off, c->committer.len,\n \t\t\t\t   c->pretty_ctx->date_mode);\n \tcase 'e':\t/* encoding */\n-\t\tstrbuf_add(sb, msg + c->encoding.off, c->encoding.len);\n+\t\tif (c->commit_encoding)\n+\t\t\tstrbuf_addstr(sb, c->commit_encoding);\n \t\treturn 1;\n \tcase 'B':\t/* raw body */\n \t\t/* message_off is always left at the initial newline */\n@@ -1296,11 +1294,14 @@ 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, NULL, output_enc);\n+\tcontext.message = logmsg_reencode(commit,\n+\t\t\t\t\t  &context.commit_encoding,\n+\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 \n+\tfree(context.commit_encoding);\n \tlogmsg_free(context.message, commit);\n \tfree(context.signature_check.gpg_output);\n \tfree(context.signature_check.signer);\n-- \n1.8.2.82.gc24b958\n"},{"id":"214452","messageId":"1366100702-31745-4-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 03/13] pretty-formats.txt: wrap long lines","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:52Z","receivedAt":"2013-04-16T08:24:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt | 18 ++++++++++++------\n 1 file changed, 12 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex afac703..6bde67e 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -106,18 +106,22 @@ The placeholders are:\n - '%P': parent hashes\n - '%p': abbreviated parent hashes\n - '%an': author name\n-- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1]\n+  or linkgit:git-blame[1])\n - '%ae': author email\n-- '%aE': author email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%aE': author email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ad': author date (format respects --date= option)\n - '%aD': author date, RFC2822 style\n - '%ar': author date, relative\n - '%at': author date, UNIX timestamp\n - '%ai': author date, ISO 8601 format\n - '%cn': committer name\n-- '%cN': committer name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%cN': committer name (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ce': committer email\n-- '%cE': committer email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%cE': committer email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%cd': committer date\n - '%cD': committer date, RFC2822 style\n - '%cr': committer date, relative\n@@ -138,9 +142,11 @@ The placeholders are:\n - '%gD': reflog selector, e.g., `refs/stash@{1}`\n - '%gd': shortened reflog selector, e.g., `stash@{1}`\n - '%gn': reflog identity name\n-- '%gN': reflog identity name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%gN': reflog identity name (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ge': reflog identity email\n-- '%gE': reflog identity email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%gE': reflog identity email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%gs': reflog subject\n - '%Cred': switch color to red\n - '%Cgreen': switch color to green\n-- \n1.8.2.82.gc24b958\n"},{"id":"214453","messageId":"1366100702-31745-5-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 04/13] pretty: share code between format_decoration and show_decorations","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:53Z","receivedAt":"2013-04-16T08:24:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This also adds color support to format_decorations()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n log-tree.c                       | 48 ++++++++++++++++++++++++++--------------\n log-tree.h                       |  1 +\n pretty.c                         | 20 ++---------------\n t/t4207-log-decoration-colors.sh |  8 +++----\n 4 files changed, 39 insertions(+), 38 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex 7cc7d59..1946e9c 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -175,36 +175,52 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n \t}\n }\n \n-void show_decorations(struct rev_info *opt, struct commit *commit)\n+/*\n+ * The caller makes sure there is no funny color before\n+ * calling. format_decorations makes sure the same after return.\n+ */\n+void format_decorations(struct strbuf *sb,\n+\t\t\tconst struct commit *commit,\n+\t\t\tint use_color)\n {\n \tconst char *prefix;\n \tstruct name_decoration *decoration;\n \tconst char *color_commit =\n-\t\tdiff_get_color_opt(&opt->diffopt, DIFF_COMMIT);\n+\t\tdiff_get_color(use_color, DIFF_COMMIT);\n \tconst char *color_reset =\n-\t\tdecorate_get_color_opt(&opt->diffopt, DECORATION_NONE);\n+\t\tdecorate_get_color(use_color, DECORATION_NONE);\n \n-\tif (opt->show_source && commit->util)\n-\t\tprintf(\"\\t%s\", (char *) commit->util);\n-\tif (!opt->show_decorations)\n-\t\treturn;\n \tdecoration = lookup_decoration(&name_decoration, &commit->object);\n \tif (!decoration)\n \t\treturn;\n \tprefix = \" (\";\n \twhile (decoration) {\n-\t\tprintf(\"%s\", prefix);\n-\t\tfputs(decorate_get_color_opt(&opt->diffopt, decoration->type),\n-\t\t      stdout);\n+\t\tstrbuf_addstr(sb, color_commit);\n+\t\tstrbuf_addstr(sb, prefix);\n+\t\tstrbuf_addstr(sb, decorate_get_color(use_color, decoration->type));\n \t\tif (decoration->type == DECORATION_REF_TAG)\n-\t\t\tfputs(\"tag: \", stdout);\n-\t\tprintf(\"%s\", decoration->name);\n-\t\tfputs(color_reset, stdout);\n-\t\tfputs(color_commit, stdout);\n+\t\t\tstrbuf_addstr(sb, \"tag: \");\n+\t\tstrbuf_addstr(sb, decoration->name);\n+\t\tstrbuf_addstr(sb, color_reset);\n \t\tprefix = \", \";\n \t\tdecoration = decoration->next;\n \t}\n-\tputchar(')');\n+\tstrbuf_addstr(sb, color_commit);\n+\tstrbuf_addch(sb, ')');\n+\tstrbuf_addstr(sb, color_reset);\n+}\n+\n+void show_decorations(struct rev_info *opt, struct commit *commit)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tif (opt->show_source && commit->util)\n+\t\tprintf(\"\\t%s\", (char *) commit->util);\n+\tif (!opt->show_decorations)\n+\t\treturn;\n+\tformat_decorations(&sb, commit, opt->diffopt.use_color);\n+\tfputs(sb.buf, stdout);\n+\tstrbuf_release(&sb);\n }\n \n static unsigned int digits_in_number(unsigned int number)\n@@ -540,8 +556,8 @@ void show_log(struct rev_info *opt)\n \t\t\tprintf(\" (from %s)\",\n \t\t\t       find_unique_abbrev(parent->object.sha1,\n \t\t\t\t\t\t  abbrev_commit));\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n \t\tshow_decorations(opt, commit);\n-\t\tprintf(\"%s\", diff_get_color_opt(&opt->diffopt, DIFF_RESET));\n \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n \t\t\tputchar(' ');\n \t\t} else {\ndiff --git a/log-tree.h b/log-tree.h\nindex 9140f48..d6ecd4d 100644\n--- a/log-tree.h\n+++ b/log-tree.h\n@@ -13,6 +13,7 @@ int log_tree_diff_flush(struct rev_info *);\n int log_tree_commit(struct rev_info *, struct commit *);\n int log_tree_opt_parse(struct rev_info *, const char **, int);\n void show_log(struct rev_info *opt);\n+void format_decorations(struct strbuf *sb, const struct commit *commit, int use_color);\n void show_decorations(struct rev_info *opt, struct commit *commit);\n void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\t\t     const char **subject_p,\ndiff --git a/pretty.c b/pretty.c\nindex e59688b..e0f93ba 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -908,23 +908,6 @@ static void parse_commit_message(struct format_commit_context *c)\n \tc->commit_message_parsed = 1;\n }\n \n-static void format_decoration(struct strbuf *sb, const struct commit *commit)\n-{\n-\tstruct name_decoration *d;\n-\tconst char *prefix = \" (\";\n-\n-\tload_ref_decorations(DECORATE_SHORT_REFS);\n-\td = lookup_decoration(&name_decoration, &commit->object);\n-\twhile (d) {\n-\t\tstrbuf_addstr(sb, prefix);\n-\t\tprefix = \", \";\n-\t\tstrbuf_addstr(sb, d->name);\n-\t\td = d->next;\n-\t}\n-\tif (prefix[0] == ',')\n-\t\tstrbuf_addch(sb, ')');\n-}\n-\n static void strbuf_wrap(struct strbuf *sb, size_t pos,\n \t\t\tsize_t width, size_t indent1, size_t indent2)\n {\n@@ -1103,7 +1086,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\tstrbuf_addstr(sb, get_revision_mark(NULL, commit));\n \t\treturn 1;\n \tcase 'd':\n-\t\tformat_decoration(sb, commit);\n+\t\tload_ref_decorations(DECORATE_SHORT_REFS);\n+\t\tformat_decorations(sb, commit, 0);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex bbde31b..925f577 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -44,15 +44,15 @@ test_expect_success setup '\n '\n \n cat >expected <<EOF\n-${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_HEAD}HEAD${c_reset}${c_commit},\\\n  ${c_tag}tag: v1.0${c_reset}${c_commit},\\\n  ${c_tag}tag: B${c_reset}${c_commit},\\\n  ${c_branch}master${c_reset}${c_commit})${c_reset} B\n-${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A1${c_reset}${c_commit},\\\n  ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1\n-${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n  On master: Changes to A.t\n-${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n EOF\n \n # We want log to show all, but the second parent to refs/stash is irrelevant\n-- \n1.8.2.82.gc24b958\n"},{"id":"214454","messageId":"1366100702-31745-6-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 05/13] utf8.c: move display_mode_esc_sequence_len() for use by other functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:54Z","receivedAt":"2013-04-16T08:24:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex 7f64857..6ed93c3 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,6 +9,20 @@ struct interval {\n   int last;\n };\n \n+static size_t display_mode_esc_sequence_len(const char *s)\n+{\n+\tconst char *p = s;\n+\tif (*p++ != '\\033')\n+\t\treturn 0;\n+\tif (*p++ != '[')\n+\t\treturn 0;\n+\twhile (isdigit(*p) || *p == ';')\n+\t\tp++;\n+\tif (*p++ != 'm')\n+\t\treturn 0;\n+\treturn p - s;\n+}\n+\n /* auxiliary function for binary search in interval table */\n static int bisearch(ucs_char_t ucs, const struct interval *table, int max)\n {\n@@ -303,20 +317,6 @@ static void strbuf_add_indented_text(struct strbuf *buf, const char *text,\n \t}\n }\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n-{\n-\tconst char *p = s;\n-\tif (*p++ != '\\033')\n-\t\treturn 0;\n-\tif (*p++ != '[')\n-\t\treturn 0;\n-\twhile (isdigit(*p) || *p == ';')\n-\t\tp++;\n-\tif (*p++ != 'm')\n-\t\treturn 0;\n-\treturn p - s;\n-}\n-\n /*\n  * Wrap the text, if necessary. The variable indent is the indent for the\n  * first line, indent2 is the indent for all other lines.\n-- \n1.8.2.82.gc24b958\n"},{"id":"214455","messageId":"1366100702-31745-7-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 06/13] utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:55Z","receivedAt":"2013-04-16T08:24:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 20 ++++++++++++++------\n utf8.h |  1 +\n 2 files changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex 6ed93c3..9df087f 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -266,18 +266,26 @@ int utf8_width(const char **start, size_t *remainder_p)\n  * string, assuming that the string is utf8.  Returns strlen() instead\n  * if the string does not look like a valid utf8 string.\n  */\n-int utf8_strwidth(const char *string)\n+int utf8_strnwidth(const char *string, int len, int skip_ansi)\n {\n \tint width = 0;\n \tconst char *orig = string;\n \n-\twhile (1) {\n-\t\tif (!string)\n-\t\t\treturn strlen(orig);\n-\t\tif (!*string)\n-\t\t\treturn width;\n+\tif (len == -1)\n+\t\tlen = strlen(string);\n+\twhile (string && string < orig + len) {\n+\t\tint skip;\n+\t\twhile (skip_ansi &&\n+\t\t       (skip = display_mode_esc_sequence_len(string)))\n+\t\t\tstring += skip;\n \t\twidth += utf8_width(&string, NULL);\n \t}\n+\treturn string ? width : len;\n+}\n+\n+int utf8_strwidth(const char *string)\n+{\n+\treturn utf8_strnwidth(string, -1, 0);\n }\n \n int is_utf8(const char *text)\ndiff --git a/utf8.h b/utf8.h\nindex 1f8ecad..d3da96f 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -4,6 +4,7 @@\n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n int utf8_width(const char **start, size_t *remainder_p);\n+int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n int is_utf8(const char *text);\n int is_encoding_utf8(const char *name);\n-- \n1.8.2.82.gc24b958\n"},{"id":"214456","messageId":"1366100702-31745-8-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 07/13] utf8.c: add reencode_string_len() that can handle NULs in string","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:56Z","receivedAt":"2013-04-16T08:24:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n compat/precompose_utf8.c |  2 +-\n utf8.c                   | 10 +++++++---\n utf8.h                   | 19 ++++++++++++++++---\n 3 files changed, 24 insertions(+), 7 deletions(-)\n\ndiff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\nindex 8cf5955..d9203d0 100644\n--- a/compat/precompose_utf8.c\n+++ b/compat/precompose_utf8.c\n@@ -78,7 +78,7 @@ void precompose_argv(int argc, const char **argv)\n \t\tsize_t namelen;\n \t\toldarg = argv[i];\n \t\tif (has_non_ascii(oldarg, (size_t)-1, &namelen)) {\n-\t\t\tnewarg = reencode_string_iconv(oldarg, namelen, ic_precompose);\n+\t\t\tnewarg = reencode_string_iconv(oldarg, namelen, ic_precompose, NULL);\n \t\t\tif (newarg)\n \t\t\t\targv[i] = newarg;\n \t\t}\ndiff --git a/utf8.c b/utf8.c\nindex 9df087f..ac630bc 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -468,7 +468,7 @@ int utf8_fprintf(FILE *stream, const char *format, ...)\n #else\n \ttypedef char * iconv_ibp;\n #endif\n-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)\n+char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, int *outsz_p)\n {\n \tsize_t outsz, outalloc;\n \tchar *out, *outpos;\n@@ -502,13 +502,17 @@ char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)\n \t\t}\n \t\telse {\n \t\t\t*outpos = '\\0';\n+\t\t\tif (outsz_p)\n+\t\t\t\t*outsz_p = outpos - out;\n \t\t\tbreak;\n \t\t}\n \t}\n \treturn out;\n }\n \n-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding)\n+char *reencode_string_len(const char *in, int insz,\n+\t\t\t  const char *out_encoding, const char *in_encoding,\n+\t\t\t  int *outsz)\n {\n \ticonv_t conv;\n \tchar *out;\n@@ -534,7 +538,7 @@ char *reencode_string(const char *in, const char *out_encoding, const char *in_e\n \t\t\treturn NULL;\n \t}\n \n-\tout = reencode_string_iconv(in, strlen(in), conv);\n+\tout = reencode_string_iconv(in, insz, conv, outsz);\n \ticonv_close(conv);\n \treturn out;\n }\ndiff --git a/utf8.h b/utf8.h\nindex d3da96f..a43ef9a 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -17,12 +17,25 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \t\t\t     int indent, int indent2, int width);\n \n #ifndef NO_ICONV\n-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv);\n-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);\n+char *reencode_string_iconv(const char *in, size_t insz,\n+\t\t\t    iconv_t conv, int *outsz);\n+char *reencode_string_len(const char *in, int insz,\n+\t\t\t  const char *out_encoding,\n+\t\t\t  const char *in_encoding,\n+\t\t\t  int *outsz);\n #else\n-#define reencode_string(a,b,c) NULL\n+#define reencode_string_len(a,b,c,d,e) NULL\n #endif\n \n+static inline char *reencode_string(const char *in,\n+\t\t\t\t    const char *out_encoding,\n+\t\t\t\t    const char *in_encoding)\n+{\n+\treturn reencode_string_len(in, strlen(in),\n+\t\t\t\t   out_encoding, in_encoding,\n+\t\t\t\t   NULL);\n+}\n+\n int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);\n \n #endif\n-- \n1.8.2.82.gc24b958\n"},{"id":"214457","messageId":"1366100702-31745-9-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 08/13] pretty: two phase conversion for non utf-8 commits","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:57Z","receivedAt":"2013-04-16T08:24:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Always assume format_commit_item() takes an utf-8 string for string\nhandling simplicity (we can handle utf-8 strings, but can't with other\nencodings).\n\nIf commit message is in non-utf8, or output encoding is not, then the\ncommit is first converted to utf-8, processed, then output converted\nto output encoding. This of course only works with encodings that are\ncompatible with Unicode.\n\nThis also fixes the iso8859-1 test in t6006. It's supposed to create\nan iso8859-1 commit, but the commit content in t6006 is in UTF-8.\nt6006 is now converted back in UTF-8 (the downside is we can't put\nutf-8 strings there anymore).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c                   | 24 ++++++++++++++++++++++--\n t/t6006-rev-list-format.sh | 12 ++++++------\n 2 files changed, 28 insertions(+), 8 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex e0f93ba..5947275 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -954,7 +954,8 @@ static int format_reflog_person(struct strbuf *sb,\n \treturn format_person_part(sb, part, ident, strlen(ident), dmode);\n }\n \n-static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\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 {\n \tstruct format_commit_context *c = context;\n@@ -1193,7 +1194,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \treturn 0;\t/* unknown placeholder */\n }\n \n-static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n+static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n+\t\t\t\t const char *placeholder,\n \t\t\t\t void *context)\n {\n \tint consumed;\n@@ -1273,6 +1275,7 @@ void format_commit_message(const struct commit *commit,\n {\n \tstruct format_commit_context context;\n \tconst char *output_enc = pretty_ctx->output_encoding;\n+\tconst char *utf8 = \"UTF-8\";\n \n \tmemset(&context, 0, sizeof(context));\n \tcontext.commit = commit;\n@@ -1285,6 +1288,23 @@ void format_commit_message(const struct commit *commit,\n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n \n+\tif (output_enc) {\n+\t\tif (same_encoding(utf8, output_enc))\n+\t\t\toutput_enc = NULL;\n+\t} else {\n+\t\tif (context.commit_encoding &&\n+\t\t    !same_encoding(context.commit_encoding, utf8))\n+\t\t\toutput_enc = context.commit_encoding;\n+\t}\n+\n+\tif (output_enc) {\n+\t\tint outsz;\n+\t\tchar *out = reencode_string_len(sb->buf, sb->len,\n+\t\t\t\t\t\toutput_enc, utf8, &outsz);\n+\t\tif (out)\n+\t\t\tstrbuf_attach(sb, out, outsz, outsz + 1);\n+\t}\n+\n \tfree(context.commit_encoding);\n \tlogmsg_free(context.message, commit);\n \tfree(context.signature_check.gpg_output);\ndiff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh\nindex 3fc3b74..0393c9f 100755\n--- a/t/t6006-rev-list-format.sh\n+++ b/t/t6006-rev-list-format.sh\n@@ -184,7 +184,7 @@ Test printing of complex bodies\n \n This commit message is much longer than the others,\n and it will be encoded in iso8859-1. We should therefore\n-include an iso8859 character: ¡bueno!\n+include an iso8859 character: �bueno!\n EOF\n test_expect_success 'setup complex body' '\n git config i18n.commitencoding iso8859-1 &&\n@@ -192,14 +192,14 @@ git config i18n.commitencoding iso8859-1 &&\n '\n \n test_format complex-encoding %e <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n iso8859-1\n commit 131a310eb913d107dd3c09a65d1651175898735d\n commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n EOF\n \n test_format complex-subject %s <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n Test printing of complex bodies\n commit 131a310eb913d107dd3c09a65d1651175898735d\n changed foo\n@@ -208,17 +208,17 @@ added foo\n EOF\n \n test_format complex-body %b <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n This commit message is much longer than the others,\n and it will be encoded in iso8859-1. We should therefore\n-include an iso8859 character: ¡bueno!\n+include an iso8859 character: �bueno!\n \n commit 131a310eb913d107dd3c09a65d1651175898735d\n commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n EOF\n \n test_expect_success '%x00 shows NUL' '\n-\techo  >expect commit f58db70b055c5718631e5c61528b28b12090cdea &&\n+\techo  >expect commit 1ed88da4a5b5ed8c449114ac131efc62178734c3 &&\n \techo >>expect fooQbar &&\n \tgit rev-list -1 --format=foo%x00bar HEAD >actual.nul &&\n \tnul_to_q <actual.nul >actual &&\n-- \n1.8.2.82.gc24b958\n"},{"id":"214458","messageId":"1366100702-31745-10-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 09/13] pretty: split color parsing into a separate function","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:58Z","receivedAt":"2013-04-16T08:24:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c | 71 +++++++++++++++++++++++++++++++++++-----------------------------\n 1 file changed, 39 insertions(+), 32 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 5947275..e0413e3 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -954,6 +954,44 @@ static int format_reflog_person(struct strbuf *sb,\n \treturn format_person_part(sb, part, ident, strlen(ident), dmode);\n }\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+{\n+\tif (placeholder[1] == '(') {\n+\t\tconst char *begin = placeholder + 2;\n+\t\tconst char *end = strchr(begin, ')');\n+\t\tchar color[COLOR_MAXLEN];\n+\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\t\treturn end - placeholder + 1;\n+\t\t\tbegin += 5;\n+\t\t}\n+\t\tcolor_parse_mem(begin,\n+\t\t\t\tend - begin,\n+\t\t\t\t\"--pretty format\", color);\n+\t\tstrbuf_addstr(sb, color);\n+\t\treturn end - placeholder + 1;\n+\t}\n+\tif (!prefixcmp(placeholder + 1, \"red\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_RED);\n+\t\treturn 4;\n+\t} else if (!prefixcmp(placeholder + 1, \"green\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_GREEN);\n+\t\treturn 6;\n+\t} else if (!prefixcmp(placeholder + 1, \"blue\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_BLUE);\n+\t\treturn 5;\n+\t} else if (!prefixcmp(placeholder + 1, \"reset\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_RESET);\n+\t\treturn 6;\n+\t} else\n+\t\treturn 0;\n+}\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@@ -967,38 +1005,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t/* these are independent of the commit */\n \tswitch (placeholder[0]) {\n \tcase 'C':\n-\t\tif (placeholder[1] == '(') {\n-\t\t\tconst char *begin = placeholder + 2;\n-\t\t\tconst char *end = strchr(begin, ')');\n-\t\t\tchar color[COLOR_MAXLEN];\n-\n-\t\t\tif (!end)\n-\t\t\t\treturn 0;\n-\t\t\tif (!prefixcmp(begin, \"auto,\")) {\n-\t\t\t\tif (!want_color(c->pretty_ctx->color))\n-\t\t\t\t\treturn end - placeholder + 1;\n-\t\t\t\tbegin += 5;\n-\t\t\t}\n-\t\t\tcolor_parse_mem(begin,\n-\t\t\t\t\tend - begin,\n-\t\t\t\t\t\"--pretty format\", color);\n-\t\t\tstrbuf_addstr(sb, color);\n-\t\t\treturn end - placeholder + 1;\n-\t\t}\n-\t\tif (!prefixcmp(placeholder + 1, \"red\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_RED);\n-\t\t\treturn 4;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"green\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_GREEN);\n-\t\t\treturn 6;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"blue\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_BLUE);\n-\t\t\treturn 5;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"reset\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_RESET);\n-\t\t\treturn 6;\n-\t\t} else\n-\t\t\treturn 0;\n+\t\treturn parse_color(sb, placeholder, c);\n \tcase 'n':\t\t/* newline */\n \t\tstrbuf_addch(sb, '\\n');\n \t\treturn 1;\n-- \n1.8.2.82.gc24b958\n"},{"id":"214459","messageId":"1366100702-31745-11-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 10/13] pretty: add %C(auto) for auto-coloring","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:24:59Z","receivedAt":"2013-04-16T08:24:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is not simply convenient over %C(auto,xxx). Some placeholders\n(actually only one, %d) do multi coloring and we can't emit a multiple\ncolors with %C(auto,xxx).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  3 ++-\n pretty.c                         | 17 +++++++++++++++--\n 2 files changed, 17 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 6bde67e..bad627a 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -156,7 +156,8 @@ The placeholders are:\n   adding `auto,` at the beginning will emit color only when colors are\n   enabled for log output (by `color.diff`, `color.ui`, or `--color`, and\n   respecting the `auto` settings of the former if we are going to a\n-  terminal)\n+  terminal). `auto` alone (i.e. `%C(auto)`) will turn on auto coloring\n+  on the next placeholders until the color is switched again.\n - '%m': left, right or boundary mark\n - '%n': newline\n - '%%': a raw '%'\ndiff --git a/pretty.c b/pretty.c\nindex e0413e3..f385176 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -778,6 +778,7 @@ struct format_commit_context {\n \tchar *message;\n \tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n+\tint auto_color_next;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -1005,7 +1006,15 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t/* these are independent of the commit */\n \tswitch (placeholder[0]) {\n \tcase 'C':\n-\t\treturn parse_color(sb, placeholder, c);\n+\t\tif (!prefixcmp(placeholder + 1, \"(auto)\")) {\n+\t\t\tc->auto_color_next = 1;\n+\t\t\treturn 7;\n+\t\t} else {\n+\t\t\tint ret = parse_color(sb, placeholder, c);\n+\t\t\tif (ret)\n+\t\t\t\tc->auto_color_next = 0;\n+\t\t\treturn ret;\n+\t\t}\n \tcase 'n':\t\t/* newline */\n \t\tstrbuf_addch(sb, '\\n');\n \t\treturn 1;\n@@ -1051,13 +1060,17 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \n \tswitch (placeholder[0]) {\n \tcase 'H':\t\t/* commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color_next, DIFF_COMMIT));\n \t\tstrbuf_addstr(sb, sha1_to_hex(commit->object.sha1));\n+\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color_next, DIFF_RESET));\n \t\treturn 1;\n \tcase 'h':\t\t/* abbreviated commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color_next, DIFF_COMMIT));\n \t\tif (add_again(sb, &c->abbrev_commit_hash))\n \t\t\treturn 1;\n \t\tstrbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,\n \t\t\t\t\t\t     c->pretty_ctx->abbrev));\n+\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color_next, DIFF_RESET));\n \t\tc->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;\n \t\treturn 1;\n \tcase 'T':\t\t/* tree hash */\n@@ -1095,7 +1108,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\treturn 1;\n \tcase 'd':\n \t\tload_ref_decorations(DECORATE_SHORT_REFS);\n-\t\tformat_decorations(sb, commit, 0);\n+\t\tformat_decorations(sb, commit, c->auto_color_next);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\n-- \n1.8.2.82.gc24b958\n"},{"id":"214460","messageId":"1366100702-31745-12-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 11/13] pretty: support padding placeholders, %< %> and %><","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:25:00Z","receivedAt":"2013-04-16T08:25:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Either %<, %> or %>< standing before a placeholder specifies how many\ncolumns (at least as the placeholder can exceed it) it takes. Each\ndiffers on how spaces are padded:\n\n  %< pads on the right (aka left alignment)\n  %> pads on the left (aka right alignment)\n  %>< pads both ways equally (aka centered)\n\nThe (<N>) follows them, e.g. `%<(100)', to specify the number of\ncolumns the next placeholder takes.\n\nHowever, if '|' stands before (<N>), e.g. `%>|(100)', then the number\nof columns is calculated so that it reaches the Nth column on screen.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |   8 +++\n pretty.c                         | 117 +++++++++++++++++++++++++++++++++++-\n t/t4205-log-pretty-formats.sh    | 126 +++++++++++++++++++++++++++++++++++++++\n 3 files changed, 250 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex bad627a..e2345d2 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -164,6 +164,14 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n+- '%<(<N>)': make the next placeholder take at least N columns,\n+  padding spaces on the right if necessary\n+- '%<|(<N>)': make the next placeholder take at least until Nth\n+  columns, padding spaces on the right if necessary\n+- '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n+  respectively, but padding spaces on the left\n+- '%><(<N>)', '%><|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n+  respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\n revision traversal engine. For example, the `%g*` reflog options will\ndiff --git a/pretty.c b/pretty.c\nindex f385176..7debfb2 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -769,16 +769,25 @@ struct chunk {\n \tsize_t len;\n };\n \n+enum flush_type {\n+\tno_flush,\n+\tflush_right,\n+\tflush_left,\n+\tflush_both\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 \tchar *message;\n \tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n \tint auto_color_next;\n+\tint padding;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -993,6 +1002,52 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */\n \t\treturn 0;\n }\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+{\n+\tconst char *ch = placeholder;\n+\tenum flush_type flush_type;\n+\tint to_column = 0;\n+\n+\tswitch (*ch++) {\n+\tcase '<':\n+\t\tflush_type = flush_right;\n+\t\tbreak;\n+\tcase '>':\n+\t\tif (*ch == '<') {\n+\t\t\tflush_type = flush_both;\n+\t\t\tch++;\n+\t\t} else\n+\t\t\tflush_type = flush_left;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\t/* the next value means \"wide enough to that column\" */\n+\tif (*ch == '|') {\n+\t\tto_column = 1;\n+\t\tch++;\n+\t}\n+\n+\tif (*ch == '(') {\n+\t\tconst char *start = ch + 1;\n+\t\tconst char *end = strchr(start, ')');\n+\t\tchar *next;\n+\t\tint width;\n+\t\tif (!end || end == start)\n+\t\t\treturn 0;\n+\t\twidth = strtoul(start, &next, 10);\n+\t\tif (next == start || width == 0)\n+\t\t\treturn 0;\n+\t\tc->padding = to_column ? -width : width;\n+\t\tc->flush_type = flush_type;\n+\t\treturn end - placeholder + 1;\n+\t}\n+\treturn 0;\n+}\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@@ -1052,6 +1107,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\t\treturn end - placeholder + 1;\n \t\t} else\n \t\t\treturn 0;\n+\n+\tcase '<':\n+\tcase '>':\n+\t\treturn parse_padding_placeholder(sb, placeholder, c);\n \t}\n \n \t/* these depend on the commit */\n@@ -1214,6 +1273,59 @@ 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+{\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+\t\tif (!start)\n+\t\t\tstart = sb->buf;\n+\t\toccupied = utf8_strnwidth(start, -1, 1);\n+\t\tpadding = (-padding) - occupied;\n+\t}\n+\twhile (1) {\n+\t\tint modifier = *placeholder == 'C';\n+\t\tint consumed = format_commit_one(&local_sb, placeholder, c);\n+\t\ttotal_consumed += consumed;\n+\n+\t\tif (!modifier)\n+\t\t\tbreak;\n+\n+\t\tplaceholder += consumed;\n+\t\tif (*placeholder != '%')\n+\t\t\tbreak;\n+\t\tplaceholder++;\n+\t\ttotal_consumed++;\n+\t}\n+\tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\tif (len > padding)\n+\t\tstrbuf_addstr(sb, local_sb.buf);\n+\telse {\n+\t\tint sb_len = sb->len, offset = 0;\n+\t\tif (c->flush_type == flush_left)\n+\t\t\toffset = padding - len;\n+\t\telse if (c->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\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}\n+\tstrbuf_release(&local_sb);\n+\tc->flush_type = no_flush;\n+\treturn total_consumed;\n+}\n+\n static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\t\t\t const char *placeholder,\n \t\t\t\t void *context)\n@@ -1244,7 +1356,10 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\tplaceholder++;\n \n \torig_len = sb->len;\n-\tconsumed = format_commit_one(sb, placeholder, context);\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 \tif (magic == NO_MAGIC)\n \t\treturn consumed;\n \ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex 98a43d4..e9d7c2c 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -99,4 +99,130 @@ test_expect_failure 'NUL termination with --stat' '\n \ttest_i18ncmp expected actual\n '\n \n+test_expect_success 'setup more commits' '\n+\ttest_commit \"message one\" one one message-one &&\n+\ttest_commit \"message two\" two two message-two\n+'\n+\n+delete_trailing_dollar() {\n+\tsed 's/\\$$//'\n+}\n+\n+test_expect_success 'left alignment formatting' '\n+\tgit log --pretty=\"format:%<(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF | delete_trailing_dollar >expected &&\n+message two                             $\n+message one                             $\n+add bar                                 $\n+initial                                 $\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'left alignment formatting at the nth column' '\n+\tgit log --pretty=\"format:%h %<|(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF | delete_trailing_dollar >expected &&\n+fa33ab1 message two                     $\n+7cd6c63 message one                     $\n+1711bf9 add bar                         $\n+af20c06 initial                         $\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'left alignment formatting with no padding' '\n+\tgit log --pretty=\"format:%<(1)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF >expected &&\n+message two\n+message one\n+add bar\n+initial\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'right alignment formatting' '\n+\tgit log --pretty=\"format:%>(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF | delete_trailing_dollar >expected &&\n+                             message two$\n+                             message one$\n+                                 add bar$\n+                                 initial$\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'right alignment formatting at the nth column' '\n+\tgit log --pretty=\"format:%h %>|(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF | delete_trailing_dollar >expected &&\n+fa33ab1                      message two$\n+7cd6c63                      message one$\n+1711bf9                          add bar$\n+af20c06                          initial$\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'right alignment formatting with no padding' '\n+\tgit log --pretty=\"format:%>(1)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF >expected &&\n+message two\n+message one\n+add bar\n+initial\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'center alignment formatting' '\n+\tgit log --pretty=\"format:%><(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF | delete_trailing_dollar >expected &&\n+              message two               $\n+              message one               $\n+                add bar                 $\n+                initial                 $\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'center alignment formatting at the nth column' '\n+\tgit log --pretty=\"format:%h %><|(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF | delete_trailing_dollar >expected &&\n+fa33ab1           message two           $\n+7cd6c63           message one           $\n+1711bf9             add bar             $\n+af20c06             initial             $\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'center alignment formatting with no padding' '\n+\tgit log --pretty=\"format:%><(1)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF >expected &&\n+message two\n+message one\n+add bar\n+initial\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.8.2.82.gc24b958\n"},{"id":"214461","messageId":"1366100702-31745-13-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 12/13] pretty: support truncating in %>, %< and %><","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:25:01Z","receivedAt":"2013-04-16T08:25:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"%>(N,trunc) truncates the right part after N columns and replace the\nlast two letters with \"..\". ltrunc does the same on the left. mtrunc\ncuts the middle out.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  7 ++++--\n pretty.c                         | 51 +++++++++++++++++++++++++++++++++++++---\n t/t4205-log-pretty-formats.sh    | 39 ++++++++++++++++++++++++++++++\n utf8.c                           | 46 ++++++++++++++++++++++++++++++++++++\n utf8.h                           |  2 ++\n 5 files changed, 140 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex e2345d2..d3450bf 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -164,8 +164,11 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n-- '%<(<N>)': make the next placeholder take at least N columns,\n-  padding spaces on the right if necessary\n+- '%<(<N>[,trunc|ltrunc|mtrunc])': make the next placeholder take at\n+  least N columns, padding spaces on the right if necessary.\n+  Optionally truncate at the beginning (ltrunc), the middle (mtrunc)\n+  or the end (trunc) if the output is longer than N columns.\n+  Note that truncating only works correctly with N >= 2.\n - '%<|(<N>)': make the next placeholder take at least until Nth\n   columns, padding spaces on the right if necessary\n - '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\ndiff --git a/pretty.c b/pretty.c\nindex 7debfb2..a6c029c 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -776,6 +776,13 @@ enum flush_type {\n \tflush_both\n };\n \n+enum trunc_type {\n+\ttrunc_none,\n+\ttrunc_left,\n+\ttrunc_middle,\n+\ttrunc_right\n+};\n+\n struct format_commit_context {\n \tconst struct commit *commit;\n \tconst struct pretty_print_context *pretty_ctx;\n@@ -783,6 +790,7 @@ struct format_commit_context {\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@@ -1033,7 +1041,7 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \n \tif (*ch == '(') {\n \t\tconst char *start = ch + 1;\n-\t\tconst char *end = strchr(start, ')');\n+\t\tconst char *end = start + strcspn(start, \",)\");\n \t\tchar *next;\n \t\tint width;\n \t\tif (!end || end == start)\n@@ -1043,6 +1051,23 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\t\treturn 0;\n \t\tc->padding = to_column ? -width : width;\n \t\tc->flush_type = flush_type;\n+\n+\t\tif (*end == ',') {\n+\t\t\tstart = end + 1;\n+\t\t\tend = strchr(start, ')');\n+\t\t\tif (!end || end == start)\n+\t\t\t\treturn 0;\n+\t\t\tif (!prefixcmp(start, \"trunc)\"))\n+\t\t\t\tc->truncate = trunc_right;\n+\t\t\telse if (!prefixcmp(start, \"ltrunc)\"))\n+\t\t\t\tc->truncate = trunc_left;\n+\t\t\telse if (!prefixcmp(start, \"mtrunc)\"))\n+\t\t\t\tc->truncate = trunc_middle;\n+\t\t\telse\n+\t\t\t\treturn 0;\n+\t\t} else\n+\t\t\tc->truncate = trunc_none;\n+\n \t\treturn end - placeholder + 1;\n \t}\n \treturn 0;\n@@ -1302,9 +1327,29 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\ttotal_consumed++;\n \t}\n \tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n-\tif (len > padding)\n+\tif (len > padding) {\n+\t\tswitch (c->truncate) {\n+\t\tcase trunc_left:\n+\t\t\tstrbuf_utf8_replace(&local_sb,\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\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\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-\telse {\n+\t} else {\n \t\tint sb_len = sb->len, offset = 0;\n \t\tif (c->flush_type == flush_left)\n \t\t\toffset = padding - len;\ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex e9d7c2c..d1e9677 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -147,6 +147,45 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'left alignment formatting with trunc' '\n+\tgit log --pretty=\"format:%<(10,trunc)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF | delete_trailing_dollar >expected &&\n+message ..$\n+message ..$\n+add bar   $\n+initial   $\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'left alignment formatting with ltrunc' '\n+\tgit log --pretty=\"format:%<(10,ltrunc)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF | delete_trailing_dollar >expected &&\n+..sage two$\n+..sage one$\n+add bar   $\n+initial   $\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'left alignment formatting with mtrunc' '\n+\tgit log --pretty=\"format:%<(10,mtrunc)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF | delete_trailing_dollar >expected &&\n+mess.. two$\n+mess.. one$\n+add bar   $\n+initial   $\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'right alignment formatting' '\n \tgit log --pretty=\"format:%>(40)%s\" >actual &&\n \t# complete the incomplete line at the end\ndiff --git a/utf8.c b/utf8.c\nindex ac630bc..0428e76 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -421,6 +421,52 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \tfree(tmp);\n }\n \n+void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,\n+\t\t\t const char *subst)\n+{\n+\tstruct strbuf sb_dst = STRBUF_INIT;\n+\tchar *src = sb_src->buf;\n+\tchar *end = src + sb_src->len;\n+\tchar *dst;\n+\tint w = 0, subst_len = 0;\n+\n+\tif (subst)\n+\t\tsubst_len = strlen(subst);\n+\tstrbuf_grow(&sb_dst, sb_src->len + subst_len);\n+\tdst = sb_dst.buf;\n+\n+\twhile (src < end) {\n+\t\tchar *old;\n+\t\tsize_t n;\n+\n+\t\twhile ((n = display_mode_esc_sequence_len(src))) {\n+\t\t\tmemcpy(dst, src, n);\n+\t\t\tsrc += n;\n+\t\t\tdst += n;\n+\t\t}\n+\n+\t\told = src;\n+\t\tn = utf8_width((const char**)&src, NULL);\n+\t\tif (!src) \t/* broken utf-8, do nothing */\n+\t\t\treturn;\n+\t\tif (n && w >= pos && w < pos + width) {\n+\t\t\tif (subst) {\n+\t\t\t\tmemcpy(dst, subst, subst_len);\n+\t\t\t\tdst += subst_len;\n+\t\t\t\tsubst = NULL;\n+\t\t\t}\n+\t\t\tw += n;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tmemcpy(dst, old, src - old);\n+\t\tdst += src - old;\n+\t\tw += n;\n+\t}\n+\tstrbuf_setlen(&sb_dst, dst - sb_dst.buf);\n+\tstrbuf_attach(sb_src, strbuf_detach(&sb_dst, NULL),\n+\t\t      sb_dst.len, sb_dst.alloc);\n+}\n+\n int is_encoding_utf8(const char *name)\n {\n \tif (!name)\ndiff --git a/utf8.h b/utf8.h\nindex a43ef9a..edde8ee 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -15,6 +15,8 @@ void strbuf_add_wrapped_text(struct strbuf *buf,\n \t\tconst char *text, int indent, int indent2, int width);\n void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \t\t\t     int indent, int indent2, int width);\n+void strbuf_utf8_replace(struct strbuf *sb, int pos, int width,\n+\t\t\t const char *subst);\n \n #ifndef NO_ICONV\n char *reencode_string_iconv(const char *in, size_t insz,\n-- \n1.8.2.82.gc24b958\n"},{"id":"214462","messageId":"1366100702-31745-14-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 13/13] pretty: support %>> that steal trailing spaces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:25:02Z","receivedAt":"2013-04-16T08:25:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is pretty useful in `%<(100)%s%Cred%>(20)% an' where %s does not\nuse up all 100 columns and %an needs more than 20 columns. By\nreplacing %>(20) with %>>(20), %an can steal spaces from %s.\n\n%>> understands escape sequences, so %Cred does not stop it from\nstealing spaces in %<(100).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  5 ++++-\n pretty.c                         | 34 ++++++++++++++++++++++++++++++++++\n t/t4205-log-pretty-formats.sh    | 14 ++++++++++++++\n utf8.c                           |  2 +-\n utf8.h                           |  1 +\n 5 files changed, 54 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex d3450bf..c96ff41 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -173,7 +173,10 @@ The placeholders are:\n   columns, padding spaces on the right if necessary\n - '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n   respectively, but padding spaces on the left\n-- '%><(<N>)', '%><|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n+- '%>>(<N>)', '%>>|(<N>)': similar to '%>(<N>)', '%>|(<N>)'\n+  respectively, except that if the next placeholder takes more spaces\n+  than given and there are spaces on its left, use those spaces\n+- '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'\n   respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\ndiff --git a/pretty.c b/pretty.c\nindex a6c029c..306ba08 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -773,6 +773,7 @@ enum flush_type {\n \tno_flush,\n \tflush_right,\n \tflush_left,\n+\tflush_left_and_steal,\n \tflush_both\n };\n \n@@ -1026,6 +1027,9 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\tif (*ch == '<') {\n \t\t\tflush_type = flush_both;\n \t\t\tch++;\n+\t\t} else if (*ch == '>') {\n+\t\t\tflush_type = flush_left_and_steal;\n+\t\t\tch++;\n \t\t} else\n \t\t\tflush_type = flush_left;\n \t\tbreak;\n@@ -1327,6 +1331,36 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\ttotal_consumed++;\n \t}\n \tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\n+\tif (c->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+\t\t\tif (*ch == ' ') {\n+\t\t\t\tch--;\n+\t\t\t\tpadding++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\t/* check for trailing ansi sequences */\n+\t\t\tif (*ch != 'm')\n+\t\t\t\tbreak;\n+\t\t\tp = ch - 1;\n+\t\t\twhile (ch - p < 10 && *p != '\\033')\n+\t\t\t\tp--;\n+\t\t\tif (*p != '\\033' ||\n+\t\t\t    ch + 1 - p != display_mode_esc_sequence_len(p))\n+\t\t\t\tbreak;\n+\t\t\t/*\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\tch = p - 1;\n+\t\t}\n+\t\tstrbuf_setlen(sb, ch + 1 - sb->buf);\n+\t\tc->flush_type = flush_left;\n+\t}\n+\n \tif (len > padding) {\n \t\tswitch (c->truncate) {\n \t\tcase trunc_left:\ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex d1e9677..d006f76 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -264,4 +264,18 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'left/right alignment formatting with stealing' '\n+\tgit commit --amend -m short --author \"long long long <long@me.com>\" &&\n+\tgit log --pretty=\"format:%<(10,trunc)%s%>>(10,ltrunc)% an\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF | delete_trailing_dollar >expected &&\n+short long  long long$\n+message ..   A U Thor$\n+add bar      A U Thor$\n+initial      A U Thor$\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n test_done\ndiff --git a/utf8.c b/utf8.c\nindex 0428e76..76e5c04 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,7 +9,7 @@ struct interval {\n   int last;\n };\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n+size_t display_mode_esc_sequence_len(const char *s)\n {\n \tconst char *p = s;\n \tif (*p++ != '\\033')\ndiff --git a/utf8.h b/utf8.h\nindex edde8ee..32a7bfb 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -3,6 +3,7 @@\n \n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n+size_t display_mode_esc_sequence_len(const char *s);\n int utf8_width(const char **start, size_t *remainder_p);\n int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n-- \n1.8.2.82.gc24b958\n"},{"id":"214463","messageId":"CACsJy8D9+4gPmevygE+5qVjoNLb6E3tppeayZ89qGBsGbdmZ8w@mail.gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 07/13] utf8.c: add reencode_string_len() that can handle NULs in string","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-16T08:30:28Z","receivedAt":"2013-04-16T08:30:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Torsten,\n\nI can't compile compat/precomposed_utf8.c on linux even though I make\nsome changes there. Can you check if I break something? I'm pretty\nsure I don't, but just in case.\n\nOn Tue, Apr 16, 2013 at 6:24 PM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  compat/precompose_utf8.c |  2 +-\n>  utf8.c                   | 10 +++++++---\n>  utf8.h                   | 19 ++++++++++++++++---\n>  3 files changed, 24 insertions(+), 7 deletions(-)\n>\n> diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\n> index 8cf5955..d9203d0 100644\n> --- a/compat/precompose_utf8.c\n> +++ b/compat/precompose_utf8.c\n> @@ -78,7 +78,7 @@ void precompose_argv(int argc, const char **argv)\n>                 size_t namelen;\n>                 oldarg = argv[i];\n>                 if (has_non_ascii(oldarg, (size_t)-1, &namelen)) {\n> -                       newarg = reencode_string_iconv(oldarg, namelen, ic_precompose);\n> +                       newarg = reencode_string_iconv(oldarg, namelen, ic_precompose, NULL);\n>                         if (newarg)\n>                                 argv[i] = newarg;\n>                 }\n> diff --git a/utf8.c b/utf8.c\n> index 9df087f..ac630bc 100644\n> --- a/utf8.c\n> +++ b/utf8.c\n> @@ -468,7 +468,7 @@ int utf8_fprintf(FILE *stream, const char *format, ...)\n>  #else\n>         typedef char * iconv_ibp;\n>  #endif\n> -char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)\n> +char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, int *outsz_p)\n>  {\n>         size_t outsz, outalloc;\n>         char *out, *outpos;\n> @@ -502,13 +502,17 @@ char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)\n>                 }\n>                 else {\n>                         *outpos = '\\0';\n> +                       if (outsz_p)\n> +                               *outsz_p = outpos - out;\n>                         break;\n>                 }\n>         }\n>         return out;\n>  }\n>\n> -char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding)\n> +char *reencode_string_len(const char *in, int insz,\n> +                         const char *out_encoding, const char *in_encoding,\n> +                         int *outsz)\n>  {\n>         iconv_t conv;\n>         char *out;\n> @@ -534,7 +538,7 @@ char *reencode_string(const char *in, const char *out_encoding, const char *in_e\n>                         return NULL;\n>         }\n>\n> -       out = reencode_string_iconv(in, strlen(in), conv);\n> +       out = reencode_string_iconv(in, insz, conv, outsz);\n>         iconv_close(conv);\n>         return out;\n>  }\n> diff --git a/utf8.h b/utf8.h\n> index d3da96f..a43ef9a 100644\n> --- a/utf8.h\n> +++ b/utf8.h\n> @@ -17,12 +17,25 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n>                              int indent, int indent2, int width);\n>\n>  #ifndef NO_ICONV\n> -char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv);\n> -char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);\n> +char *reencode_string_iconv(const char *in, size_t insz,\n> +                           iconv_t conv, int *outsz);\n> +char *reencode_string_len(const char *in, int insz,\n> +                         const char *out_encoding,\n> +                         const char *in_encoding,\n> +                         int *outsz);\n>  #else\n> -#define reencode_string(a,b,c) NULL\n> +#define reencode_string_len(a,b,c,d,e) NULL\n>  #endif\n>\n> +static inline char *reencode_string(const char *in,\n> +                                   const char *out_encoding,\n> +                                   const char *in_encoding)\n> +{\n> +       return reencode_string_len(in, strlen(in),\n> +                                  out_encoding, in_encoding,\n> +                                  NULL);\n> +}\n> +\n>  int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);\n>\n>  #endif\n> --\n> 1.8.2.82.gc24b958\n>\n--\nDuy\n"},{"id":"214490","messageId":"516D6466.1080906@web.de","threadId":"33195","inReplyTo":"516D57BD.7080208@web.de","subject":"Re: [PATCH v3 00/13] nd/pretty-formats","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2013-04-16T14:47:02Z","receivedAt":"2013-04-16T14:47:02Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"The short version:\nall applied, compiled and test OK.\n\nPossible minor nits from apply:\napplying: pretty: support padding placeholders, %< %> and %><\n/Users/tb/projects/git/tb.duy/.git/rebase-apply/patch:253: indent with spaces.\n                             message two$\n/Users/tb/projects/git/tb.duy/.git/rebase-apply/patch:254: indent with spaces.\n                             message one$\n/Users/tb/projects/git/tb.duy/.git/rebase-apply/patch:255: indent with spaces.\n                                 add bar$\n/Users/tb/projects/git/tb.duy/.git/rebase-apply/patch:256: indent with spaces.\n                                 initial$\n/Users/tb/projects/git/tb.duy/.git/rebase-apply/patch:292: indent with spaces.\n              message two               $\nwarning: squelched 3 whitespace errors\n=========================\nAnd a possible micronit: what happened to that?\n\nOn Sun, Mar 31, 2013 at 12:06 AM, Torsten Bögershausen <tboegi@web.de> wrote:\n\n> On 30.03.13 10:35, Nguyễn Thái Ngọc Duy wrote:\n> [...]\n> The short version of a review:\n> Would it make sense to leave  reencode_string() as it is,\n> and add a new function reencode_string_len()\n\nHmm.. yeah.\n\n/Torsten\n"},{"id":"214534","messageId":"7v1uaaky2q.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"1366100702-31745-12-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 11/13] pretty: support padding placeholders, %< %> and %><","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-16T20:41:33Z","receivedAt":"2013-04-16T20:41:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> +delete_trailing_dollar() {\n> +\tsed 's/\\$$//'\n> +}\n\nThis is what we have qz_to_tab_space for, isn't it?  With it, you\ncan not just avoid \"trailing whitespace\", but also \"indent with\nspaces\", like this:\n\nQ         message thousand    Z\n"},{"id":"214536","messageId":"7vwqs2jjez.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"7v1uaaky2q.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 11/13] pretty: support padding placeholders, %< %> and %><","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-16T20:43:32Z","receivedAt":"2013-04-16T20:43:32Z","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> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> +delete_trailing_dollar() {\n>> +\tsed 's/\\$$//'\n>> +}\n>\n> This is what we have qz_to_tab_space for, isn't it?  With it, you\n> can not just avoid \"trailing whitespace\", but also \"indent with\n> spaces\", like this:\n>\n> Q         message thousand    Z\n\nSorry, the above needs s/Q/Z/;\n"},{"id":"214545","messageId":"7vd2tujh3o.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"1366100702-31745-11-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 10/13] pretty: add %C(auto) for auto-coloring","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-16T21:33:31Z","receivedAt":"2013-04-16T21:33:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This is not simply convenient over %C(auto,xxx). Some placeholders\n> (actually only one, %d) do multi coloring and we can't emit a multiple\n> colors with %C(auto,xxx).\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/pretty-formats.txt |  3 ++-\n>  pretty.c                         | 17 +++++++++++++++--\n>  2 files changed, 17 insertions(+), 3 deletions(-)\n>\n> diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\n> index 6bde67e..bad627a 100644\n> --- a/Documentation/pretty-formats.txt\n> +++ b/Documentation/pretty-formats.txt\n> @@ -156,7 +156,8 @@ The placeholders are:\n>    adding `auto,` at the beginning will emit color only when colors are\n>    enabled for log output (by `color.diff`, `color.ui`, or `--color`, and\n>    respecting the `auto` settings of the former if we are going to a\n> -  terminal)\n> +  terminal). `auto` alone (i.e. `%C(auto)`) will turn on auto coloring\n> +  on the next placeholders until the color is switched again.\n>  - '%m': left, right or boundary mark\n>  - '%n': newline\n>  - '%%': a raw '%'\n\nGood addition.\n\nIn the previous round, it used to be \"%C(auto)\" was remembered in\nthis field to color the _next_ one, and the momorized value was\ntoggled off.  As the field no longer toggles automatically once it\nis used, perhaps it should simply be called \"auto_color\" without\n\"next\", though?\n\nI can locally tweak that before queuing if you think it is better.\n\n> diff --git a/pretty.c b/pretty.c\n> index e0413e3..f385176 100644\n> --- a/pretty.c\n> +++ b/pretty.c\n> @@ -778,6 +778,7 @@ struct format_commit_context {\n>  \tchar *message;\n>  \tchar *commit_encoding;\n>  \tsize_t width, indent1, indent2;\n> +\tint auto_color_next;\n>  \n>  \t/* These offsets are relative to the start of the commit message. */\n>  \tstruct chunk author;\n> @@ -1005,7 +1006,15 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n>  \t/* these are independent of the commit */\n>  \tswitch (placeholder[0]) {\n>  \tcase 'C':\n> -\t\treturn parse_color(sb, placeholder, c);\n> +\t\tif (!prefixcmp(placeholder + 1, \"(auto)\")) {\n> +\t\t\tc->auto_color_next = 1;\n> +\t\t\treturn 7;\n> +\t\t} else {\n> +\t\t\tint ret = parse_color(sb, placeholder, c);\n> +\t\t\tif (ret)\n> +\t\t\t\tc->auto_color_next = 0;\n> +\t\t\treturn ret;\n> +\t\t}\n\nThis is to handle a corrupt input, e.g. \"%C(auto)%Cbleu%H\" where\n(perhaps deprecated) \"%Cblue\" is misspelled, and parse_color()\nreturns 0 without consuming any byte.\n\nDoes it make sense not to turn auto off in such a case?  Otherwise\nthe above would become\n\n\tif (!prefixcmp(placeholder + 1, \"(auto)\")) {\n        \tc->auto_color_next = 1;\n                return 7; /* consumed 7 bytes, \"C(auto)\" */\n\t}\n        c->auto_color_next = 0;\n        return parse_color(sb, placeholder, c);\n\nwhich may be simpler.  When we see %C, previous %C(auto) is\ncancelled.\n\n>  \tcase 'n':\t\t/* newline */\n>  \t\tstrbuf_addch(sb, '\\n');\n>  \t\treturn 1;\n> @@ -1051,13 +1060,17 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n>  \n>  \tswitch (placeholder[0]) {\n>  \tcase 'H':\t\t/* commit hash */\n> +\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color_next, DIFF_COMMIT));\n>  \t\tstrbuf_addstr(sb, sha1_to_hex(commit->object.sha1));\n> +\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color_next, DIFF_RESET));\n>  \t\treturn 1;\n"},{"id":"214547","messageId":"7v8v4ijgx1.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"1366100702-31745-11-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 10/13] pretty: add %C(auto) for auto-coloring","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-16T21:37:30Z","receivedAt":"2013-04-16T21:37:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n>  \tcase 'h':\t\t/* abbreviated commit hash */\n> +\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color_next, DIFF_COMMIT));\n>  \t\tif (add_again(sb, &c->abbrev_commit_hash))\n>  \t\t\treturn 1;\n\nDoesn't this early return mess things up?\n\n>  \t\tstrbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,\n>  \t\t\t\t\t\t     c->pretty_ctx->abbrev));\n> +\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color_next, DIFF_RESET));\n>  \t\tc->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;\n>  \t\treturn 1;\n"},{"id":"214594","messageId":"CACsJy8AsWP8C4W=h8eVQbHMvvNT8Fd6iZV+P6edooMacGHfRwg@mail.gmail.com","threadId":"33195","inReplyTo":"7v1uaaky2q.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 11/13] pretty: support padding placeholders, %< %> and %><","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-17T09:45:41Z","receivedAt":"2013-04-17T09:45:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Apr 17, 2013 at 6:41 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> +delete_trailing_dollar() {\n>> +     sed 's/\\$$//'\n>> +}\n>\n> This is what we have qz_to_tab_space for, isn't it?\n\nThanks! I looked for something like this, but my eyes focused on\nsimilar sed line and forgot the \"tr\"\n--\nDuy\n"},{"id":"214596","messageId":"CACsJy8DLj90aynu376O0LZKpQqvcmyoA_8gMNoek3WK6HTRtLQ@mail.gmail.com","threadId":"33195","inReplyTo":"7vd2tujh3o.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 10/13] pretty: add %C(auto) for auto-coloring","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-17T09:55:25Z","receivedAt":"2013-04-17T09:55:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Apr 17, 2013 at 7:33 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> @@ -1005,7 +1006,15 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n>>       /* these are independent of the commit */\n>>       switch (placeholder[0]) {\n>>       case 'C':\n>> -             return parse_color(sb, placeholder, c);\n>> +             if (!prefixcmp(placeholder + 1, \"(auto)\")) {\n>> +                     c->auto_color_next = 1;\n>> +                     return 7;\n>> +             } else {\n>> +                     int ret = parse_color(sb, placeholder, c);\n>> +                     if (ret)\n>> +                             c->auto_color_next = 0;\n>> +                     return ret;\n>> +             }\n>\n> This is to handle a corrupt input, e.g. \"%C(auto)%Cbleu%H\" where\n> (perhaps deprecated) \"%Cblue\" is misspelled, and parse_color()\n> returns 0 without consuming any byte.\n>\n> Does it make sense not to turn auto off in such a case?\n\nWe don't have any mechanism to report invalid %C. Instead we let them\nthrough as literals. If they are literals, they should not have any\nside effects. So I think it makes sense not to turn off things.\n\n> Otherwise the above would become\n>\n>         if (!prefixcmp(placeholder + 1, \"(auto)\")) {\n>                 c->auto_color_next = 1;\n>                 return 7; /* consumed 7 bytes, \"C(auto)\" */\n>         }\n>         c->auto_color_next = 0;\n>         return parse_color(sb, placeholder, c);\n>\n> which may be simpler.  When we see %C, previous %C(auto) is\n> cancelled.\n\nIf we do this, maybe we could show invalid %C with blinking. Quite\ncatchy and might make the user wonder why. Of course it won't work\nwithout coloring. But who would add %C in that case.\n--\nDuy\n"},{"id":"214613","messageId":"7v4nf5goqx.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"CACsJy8DLj90aynu376O0LZKpQqvcmyoA_8gMNoek3WK6HTRtLQ@mail.gmail.com","subject":"Re: [PATCH v3 10/13] pretty: add %C(auto) for auto-coloring","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-17T15:28:54Z","receivedAt":"2013-04-17T15:28:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> This is to handle a corrupt input, e.g. \"%C(auto)%Cbleu%H\" where\n>> (perhaps deprecated) \"%Cblue\" is misspelled, and parse_color()\n>> returns 0 without consuming any byte.\n>>\n>> Does it make sense not to turn auto off in such a case?\n>\n> We don't have any mechanism to report invalid %C. Instead we let them\n> through as literals. If they are literals, they should not have any\n> side effects. So I think it makes sense not to turn off things.\n\nOh, you are right.  Thanks for a dose of sanity.\n"},{"id":"214738","messageId":"7vobdb7ntm.fsf@alter.siamese.dyndns.org","threadId":"33195","inReplyTo":"1366100702-31745-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 07/13] utf8.c: add reencode_string_len() that can handle NULs in string","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-04-18T17:25:57Z","receivedAt":"2013-04-18T17:25:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> diff --git a/utf8.h b/utf8.h\n> index d3da96f..a43ef9a 100644\n> --- a/utf8.h\n> +++ b/utf8.h\n> @@ -17,12 +17,25 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n>  \t\t\t     int indent, int indent2, int width);\n>  \n>  #ifndef NO_ICONV\n> -char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv);\n> -char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);\n> +char *reencode_string_iconv(const char *in, size_t insz,\n> +\t\t\t    iconv_t conv, int *outsz);\n> +char *reencode_string_len(const char *in, int insz,\n> +\t\t\t  const char *out_encoding,\n> +\t\t\t  const char *in_encoding,\n> +\t\t\t  int *outsz);\n>  #else\n> -#define reencode_string(a,b,c) NULL\n> +#define reencode_string_len(a,b,c,d,e) NULL\n>  #endif\n>  \n> +static inline char *reencode_string(const char *in,\n> +\t\t\t\t    const char *out_encoding,\n> +\t\t\t\t    const char *in_encoding)\n> +{\n> +\treturn reencode_string_len(in, strlen(in),\n> +\t\t\t\t   out_encoding, in_encoding,\n> +\t\t\t\t   NULL);\n> +}\n> +\n>  int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);\n>  \n>  #endif\n\nNicely done.\n"},{"id":"214770","messageId":"1366326532-28517-1-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366100702-31745-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 00/13] nd/pretty-formats","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:39Z","receivedAt":"2013-04-18T23:08:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v4 fixes comments from v3, mainly in the auto-coloring patch, and uses\nqz_to_tab_space for changes in t4205. It also fixes a coding style\nissue in 06/13, spotted in v2 but I missed it in v3.\n\nNguyễn Thái Ngọc Duy (13):\n  pretty: save commit encoding from logmsg_reencode if the caller needs it\n  pretty: get the correct encoding for --pretty:format=%e\n  pretty-formats.txt: wrap long lines\n  pretty: share code between format_decoration and show_decorations\n  utf8.c: move display_mode_esc_sequence_len() for use by other functions\n  utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences\n  utf8.c: add reencode_string_len() that can handle NULs in string\n  pretty: two phase conversion for non utf-8 commits\n  pretty: split color parsing into a separate function\n  pretty: add %C(auto) for auto-coloring\n  pretty: support padding placeholders, %< %> and %><\n  pretty: support truncating in %>, %< and %><\n  pretty: support %>> that steal trailing spaces\n\n Documentation/pretty-formats.txt |  35 +++-\n builtin/blame.c                  |   2 +-\n builtin/commit.c                 |   2 +-\n commit.h                         |   1 +\n compat/precompose_utf8.c         |   2 +-\n log-tree.c                       |  48 ++++--\n log-tree.h                       |   1 +\n pretty.c                         | 358 ++++++++++++++++++++++++++++++++-------\n revision.c                       |   2 +-\n t/t4205-log-pretty-formats.sh    | 175 +++++++++++++++++++\n t/t4207-log-decoration-colors.sh |   8 +-\n t/t6006-rev-list-format.sh       |  12 +-\n utf8.c                           | 104 +++++++++---\n utf8.h                           |  23 ++-\n 14 files changed, 648 insertions(+), 125 deletions(-)\n\n-- \n1.8.2.82.gc24b958\n"},{"id":"214771","messageId":"1366326532-28517-2-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 01/13] pretty: save commit encoding from logmsg_reencode if the caller needs it","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:40Z","receivedAt":"2013-04-18T23:08:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The commit encoding is parsed by logmsg_reencode, there's no need for\nthe caller to re-parse it again. The reencoded message now has the new\nencoding, not the original one. The caller would need to read commit\nobject again before parsing.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/blame.c  |  2 +-\n builtin/commit.c |  2 +-\n commit.h         |  1 +\n pretty.c         | 16 ++++++++++++----\n revision.c       |  2 +-\n 5 files changed, 16 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 86100e9..104a948 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1425,7 +1425,7 @@ static void get_commit_info(struct commit *commit,\n \tcommit_info_init(ret);\n \n \tencoding = get_log_output_encoding();\n-\tmessage = logmsg_reencode(commit, encoding);\n+\tmessage = logmsg_reencode(commit, NULL, encoding);\n \tget_ac_line(message, \"\\nauthor \",\n \t\t    &ret->author, &ret->author_mail,\n \t\t    &ret->author_time, &ret->author_tz);\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 4620437..d2f30d9 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -955,7 +955,7 @@ static const char *read_commit_message(const char *name)\n \tif (!commit)\n \t\tdie(_(\"could not lookup commit %s\"), name);\n \tout_enc = get_commit_output_encoding();\n-\treturn logmsg_reencode(commit, out_enc);\n+\treturn logmsg_reencode(commit, NULL, out_enc);\n }\n \n static int parse_and_validate_options(int argc, const char *argv[],\ndiff --git a/commit.h b/commit.h\nindex 87b4b6c..ad55213 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -101,6 +101,7 @@ struct userformat_want {\n extern int has_non_ascii(const char *text);\n struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */\n extern char *logmsg_reencode(const struct commit *commit,\n+\t\t\t     char **commit_encoding,\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 *);\ndiff --git a/pretty.c b/pretty.c\nindex d3a82d2..c361b9b 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -594,6 +594,7 @@ static char *replace_encoding_header(char *buf, const char *encoding)\n }\n \n char *logmsg_reencode(const struct commit *commit,\n+\t\t      char **commit_encoding,\n \t\t      const char *output_encoding)\n {\n \tstatic const char *utf8 = \"UTF-8\";\n@@ -615,9 +616,15 @@ char *logmsg_reencode(const struct commit *commit,\n \t\t\t    sha1_to_hex(commit->object.sha1), typename(type));\n \t}\n \n-\tif (!output_encoding || !*output_encoding)\n+\tif (!output_encoding || !*output_encoding) {\n+\t\tif (commit_encoding)\n+\t\t\t*commit_encoding =\n+\t\t\t\tget_header(commit, msg, \"encoding\");\n \t\treturn msg;\n+\t}\n \tencoding = get_header(commit, msg, \"encoding\");\n+\tif (commit_encoding)\n+\t\t*commit_encoding = encoding;\n \tuse_encoding = encoding ? encoding : utf8;\n \tif (same_encoding(use_encoding, output_encoding)) {\n \t\t/*\n@@ -658,7 +665,8 @@ char *logmsg_reencode(const struct commit *commit,\n \tif (out)\n \t\tout = replace_encoding_header(out, output_encoding);\n \n-\tfree(encoding);\n+\tif (!commit_encoding)\n+\t\tfree(encoding);\n \t/*\n \t * If the re-encoding failed, out might be NULL here; in that\n \t * case we just return the commit message verbatim.\n@@ -1288,7 +1296,7 @@ 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, output_enc);\n+\tcontext.message = logmsg_reencode(commit, NULL, output_enc);\n \n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n@@ -1451,7 +1459,7 @@ void pretty_print_commit(const struct pretty_print_context *pp,\n \t}\n \n \tencoding = get_log_output_encoding();\n-\tmsg = reencoded = logmsg_reencode(commit, encoding);\n+\tmsg = reencoded = logmsg_reencode(commit, NULL, encoding);\n \n \tif (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)\n \t\tindent = 0;\ndiff --git a/revision.c b/revision.c\nindex 71e62d8..2e77397 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2314,7 +2314,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)\n \t * in it.\n \t */\n \tencoding = get_log_output_encoding();\n-\tmessage = logmsg_reencode(commit, encoding);\n+\tmessage = logmsg_reencode(commit, NULL, encoding);\n \n \t/* Copy the commit to temporary if we are using \"fake\" headers */\n \tif (buf.len)\n-- \n1.8.2.82.gc24b958\n"},{"id":"214772","messageId":"1366326532-28517-3-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 02/13] pretty: get the correct encoding for --pretty:format=%e","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:41Z","receivedAt":"2013-04-18T23:08:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"parse_commit_header() provides the commit encoding for '%e' and it\nreads it from the re-encoded message, which contains the new encoding,\nnot the original one in the commit object. This never happens because\n--pretty=format:xxx never respects i18n.logoutputencoding. But that's\na different story.\n\nGet the commit encoding from logmsg_reencode() instead.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c | 13 +++++++------\n 1 file changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex c361b9b..e59688b 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -776,12 +776,12 @@ struct format_commit_context {\n \tunsigned commit_message_parsed:1;\n \tstruct signature_check signature_check;\n \tchar *message;\n+\tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n \tstruct chunk committer;\n-\tstruct chunk encoding;\n \tsize_t message_off;\n \tsize_t subject_off;\n \tsize_t body_off;\n@@ -828,9 +828,6 @@ static void parse_commit_header(struct format_commit_context *context)\n \t\t} else if (!prefixcmp(msg + i, \"committer \")) {\n \t\t\tcontext->committer.off = i + 10;\n \t\t\tcontext->committer.len = eol - i - 10;\n-\t\t} else if (!prefixcmp(msg + i, \"encoding \")) {\n-\t\t\tcontext->encoding.off = i + 9;\n-\t\t\tcontext->encoding.len = eol - i - 9;\n \t\t}\n \t\ti = eol;\n \t}\n@@ -1185,7 +1182,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\t\t\t   msg + c->committer.off, c->committer.len,\n \t\t\t\t   c->pretty_ctx->date_mode);\n \tcase 'e':\t/* encoding */\n-\t\tstrbuf_add(sb, msg + c->encoding.off, c->encoding.len);\n+\t\tif (c->commit_encoding)\n+\t\t\tstrbuf_addstr(sb, c->commit_encoding);\n \t\treturn 1;\n \tcase 'B':\t/* raw body */\n \t\t/* message_off is always left at the initial newline */\n@@ -1296,11 +1294,14 @@ 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, NULL, output_enc);\n+\tcontext.message = logmsg_reencode(commit,\n+\t\t\t\t\t  &context.commit_encoding,\n+\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 \n+\tfree(context.commit_encoding);\n \tlogmsg_free(context.message, commit);\n \tfree(context.signature_check.gpg_output);\n \tfree(context.signature_check.signer);\n-- \n1.8.2.82.gc24b958\n"},{"id":"214773","messageId":"1366326532-28517-4-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 03/13] pretty-formats.txt: wrap long lines","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:42Z","receivedAt":"2013-04-18T23:08:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt | 18 ++++++++++++------\n 1 file changed, 12 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex afac703..6bde67e 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -106,18 +106,22 @@ The placeholders are:\n - '%P': parent hashes\n - '%p': abbreviated parent hashes\n - '%an': author name\n-- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1]\n+  or linkgit:git-blame[1])\n - '%ae': author email\n-- '%aE': author email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%aE': author email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ad': author date (format respects --date= option)\n - '%aD': author date, RFC2822 style\n - '%ar': author date, relative\n - '%at': author date, UNIX timestamp\n - '%ai': author date, ISO 8601 format\n - '%cn': committer name\n-- '%cN': committer name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%cN': committer name (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ce': committer email\n-- '%cE': committer email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%cE': committer email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%cd': committer date\n - '%cD': committer date, RFC2822 style\n - '%cr': committer date, relative\n@@ -138,9 +142,11 @@ The placeholders are:\n - '%gD': reflog selector, e.g., `refs/stash@{1}`\n - '%gd': shortened reflog selector, e.g., `stash@{1}`\n - '%gn': reflog identity name\n-- '%gN': reflog identity name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%gN': reflog identity name (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%ge': reflog identity email\n-- '%gE': reflog identity email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])\n+- '%gE': reflog identity email (respecting .mailmap, see\n+  linkgit:git-shortlog[1] or linkgit:git-blame[1])\n - '%gs': reflog subject\n - '%Cred': switch color to red\n - '%Cgreen': switch color to green\n-- \n1.8.2.82.gc24b958\n"},{"id":"214774","messageId":"1366326532-28517-5-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 04/13] pretty: share code between format_decoration and show_decorations","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:43Z","receivedAt":"2013-04-18T23:08:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This also adds color support to format_decorations()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n log-tree.c                       | 48 ++++++++++++++++++++++++++--------------\n log-tree.h                       |  1 +\n pretty.c                         | 20 ++---------------\n t/t4207-log-decoration-colors.sh |  8 +++----\n 4 files changed, 39 insertions(+), 38 deletions(-)\n\ndiff --git a/log-tree.c b/log-tree.c\nindex 7cc7d59..1946e9c 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -175,36 +175,52 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre\n \t}\n }\n \n-void show_decorations(struct rev_info *opt, struct commit *commit)\n+/*\n+ * The caller makes sure there is no funny color before\n+ * calling. format_decorations makes sure the same after return.\n+ */\n+void format_decorations(struct strbuf *sb,\n+\t\t\tconst struct commit *commit,\n+\t\t\tint use_color)\n {\n \tconst char *prefix;\n \tstruct name_decoration *decoration;\n \tconst char *color_commit =\n-\t\tdiff_get_color_opt(&opt->diffopt, DIFF_COMMIT);\n+\t\tdiff_get_color(use_color, DIFF_COMMIT);\n \tconst char *color_reset =\n-\t\tdecorate_get_color_opt(&opt->diffopt, DECORATION_NONE);\n+\t\tdecorate_get_color(use_color, DECORATION_NONE);\n \n-\tif (opt->show_source && commit->util)\n-\t\tprintf(\"\\t%s\", (char *) commit->util);\n-\tif (!opt->show_decorations)\n-\t\treturn;\n \tdecoration = lookup_decoration(&name_decoration, &commit->object);\n \tif (!decoration)\n \t\treturn;\n \tprefix = \" (\";\n \twhile (decoration) {\n-\t\tprintf(\"%s\", prefix);\n-\t\tfputs(decorate_get_color_opt(&opt->diffopt, decoration->type),\n-\t\t      stdout);\n+\t\tstrbuf_addstr(sb, color_commit);\n+\t\tstrbuf_addstr(sb, prefix);\n+\t\tstrbuf_addstr(sb, decorate_get_color(use_color, decoration->type));\n \t\tif (decoration->type == DECORATION_REF_TAG)\n-\t\t\tfputs(\"tag: \", stdout);\n-\t\tprintf(\"%s\", decoration->name);\n-\t\tfputs(color_reset, stdout);\n-\t\tfputs(color_commit, stdout);\n+\t\t\tstrbuf_addstr(sb, \"tag: \");\n+\t\tstrbuf_addstr(sb, decoration->name);\n+\t\tstrbuf_addstr(sb, color_reset);\n \t\tprefix = \", \";\n \t\tdecoration = decoration->next;\n \t}\n-\tputchar(')');\n+\tstrbuf_addstr(sb, color_commit);\n+\tstrbuf_addch(sb, ')');\n+\tstrbuf_addstr(sb, color_reset);\n+}\n+\n+void show_decorations(struct rev_info *opt, struct commit *commit)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tif (opt->show_source && commit->util)\n+\t\tprintf(\"\\t%s\", (char *) commit->util);\n+\tif (!opt->show_decorations)\n+\t\treturn;\n+\tformat_decorations(&sb, commit, opt->diffopt.use_color);\n+\tfputs(sb.buf, stdout);\n+\tstrbuf_release(&sb);\n }\n \n static unsigned int digits_in_number(unsigned int number)\n@@ -540,8 +556,8 @@ void show_log(struct rev_info *opt)\n \t\t\tprintf(\" (from %s)\",\n \t\t\t       find_unique_abbrev(parent->object.sha1,\n \t\t\t\t\t\t  abbrev_commit));\n+\t\tfputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);\n \t\tshow_decorations(opt, commit);\n-\t\tprintf(\"%s\", diff_get_color_opt(&opt->diffopt, DIFF_RESET));\n \t\tif (opt->commit_format == CMIT_FMT_ONELINE) {\n \t\t\tputchar(' ');\n \t\t} else {\ndiff --git a/log-tree.h b/log-tree.h\nindex 9140f48..d6ecd4d 100644\n--- a/log-tree.h\n+++ b/log-tree.h\n@@ -13,6 +13,7 @@ int log_tree_diff_flush(struct rev_info *);\n int log_tree_commit(struct rev_info *, struct commit *);\n int log_tree_opt_parse(struct rev_info *, const char **, int);\n void show_log(struct rev_info *opt);\n+void format_decorations(struct strbuf *sb, const struct commit *commit, int use_color);\n void show_decorations(struct rev_info *opt, struct commit *commit);\n void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\t\t     const char **subject_p,\ndiff --git a/pretty.c b/pretty.c\nindex e59688b..e0f93ba 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -908,23 +908,6 @@ static void parse_commit_message(struct format_commit_context *c)\n \tc->commit_message_parsed = 1;\n }\n \n-static void format_decoration(struct strbuf *sb, const struct commit *commit)\n-{\n-\tstruct name_decoration *d;\n-\tconst char *prefix = \" (\";\n-\n-\tload_ref_decorations(DECORATE_SHORT_REFS);\n-\td = lookup_decoration(&name_decoration, &commit->object);\n-\twhile (d) {\n-\t\tstrbuf_addstr(sb, prefix);\n-\t\tprefix = \", \";\n-\t\tstrbuf_addstr(sb, d->name);\n-\t\td = d->next;\n-\t}\n-\tif (prefix[0] == ',')\n-\t\tstrbuf_addch(sb, ')');\n-}\n-\n static void strbuf_wrap(struct strbuf *sb, size_t pos,\n \t\t\tsize_t width, size_t indent1, size_t indent2)\n {\n@@ -1103,7 +1086,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \t\tstrbuf_addstr(sb, get_revision_mark(NULL, commit));\n \t\treturn 1;\n \tcase 'd':\n-\t\tformat_decoration(sb, commit);\n+\t\tload_ref_decorations(DECORATE_SHORT_REFS);\n+\t\tformat_decorations(sb, commit, 0);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex bbde31b..925f577 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -44,15 +44,15 @@ test_expect_success setup '\n '\n \n cat >expected <<EOF\n-${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_HEAD}HEAD${c_reset}${c_commit},\\\n  ${c_tag}tag: v1.0${c_reset}${c_commit},\\\n  ${c_tag}tag: B${c_reset}${c_commit},\\\n  ${c_branch}master${c_reset}${c_commit})${c_reset} B\n-${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A1${c_reset}${c_commit},\\\n  ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1\n-${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\\\n  On master: Changes to A.t\n-${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A\n EOF\n \n # We want log to show all, but the second parent to refs/stash is irrelevant\n-- \n1.8.2.82.gc24b958\n"},{"id":"214775","messageId":"1366326532-28517-6-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 05/13] utf8.c: move display_mode_esc_sequence_len() for use by other functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:44Z","receivedAt":"2013-04-18T23:08:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex 7f64857..6ed93c3 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,6 +9,20 @@ struct interval {\n   int last;\n };\n \n+static size_t display_mode_esc_sequence_len(const char *s)\n+{\n+\tconst char *p = s;\n+\tif (*p++ != '\\033')\n+\t\treturn 0;\n+\tif (*p++ != '[')\n+\t\treturn 0;\n+\twhile (isdigit(*p) || *p == ';')\n+\t\tp++;\n+\tif (*p++ != 'm')\n+\t\treturn 0;\n+\treturn p - s;\n+}\n+\n /* auxiliary function for binary search in interval table */\n static int bisearch(ucs_char_t ucs, const struct interval *table, int max)\n {\n@@ -303,20 +317,6 @@ static void strbuf_add_indented_text(struct strbuf *buf, const char *text,\n \t}\n }\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n-{\n-\tconst char *p = s;\n-\tif (*p++ != '\\033')\n-\t\treturn 0;\n-\tif (*p++ != '[')\n-\t\treturn 0;\n-\twhile (isdigit(*p) || *p == ';')\n-\t\tp++;\n-\tif (*p++ != 'm')\n-\t\treturn 0;\n-\treturn p - s;\n-}\n-\n /*\n  * Wrap the text, if necessary. The variable indent is the indent for the\n  * first line, indent2 is the indent for all other lines.\n-- \n1.8.2.82.gc24b958\n"},{"id":"214776","messageId":"1366326532-28517-7-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 06/13] utf8.c: add utf8_strnwidth() with the ability to skip ansi sequences","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:45Z","receivedAt":"2013-04-18T23:08:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n utf8.c | 20 ++++++++++++++------\n utf8.h |  1 +\n 2 files changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/utf8.c b/utf8.c\nindex 6ed93c3..e7ba33c 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -266,18 +266,26 @@ int utf8_width(const char **start, size_t *remainder_p)\n  * string, assuming that the string is utf8.  Returns strlen() instead\n  * if the string does not look like a valid utf8 string.\n  */\n-int utf8_strwidth(const char *string)\n+int utf8_strnwidth(const char *string, int len, int skip_ansi)\n {\n \tint width = 0;\n \tconst char *orig = string;\n \n-\twhile (1) {\n-\t\tif (!string)\n-\t\t\treturn strlen(orig);\n-\t\tif (!*string)\n-\t\t\treturn width;\n+\tif (len == -1)\n+\t\tlen = strlen(string);\n+\twhile (string && string < orig + len) {\n+\t\tint skip;\n+\t\twhile (skip_ansi &&\n+\t\t       (skip = display_mode_esc_sequence_len(string)) != 0)\n+\t\t\tstring += skip;\n \t\twidth += utf8_width(&string, NULL);\n \t}\n+\treturn string ? width : len;\n+}\n+\n+int utf8_strwidth(const char *string)\n+{\n+\treturn utf8_strnwidth(string, -1, 0);\n }\n \n int is_utf8(const char *text)\ndiff --git a/utf8.h b/utf8.h\nindex 1f8ecad..d3da96f 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -4,6 +4,7 @@\n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n int utf8_width(const char **start, size_t *remainder_p);\n+int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n int is_utf8(const char *text);\n int is_encoding_utf8(const char *name);\n-- \n1.8.2.82.gc24b958\n"},{"id":"214777","messageId":"1366326532-28517-8-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 07/13] utf8.c: add reencode_string_len() that can handle NULs in string","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:46Z","receivedAt":"2013-04-18T23:08:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n compat/precompose_utf8.c |  2 +-\n utf8.c                   | 10 +++++++---\n utf8.h                   | 19 ++++++++++++++++---\n 3 files changed, 24 insertions(+), 7 deletions(-)\n\ndiff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c\nindex 8cf5955..d9203d0 100644\n--- a/compat/precompose_utf8.c\n+++ b/compat/precompose_utf8.c\n@@ -78,7 +78,7 @@ void precompose_argv(int argc, const char **argv)\n \t\tsize_t namelen;\n \t\toldarg = argv[i];\n \t\tif (has_non_ascii(oldarg, (size_t)-1, &namelen)) {\n-\t\t\tnewarg = reencode_string_iconv(oldarg, namelen, ic_precompose);\n+\t\t\tnewarg = reencode_string_iconv(oldarg, namelen, ic_precompose, NULL);\n \t\t\tif (newarg)\n \t\t\t\targv[i] = newarg;\n \t\t}\ndiff --git a/utf8.c b/utf8.c\nindex e7ba33c..7c342ff 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -468,7 +468,7 @@ int utf8_fprintf(FILE *stream, const char *format, ...)\n #else\n \ttypedef char * iconv_ibp;\n #endif\n-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)\n+char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, int *outsz_p)\n {\n \tsize_t outsz, outalloc;\n \tchar *out, *outpos;\n@@ -502,13 +502,17 @@ char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)\n \t\t}\n \t\telse {\n \t\t\t*outpos = '\\0';\n+\t\t\tif (outsz_p)\n+\t\t\t\t*outsz_p = outpos - out;\n \t\t\tbreak;\n \t\t}\n \t}\n \treturn out;\n }\n \n-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding)\n+char *reencode_string_len(const char *in, int insz,\n+\t\t\t  const char *out_encoding, const char *in_encoding,\n+\t\t\t  int *outsz)\n {\n \ticonv_t conv;\n \tchar *out;\n@@ -534,7 +538,7 @@ char *reencode_string(const char *in, const char *out_encoding, const char *in_e\n \t\t\treturn NULL;\n \t}\n \n-\tout = reencode_string_iconv(in, strlen(in), conv);\n+\tout = reencode_string_iconv(in, insz, conv, outsz);\n \ticonv_close(conv);\n \treturn out;\n }\ndiff --git a/utf8.h b/utf8.h\nindex d3da96f..a43ef9a 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -17,12 +17,25 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \t\t\t     int indent, int indent2, int width);\n \n #ifndef NO_ICONV\n-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv);\n-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);\n+char *reencode_string_iconv(const char *in, size_t insz,\n+\t\t\t    iconv_t conv, int *outsz);\n+char *reencode_string_len(const char *in, int insz,\n+\t\t\t  const char *out_encoding,\n+\t\t\t  const char *in_encoding,\n+\t\t\t  int *outsz);\n #else\n-#define reencode_string(a,b,c) NULL\n+#define reencode_string_len(a,b,c,d,e) NULL\n #endif\n \n+static inline char *reencode_string(const char *in,\n+\t\t\t\t    const char *out_encoding,\n+\t\t\t\t    const char *in_encoding)\n+{\n+\treturn reencode_string_len(in, strlen(in),\n+\t\t\t\t   out_encoding, in_encoding,\n+\t\t\t\t   NULL);\n+}\n+\n int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);\n \n #endif\n-- \n1.8.2.82.gc24b958\n"},{"id":"214778","messageId":"1366326532-28517-9-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 08/13] pretty: two phase conversion for non utf-8 commits","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:47Z","receivedAt":"2013-04-18T23:08:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Always assume format_commit_item() takes an utf-8 string for string\nhandling simplicity (we can handle utf-8 strings, but can't with other\nencodings).\n\nIf commit message is in non-utf8, or output encoding is not, then the\ncommit is first converted to utf-8, processed, then output converted\nto output encoding. This of course only works with encodings that are\ncompatible with Unicode.\n\nThis also fixes the iso8859-1 test in t6006. It's supposed to create\nan iso8859-1 commit, but the commit content in t6006 is in UTF-8.\nt6006 is now converted back in UTF-8 (the downside is we can't put\nutf-8 strings there anymore).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c                   | 24 ++++++++++++++++++++++--\n t/t6006-rev-list-format.sh | 12 ++++++------\n 2 files changed, 28 insertions(+), 8 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex e0f93ba..5947275 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -954,7 +954,8 @@ static int format_reflog_person(struct strbuf *sb,\n \treturn format_person_part(sb, part, ident, strlen(ident), dmode);\n }\n \n-static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\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 {\n \tstruct format_commit_context *c = context;\n@@ -1193,7 +1194,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,\n \treturn 0;\t/* unknown placeholder */\n }\n \n-static size_t format_commit_item(struct strbuf *sb, const char *placeholder,\n+static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n+\t\t\t\t const char *placeholder,\n \t\t\t\t void *context)\n {\n \tint consumed;\n@@ -1273,6 +1275,7 @@ void format_commit_message(const struct commit *commit,\n {\n \tstruct format_commit_context context;\n \tconst char *output_enc = pretty_ctx->output_encoding;\n+\tconst char *utf8 = \"UTF-8\";\n \n \tmemset(&context, 0, sizeof(context));\n \tcontext.commit = commit;\n@@ -1285,6 +1288,23 @@ void format_commit_message(const struct commit *commit,\n \tstrbuf_expand(sb, format, format_commit_item, &context);\n \trewrap_message_tail(sb, &context, 0, 0, 0);\n \n+\tif (output_enc) {\n+\t\tif (same_encoding(utf8, output_enc))\n+\t\t\toutput_enc = NULL;\n+\t} else {\n+\t\tif (context.commit_encoding &&\n+\t\t    !same_encoding(context.commit_encoding, utf8))\n+\t\t\toutput_enc = context.commit_encoding;\n+\t}\n+\n+\tif (output_enc) {\n+\t\tint outsz;\n+\t\tchar *out = reencode_string_len(sb->buf, sb->len,\n+\t\t\t\t\t\toutput_enc, utf8, &outsz);\n+\t\tif (out)\n+\t\t\tstrbuf_attach(sb, out, outsz, outsz + 1);\n+\t}\n+\n \tfree(context.commit_encoding);\n \tlogmsg_free(context.message, commit);\n \tfree(context.signature_check.gpg_output);\ndiff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh\nindex 3fc3b74..0393c9f 100755\n--- a/t/t6006-rev-list-format.sh\n+++ b/t/t6006-rev-list-format.sh\n@@ -184,7 +184,7 @@ Test printing of complex bodies\n \n This commit message is much longer than the others,\n and it will be encoded in iso8859-1. We should therefore\n-include an iso8859 character: ¡bueno!\n+include an iso8859 character: �bueno!\n EOF\n test_expect_success 'setup complex body' '\n git config i18n.commitencoding iso8859-1 &&\n@@ -192,14 +192,14 @@ git config i18n.commitencoding iso8859-1 &&\n '\n \n test_format complex-encoding %e <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n iso8859-1\n commit 131a310eb913d107dd3c09a65d1651175898735d\n commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n EOF\n \n test_format complex-subject %s <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n Test printing of complex bodies\n commit 131a310eb913d107dd3c09a65d1651175898735d\n changed foo\n@@ -208,17 +208,17 @@ added foo\n EOF\n \n test_format complex-body %b <<'EOF'\n-commit f58db70b055c5718631e5c61528b28b12090cdea\n+commit 1ed88da4a5b5ed8c449114ac131efc62178734c3\n This commit message is much longer than the others,\n and it will be encoded in iso8859-1. We should therefore\n-include an iso8859 character: ¡bueno!\n+include an iso8859 character: �bueno!\n \n commit 131a310eb913d107dd3c09a65d1651175898735d\n commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873\n EOF\n \n test_expect_success '%x00 shows NUL' '\n-\techo  >expect commit f58db70b055c5718631e5c61528b28b12090cdea &&\n+\techo  >expect commit 1ed88da4a5b5ed8c449114ac131efc62178734c3 &&\n \techo >>expect fooQbar &&\n \tgit rev-list -1 --format=foo%x00bar HEAD >actual.nul &&\n \tnul_to_q <actual.nul >actual &&\n-- \n1.8.2.82.gc24b958\n"},{"id":"214779","messageId":"1366326532-28517-10-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 09/13] pretty: split color parsing into a separate function","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:48Z","receivedAt":"2013-04-18T23:08:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n pretty.c | 71 +++++++++++++++++++++++++++++++++++-----------------------------\n 1 file changed, 39 insertions(+), 32 deletions(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 5947275..e0413e3 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -954,6 +954,44 @@ static int format_reflog_person(struct strbuf *sb,\n \treturn format_person_part(sb, part, ident, strlen(ident), dmode);\n }\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+{\n+\tif (placeholder[1] == '(') {\n+\t\tconst char *begin = placeholder + 2;\n+\t\tconst char *end = strchr(begin, ')');\n+\t\tchar color[COLOR_MAXLEN];\n+\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\t\treturn end - placeholder + 1;\n+\t\t\tbegin += 5;\n+\t\t}\n+\t\tcolor_parse_mem(begin,\n+\t\t\t\tend - begin,\n+\t\t\t\t\"--pretty format\", color);\n+\t\tstrbuf_addstr(sb, color);\n+\t\treturn end - placeholder + 1;\n+\t}\n+\tif (!prefixcmp(placeholder + 1, \"red\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_RED);\n+\t\treturn 4;\n+\t} else if (!prefixcmp(placeholder + 1, \"green\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_GREEN);\n+\t\treturn 6;\n+\t} else if (!prefixcmp(placeholder + 1, \"blue\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_BLUE);\n+\t\treturn 5;\n+\t} else if (!prefixcmp(placeholder + 1, \"reset\")) {\n+\t\tstrbuf_addstr(sb, GIT_COLOR_RESET);\n+\t\treturn 6;\n+\t} else\n+\t\treturn 0;\n+}\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@@ -967,38 +1005,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t/* these are independent of the commit */\n \tswitch (placeholder[0]) {\n \tcase 'C':\n-\t\tif (placeholder[1] == '(') {\n-\t\t\tconst char *begin = placeholder + 2;\n-\t\t\tconst char *end = strchr(begin, ')');\n-\t\t\tchar color[COLOR_MAXLEN];\n-\n-\t\t\tif (!end)\n-\t\t\t\treturn 0;\n-\t\t\tif (!prefixcmp(begin, \"auto,\")) {\n-\t\t\t\tif (!want_color(c->pretty_ctx->color))\n-\t\t\t\t\treturn end - placeholder + 1;\n-\t\t\t\tbegin += 5;\n-\t\t\t}\n-\t\t\tcolor_parse_mem(begin,\n-\t\t\t\t\tend - begin,\n-\t\t\t\t\t\"--pretty format\", color);\n-\t\t\tstrbuf_addstr(sb, color);\n-\t\t\treturn end - placeholder + 1;\n-\t\t}\n-\t\tif (!prefixcmp(placeholder + 1, \"red\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_RED);\n-\t\t\treturn 4;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"green\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_GREEN);\n-\t\t\treturn 6;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"blue\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_BLUE);\n-\t\t\treturn 5;\n-\t\t} else if (!prefixcmp(placeholder + 1, \"reset\")) {\n-\t\t\tstrbuf_addstr(sb, GIT_COLOR_RESET);\n-\t\t\treturn 6;\n-\t\t} else\n-\t\t\treturn 0;\n+\t\treturn parse_color(sb, placeholder, c);\n \tcase 'n':\t\t/* newline */\n \t\tstrbuf_addch(sb, '\\n');\n \t\treturn 1;\n-- \n1.8.2.82.gc24b958\n"},{"id":"214780","messageId":"1366326532-28517-11-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 10/13] pretty: add %C(auto) for auto-coloring","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:49Z","receivedAt":"2013-04-18T23:08:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is not simply convenient over %C(auto,xxx). Some placeholders\n(actually only one, %d) do multi coloring and we can't emit a multiple\ncolors with %C(auto,xxx).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  3 ++-\n pretty.c                         | 26 +++++++++++++++++++++++---\n 2 files changed, 25 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex 6bde67e..bad627a 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -156,7 +156,8 @@ The placeholders are:\n   adding `auto,` at the beginning will emit color only when colors are\n   enabled for log output (by `color.diff`, `color.ui`, or `--color`, and\n   respecting the `auto` settings of the former if we are going to a\n-  terminal)\n+  terminal). `auto` alone (i.e. `%C(auto)`) will turn on auto coloring\n+  on the next placeholders until the color is switched again.\n - '%m': left, right or boundary mark\n - '%n': newline\n - '%%': a raw '%'\ndiff --git a/pretty.c b/pretty.c\nindex e0413e3..3612f82 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -778,6 +778,7 @@ struct format_commit_context {\n \tchar *message;\n \tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n+\tint auto_color;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -1005,7 +1006,20 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t/* these are independent of the commit */\n \tswitch (placeholder[0]) {\n \tcase 'C':\n-\t\treturn parse_color(sb, placeholder, c);\n+\t\tif (!prefixcmp(placeholder + 1, \"(auto)\")) {\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\tif (ret)\n+\t\t\t\tc->auto_color = 0;\n+\t\t\t/*\n+\t\t\t * Otherwise, we decided to treat %C<unknown>\n+\t\t\t * as a literal string, and the previous\n+\t\t\t * %C(auto) is still valid.\n+\t\t\t */\n+\t\t\treturn ret;\n+\t\t}\n \tcase 'n':\t\t/* newline */\n \t\tstrbuf_addch(sb, '\\n');\n \t\treturn 1;\n@@ -1051,13 +1065,19 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \n \tswitch (placeholder[0]) {\n \tcase 'H':\t\t/* commit hash */\n+\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT));\n \t\tstrbuf_addstr(sb, sha1_to_hex(commit->object.sha1));\n+\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));\n \t\treturn 1;\n \tcase 'h':\t\t/* abbreviated commit hash */\n-\t\tif (add_again(sb, &c->abbrev_commit_hash))\n+\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT));\n+\t\tif (add_again(sb, &c->abbrev_commit_hash)) {\n+\t\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));\n \t\t\treturn 1;\n+\t\t}\n \t\tstrbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,\n \t\t\t\t\t\t     c->pretty_ctx->abbrev));\n+\t\tstrbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));\n \t\tc->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;\n \t\treturn 1;\n \tcase 'T':\t\t/* tree hash */\n@@ -1095,7 +1115,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\treturn 1;\n \tcase 'd':\n \t\tload_ref_decorations(DECORATE_SHORT_REFS);\n-\t\tformat_decorations(sb, commit, 0);\n+\t\tformat_decorations(sb, commit, c->auto_color);\n \t\treturn 1;\n \tcase 'g':\t\t/* reflog info */\n \t\tswitch(placeholder[1]) {\n-- \n1.8.2.82.gc24b958\n"},{"id":"214781","messageId":"1366326532-28517-12-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 11/13] pretty: support padding placeholders, %< %> and %><","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:50Z","receivedAt":"2013-04-18T23:08:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Either %<, %> or %>< standing before a placeholder specifies how many\ncolumns (at least as the placeholder can exceed it) it takes. Each\ndiffers on how spaces are padded:\n\n  %< pads on the right (aka left alignment)\n  %> pads on the left (aka right alignment)\n  %>< pads both ways equally (aka centered)\n\nThe (<N>) follows them, e.g. `%<(100)', to specify the number of\ncolumns the next placeholder takes.\n\nHowever, if '|' stands before (<N>), e.g. `%>|(100)', then the number\nof columns is calculated so that it reaches the Nth column on screen.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |   8 +++\n pretty.c                         | 117 ++++++++++++++++++++++++++++++++++++-\n t/t4205-log-pretty-formats.sh    | 122 +++++++++++++++++++++++++++++++++++++++\n 3 files changed, 246 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex bad627a..e2345d2 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -164,6 +164,14 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n+- '%<(<N>)': make the next placeholder take at least N columns,\n+  padding spaces on the right if necessary\n+- '%<|(<N>)': make the next placeholder take at least until Nth\n+  columns, padding spaces on the right if necessary\n+- '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n+  respectively, but padding spaces on the left\n+- '%><(<N>)', '%><|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n+  respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\n revision traversal engine. For example, the `%g*` reflog options will\ndiff --git a/pretty.c b/pretty.c\nindex 3612f82..b50ebf5 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -769,16 +769,25 @@ struct chunk {\n \tsize_t len;\n };\n \n+enum flush_type {\n+\tno_flush,\n+\tflush_right,\n+\tflush_left,\n+\tflush_both\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 \tchar *message;\n \tchar *commit_encoding;\n \tsize_t width, indent1, indent2;\n \tint auto_color;\n+\tint padding;\n \n \t/* These offsets are relative to the start of the commit message. */\n \tstruct chunk author;\n@@ -993,6 +1002,52 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */\n \t\treturn 0;\n }\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+{\n+\tconst char *ch = placeholder;\n+\tenum flush_type flush_type;\n+\tint to_column = 0;\n+\n+\tswitch (*ch++) {\n+\tcase '<':\n+\t\tflush_type = flush_right;\n+\t\tbreak;\n+\tcase '>':\n+\t\tif (*ch == '<') {\n+\t\t\tflush_type = flush_both;\n+\t\t\tch++;\n+\t\t} else\n+\t\t\tflush_type = flush_left;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\t/* the next value means \"wide enough to that column\" */\n+\tif (*ch == '|') {\n+\t\tto_column = 1;\n+\t\tch++;\n+\t}\n+\n+\tif (*ch == '(') {\n+\t\tconst char *start = ch + 1;\n+\t\tconst char *end = strchr(start, ')');\n+\t\tchar *next;\n+\t\tint width;\n+\t\tif (!end || end == start)\n+\t\t\treturn 0;\n+\t\twidth = strtoul(start, &next, 10);\n+\t\tif (next == start || width == 0)\n+\t\t\treturn 0;\n+\t\tc->padding = to_column ? -width : width;\n+\t\tc->flush_type = flush_type;\n+\t\treturn end - placeholder + 1;\n+\t}\n+\treturn 0;\n+}\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@@ -1057,6 +1112,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */\n \t\t\treturn end - placeholder + 1;\n \t\t} else\n \t\t\treturn 0;\n+\n+\tcase '<':\n+\tcase '>':\n+\t\treturn parse_padding_placeholder(sb, placeholder, c);\n \t}\n \n \t/* these depend on the commit */\n@@ -1221,6 +1280,59 @@ 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+{\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+\t\tif (!start)\n+\t\t\tstart = sb->buf;\n+\t\toccupied = utf8_strnwidth(start, -1, 1);\n+\t\tpadding = (-padding) - occupied;\n+\t}\n+\twhile (1) {\n+\t\tint modifier = *placeholder == 'C';\n+\t\tint consumed = format_commit_one(&local_sb, placeholder, c);\n+\t\ttotal_consumed += consumed;\n+\n+\t\tif (!modifier)\n+\t\t\tbreak;\n+\n+\t\tplaceholder += consumed;\n+\t\tif (*placeholder != '%')\n+\t\t\tbreak;\n+\t\tplaceholder++;\n+\t\ttotal_consumed++;\n+\t}\n+\tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\tif (len > padding)\n+\t\tstrbuf_addstr(sb, local_sb.buf);\n+\telse {\n+\t\tint sb_len = sb->len, offset = 0;\n+\t\tif (c->flush_type == flush_left)\n+\t\t\toffset = padding - len;\n+\t\telse if (c->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\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}\n+\tstrbuf_release(&local_sb);\n+\tc->flush_type = no_flush;\n+\treturn total_consumed;\n+}\n+\n static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\t\t\t const char *placeholder,\n \t\t\t\t void *context)\n@@ -1251,7 +1363,10 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */\n \t\tplaceholder++;\n \n \torig_len = sb->len;\n-\tconsumed = format_commit_one(sb, placeholder, context);\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 \tif (magic == NO_MAGIC)\n \t\treturn consumed;\n \ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex 98a43d4..88f2427 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -99,4 +99,126 @@ test_expect_failure 'NUL termination with --stat' '\n \ttest_i18ncmp expected actual\n '\n \n+test_expect_success 'setup more commits' '\n+\ttest_commit \"message one\" one one message-one &&\n+\ttest_commit \"message two\" two two message-two\n+'\n+\n+test_expect_success 'left alignment formatting' '\n+\tgit log --pretty=\"format:%<(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tqz_to_tab_space <<\\EOF >expected &&\n+message two                            Z\n+message one                            Z\n+add bar                                Z\n+initial                                Z\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'left alignment formatting at the nth column' '\n+\tgit log --pretty=\"format:%h %<|(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tqz_to_tab_space <<\\EOF >expected &&\n+fa33ab1 message two                    Z\n+7cd6c63 message one                    Z\n+1711bf9 add bar                        Z\n+af20c06 initial                        Z\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'left alignment formatting with no padding' '\n+\tgit log --pretty=\"format:%<(1)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF >expected &&\n+message two\n+message one\n+add bar\n+initial\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'right alignment formatting' '\n+\tgit log --pretty=\"format:%>(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tqz_to_tab_space <<\\EOF >expected &&\n+Z                            message two\n+Z                            message one\n+Z                                add bar\n+Z                                initial\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'right alignment formatting at the nth column' '\n+\tgit log --pretty=\"format:%h %>|(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tqz_to_tab_space <<\\EOF >expected &&\n+fa33ab1                      message two\n+7cd6c63                      message one\n+1711bf9                          add bar\n+af20c06                          initial\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'right alignment formatting with no padding' '\n+\tgit log --pretty=\"format:%>(1)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF >expected &&\n+message two\n+message one\n+add bar\n+initial\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'center alignment formatting' '\n+\tgit log --pretty=\"format:%><(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tqz_to_tab_space <<\\EOF >expected &&\n+Z             message two              Z\n+Z             message one              Z\n+Z               add bar                Z\n+Z               initial                Z\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'center alignment formatting at the nth column' '\n+\tgit log --pretty=\"format:%h %><|(40)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tqz_to_tab_space <<\\EOF >expected &&\n+fa33ab1           message two          Z\n+7cd6c63           message one          Z\n+1711bf9             add bar            Z\n+af20c06             initial            Z\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'center alignment formatting with no padding' '\n+\tgit log --pretty=\"format:%><(1)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF >expected &&\n+message two\n+message one\n+add bar\n+initial\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.8.2.82.gc24b958\n"},{"id":"214782","messageId":"1366326532-28517-13-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 12/13] pretty: support truncating in %>, %< and %><","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:51Z","receivedAt":"2013-04-18T23:08:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"%>(N,trunc) truncates the right part after N columns and replace the\nlast two letters with \"..\". ltrunc does the same on the left. mtrunc\ncuts the middle out.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  7 ++++--\n pretty.c                         | 51 +++++++++++++++++++++++++++++++++++++---\n t/t4205-log-pretty-formats.sh    | 39 ++++++++++++++++++++++++++++++\n utf8.c                           | 46 ++++++++++++++++++++++++++++++++++++\n utf8.h                           |  2 ++\n 5 files changed, 140 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex e2345d2..d3450bf 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -164,8 +164,11 @@ The placeholders are:\n - '%x00': print a byte from a hex code\n - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of\n   linkgit:git-shortlog[1].\n-- '%<(<N>)': make the next placeholder take at least N columns,\n-  padding spaces on the right if necessary\n+- '%<(<N>[,trunc|ltrunc|mtrunc])': make the next placeholder take at\n+  least N columns, padding spaces on the right if necessary.\n+  Optionally truncate at the beginning (ltrunc), the middle (mtrunc)\n+  or the end (trunc) if the output is longer than N columns.\n+  Note that truncating only works correctly with N >= 2.\n - '%<|(<N>)': make the next placeholder take at least until Nth\n   columns, padding spaces on the right if necessary\n - '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\ndiff --git a/pretty.c b/pretty.c\nindex b50ebf5..f7c6442 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -776,6 +776,13 @@ enum flush_type {\n \tflush_both\n };\n \n+enum trunc_type {\n+\ttrunc_none,\n+\ttrunc_left,\n+\ttrunc_middle,\n+\ttrunc_right\n+};\n+\n struct format_commit_context {\n \tconst struct commit *commit;\n \tconst struct pretty_print_context *pretty_ctx;\n@@ -783,6 +790,7 @@ struct format_commit_context {\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@@ -1033,7 +1041,7 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \n \tif (*ch == '(') {\n \t\tconst char *start = ch + 1;\n-\t\tconst char *end = strchr(start, ')');\n+\t\tconst char *end = start + strcspn(start, \",)\");\n \t\tchar *next;\n \t\tint width;\n \t\tif (!end || end == start)\n@@ -1043,6 +1051,23 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\t\treturn 0;\n \t\tc->padding = to_column ? -width : width;\n \t\tc->flush_type = flush_type;\n+\n+\t\tif (*end == ',') {\n+\t\t\tstart = end + 1;\n+\t\t\tend = strchr(start, ')');\n+\t\t\tif (!end || end == start)\n+\t\t\t\treturn 0;\n+\t\t\tif (!prefixcmp(start, \"trunc)\"))\n+\t\t\t\tc->truncate = trunc_right;\n+\t\t\telse if (!prefixcmp(start, \"ltrunc)\"))\n+\t\t\t\tc->truncate = trunc_left;\n+\t\t\telse if (!prefixcmp(start, \"mtrunc)\"))\n+\t\t\t\tc->truncate = trunc_middle;\n+\t\t\telse\n+\t\t\t\treturn 0;\n+\t\t} else\n+\t\t\tc->truncate = trunc_none;\n+\n \t\treturn end - placeholder + 1;\n \t}\n \treturn 0;\n@@ -1309,9 +1334,29 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\ttotal_consumed++;\n \t}\n \tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n-\tif (len > padding)\n+\tif (len > padding) {\n+\t\tswitch (c->truncate) {\n+\t\tcase trunc_left:\n+\t\t\tstrbuf_utf8_replace(&local_sb,\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\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\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-\telse {\n+\t} else {\n \t\tint sb_len = sb->len, offset = 0;\n \t\tif (c->flush_type == flush_left)\n \t\t\toffset = padding - len;\ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex 88f2427..b8685b9 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -143,6 +143,45 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'left alignment formatting with trunc' '\n+\tgit log --pretty=\"format:%<(10,trunc)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tqz_to_tab_space <<\\EOF >expected &&\n+message ..\n+message ..\n+add bar  Z\n+initial  Z\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'left alignment formatting with ltrunc' '\n+\tgit log --pretty=\"format:%<(10,ltrunc)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tqz_to_tab_space <<\\EOF >expected &&\n+..sage two\n+..sage one\n+add bar  Z\n+initial  Z\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'left alignment formatting with mtrunc' '\n+\tgit log --pretty=\"format:%<(10,mtrunc)%s\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tqz_to_tab_space <<\\EOF >expected &&\n+mess.. two\n+mess.. one\n+add bar  Z\n+initial  Z\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'right alignment formatting' '\n \tgit log --pretty=\"format:%>(40)%s\" >actual &&\n \t# complete the incomplete line at the end\ndiff --git a/utf8.c b/utf8.c\nindex 7c342ff..197414a 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -421,6 +421,52 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \tfree(tmp);\n }\n \n+void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,\n+\t\t\t const char *subst)\n+{\n+\tstruct strbuf sb_dst = STRBUF_INIT;\n+\tchar *src = sb_src->buf;\n+\tchar *end = src + sb_src->len;\n+\tchar *dst;\n+\tint w = 0, subst_len = 0;\n+\n+\tif (subst)\n+\t\tsubst_len = strlen(subst);\n+\tstrbuf_grow(&sb_dst, sb_src->len + subst_len);\n+\tdst = sb_dst.buf;\n+\n+\twhile (src < end) {\n+\t\tchar *old;\n+\t\tsize_t n;\n+\n+\t\twhile ((n = display_mode_esc_sequence_len(src))) {\n+\t\t\tmemcpy(dst, src, n);\n+\t\t\tsrc += n;\n+\t\t\tdst += n;\n+\t\t}\n+\n+\t\told = src;\n+\t\tn = utf8_width((const char**)&src, NULL);\n+\t\tif (!src) \t/* broken utf-8, do nothing */\n+\t\t\treturn;\n+\t\tif (n && w >= pos && w < pos + width) {\n+\t\t\tif (subst) {\n+\t\t\t\tmemcpy(dst, subst, subst_len);\n+\t\t\t\tdst += subst_len;\n+\t\t\t\tsubst = NULL;\n+\t\t\t}\n+\t\t\tw += n;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tmemcpy(dst, old, src - old);\n+\t\tdst += src - old;\n+\t\tw += n;\n+\t}\n+\tstrbuf_setlen(&sb_dst, dst - sb_dst.buf);\n+\tstrbuf_attach(sb_src, strbuf_detach(&sb_dst, NULL),\n+\t\t      sb_dst.len, sb_dst.alloc);\n+}\n+\n int is_encoding_utf8(const char *name)\n {\n \tif (!name)\ndiff --git a/utf8.h b/utf8.h\nindex a43ef9a..edde8ee 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -15,6 +15,8 @@ void strbuf_add_wrapped_text(struct strbuf *buf,\n \t\tconst char *text, int indent, int indent2, int width);\n void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,\n \t\t\t     int indent, int indent2, int width);\n+void strbuf_utf8_replace(struct strbuf *sb, int pos, int width,\n+\t\t\t const char *subst);\n \n #ifndef NO_ICONV\n char *reencode_string_iconv(const char *in, size_t insz,\n-- \n1.8.2.82.gc24b958\n"},{"id":"214783","messageId":"1366326532-28517-14-git-send-email-pclouds@gmail.com","threadId":"33195","inReplyTo":"1366326532-28517-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 13/13] pretty: support %>> that steal trailing spaces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-04-18T23:08:52Z","receivedAt":"2013-04-18T23:08:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is pretty useful in `%<(100)%s%Cred%>(20)% an' where %s does not\nuse up all 100 columns and %an needs more than 20 columns. By\nreplacing %>(20) with %>>(20), %an can steal spaces from %s.\n\n%>> understands escape sequences, so %Cred does not stop it from\nstealing spaces in %<(100).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/pretty-formats.txt |  5 ++++-\n pretty.c                         | 34 ++++++++++++++++++++++++++++++++++\n t/t4205-log-pretty-formats.sh    | 14 ++++++++++++++\n utf8.c                           |  2 +-\n utf8.h                           |  1 +\n 5 files changed, 54 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt\nindex d3450bf..c96ff41 100644\n--- a/Documentation/pretty-formats.txt\n+++ b/Documentation/pretty-formats.txt\n@@ -173,7 +173,10 @@ The placeholders are:\n   columns, padding spaces on the right if necessary\n - '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n   respectively, but padding spaces on the left\n-- '%><(<N>)', '%><|(<N>)': similar to '%<(<N>)', '%<|(<N>)'\n+- '%>>(<N>)', '%>>|(<N>)': similar to '%>(<N>)', '%>|(<N>)'\n+  respectively, except that if the next placeholder takes more spaces\n+  than given and there are spaces on its left, use those spaces\n+- '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'\n   respectively, but padding both sides (i.e. the text is centered)\n \n NOTE: Some placeholders may depend on other options given to the\ndiff --git a/pretty.c b/pretty.c\nindex f7c6442..d59579a 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -773,6 +773,7 @@ enum flush_type {\n \tno_flush,\n \tflush_right,\n \tflush_left,\n+\tflush_left_and_steal,\n \tflush_both\n };\n \n@@ -1026,6 +1027,9 @@ static size_t parse_padding_placeholder(struct strbuf *sb,\n \t\tif (*ch == '<') {\n \t\t\tflush_type = flush_both;\n \t\t\tch++;\n+\t\t} else if (*ch == '>') {\n+\t\t\tflush_type = flush_left_and_steal;\n+\t\t\tch++;\n \t\t} else\n \t\t\tflush_type = flush_left;\n \t\tbreak;\n@@ -1334,6 +1338,36 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */\n \t\ttotal_consumed++;\n \t}\n \tlen = utf8_strnwidth(local_sb.buf, -1, 1);\n+\n+\tif (c->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+\t\t\tif (*ch == ' ') {\n+\t\t\t\tch--;\n+\t\t\t\tpadding++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\t/* check for trailing ansi sequences */\n+\t\t\tif (*ch != 'm')\n+\t\t\t\tbreak;\n+\t\t\tp = ch - 1;\n+\t\t\twhile (ch - p < 10 && *p != '\\033')\n+\t\t\t\tp--;\n+\t\t\tif (*p != '\\033' ||\n+\t\t\t    ch + 1 - p != display_mode_esc_sequence_len(p))\n+\t\t\t\tbreak;\n+\t\t\t/*\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\tch = p - 1;\n+\t\t}\n+\t\tstrbuf_setlen(sb, ch + 1 - sb->buf);\n+\t\tc->flush_type = flush_left;\n+\t}\n+\n \tif (len > padding) {\n \t\tswitch (c->truncate) {\n \t\tcase trunc_left:\ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex b8685b9..26fbfde 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -260,4 +260,18 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'left/right alignment formatting with stealing' '\n+\tgit commit --amend -m short --author \"long long long <long@me.com>\" &&\n+\tgit log --pretty=\"format:%<(10,trunc)%s%>>(10,ltrunc)% an\" >actual &&\n+\t# complete the incomplete line at the end\n+\techo >>actual &&\n+\tcat <<\\EOF >expected &&\n+short long  long long\n+message ..   A U Thor\n+add bar      A U Thor\n+initial      A U Thor\n+EOF\n+\ttest_cmp expected actual\n+'\n+\n test_done\ndiff --git a/utf8.c b/utf8.c\nindex 197414a..b1e1303 100644\n--- a/utf8.c\n+++ b/utf8.c\n@@ -9,7 +9,7 @@ struct interval {\n   int last;\n };\n \n-static size_t display_mode_esc_sequence_len(const char *s)\n+size_t display_mode_esc_sequence_len(const char *s)\n {\n \tconst char *p = s;\n \tif (*p++ != '\\033')\ndiff --git a/utf8.h b/utf8.h\nindex edde8ee..32a7bfb 100644\n--- a/utf8.h\n+++ b/utf8.h\n@@ -3,6 +3,7 @@\n \n typedef unsigned int ucs_char_t;  /* assuming 32bit int */\n \n+size_t display_mode_esc_sequence_len(const char *s);\n int utf8_width(const char **start, size_t *remainder_p);\n int utf8_strnwidth(const char *string, int len, int skip_ansi);\n int utf8_strwidth(const char *string);\n-- \n1.8.2.82.gc24b958\n"}]}