]> git.ipfire.org Git - thirdparty/git.git/commitdiff
pretty format %(trailers): add a "key_value_separator"
authorÆvar Arnfjörð Bjarmason <avarab@gmail.com>
Wed, 9 Dec 2020 15:52:08 +0000 (16:52 +0100)
committerJunio C Hamano <gitster@pobox.com>
Wed, 9 Dec 2020 22:16:42 +0000 (14:16 -0800)
Add a "key_value_separator" option to the "%(trailers)" pretty format,
to go along with the existing "separator" argument. In combination
these two options make it trivial to produce machine-readable (e.g. \0
and \0\0-delimited) format output.

As elaborated on in a previous commit which added "keyonly" it was
needlessly tedious to extract structured data from "%(trailers)"
before the addition of this "key_value_separator" option. As seen by
the test being added here extracting this data now becomes trivial.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/pretty-formats.txt
pretty.c
t/t4205-log-pretty-formats.sh
trailer.c
trailer.h

index 5eac36500d4685294cc270d77b849ffac387dfdd..6b59e28d444f8e1f033f435ce0c396aca2516865 100644 (file)
@@ -284,6 +284,10 @@ option is given with no value, it's enabled.
    `%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
 ** 'keyonly[=<BOOL>]': only show the key part of the trailer.
 ** '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
 revision traversal engine. For example, the `%g*` reflog options will
index 1237ee0e45d27ca326b8e03cf0e4652d7168f46c..05eef7fda0b6e3f003e9df7e261baba0590ed00c 100644 (file)
--- a/pretty.c
+++ b/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 string_list filter_list = STRING_LIST_INIT_NODUP;
                struct strbuf sepbuf = STRBUF_INIT;
+               struct strbuf kvsepbuf = STRBUF_INIT;
                size_t ret = 0;
 
                opts.no_divider = 1;
@@ -1449,6 +1450,14 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                                        strbuf_expand(&sepbuf, fmt, strbuf_expand_literal_cb, NULL);
                                        free(fmt);
                                        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) &&
                                           !match_placeholder_bool_arg(arg, "unfold", &arg, &opts.unfold) &&
                                           !match_placeholder_bool_arg(arg, "keyonly", &arg, &opts.key_only) &&
index 4c9f6eb7946fe272731ecd05497718aaa5324acf..749bc1431ac26660a477ad33364feba62c426956 100755 (executable)
@@ -776,6 +776,39 @@ test_expect_success 'pretty format %(trailers:separator=X,unfold) changes separa
        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 &&
        Important fix
index 889b419a4f6fac85f372be0805400a42db28cfbc..249ed618ed8e26c966c6d9815db0645c994ae4f5 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -1132,7 +1132,8 @@ static void format_trailer_info(struct strbuf *out,
 
        /* If we want the whole block untouched, we can take the fast path. */
        if (!opts->only_trailers && !opts->unfold && !opts->filter &&
-           !opts->separator && !opts->key_only && !opts->value_only) {
+           !opts->separator && !opts->key_only && !opts->value_only &&
+           !opts->key_value_separator) {
                strbuf_add(out, info->trailer_start,
                           info->trailer_end - info->trailer_start);
                return;
@@ -1155,8 +1156,12 @@ static void format_trailer_info(struct strbuf *out,
                                        strbuf_addbuf(out, opts->separator);
                                if (!opts->value_only)
                                        strbuf_addbuf(out, &tok);
-                               if (!opts->key_only && !opts->value_only)
-                                       strbuf_addstr(out, ": ");
+                               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)
index d2f28776be6f63d81f155d34b8b0f2ba6bcd39a4..795d2fccfd9579500b798ac667feef2ee549f4b4 100644 (file)
--- a/trailer.h
+++ b/trailer.h
@@ -74,6 +74,7 @@ struct process_trailer_options {
        int key_only;
        int value_only;
        const struct strbuf *separator;
+       const struct strbuf *key_value_separator;
        int (*filter)(const struct strbuf *, void *);
        void *filter_data;
 };