зеркало из https://github.com/microsoft/git.git
Merge branch 'ab/trailers-extra-format'
The "--format=%(trailers)" mechanism gets enhanced to make it easier to design output for machine consumption. * ab/trailers-extra-format: pretty format %(trailers): add a "key_value_separator" pretty format %(trailers): add a "keyonly" pretty-format %(trailers): fix broken standalone "valueonly" pretty format %(trailers) doc: avoid repetition pretty format %(trailers) test: split a long line
This commit is contained in:
Коммит
b62bbd3580
|
@ -252,7 +252,15 @@ endif::git-rev-list[]
|
||||||
interpreted by
|
interpreted by
|
||||||
linkgit:git-interpret-trailers[1]. The
|
linkgit:git-interpret-trailers[1]. The
|
||||||
`trailers` string may be followed by a colon
|
`trailers` string may be followed by a colon
|
||||||
and zero or more comma-separated options:
|
and zero or more comma-separated options.
|
||||||
|
If any option is provided multiple times the
|
||||||
|
last occurance wins.
|
||||||
|
+
|
||||||
|
The boolean options accept an optional value `[=<BOOL>]`. The values
|
||||||
|
`true`, `false`, `on`, `off` etc. are all accepted. See the "boolean"
|
||||||
|
sub-section in "EXAMPLES" in linkgit:git-config[1]. If a boolean
|
||||||
|
option is given with no value, it's enabled.
|
||||||
|
+
|
||||||
** 'key=<K>': only show trailers with specified key. Matching is done
|
** 'key=<K>': only show trailers with specified key. Matching is done
|
||||||
case-insensitively and trailing colon is optional. If option is
|
case-insensitively and trailing colon is optional. If option is
|
||||||
given multiple times trailer lines matching any of the keys are
|
given multiple times trailer lines matching any of the keys are
|
||||||
|
@ -261,27 +269,25 @@ endif::git-rev-list[]
|
||||||
desired it can be disabled with `only=false`. E.g.,
|
desired it can be disabled with `only=false`. E.g.,
|
||||||
`%(trailers:key=Reviewed-by)` shows trailer lines with key
|
`%(trailers:key=Reviewed-by)` shows trailer lines with key
|
||||||
`Reviewed-by`.
|
`Reviewed-by`.
|
||||||
** 'only[=val]': select whether non-trailer lines from the trailer
|
** 'only[=<BOOL>]': select whether non-trailer lines from the trailer
|
||||||
block should be included. The `only` keyword may optionally be
|
block should be included.
|
||||||
followed by an equal sign and one of `true`, `on`, `yes` to omit or
|
|
||||||
`false`, `off`, `no` to show the non-trailer lines. If option is
|
|
||||||
given without value it is enabled. If given multiple times the last
|
|
||||||
value is used.
|
|
||||||
** 'separator=<SEP>': specify a separator inserted between trailer
|
** 'separator=<SEP>': specify a separator inserted between trailer
|
||||||
lines. When this option is not given each trailer line is
|
lines. When this option is not given each trailer line is
|
||||||
terminated with a line feed character. The string SEP may contain
|
terminated with a line feed character. The string SEP may contain
|
||||||
the literal formatting codes described above. To use comma as
|
the literal formatting codes described above. To use comma as
|
||||||
separator one must use `%x2C` as it would otherwise be parsed as
|
separator one must use `%x2C` as it would otherwise be parsed as
|
||||||
next option. If separator option is given multiple times only the
|
next option. E.g., `%(trailers:key=Ticket,separator=%x2C )`
|
||||||
last one is used. E.g., `%(trailers:key=Ticket,separator=%x2C )`
|
|
||||||
shows all trailer lines whose key is "Ticket" separated by a comma
|
shows all trailer lines whose key is "Ticket" separated by a comma
|
||||||
and a space.
|
and a space.
|
||||||
** 'unfold[=val]': make it behave as if interpret-trailer's `--unfold`
|
** 'unfold[=<BOOL>]': make it behave as if interpret-trailer's `--unfold`
|
||||||
option was given. In same way as to for `only` it can be followed
|
option was given. E.g.,
|
||||||
by an equal sign and explicit value. E.g.,
|
|
||||||
`%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
|
`%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
|
||||||
** 'valueonly[=val]': skip over the key part of the trailer line and only
|
** 'keyonly[=<BOOL>]': only show the key part of the trailer.
|
||||||
show the value part. Also this optionally allows explicit value.
|
** 'valueonly[=<BOOL>]': only show the value part of the trailer.
|
||||||
|
** 'key_value_separator=<SEP>': specify a separator inserted between
|
||||||
|
trailer lines. When this option is not given each trailer key-value
|
||||||
|
pair is separated by ": ". Otherwise it shares the same semantics
|
||||||
|
as 'separator=<SEP>' above.
|
||||||
|
|
||||||
NOTE: Some placeholders may depend on other options given to the
|
NOTE: Some placeholders may depend on other options given to the
|
||||||
revision traversal engine. For example, the `%g*` reflog options will
|
revision traversal engine. For example, the `%g*` reflog options will
|
||||||
|
|
10
pretty.c
10
pretty.c
|
@ -1418,6 +1418,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
|
||||||
struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
|
struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
|
||||||
struct string_list filter_list = STRING_LIST_INIT_NODUP;
|
struct string_list filter_list = STRING_LIST_INIT_NODUP;
|
||||||
struct strbuf sepbuf = STRBUF_INIT;
|
struct strbuf sepbuf = STRBUF_INIT;
|
||||||
|
struct strbuf kvsepbuf = STRBUF_INIT;
|
||||||
size_t ret = 0;
|
size_t ret = 0;
|
||||||
|
|
||||||
opts.no_divider = 1;
|
opts.no_divider = 1;
|
||||||
|
@ -1449,8 +1450,17 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
|
||||||
strbuf_expand(&sepbuf, fmt, strbuf_expand_literal_cb, NULL);
|
strbuf_expand(&sepbuf, fmt, strbuf_expand_literal_cb, NULL);
|
||||||
free(fmt);
|
free(fmt);
|
||||||
opts.separator = &sepbuf;
|
opts.separator = &sepbuf;
|
||||||
|
} else if (match_placeholder_arg_value(arg, "key_value_separator", &arg, &argval, &arglen)) {
|
||||||
|
char *fmt;
|
||||||
|
|
||||||
|
strbuf_reset(&kvsepbuf);
|
||||||
|
fmt = xstrndup(argval, arglen);
|
||||||
|
strbuf_expand(&kvsepbuf, fmt, strbuf_expand_literal_cb, NULL);
|
||||||
|
free(fmt);
|
||||||
|
opts.key_value_separator = &kvsepbuf;
|
||||||
} else if (!match_placeholder_bool_arg(arg, "only", &arg, &opts.only_trailers) &&
|
} else if (!match_placeholder_bool_arg(arg, "only", &arg, &opts.only_trailers) &&
|
||||||
!match_placeholder_bool_arg(arg, "unfold", &arg, &opts.unfold) &&
|
!match_placeholder_bool_arg(arg, "unfold", &arg, &opts.unfold) &&
|
||||||
|
!match_placeholder_bool_arg(arg, "keyonly", &arg, &opts.key_only) &&
|
||||||
!match_placeholder_bool_arg(arg, "valueonly", &arg, &opts.value_only))
|
!match_placeholder_bool_arg(arg, "valueonly", &arg, &opts.value_only))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -605,6 +605,12 @@ test_expect_success 'pretty format %(trailers) shows trailers' '
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'pretty format %(trailers:) enables no options' '
|
||||||
|
git log --no-walk --pretty="%(trailers:)" >actual &&
|
||||||
|
# "expect" the same as the test above
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success '%(trailers:only) shows only "key: value" trailers' '
|
test_expect_success '%(trailers:only) shows only "key: value" trailers' '
|
||||||
git log --no-walk --pretty="%(trailers:only)" >actual &&
|
git log --no-walk --pretty="%(trailers:only)" >actual &&
|
||||||
{
|
{
|
||||||
|
@ -709,19 +715,101 @@ test_expect_success '%(trailers:key) without value is error' '
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success '%(trailers:keyonly) shows only keys' '
|
||||||
|
git log --no-walk --pretty="format:%(trailers:keyonly)" >actual &&
|
||||||
|
test_write_lines \
|
||||||
|
"Signed-off-by" \
|
||||||
|
"Acked-by" \
|
||||||
|
"[ v2 updated patch description ]" \
|
||||||
|
"Signed-off-by" >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '%(trailers:key=foo,keyonly) shows only key' '
|
||||||
|
git log --no-walk --pretty="format:%(trailers:key=Acked-by,keyonly)" >actual &&
|
||||||
|
echo "Acked-by" >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success '%(trailers:key=foo,valueonly) shows only value' '
|
test_expect_success '%(trailers:key=foo,valueonly) shows only value' '
|
||||||
git log --no-walk --pretty="format:%(trailers:key=Acked-by,valueonly)" >actual &&
|
git log --no-walk --pretty="format:%(trailers:key=Acked-by,valueonly)" >actual &&
|
||||||
echo "A U Thor <author@example.com>" >expect &&
|
echo "A U Thor <author@example.com>" >expect &&
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'pretty format %(trailers:separator) changes separator' '
|
test_expect_success '%(trailers:valueonly) shows only values' '
|
||||||
git log --no-walk --pretty=format:"X%(trailers:separator=%x00,unfold)X" >actual &&
|
git log --no-walk --pretty="format:%(trailers:valueonly)" >actual &&
|
||||||
printf "XSigned-off-by: A U Thor <author@example.com>\0Acked-by: A U Thor <author@example.com>\0[ v2 updated patch description ]\0Signed-off-by: A U Thor <author@example.com>X" >expect &&
|
test_write_lines \
|
||||||
|
"A U Thor <author@example.com>" \
|
||||||
|
"A U Thor <author@example.com>" \
|
||||||
|
"[ v2 updated patch description ]" \
|
||||||
|
"A U Thor" \
|
||||||
|
" <author@example.com>" >expect &&
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'pretty format %(trailers) combining separator/key/valueonly' '
|
test_expect_success '%(trailers:key=foo,keyonly,valueonly) shows nothing' '
|
||||||
|
git log --no-walk --pretty="format:%(trailers:key=Acked-by,keyonly,valueonly)" >actual &&
|
||||||
|
echo >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'pretty format %(trailers:separator) changes separator' '
|
||||||
|
git log --no-walk --pretty=format:"X%(trailers:separator=%x00)X" >actual &&
|
||||||
|
(
|
||||||
|
printf "XSigned-off-by: A U Thor <author@example.com>\0" &&
|
||||||
|
printf "Acked-by: A U Thor <author@example.com>\0" &&
|
||||||
|
printf "[ v2 updated patch description ]\0" &&
|
||||||
|
printf "Signed-off-by: A U Thor\n <author@example.com>X"
|
||||||
|
) >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'pretty format %(trailers:separator=X,unfold) changes separator' '
|
||||||
|
git log --no-walk --pretty=format:"X%(trailers:separator=%x00,unfold)X" >actual &&
|
||||||
|
(
|
||||||
|
printf "XSigned-off-by: A U Thor <author@example.com>\0" &&
|
||||||
|
printf "Acked-by: A U Thor <author@example.com>\0" &&
|
||||||
|
printf "[ v2 updated patch description ]\0" &&
|
||||||
|
printf "Signed-off-by: A U Thor <author@example.com>X"
|
||||||
|
) >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'pretty format %(trailers:key_value_separator) changes key-value separator' '
|
||||||
|
git log --no-walk --pretty=format:"X%(trailers:key_value_separator=%x00)X" >actual &&
|
||||||
|
(
|
||||||
|
printf "XSigned-off-by\0A U Thor <author@example.com>\n" &&
|
||||||
|
printf "Acked-by\0A U Thor <author@example.com>\n" &&
|
||||||
|
printf "[ v2 updated patch description ]\n" &&
|
||||||
|
printf "Signed-off-by\0A U Thor\n <author@example.com>\nX"
|
||||||
|
) >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'pretty format %(trailers:key_value_separator,unfold) changes key-value separator' '
|
||||||
|
git log --no-walk --pretty=format:"X%(trailers:key_value_separator=%x00,unfold)X" >actual &&
|
||||||
|
(
|
||||||
|
printf "XSigned-off-by\0A U Thor <author@example.com>\n" &&
|
||||||
|
printf "Acked-by\0A U Thor <author@example.com>\n" &&
|
||||||
|
printf "[ v2 updated patch description ]\n" &&
|
||||||
|
printf "Signed-off-by\0A U Thor <author@example.com>\nX"
|
||||||
|
) >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'pretty format %(trailers:separator,key_value_separator) changes both separators' '
|
||||||
|
git log --no-walk --pretty=format:"%(trailers:separator=%x00,key_value_separator=%x00%x00,unfold)" >actual &&
|
||||||
|
(
|
||||||
|
printf "Signed-off-by\0\0A U Thor <author@example.com>\0" &&
|
||||||
|
printf "Acked-by\0\0A U Thor <author@example.com>\0" &&
|
||||||
|
printf "[ v2 updated patch description ]\0" &&
|
||||||
|
printf "Signed-off-by\0\0A U Thor <author@example.com>"
|
||||||
|
) >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'pretty format %(trailers) combining separator/key/keyonly/valueonly' '
|
||||||
git commit --allow-empty -F - <<-\EOF &&
|
git commit --allow-empty -F - <<-\EOF &&
|
||||||
Important fix
|
Important fix
|
||||||
|
|
||||||
|
@ -748,6 +836,13 @@ test_expect_success 'pretty format %(trailers) combining separator/key/valueonly
|
||||||
"Does not close any tickets" \
|
"Does not close any tickets" \
|
||||||
"Another fix #567, #890" \
|
"Another fix #567, #890" \
|
||||||
"Important fix #1234" >expect &&
|
"Important fix #1234" >expect &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
git log --pretty="%s% (trailers:separator=%x2c%x20,key=Closes,keyonly)" HEAD~3.. >actual &&
|
||||||
|
test_write_lines \
|
||||||
|
"Does not close any tickets" \
|
||||||
|
"Another fix Closes, Closes" \
|
||||||
|
"Important fix Closes" >expect &&
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
|
15
trailer.c
15
trailer.c
|
@ -1131,7 +1131,9 @@ static void format_trailer_info(struct strbuf *out,
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
/* If we want the whole block untouched, we can take the fast path. */
|
/* If we want the whole block untouched, we can take the fast path. */
|
||||||
if (!opts->only_trailers && !opts->unfold && !opts->filter && !opts->separator) {
|
if (!opts->only_trailers && !opts->unfold && !opts->filter &&
|
||||||
|
!opts->separator && !opts->key_only && !opts->value_only &&
|
||||||
|
!opts->key_value_separator) {
|
||||||
strbuf_add(out, info->trailer_start,
|
strbuf_add(out, info->trailer_start,
|
||||||
info->trailer_end - info->trailer_start);
|
info->trailer_end - info->trailer_start);
|
||||||
return;
|
return;
|
||||||
|
@ -1153,8 +1155,15 @@ static void format_trailer_info(struct strbuf *out,
|
||||||
if (opts->separator && out->len != origlen)
|
if (opts->separator && out->len != origlen)
|
||||||
strbuf_addbuf(out, opts->separator);
|
strbuf_addbuf(out, opts->separator);
|
||||||
if (!opts->value_only)
|
if (!opts->value_only)
|
||||||
strbuf_addf(out, "%s: ", tok.buf);
|
strbuf_addbuf(out, &tok);
|
||||||
strbuf_addbuf(out, &val);
|
if (!opts->key_only && !opts->value_only) {
|
||||||
|
if (opts->key_value_separator)
|
||||||
|
strbuf_addbuf(out, opts->key_value_separator);
|
||||||
|
else
|
||||||
|
strbuf_addstr(out, ": ");
|
||||||
|
}
|
||||||
|
if (!opts->key_only)
|
||||||
|
strbuf_addbuf(out, &val);
|
||||||
if (!opts->separator)
|
if (!opts->separator)
|
||||||
strbuf_addch(out, '\n');
|
strbuf_addch(out, '\n');
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,8 +71,10 @@ struct process_trailer_options {
|
||||||
int only_input;
|
int only_input;
|
||||||
int unfold;
|
int unfold;
|
||||||
int no_divider;
|
int no_divider;
|
||||||
|
int key_only;
|
||||||
int value_only;
|
int value_only;
|
||||||
const struct strbuf *separator;
|
const struct strbuf *separator;
|
||||||
|
const struct strbuf *key_value_separator;
|
||||||
int (*filter)(const struct strbuf *, void *);
|
int (*filter)(const struct strbuf *, void *);
|
||||||
void *filter_data;
|
void *filter_data;
|
||||||
};
|
};
|
||||||
|
|
Загрузка…
Ссылка в новой задаче