Merge branch 'jk/refs-double-abort'
[gitweb.git] / pretty.c
index 0ab45d10d702373e212fb36e4fa66feb26669ee7..f496f0f1284db4ea7c6a20022311381bd9dc1b64 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -98,6 +98,10 @@ static void setup_commit_formats(void)
                { "fuller",     CMIT_FMT_FULLER,        0,      8 },
                { "full",       CMIT_FMT_FULL,          0,      8 },
                { "oneline",    CMIT_FMT_ONELINE,       1,      0 }
+               /*
+                * Please update $__git_log_pretty_formats in
+                * git-completion.bash when you add new formats.
+                */
        };
        commit_formats_len = ARRAY_SIZE(builtin_formats);
        builtin_formats_len = commit_formats_len;
@@ -1057,13 +1061,26 @@ static size_t parse_padding_placeholder(struct strbuf *sb,
        return 0;
 }
 
-static int match_placeholder_arg(const char *to_parse, const char *candidate,
-                                const char **end)
+static int match_placeholder_arg_value(const char *to_parse, const char *candidate,
+                                      const char **end, const char **valuestart,
+                                      size_t *valuelen)
 {
        const char *p;
 
        if (!(skip_prefix(to_parse, candidate, &p)))
                return 0;
+       if (valuestart) {
+               if (*p == '=') {
+                       *valuestart = p + 1;
+                       *valuelen = strcspn(*valuestart, ",)");
+                       p = *valuestart + *valuelen;
+               } else {
+                       if (*p != ',' && *p != ')')
+                               return 0;
+                       *valuestart = NULL;
+                       *valuelen = 0;
+               }
+       }
        if (*p == ',') {
                *end = p + 1;
                return 1;
@@ -1075,6 +1092,47 @@ static int match_placeholder_arg(const char *to_parse, const char *candidate,
        return 0;
 }
 
+static int match_placeholder_bool_arg(const char *to_parse, const char *candidate,
+                                     const char **end, int *val)
+{
+       const char *argval;
+       char *strval;
+       size_t arglen;
+       int v;
+
+       if (!match_placeholder_arg_value(to_parse, candidate, end, &argval, &arglen))
+               return 0;
+
+       if (!argval) {
+               *val = 1;
+               return 1;
+       }
+
+       strval = xstrndup(argval, arglen);
+       v = git_parse_maybe_bool(strval);
+       free(strval);
+
+       if (v == -1)
+               return 0;
+
+       *val = v;
+
+       return 1;
+}
+
+static int format_trailer_match_cb(const struct strbuf *key, void *ud)
+{
+       const struct string_list *list = ud;
+       const struct string_list_item *item;
+
+       for_each_string_list_item (item, list) {
+               if (key->len == (uintptr_t)item->util &&
+                   !strncasecmp(item->string, key->buf, key->len))
+                       return 1;
+       }
+       return 0;
+}
+
 static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                                const char *placeholder,
                                void *context)
@@ -1084,10 +1142,14 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
        const char *msg = c->message;
        struct commit_list *p;
        const char *arg;
-       int ch;
+       size_t res;
        char **slot;
 
        /* these are independent of the commit */
+       res = strbuf_expand_literal_cb(sb, placeholder, NULL);
+       if (res)
+               return res;
+
        switch (placeholder[0]) {
        case 'C':
                if (starts_with(placeholder + 1, "(auto)")) {
@@ -1106,16 +1168,6 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                         */
                        return ret;
                }
-       case 'n':               /* newline */
-               strbuf_addch(sb, '\n');
-               return 1;
-       case 'x':
-               /* %x00 == NUL, %x0a == LF, etc. */
-               ch = hex2chr(placeholder + 1);
-               if (ch < 0)
-                       return 0;
-               strbuf_addch(sb, ch);
-               return 3;
        case 'w':
                if (placeholder[1] == '(') {
                        unsigned long width = 0, indent1 = 0, indent2 = 0;
@@ -1322,24 +1374,53 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 
        if (skip_prefix(placeholder, "(trailers", &arg)) {
                struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+               struct string_list filter_list = STRING_LIST_INIT_NODUP;
+               struct strbuf sepbuf = STRBUF_INIT;
+               size_t ret = 0;
 
                opts.no_divider = 1;
 
                if (*arg == ':') {
                        arg++;
                        for (;;) {
-                               if (match_placeholder_arg(arg, "only", &arg))
+                               const char *argval;
+                               size_t arglen;
+
+                               if (match_placeholder_arg_value(arg, "key", &arg, &argval, &arglen)) {
+                                       uintptr_t len = arglen;
+
+                                       if (!argval)
+                                               goto trailer_out;
+
+                                       if (len && argval[len - 1] == ':')
+                                               len--;
+                                       string_list_append(&filter_list, argval)->util = (char *)len;
+
+                                       opts.filter = format_trailer_match_cb;
+                                       opts.filter_data = &filter_list;
                                        opts.only_trailers = 1;
-                               else if (match_placeholder_arg(arg, "unfold", &arg))
-                                       opts.unfold = 1;
-                               else
+                               } else if (match_placeholder_arg_value(arg, "separator", &arg, &argval, &arglen)) {
+                                       char *fmt;
+
+                                       strbuf_reset(&sepbuf);
+                                       fmt = xstrndup(argval, arglen);
+                                       strbuf_expand(&sepbuf, fmt, strbuf_expand_literal_cb, NULL);
+                                       free(fmt);
+                                       opts.separator = &sepbuf;
+                               } 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, "valueonly", &arg, &opts.value_only))
                                        break;
                        }
                }
                if (*arg == ')') {
                        format_trailers_from_commit(sb, msg + c->subject_off, &opts);
-                       return arg - placeholder + 1;
+                       ret = arg - placeholder + 1;
                }
+       trailer_out:
+               string_list_clear(&filter_list, 0);
+               strbuf_release(&sepbuf);
+               return ret;
        }
 
        return 0;       /* unknown placeholder */