Skip to content

Commit cafef3d

Browse files
committed
Merge branch 'lt/pretty-expand-tabs'
When "git log" shows the log message indented by 4-spaces, the remainder of a line after a HT does not align in the way the author originally intended. The command now expands tabs by default in such a case, and allows the users to override it with a new option, '--no-expand-tabs'. * lt/pretty-expand-tabs: pretty: test --expand-tabs pretty: allow tweaking tabwidth in --expand-tabs pretty: enable --expand-tabs by default for selected pretty formats pretty: expand tabs in indented logs to make things line up properly
2 parents 7c137bb + 915c96d commit cafef3d

File tree

9 files changed

+220
-10
lines changed

9 files changed

+220
-10
lines changed

Documentation/pretty-options.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ people using 80-column terminals.
4242
verbatim; this means that invalid sequences in the original
4343
commit may be copied to the output.
4444

45+
--expand-tabs=<n>::
46+
--expand-tabs::
47+
--no-expand-tabs::
48+
Perform a tab expansion (replace each tab with enough spaces
49+
to fill to the next display column that is multiple of '<n>')
50+
in the log message before showing it in the output.
51+
`--expand-tabs` is a short-hand for `--expand-tabs=8`, and
52+
`--no-expand-tabs` is a short-hand for `--expand-tabs=0`,
53+
which disables tab expansion.
54+
+
55+
By default, tabs are expanded in pretty formats that indent the log
56+
message by 4 spaces (i.e. 'medium', which is the default, 'full',
57+
and 'fuller').
58+
4559
ifndef::git-rev-list[]
4660
--notes[=<treeish>]::
4761
Show the notes (see linkgit:git-notes[1]) that annotate the

builtin/log.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
12901290
git_config(git_format_config, NULL);
12911291
init_revisions(&rev, prefix);
12921292
rev.commit_format = CMIT_FMT_EMAIL;
1293+
rev.expand_tabs_in_log_default = 0;
12931294
rev.verbose_header = 1;
12941295
rev.diff = 1;
12951296
rev.max_parents = 1;

