pretty: expand tabs in indented logs to make things line up properly

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>
This commit is contained in:
Linus Torvalds 2016-03-16 09:15:53 -07:00 committed by Junio C Hamano
parent 594730e980
commit 7cc13c717b
6 changed files with 79 additions and 2 deletions

View 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

View File

@ -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;

View 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;

View File

@ -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');
}
}

View 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;

View 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,