From: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> A commit log message sometimes tries to line things up using tabs, assuming fixed-width font with the standard 8-place tab settings. Viewing such a commit however does not work well in "git log", as we indent the lines by prefixing 4 spaces in front of them. This should all line up: Column 1 Column 2 -------- -------- A B ABCD EFGH SPACES Instead of Tabs Even with multi-byte UTF8 characters: Column 1 Column 2 -------- -------- Ä B åäö 100 A Møøse once bit my sister.. Tab-expand the lines in "git log --expand-tabs" output before prefixing 4 spaces. This is based on the patch by Linus Torvalds, but changed to require an explicit command line option to enable the behaviour. Signed-off-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx> --- Documentation/pretty-options.txt | 6 ++++ commit.h | 1 + log-tree.c | 1 + pretty.c | 71 ++++++++++++++++++++++++++++++++++++++-- revision.c | 2 ++ revision.h | 1 + 6 files changed, 80 insertions(+), 2 deletions(-) diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt index 4b659ac..4fb5c76 100644 --- a/Documentation/pretty-options.txt +++ b/Documentation/pretty-options.txt @@ -42,6 +42,12 @@ people using 80-column terminals. verbatim; this means that invalid sequences in the original commit may be copied to the output. +--expand-tabs:: + Perform a tab expansion (replace each tab with enough number + of spaces to fill to the next display column that is + multiple of 8) in the log message before using the message + to show in the output. + ifndef::git-rev-list[] --notes[=<ref>]:: Show the notes (see linkgit:git-notes[1]) that annotate the diff --git a/commit.h b/commit.h index 5d58be0..a7ef682 100644 --- a/commit.h +++ b/commit.h @@ -147,6 +147,7 @@ struct pretty_print_context { int preserve_subject; struct date_mode date_mode; unsigned date_mode_explicit:1; + unsigned expand_tabs_in_log:1; int need_8bit_cte; char *notes_message; struct reflog_walk_info *reflog_info; diff --git a/log-tree.c b/log-tree.c index 60f9839..78a5381 100644 --- a/log-tree.c +++ b/log-tree.c @@ -683,6 +683,7 @@ void show_log(struct rev_info *opt) ctx.fmt = opt->commit_format; ctx.mailmap = opt->mailmap; ctx.color = opt->diffopt.use_color; + ctx.expand_tabs_in_log = opt->expand_tabs_in_log; ctx.output_encoding = get_log_output_encoding(); if (opt->from_ident.mail_begin && opt->from_ident.name_begin) ctx.from_ident = &opt->from_ident; diff --git a/pretty.c b/pretty.c index 92b2870..c8b075d 100644 --- a/pretty.c +++ b/pretty.c @@ -1629,6 +1629,72 @@ void pp_title_line(struct pretty_print_context *pp, strbuf_release(&title); } +static int pp_utf8_width(const char *start, const char *end) +{ + int width = 0; + size_t remain = end - start; + + while (remain) { + int n = utf8_width(&start, &remain); + if (n < 0 || !start) + return -1; + width += n; + } + return width; +} + +static void strbuf_add_tabexpand(struct strbuf *sb, + const char *line, int linelen) +{ + const char *tab; + + while ((tab = memchr(line, '\t', linelen)) != NULL) { + int width = pp_utf8_width(line, tab); + + /* + * If it wasn't well-formed utf8, or it + * had characters with badly defined + * width (control characters etc), just + * give up on trying to align things. + */ + if (width < 0) + break; + + /* Output the data .. */ + strbuf_add(sb, line, tab - line); + + /* .. and the de-tabified tab */ + strbuf_addchars(sb, ' ', 8 - (width % 8)); + + /* Skip over the printed part .. */ + linelen -= tab + 1 - line; + line = tab + 1; + } + + /* + * Print out everything after the last tab without + * worrying about width - there's nothing more to + * align. + */ + strbuf_add(sb, line, linelen); +} + +/* + * pp_handle_indent() prints out the intendation, and + * the whole line (without the final newline), after + * de-tabifying. + */ +static void pp_handle_indent(struct pretty_print_context *pp, + struct strbuf *sb, int indent, + const char *line, int linelen) +{ + strbuf_addchars(sb, ' ', indent); + if (pp->expand_tabs_in_log) + strbuf_add_tabexpand(sb, line, linelen); + else + strbuf_add(sb, line, linelen); +} + void pp_remainder(struct pretty_print_context *pp, const char **msg_p, struct strbuf *sb, @@ -1653,8 +1719,9 @@ void pp_remainder(struct pretty_print_context *pp, strbuf_grow(sb, linelen + indent + 20); if (indent) - strbuf_addchars(sb, ' ', indent); - strbuf_add(sb, line, linelen); + pp_handle_indent(pp, sb, indent, line, linelen); + else + strbuf_add(sb, line, linelen); strbuf_addch(sb, '\n'); } } diff --git a/revision.c b/revision.c index df56fce..e662230 100644 --- a/revision.c +++ b/revision.c @@ -1915,6 +1915,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->verbose_header = 1; revs->pretty_given = 1; get_commit_format(arg+9, revs); + } else if (!strcmp(arg, "--expand-tabs")) { + revs->expand_tabs_in_log = 1; } else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) { revs->show_notes = 1; revs->show_notes_given = 1; diff --git a/revision.h b/revision.h index 23857c0..4079753 100644 --- a/revision.h +++ b/revision.h @@ -133,6 +133,7 @@ struct rev_info { show_notes_given:1, show_signature:1, pretty_given:1, + expand_tabs_in_log:1, abbrev_commit:1, abbrev_commit_given:1, zero_commit:1, -- 2.8.0-215-gd29a7d9 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html