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>

Ævar Arnfjörð Bjarmason committed Dec 9, 2020 at 16:52 UTC 9d87d5ae026433a2993fdb757fc80dbdcb078310
5 files changed +40 -6
Documentation/pretty-formats.txt
+2 -2
@@ -282,8 +282,8 @@ option is given with no value, it's enabled.
282 ** 'unfold[=<BOOL>]': make it behave as if interpret-trailer's `--unfold`
283 option was given. E.g.,
284 `%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
285 -** 'valueonly[=<BOOL>]': skip over the key part of the trailer line and only
286 - show the value part.
285 +** 'keyonly[=<BOOL>]': only show the key part of the trailer.
286 +** 'valueonly[=<BOOL>]': only show the value part of the trailer.
287
288 NOTE: Some placeholders may depend on other options given to the
289 revision traversal engine. For example, the `%g*` reflog options will
pretty.c
+1
@@ -1451,6 +1451,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1451 opts.separator = &sepbuf;
1452 } else if (!match_placeholder_bool_arg(arg, "only", &arg, &opts.only_trailers) &&
1453 !match_placeholder_bool_arg(arg, "unfold", &arg, &opts.unfold) &&
1454 + !match_placeholder_bool_arg(arg, "keyonly", &arg, &opts.key_only) &&
1455 !match_placeholder_bool_arg(arg, "valueonly", &arg, &opts.value_only))
1456 break;
1457 }
t/t4205-log-pretty-formats.sh
+30 -1
@@ -715,6 +715,22 @@ test_expect_success '%(trailers:key) without value is error' '
715 test_cmp expect actual
716 '
717
718 +test_expect_success '%(trailers:keyonly) shows only keys' '
719 + git log --no-walk --pretty="format:%(trailers:keyonly)" >actual &&
720 + test_write_lines \
721 + "Signed-off-by" \
722 + "Acked-by" \
723 + "[ v2 updated patch description ]" \
724 + "Signed-off-by" >expect &&
725 + test_cmp expect actual
726 +'
727 +
728 +test_expect_success '%(trailers:key=foo,keyonly) shows only key' '
729 + git log --no-walk --pretty="format:%(trailers:key=Acked-by,keyonly)" >actual &&
730 + echo "Acked-by" >expect &&
731 + test_cmp expect actual
732 +'
733 +
734 test_expect_success '%(trailers:key=foo,valueonly) shows only value' '
735 git log --no-walk --pretty="format:%(trailers:key=Acked-by,valueonly)" >actual &&
736 echo "A U Thor <author@example.com>" >expect &&
@@ -732,6 +748,12 @@ test_expect_success '%(trailers:valueonly) shows only values' '
748 test_cmp expect actual
749 '
750
751 +test_expect_success '%(trailers:key=foo,keyonly,valueonly) shows nothing' '
752 + git log --no-walk --pretty="format:%(trailers:key=Acked-by,keyonly,valueonly)" >actual &&
753 + echo >expect &&
754 + test_cmp expect actual
755 +'
756 +
757 test_expect_success 'pretty format %(trailers:separator) changes separator' '
758 git log --no-walk --pretty=format:"X%(trailers:separator=%x00)X" >actual &&
759 (
@@ -754,7 +776,7 @@ test_expect_success 'pretty format %(trailers:separator=X,unfold) changes separa
776 test_cmp expect actual
777 '
778
757 -test_expect_success 'pretty format %(trailers) combining separator/key/valueonly' '
779 +test_expect_success 'pretty format %(trailers) combining separator/key/keyonly/valueonly' '
780 git commit --allow-empty -F - <<-\EOF &&
781 Important fix
782
@@ -781,6 +803,13 @@ test_expect_success 'pretty format %(trailers) combining separator/key/valueonly
803 "Does not close any tickets" \
804 "Another fix #567, #890" \
805 "Important fix #1234" >expect &&
806 + test_cmp expect actual &&
807 +
808 + git log --pretty="%s% (trailers:separator=%x2c%x20,key=Closes,keyonly)" HEAD~3.. >actual &&
809 + test_write_lines \
810 + "Does not close any tickets" \
811 + "Another fix Closes, Closes" \
812 + "Important fix Closes" >expect &&
813 test_cmp expect actual
814 '
815
trailer.c
+6 -3
@@ -1132,7 +1132,7 @@ static void format_trailer_info(struct strbuf *out,
1132
1133 /* If we want the whole block untouched, we can take the fast path. */
1134 if (!opts->only_trailers && !opts->unfold && !opts->filter &&
1135 - !opts->separator && !opts->value_only) {
1135 + !opts->separator && !opts->key_only && !opts->value_only) {
1136 strbuf_add(out, info->trailer_start,
1137 info->trailer_end - info->trailer_start);
1138 return;
@@ -1154,8 +1154,11 @@ static void format_trailer_info(struct strbuf *out,
1154 if (opts->separator && out->len != origlen)
1155 strbuf_addbuf(out, opts->separator);
1156 if (!opts->value_only)
1157 - strbuf_addf(out, "%s: ", tok.buf);
1158 - strbuf_addbuf(out, &val);
1157 + strbuf_addbuf(out, &tok);
1158 + if (!opts->key_only && !opts->value_only)
1159 + strbuf_addstr(out, ": ");
1160 + if (!opts->key_only)
1161 + strbuf_addbuf(out, &val);
1162 if (!opts->separator)
1163 strbuf_addch(out, '\n');
1164 }
trailer.h
+1
@@ -71,6 +71,7 @@ struct process_trailer_options {
71 int only_input;
72 int unfold;
73 int no_divider;
74 + int key_only;
75 int value_only;
76 const struct strbuf *separator;
77 int (*filter)(const struct strbuf *, void *);