зеркало из https://github.com/microsoft/git.git
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
This commit is contained in:
Коммит
cafef3d7ad
|
@ -42,6 +42,20 @@ people using 80-column terminals.
|
|||
verbatim; this means that invalid sequences in the original
|
||||
commit may be copied to the output.
|
||||
|
||||
--expand-tabs=<n>::
|
||||
--expand-tabs::
|
||||
--no-expand-tabs::
|
||||
Perform a tab expansion (replace each tab with enough spaces
|
||||
to fill to the next display column that is multiple of '<n>')
|
||||
in the log message before showing it in the output.
|
||||
`--expand-tabs` is a short-hand for `--expand-tabs=8`, and
|
||||
`--no-expand-tabs` is a short-hand for `--expand-tabs=0`,
|
||||
which disables tab expansion.
|
||||
+
|
||||
By default, tabs are expanded in pretty formats that indent the log
|
||||
message by 4 spaces (i.e. 'medium', which is the default, 'full',
|
||||
and 'fuller').
|
||||
|
||||
ifndef::git-rev-list[]
|
||||
--notes[=<treeish>]::
|
||||
Show the notes (see linkgit:git-notes[1]) that annotate the
|
||||
|
|
|
@ -1290,6 +1290,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
|||
git_config(git_format_config, NULL);
|
||||
init_revisions(&rev, prefix);
|
||||
rev.commit_format = CMIT_FMT_EMAIL;
|
||||
rev.expand_tabs_in_log_default = 0;
|
||||
rev.verbose_header = 1;
|
||||
rev.diff = 1;
|
||||
rev.max_parents = 1;
|
||||
|
|
1
commit.h
1
commit.h
|
@ -147,6 +147,7 @@ struct pretty_print_context {
|
|||
int preserve_subject;
|
||||
struct date_mode date_mode;
|
||||
unsigned date_mode_explicit:1;
|
||||
int expand_tabs_in_log;
|
||||
int need_8bit_cte;
|
||||
char *notes_message;
|
||||
struct reflog_walk_info *reflog_info;
|
||||
|
|
|
@ -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;
|
||||
|
|
90
pretty.c
90
pretty.c
|
@ -16,6 +16,7 @@ static struct cmt_fmt_map {
|
|||
const char *name;
|
||||
enum cmit_fmt format;
|
||||
int is_tformat;
|
||||
int expand_tabs_in_log;
|
||||
int is_alias;
|
||||
const char *user_format;
|
||||
} *commit_formats;
|
||||
|
@ -87,13 +88,13 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
|
|||
static void setup_commit_formats(void)
|
||||
{
|
||||
struct cmt_fmt_map builtin_formats[] = {
|
||||
{ "raw", CMIT_FMT_RAW, 0 },
|
||||
{ "medium", CMIT_FMT_MEDIUM, 0 },
|
||||
{ "short", CMIT_FMT_SHORT, 0 },
|
||||
{ "email", CMIT_FMT_EMAIL, 0 },
|
||||
{ "fuller", CMIT_FMT_FULLER, 0 },
|
||||
{ "full", CMIT_FMT_FULL, 0 },
|
||||
{ "oneline", CMIT_FMT_ONELINE, 1 }
|
||||
{ "raw", CMIT_FMT_RAW, 0, 0 },
|
||||
{ "medium", CMIT_FMT_MEDIUM, 0, 8 },
|
||||
{ "short", CMIT_FMT_SHORT, 0, 0 },
|
||||
{ "email", CMIT_FMT_EMAIL, 0, 0 },
|
||||
{ "fuller", CMIT_FMT_FULLER, 0, 8 },
|
||||
{ "full", CMIT_FMT_FULL, 0, 8 },
|
||||
{ "oneline", CMIT_FMT_ONELINE, 1, 0 }
|
||||
};
|
||||
commit_formats_len = ARRAY_SIZE(builtin_formats);
|
||||
builtin_formats_len = commit_formats_len;
|
||||
|
@ -172,6 +173,7 @@ void get_commit_format(const char *arg, struct rev_info *rev)
|
|||
|
||||
rev->commit_format = commit_format->format;
|
||||
rev->use_terminator = commit_format->is_tformat;
|
||||
rev->expand_tabs_in_log_default = commit_format->expand_tabs_in_log;
|
||||
if (commit_format->format == CMIT_FMT_USERFORMAT) {
|
||||
save_user_format(rev, commit_format->user_format,
|
||||
commit_format->is_tformat);
|
||||
|
@ -1629,6 +1631,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, int tabwidth,
|
||||
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, ' ', tabwidth - (width % tabwidth));
|
||||
|
||||
/* 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, pp->expand_tabs_in_log, 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 +1721,12 @@ 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 if (pp->expand_tabs_in_log)
|
||||
strbuf_add_tabexpand(sb, pp->expand_tabs_in_log,
|
||||
line, linelen);
|
||||
else
|
||||
strbuf_add(sb, line, linelen);
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
}
|
||||
|
|
14
revision.c
14
revision.c
|
@ -1356,8 +1356,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)
|
|||
revs->skip_count = -1;
|
||||
revs->max_count = -1;
|
||||
revs->max_parents = -1;
|
||||
revs->expand_tabs_in_log = -1;
|
||||
|
||||
revs->commit_format = CMIT_FMT_DEFAULT;
|
||||
revs->expand_tabs_in_log_default = 8;
|
||||
|
||||
init_grep_defaults();
|
||||
grep_init(&revs->grep_filter, prefix);
|
||||
|
@ -1854,6 +1856,15 @@ 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 = 8;
|
||||
} else if (!strcmp(arg, "--no-expand-tabs")) {
|
||||
revs->expand_tabs_in_log = 0;
|
||||
} else if (skip_prefix(arg, "--expand-tabs=", &arg)) {
|
||||
int val;
|
||||
if (strtol_i(arg, 10, &val) < 0 || val < 0)
|
||||
die("'%s': not a non-negative integer", arg);
|
||||
revs->expand_tabs_in_log = val;
|
||||
} else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) {
|
||||
revs->show_notes = 1;
|
||||
revs->show_notes_given = 1;
|
||||
|
@ -2327,6 +2338,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
|||
if (revs->first_parent_only && revs->bisect)
|
||||
die(_("--first-parent is incompatible with --bisect"));
|
||||
|
||||
if (revs->expand_tabs_in_log < 0)
|
||||
revs->expand_tabs_in_log = revs->expand_tabs_in_log_default;
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
|
|
|
@ -148,6 +148,8 @@ struct rev_info {
|
|||
linear:1;
|
||||
|
||||
struct date_mode date_mode;
|
||||
int expand_tabs_in_log; /* unset if negative */
|
||||
int expand_tabs_in_log_default;
|
||||
|
||||
unsigned int abbrev;
|
||||
enum cmit_fmt commit_format;
|
||||
|
|
|
@ -115,7 +115,7 @@ EOF
|
|||
'
|
||||
|
||||
test_expect_success !MINGW 'shortlog from non-git directory' '
|
||||
git log HEAD >log &&
|
||||
git log --no-expand-tabs HEAD >log &&
|
||||
GIT_DIR=non-existing git shortlog -w <log >out &&
|
||||
test_cmp expect out
|
||||
'
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='log/show --expand-tabs'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
HT=" "
|
||||
title='tab indent at the beginning of the title line'
|
||||
body='tab indent on a line in the body'
|
||||
|
||||
# usage: count_expand $indent $numSP $numHT @format_args
|
||||
count_expand ()
|
||||
{
|
||||
expect=
|
||||
count=$(( $1 + $2 )) ;# expected spaces
|
||||
while test $count -gt 0
|
||||
do
|
||||
expect="$expect "
|
||||
count=$(( $count - 1 ))
|
||||
done
|
||||
shift 2
|
||||
count=$1 ;# expected tabs
|
||||
while test $count -gt 0
|
||||
do
|
||||
expect="$expect$HT"
|
||||
count=$(( $count - 1 ))
|
||||
done
|
||||
shift
|
||||
|
||||
# The remainder of the command line is "git show -s" options
|
||||
case " $* " in
|
||||
*' --pretty=short '*)
|
||||
line=$title ;;
|
||||
*)
|
||||
line=$body ;;
|
||||
esac
|
||||
|
||||
# Prefix the output with the command line arguments, and
|
||||
# replace SP with a dot both in the expecte and actual output
|
||||
# so that test_cmp would show the differene together with the
|
||||
# breakage in a way easier to consume by the debugging user.
|
||||
{
|
||||
echo "git show -s $*"
|
||||
echo "$expect$line"
|
||||
} | sed -e 's/ /./g' >expect
|
||||
|
||||
{
|
||||
echo "git show -s $*"
|
||||
git show -s "$@" |
|
||||
sed -n -e "/$line\$/p"
|
||||
} | sed -e 's/ /./g' >actual
|
||||
|
||||
test_cmp expect actual
|
||||
}
|
||||
|
||||
test_expand ()
|
||||
{
|
||||
fmt=$1
|
||||
case "$fmt" in
|
||||
*=raw | *=short | *=email)
|
||||
default="0 1" ;;
|
||||
*)
|
||||
default="8 0" ;;
|
||||
esac
|
||||
case "$fmt" in
|
||||
*=email)
|
||||
in=0 ;;
|
||||
*)
|
||||
in=4 ;;
|
||||
esac
|
||||
test_expect_success "expand/no-expand${fmt:+ for $fmt}" '
|
||||
count_expand $in $default $fmt &&
|
||||
count_expand $in 8 0 $fmt --expand-tabs &&
|
||||
count_expand $in 8 0 --expand-tabs $fmt &&
|
||||
count_expand $in 8 0 $fmt --expand-tabs=8 &&
|
||||
count_expand $in 8 0 --expand-tabs=8 $fmt &&
|
||||
count_expand $in 0 1 $fmt --no-expand-tabs &&
|
||||
count_expand $in 0 1 --no-expand-tabs $fmt &&
|
||||
count_expand $in 0 1 $fmt --expand-tabs=0 &&
|
||||
count_expand $in 0 1 --expand-tabs=0 $fmt &&
|
||||
count_expand $in 4 0 $fmt --expand-tabs=4 &&
|
||||
count_expand $in 4 0 --expand-tabs=4 $fmt
|
||||
'
|
||||
}
|
||||
|
||||
test_expect_success 'setup' '
|
||||
test_tick &&
|
||||
sed -e "s/Q/$HT/g" <<-EOF >msg &&
|
||||
Q$title
|
||||
|
||||
Q$body
|
||||
EOF
|
||||
git commit --allow-empty -F msg
|
||||
'
|
||||
|
||||
test_expand ""
|
||||
test_expand --pretty
|
||||
test_expand --pretty=short
|
||||
test_expand --pretty=medium
|
||||
test_expand --pretty=full
|
||||
test_expand --pretty=fuller
|
||||
test_expand --pretty=raw
|
||||
test_expand --pretty=email
|
||||
|
||||
test_done
|
Загрузка…
Ссылка в новой задаче