commit.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ struct pretty_print_context {
147147
int preserve_subject;
148148
struct date_mode date_mode;
149149
unsigned date_mode_explicit:1;
150+
int expand_tabs_in_log;
150151
int need_8bit_cte;
151152
char *notes_message;
152153
struct reflog_walk_info *reflog_info;

log-tree.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,7 @@ void show_log(struct rev_info *opt)
683683
ctx.fmt = opt->commit_format;
684684
ctx.mailmap = opt->mailmap;
685685
ctx.color = opt->diffopt.use_color;
686+
ctx.expand_tabs_in_log = opt->expand_tabs_in_log;
686687
ctx.output_encoding = get_log_output_encoding();
687688
if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
688689
ctx.from_ident = &opt->from_ident;

pretty.c

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ static struct cmt_fmt_map {
1616
const char *name;
1717
enum cmit_fmt format;
1818
int is_tformat;
19+
int expand_tabs_in_log;
1920
int is_alias;
2021
const char *user_format;
2122
} *commit_formats;
@@ -87,13 +88,13 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
8788
static void setup_commit_formats(void)
8889
{
8990
struct cmt_fmt_map builtin_formats[] = {
90-
{ "raw", CMIT_FMT_RAW, 0 },
91-
{ "medium", CMIT_FMT_MEDIUM, 0 },
92-
{ "short", CMIT_FMT_SHORT, 0 },
93-
{ "email", CMIT_FMT_EMAIL, 0 },
94-
{ "fuller", CMIT_FMT_FULLER, 0 },
95-
{ "full", CMIT_FMT_FULL, 0 },
96-
{ "oneline", CMIT_FMT_ONELINE, 1 }
91+
{ "raw", CMIT_FMT_RAW, 0, 0 },
92+
{ "medium", CMIT_FMT_MEDIUM, 0, 8 },
93+
{ "short", CMIT_FMT_SHORT, 0, 0 },
94+
{ "email", CMIT_FMT_EMAIL, 0, 0 },
95+
{ "fuller", CMIT_FMT_FULLER, 0, 8 },
96+
{ "full", CMIT_FMT_FULL, 0, 8 },
97+
{ "oneline", CMIT_FMT_ONELINE, 1, 0 }
9798
};
9899
commit_formats_len = ARRAY_SIZE(builtin_formats);
99100
builtin_formats_len = commit_formats_len;
@@ -172,6 +173,7 @@ void get_commit_format(const char *arg, struct rev_info *rev)
172173

173174
rev->commit_format = commit_format->format;
174175
rev->use_terminator = commit_format->is_tformat;
176+
rev->expand_tabs_in_log_default = commit_format->expand_tabs_in_log;
175177
if (commit_format->format == CMIT_FMT_USERFORMAT) {
176178
save_user_format(rev, commit_format->user_format,
177179
commit_format->is_tformat);
@@ -1629,6 +1631,72 @@ void pp_title_line(struct pretty_print_context *pp,
16291631
strbuf_release(&title);
16301632
}
16311633

1634+
static int pp_utf8_width(const char *start, const char *end)
1635+
{
1636+
int width = 0;
1637+
size_t remain = end - start;
1638+
1639+
while (remain) {
1640+
int n = utf8_width(&start, &remain);
1641+
if (n < 0 || !start)
1642+
return -1;
1643+
width += n;
1644+
}
1645+
return width;
1646+
}
1647+
1648+
static void strbuf_add_tabexpand(struct strbuf *sb, int tabwidth,
1649+
const char *line, int linelen)
1650+
{
1651+
const char *tab;
1652+
1653+
while ((tab = memchr(line, '\t', linelen)) != NULL) {
1654+
int width = pp_utf8_width(line, tab);
1655+
1656+
/*
1657+
* If it wasn't well-formed utf8, or it
1658+
* had characters with badly defined
1659+
* width (control characters etc), just
1660+
* give up on trying to align things.
1661+
*/
1662+
if (width < 0)
1663+
break;
1664+
1665+
/* Output the data .. */
1666+
strbuf_add(sb, line, tab - line);
1667+
1668+
/* .. and the de-tabified tab */
1669+
strbuf_addchars(sb, ' ', tabwidth - (width % tabwidth));
1670+
1671+
/* Skip over the printed part .. */
1672+
linelen -= tab + 1 - line;
1673+
line = tab + 1;
1674+
}
1675+
1676+
/*
1677+
* Print out everything after the last tab without
1678+
* worrying about width - there's nothing more to
1679+
* align.
1680+
*/
1681+
strbuf_add(sb, line, linelen);
1682+
}
1683+
1684+
/*
1685+
* pp_handle_indent() prints out the intendation, and
1686+
* the whole line (without the final newline), after
1687+
* de-tabifying.
1688+
*/
1689+
static void pp_handle_indent(struct pretty_print_context *pp,
1690+
struct strbuf *sb, int indent,
1691+
const char *line, int linelen)
1692+
{
1693+
strbuf_addchars(sb, ' ', indent);
1694+
if (pp->expand_tabs_in_log)
1695+
strbuf_add_tabexpand(sb, pp->expand_tabs_in_log, line, linelen);
1696+
else
1697+
strbuf_add(sb, line, linelen);
1698+
}
1699+
16321700
void pp_remainder(struct pretty_print_context *pp,
16331701
const char **msg_p,
16341702
struct strbuf *sb,
@@ -1653,8 +1721,12 @@ void pp_remainder(struct pretty_print_context *pp,
16531721

16541722
strbuf_grow(sb, linelen + indent + 20);
16551723
if (indent)
1656-
strbuf_addchars(sb, ' ', indent);
1657-
strbuf_add(sb, line, linelen);
1724+
pp_handle_indent(pp, sb, indent, line, linelen);
1725+
else if (pp->expand_tabs_in_log)
1726+
strbuf_add_tabexpand(sb, pp->expand_tabs_in_log,
1727+
line, linelen);
1728+
else
1729+
strbuf_add(sb, line, linelen);
16581730
strbuf_addch(sb, '\n');
16591731
}
16601732
}

revision.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,8 +1356,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)
13561356
revs->skip_count = -1;
13571357
revs->max_count = -1;
13581358
revs->max_parents = -1;
1359+
revs->expand_tabs_in_log = -1;
13591360

13601361
revs->commit_format = CMIT_FMT_DEFAULT;
1362+
revs->expand_tabs_in_log_default = 8;
13611363

13621364
init_grep_defaults();
13631365
grep_init(&revs->grep_filter, prefix);
@@ -1854,6 +1856,15 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
18541856
revs->verbose_header = 1;
18551857
revs->pretty_given = 1;
18561858
get_commit_format(arg+9, revs);
1859+
} else if (!strcmp(arg, "--expand-tabs")) {
1860+
revs->expand_tabs_in_log = 8;
1861+
} else if (!strcmp(arg, "--no-expand-tabs")) {
1862+
revs->expand_tabs_in_log = 0;
1863+
} else if (skip_prefix(arg, "--expand-tabs=", &arg)) {
1864+
int val;
1865+
if (strtol_i(arg, 10, &val) < 0 || val < 0)
1866+
die("'%s': not a non-negative integer", arg);
1867+
revs->expand_tabs_in_log = val;
18571868
} else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) {
18581869
revs->show_notes = 1;
18591870
revs->show_notes_given = 1;
@@ -2327,6 +2338,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
23272338
if (revs->first_parent_only && revs->bisect)
23282339
die(_("--first-parent is incompatible with --bisect"));
23292340

2341+
if (revs->expand_tabs_in_log < 0)
2342+
revs->expand_tabs_in_log = revs->expand_tabs_in_log_default;
2343+
23302344
return left;
23312345
}
23322346

