]> git.ipfire.org Git - thirdparty/git.git/commitdiff
pretty: add %(decorate[:<options>]) format
authorAndy Koppe <andy.koppe@gmail.com>
Sun, 20 Aug 2023 18:50:07 +0000 (19:50 +0100)
committerJunio C Hamano <gitster@pobox.com>
Mon, 21 Aug 2023 18:40:09 +0000 (11:40 -0700)
Add %(decorate[:<options>]) format that lists ref names similarly to the
%d format, but which allows the otherwise fixed prefix, suffix and
separator strings to be customized. Omitted options default to the
strings used in %d.

Rename expand_separator() function used to expand %x literal formatting
codes to expand_string_arg(), as it is now used on strings other than
separators.

Examples:
- %(decorate) is equivalent to %d.
- %(decorate:prefix=,suffix=) is equivalent to %D.
- %(decorate:prefix=[,suffix=],separator=%x3B) produces a list enclosed
in square brackets and separated by semicolons.

Test the format in t4205-log-pretty-formats.sh and document it in
pretty-formats.txt.

Signed-off-by: Andy Koppe <andy.koppe@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/pretty-formats.txt
pretty.c
t/t4205-log-pretty-formats.sh

index 851a9878e66102eff060441dd1d297931ec012d1..709d85af219da2fc9169f2d596eb066e44753c49 100644 (file)
@@ -224,6 +224,16 @@ The placeholders are:
        linkgit:git-rev-list[1])
 '%d':: ref names, like the --decorate option of linkgit:git-log[1]
 '%D':: ref names without the " (", ")" wrapping.
+'%(decorate[:<options>])'::
+ref names with custom decorations. The `decorate` string may be followed by a
+colon and zero or more comma-separated options. Option values may contain
+literal formatting codes. These must be used for commas (`%x2C`) and closing
+parentheses (`%x29`), due to their role in the option syntax.
++
+** 'prefix=<value>': Shown before the list of ref names.  Defaults to "{nbsp}`(`".
+** 'suffix=<value>': Shown after the list of ref names.  Defaults to "`)`".
+** 'separator=<value>': Shown between ref names.  Defaults to "`,`{nbsp}".
+
 '%(describe[:<options>])'::
 human-readable name, like linkgit:git-describe[1]; empty string for
 undescribable commits.  The `describe` string may be followed by a colon and
index 69b6db3340ce68ed33c3f6ce5996b0e33d26ee3c..1639efe2f859729d2c4998cf166ca28e26982eba 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1252,8 +1252,8 @@ static int format_trailer_match_cb(const struct strbuf *key, void *ud)
        return 0;
 }
 
-static struct strbuf *expand_separator(struct strbuf *sb,
-                                      const char *argval, size_t arglen)
+static struct strbuf *expand_string_arg(struct strbuf *sb,
+                                       const char *argval, size_t arglen)
 {
        char *fmt = xstrndup(argval, arglen);
        const char *format = fmt;
@@ -1301,9 +1301,9 @@ int format_set_trailers_options(struct process_trailer_options *opts,
                        opts->filter_data = filter_list;
                        opts->only_trailers = 1;
                } else if (match_placeholder_arg_value(*arg, "separator", arg, &argval, &arglen)) {
-                       opts->separator = expand_separator(sepbuf, argval, arglen);
+                       opts->separator = expand_string_arg(sepbuf, argval, arglen);
                } else if (match_placeholder_arg_value(*arg, "key_value_separator", arg, &argval, &arglen)) {
-                       opts->key_value_separator = expand_separator(kvsepbuf, argval, arglen);
+                       opts->key_value_separator = expand_string_arg(kvsepbuf, argval, arglen);
                } 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) &&
@@ -1384,6 +1384,40 @@ static size_t parse_describe_args(const char *start, struct strvec *args)
        return arg - start;
 }
 
+
+static int parse_decoration_option(const char **arg,
+                                  const char *name,
+                                  char **opt)
+{
+       const char *argval;
+       size_t arglen;
+
+       if (match_placeholder_arg_value(*arg, name, arg, &argval, &arglen)) {
+               struct strbuf sb = STRBUF_INIT;
+
+               expand_string_arg(&sb, argval, arglen);
+               *opt = strbuf_detach(&sb, NULL);
+               return 1;
+       }
+       return 0;
+}
+
+static void parse_decoration_options(const char **arg,
+                                    struct decoration_options *opts)
+{
+       while (parse_decoration_option(arg, "prefix", &opts->prefix) ||
+              parse_decoration_option(arg, "suffix", &opts->suffix) ||
+              parse_decoration_option(arg, "separator", &opts->separator))
+               ;
+}
+
+static void free_decoration_options(const struct decoration_options *opts)
+{
+       free(opts->prefix);
+       free(opts->suffix);
+       free(opts->separator);
+}
+
 static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                                const char *placeholder,
                                void *context)
@@ -1645,6 +1679,23 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                return 2;
        }
 
+       if (skip_prefix(placeholder, "(decorate", &arg)) {
+               struct decoration_options opts = { NULL };
+               size_t ret = 0;
+
+               if (*arg == ':') {
+                       arg++;
+                       parse_decoration_options(&arg, &opts);
+               }
+               if (*arg == ')') {
+                       format_decorations(sb, commit, c->auto_color, &opts);
+                       ret = arg - placeholder + 1;
+               }
+
+               free_decoration_options(&opts);
+               return ret;
+       }
+
        /* For the rest we have to parse the commit header. */
        if (!c->commit_header_parsed) {
                msg = c->message =
index dd9035aa384937bfec5793fe2f591032282837ac..6ba399c5be28cf0c049ee4c55e627e9e0ea4bccf 100755 (executable)
@@ -576,6 +576,33 @@ test_expect_success 'clean log decoration' '
        test_cmp expected actual1
 '
 
+test_expect_success 'pretty format %decorate' '
+       git checkout -b foo &&
+       git commit --allow-empty -m "new commit" &&
+       git tag bar &&
+       git branch qux &&
+
+       echo " (HEAD -> foo, tag: bar, qux)" >expect1 &&
+       git log --format="%(decorate)" -1 >actual1 &&
+       test_cmp expect1 actual1 &&
+
+       echo "HEAD -> foo, tag: bar, qux" >expect2 &&
+       git log --format="%(decorate:prefix=,suffix=)" -1 >actual2 &&
+       test_cmp expect2 actual2 &&
+
+       echo "[ HEAD -> foo; tag: bar; qux ]" >expect3 &&
+       git log --format="%(decorate:prefix=[ ,suffix= ],separator=%x3B )" \
+               -1 >actual3 &&
+       test_cmp expect3 actual3 &&
+
+       # Try with a typo (in "separator"), in which case the placeholder should
+       # not be replaced.
+       echo "%(decorate:prefix=[ ,suffix= ],separater=; )" >expect4 &&
+       git log --format="%(decorate:prefix=[ ,suffix= ],separater=%x3B )" \
+               -1 >actual4 &&
+       test_cmp expect4 actual4
+'
+
 cat >trailers <<EOF
 Signed-off-by: A U Thor <author@example.com>
 Acked-by: A U Thor <author@example.com>