pretty format %(trailers): add a "keyonly"

Add support for a "keyonly". This allows for easier parsing out of the
key and value. Before if you didn't want to make assumptions about how
the key was formatted. You'd need to parse it out as e.g.:

    --pretty=format:'%H%x00%(trailers:separator=%x00%x00)' \
                       '%x00%(trailers:separator=%x00%x00,valueonly)'

And then proceed to deduce keys by looking at those two and
subtracting the value plus the hardcoded ": " separator from the
non-valueonly %(trailers) line. Now it's possible to simply do:

    --pretty=format:'%H%x00%(trailers:separator=%x00%x00,keyonly)' \
                    '%x00%(trailers:separator=%x00%x00,valueonly)'

Which at least reduces it to a state machine where you get N keys and
correlate them with N values. Even better would be to have a way to
change the ": " delimiter to something easily machine-readable (a key
might contain ": " too). A follow-up change will add support for that.

I don't really have a use-case for just "keyonly" myself. I suppose it
would be useful in some cases as "key=*" matches case-insensitively,
so a plain "keyonly" will give you the variants of the keys you
matched. I'm mainly adding it to fix the inconsistency with
"valueonly".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Ævar Arnfjörð Bjarmason 2020-12-09 16:52:07 +01:00 committed by Junio C Hamano
parent 8b966a0506
commit 9d87d5ae02
5 changed files with 40 additions and 6 deletions

View File

@ -282,8 +282,8 @@ option is given with no value, it's enabled.
** 'unfold[=<BOOL>]': make it behave as if interpret-trailer's `--unfold` ** 'unfold[=<BOOL>]': make it behave as if interpret-trailer's `--unfold`
option was given. E.g., option was given. E.g.,
`%(trailers:only,unfold=true)` unfolds and shows all trailer lines. `%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
** 'valueonly[=<BOOL>]': 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. ** 'valueonly[=<BOOL>]': only show the value part of the trailer.
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

View File

@ -1451,6 +1451,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
opts.separator = &sepbuf; opts.separator = &sepbuf;
} 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;
} }

View File

@ -715,6 +715,22 @@ 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 &&
@ -732,6 +748,12 @@ test_expect_success '%(trailers:valueonly) shows only values' '
test_cmp expect actual test_cmp expect actual
' '
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' ' test_expect_success 'pretty format %(trailers:separator) changes separator' '
git log --no-walk --pretty=format:"X%(trailers:separator=%x00)X" >actual && git log --no-walk --pretty=format:"X%(trailers:separator=%x00)X" >actual &&
( (
@ -754,7 +776,7 @@ test_expect_success 'pretty format %(trailers:separator=X,unfold) changes separa
test_cmp expect actual test_cmp expect actual
' '
test_expect_success 'pretty format %(trailers) combining separator/key/valueonly' ' 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
@ -781,6 +803,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
' '

View File

@ -1132,7 +1132,7 @@ static void format_trailer_info(struct strbuf *out,
/* 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 && if (!opts->only_trailers && !opts->unfold && !opts->filter &&
!opts->separator && !opts->value_only) { !opts->separator && !opts->key_only && !opts->value_only) {
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;
@ -1154,8 +1154,11 @@ 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)
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');
} }

View File

@ -71,6 +71,7 @@ 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;
int (*filter)(const struct strbuf *, void *); int (*filter)(const struct strbuf *, void *);