revision.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ struct rev_info {
148148
linear:1;
149149

150150
struct date_mode date_mode;
151+
int expand_tabs_in_log; /* unset if negative */
152+
int expand_tabs_in_log_default;
151153

152154
unsigned int abbrev;
153155
enum cmit_fmt commit_format;

t/t4201-shortlog.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ EOF
115115
'
116116

117117
test_expect_success !MINGW 'shortlog from non-git directory' '
118-
git log HEAD >log &&
118+
git log --no-expand-tabs HEAD >log &&
119119
GIT_DIR=non-existing git shortlog -w <log >out &&
120120
test_cmp expect out
121121
'

t/t4213-log-tabexpand.sh

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/bin/sh
2+
3+
test_description='log/show --expand-tabs'
4+
5+
. ./test-lib.sh
6+
7+
HT=" "
8+
title='tab indent at the beginning of the title line'
9+
body='tab indent on a line in the body'
10+
11+
# usage: count_expand $indent $numSP $numHT @format_args
12+
count_expand ()
13+
{
14+
expect=
15+
count=$(( $1 + $2 )) ;# expected spaces
16+
while test $count -gt 0
17+
do
18+
expect="$expect "
19+
count=$(( $count - 1 ))
20+
done
21+
shift 2
22+
count=$1 ;# expected tabs
23+
while test $count -gt 0
24+
do
25+
expect="$expect$HT"
26+
count=$(( $count - 1 ))
27+
done
28+
shift
29+
30+
# The remainder of the command line is "git show -s" options
31+
case " $* " in
32+
*' --pretty=short '*)
33+
line=$title ;;
34+
*)
35+
line=$body ;;
36+
esac
37+
38+
# Prefix the output with the command line arguments, and
39+
# replace SP with a dot both in the expecte and actual output
40+
# so that test_cmp would show the differene together with the
41+
# breakage in a way easier to consume by the debugging user.
42+
{
43+
echo "git show -s $*"
44+
echo "$expect$line"
45+
} | sed -e 's/ /./g' >expect
46+
47+
{
48+
echo "git show -s $*"
49+
git show -s "$@" |
50+
sed -n -e "/$line\$/p"
51+
} | sed -e 's/ /./g' >actual
52+
53+
test_cmp expect actual
54+
}
55+
56+
test_expand ()
57+
{
58+
fmt=$1
59+
case "$fmt" in
60+
*=raw | *=short | *=email)
61+
default="0 1" ;;
62+
*)
63+
default="8 0" ;;
64+
esac
65+
case "$fmt" in
66+
*=email)
67+
in=0 ;;
68+
*)
69+
in=4 ;;
70+
esac
71+
test_expect_success "expand/no-expand${fmt:+ for $fmt}" '
72+
count_expand $in $default $fmt &&
73+
count_expand $in 8 0 $fmt --expand-tabs &&
74+
count_expand $in 8 0 --expand-tabs $fmt &&
75+
count_expand $in 8 0 $fmt --expand-tabs=8 &&
76+
count_expand $in 8 0 --expand-tabs=8 $fmt &&
77+
count_expand $in 0 1 $fmt --no-expand-tabs &&
78+
count_expand $in 0 1 --no-expand-tabs $fmt &&
79+
count_expand $in 0 1 $fmt --expand-tabs=0 &&
80+
count_expand $in 0 1 --expand-tabs=0 $fmt &&
81+
count_expand $in 4 0 $fmt --expand-tabs=4 &&
82+
count_expand $in 4 0 --expand-tabs=4 $fmt
83+
'
84+
}
85+
86+
test_expect_success 'setup' '
87+
test_tick &&
88+
sed -e "s/Q/$HT/g" <<-EOF >msg &&
89+
Q$title
90+
91+
Q$body
92+
EOF
93+
git commit --allow-empty -F msg
94+
'
95+
96+
test_expand ""
97+
test_expand --pretty
98+
test_expand --pretty=short
99+
test_expand --pretty=medium
100+
test_expand --pretty=full
101+
test_expand --pretty=fuller
102+
test_expand --pretty=raw
103+
test_expand --pretty=email
104+
105+
test_done

0 commit comments

Comments
 (0)