pretty: expand tabs in indented logs to make things line up properly
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 16 Mar 2016 16:15:53 +0000 (09:15 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 30 Mar 2016 18:25:35 +0000 (11:25 -0700)
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 at this step, we
require an explicit command line option to enable the behaviour.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/pretty-options.txt
commit.h
log-tree.c
pretty.c
revision.c
revision.h
index 4b659ac1a6a6c1b57d0cadc5cd69ce6b69525cc1..d820653784d27c18e3fe910a2aabae5a909c21ce 100644 (file)
@@ -42,6 +42,11 @@ 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 spaces
+       to fill to the next display column that is multiple of 8)
+       in the log message before showing it in the output.
+
 ifndef::git-rev-list[]
 --notes[=<ref>]::
        Show the notes (see linkgit:git-notes[1]) that annotate the
index 5d58be0017a8eb26cd3f58c1e3b0b610f684beba..a7ef682b5a6424bf8575851a5ec52eaa84492a59 100644 (file)
--- 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;
index 60f983934d5ea2c8e276d2c493d78caf56e9a39a..78a5381d0eaebb9092a8bb456fa656462ec65170 100644 (file)
@@ -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;
index 92b2870a7eabb6a99ffbbcfc222d2ff1c1186fa0..c8b075d91b9dfdcca505c6576b470c8d03e34ac4 100644 (file)
--- 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');
        }
 }
index df56fcea0e12a11a35414fc5e1c14d1675436671..e662230ff1f08f4f5a8b233fa45d808b0c22289b 100644 (file)
@@ -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;
index 23857c0ed1d7a304c848842711a920e678fd506d..40797535fa71d15dca7602cc850cc46218c6df1f 100644 (file)
@@ -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,