t3700: fix broken test under !SANITY
[gitweb.git] / pretty.c
index 63e03b67e932786e588c7742279d1c6d8ac9df0e..87c44971a1d2070e4d256818e96fccc29472ec4a 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -16,6 +16,7 @@ static struct cmt_fmt_map {
        const char *name;
        enum cmit_fmt format;
        int is_tformat;
+       int expand_tabs_in_log;
        int is_alias;
        const char *user_format;
 } *commit_formats;
@@ -73,10 +74,9 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
        if (git_config_string(&fmt, var, value))
                return -1;
 
-       if (starts_with(fmt, "format:") || starts_with(fmt, "tformat:")) {
-               commit_format->is_tformat = fmt[0] == 't';
-               fmt = strchr(fmt, ':') + 1;
-       } else if (strchr(fmt, '%'))
+       if (skip_prefix(fmt, "format:", &fmt))
+               commit_format->is_tformat = 0;
+       else if (skip_prefix(fmt, "tformat:", &fmt) || strchr(fmt, '%'))
                commit_format->is_tformat = 1;
        else
                commit_format->is_alias = 1;
@@ -88,13 +88,13 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
 static void setup_commit_formats(void)
 {
        struct cmt_fmt_map builtin_formats[] = {
-               { "raw",        CMIT_FMT_RAW,           0 },
-               { "medium",     CMIT_FMT_MEDIUM,        0 },
-               { "short",      CMIT_FMT_SHORT,         0 },
-               { "email",      CMIT_FMT_EMAIL,         0 },
-               { "fuller",     CMIT_FMT_FULLER,        0 },
-               { "full",       CMIT_FMT_FULL,          0 },
-               { "oneline",    CMIT_FMT_ONELINE,       1 }
+               { "raw",        CMIT_FMT_RAW,           0,      0 },
+               { "medium",     CMIT_FMT_MEDIUM,        0,      8 },
+               { "short",      CMIT_FMT_SHORT,         0,      0 },
+               { "email",      CMIT_FMT_EMAIL,         0,      0 },
+               { "fuller",     CMIT_FMT_FULLER,        0,      8 },
+               { "full",       CMIT_FMT_FULL,          0,      8 },
+               { "oneline",    CMIT_FMT_ONELINE,       1,      0 }
        };
        commit_formats_len = ARRAY_SIZE(builtin_formats);
        builtin_formats_len = commit_formats_len;
@@ -157,12 +157,12 @@ void get_commit_format(const char *arg, struct rev_info *rev)
                rev->commit_format = CMIT_FMT_DEFAULT;
                return;
        }
-       if (starts_with(arg, "format:") || starts_with(arg, "tformat:")) {
-               save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
+       if (skip_prefix(arg, "format:", &arg)) {
+               save_user_format(rev, arg, 0);
                return;
        }
 
-       if (!*arg || strchr(arg, '%')) {
+       if (!*arg || skip_prefix(arg, "tformat:", &arg) || strchr(arg, '%')) {
                save_user_format(rev, arg, 1);
                return;
        }
@@ -173,6 +173,7 @@ void get_commit_format(const char *arg, struct rev_info *rev)
 
        rev->commit_format = commit_format->format;
        rev->use_terminator = commit_format->is_tformat;
+       rev->expand_tabs_in_log_default = commit_format->expand_tabs_in_log;
        if (commit_format->format == CMIT_FMT_USERFORMAT) {
                save_user_format(rev, commit_format->user_format,
                                 commit_format->is_tformat);
@@ -400,7 +401,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
 }
 
 const char *show_ident_date(const struct ident_split *ident,
-                           enum date_mode mode)
+                           const struct date_mode *mode)
 {
        unsigned long date = 0;
        long tz = 0;
@@ -490,15 +491,15 @@ void pp_user_info(struct pretty_print_context *pp,
        switch (pp->fmt) {
        case CMIT_FMT_MEDIUM:
                strbuf_addf(sb, "Date:   %s\n",
-                           show_ident_date(&ident, pp->date_mode));
+                           show_ident_date(&ident, &pp->date_mode));
                break;
        case CMIT_FMT_EMAIL:
                strbuf_addf(sb, "Date: %s\n",
-                           show_ident_date(&ident, DATE_RFC2822));
+                           show_ident_date(&ident, DATE_MODE(RFC2822)));
                break;
        case CMIT_FMT_FULLER:
                strbuf_addf(sb, "%sDate: %s\n", what,
-                           show_ident_date(&ident, pp->date_mode));
+                           show_ident_date(&ident, &pp->date_mode));
                break;
        default:
                /* notin' */
@@ -544,9 +545,9 @@ static void add_merge_info(const struct pretty_print_context *pp,
                struct commit *p = parent->item;
                const char *hex = NULL;
                if (pp->abbrev)
-                       hex = find_unique_abbrev(p->object.sha1, pp->abbrev);
+                       hex = find_unique_abbrev(p->object.oid.hash, pp->abbrev);
                if (!hex)
-                       hex = sha1_to_hex(p->object.sha1);
+                       hex = oid_to_hex(&p->object.oid);
                parent = parent->next;
 
                strbuf_addf(sb, " %s", hex);
@@ -568,7 +569,7 @@ static char *replace_encoding_header(char *buf, const char *encoding)
        char *cp = buf;
 
        /* guess if there is an encoding header before a \n\n */
-       while (strncmp(cp, "encoding ", strlen("encoding "))) {
+       while (!starts_with(cp, "encoding ")) {
                cp = strchr(cp, '\n');
                if (!cp || *++cp == '\n')
                        return buf;
@@ -672,7 +673,8 @@ static int mailmap_name(const char **email, size_t *email_len,
 }
 
 static size_t format_person_part(struct strbuf *sb, char part,
-                                const char *msg, int len, enum date_mode dmode)
+                                const char *msg, int len,
+                                const struct date_mode *dmode)
 {
        /* currently all placeholders have same length */
        const int placeholder_len = 2;
@@ -712,16 +714,16 @@ static size_t format_person_part(struct strbuf *sb, char part,
                strbuf_addstr(sb, show_ident_date(&s, dmode));
                return placeholder_len;
        case 'D':       /* date, RFC2822 style */
-               strbuf_addstr(sb, show_ident_date(&s, DATE_RFC2822));
+               strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(RFC2822)));
                return placeholder_len;
        case 'r':       /* date, relative */
-               strbuf_addstr(sb, show_ident_date(&s, DATE_RELATIVE));
+               strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(RELATIVE)));
                return placeholder_len;
        case 'i':       /* date, ISO 8601-like */
-               strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601));
+               strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(ISO8601)));
                return placeholder_len;
        case 'I':       /* date, ISO 8601 strict */
-               strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601_STRICT));
+               strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(ISO8601_STRICT)));
                return placeholder_len;
        }
 
@@ -809,18 +811,19 @@ static void parse_commit_header(struct format_commit_context *context)
        int i;
 
        for (i = 0; msg[i]; i++) {
+               const char *name;
                int eol;
                for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
                        ; /* do nothing */
 
                if (i == eol) {
                        break;
-               } else if (starts_with(msg + i, "author ")) {
-                       context->author.off = i + 7;
-                       context->author.len = eol - i - 7;
-               } else if (starts_with(msg + i, "committer ")) {
-                       context->committer.off = i + 10;
-                       context->committer.len = eol - i - 10;
+               } else if (skip_prefix(msg + i, "author ", &name)) {
+                       context->author.off = name - msg;
+                       context->author.len = msg + eol - name;
+               } else if (skip_prefix(msg + i, "committer ", &name)) {
+                       context->committer.off = name - msg;
+                       context->committer.len = msg + eol - name;
                }
                i = eol;
        }
@@ -933,7 +936,7 @@ static void rewrap_message_tail(struct strbuf *sb,
 static int format_reflog_person(struct strbuf *sb,
                                char part,
                                struct reflog_walk_info *log,
-                               enum date_mode dmode)
+                               const struct date_mode *dmode)
 {
        const char *ident;
 
@@ -951,6 +954,8 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
                          const char *placeholder,
                          struct format_commit_context *c)
 {
+       const char *rest = placeholder;
+
        if (placeholder[1] == '(') {
                const char *begin = placeholder + 2;
                const char *end = strchr(begin, ')');
@@ -958,31 +963,24 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
 
                if (!end)
                        return 0;
-               if (starts_with(begin, "auto,")) {
+               if (skip_prefix(begin, "auto,", &begin)) {
                        if (!want_color(c->pretty_ctx->color))
                                return end - placeholder + 1;
-                       begin += 5;
                }
-               color_parse_mem(begin,
-                               end - begin,
-                               "--pretty format", color);
+               if (color_parse_mem(begin, end - begin, color) < 0)
+                       die(_("unable to parse --pretty format"));
                strbuf_addstr(sb, color);
                return end - placeholder + 1;
        }
-       if (starts_with(placeholder + 1, "red")) {
+       if (skip_prefix(placeholder + 1, "red", &rest))
                strbuf_addstr(sb, GIT_COLOR_RED);
-               return 4;
-       } else if (starts_with(placeholder + 1, "green")) {
+       else if (skip_prefix(placeholder + 1, "green", &rest))
                strbuf_addstr(sb, GIT_COLOR_GREEN);
-               return 6;
-       } else if (starts_with(placeholder + 1, "blue")) {
+       else if (skip_prefix(placeholder + 1, "blue", &rest))
                strbuf_addstr(sb, GIT_COLOR_BLUE);
-               return 5;
-       } else if (starts_with(placeholder + 1, "reset")) {
+       else if (skip_prefix(placeholder + 1, "reset", &rest))
                strbuf_addstr(sb, GIT_COLOR_RESET);
-               return 6;
-       } else
-               return 0;
+       return rest - placeholder;
 }
 
 static size_t parse_padding_placeholder(struct strbuf *sb,
@@ -1123,12 +1121,12 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 
        /* these depend on the commit */
        if (!commit->object.parsed)
-               parse_object(commit->object.sha1);
+               parse_object(commit->object.oid.hash);
 
        switch (placeholder[0]) {
        case 'H':               /* commit hash */
                strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT));
-               strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
+               strbuf_addstr(sb, oid_to_hex(&commit->object.oid));
                strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
                return 1;
        case 'h':               /* abbreviated commit hash */
@@ -1137,18 +1135,18 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
                        return 1;
                }
-               strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
+               strbuf_addstr(sb, find_unique_abbrev(commit->object.oid.hash,
                                                     c->pretty_ctx->abbrev));
                strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
                c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
                return 1;
        case 'T':               /* tree hash */
-               strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1));
+               strbuf_addstr(sb, oid_to_hex(&commit->tree->object.oid));
                return 1;
        case 't':               /* abbreviated tree hash */
                if (add_again(sb, &c->abbrev_tree_hash))
                        return 1;
-               strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
+               strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.oid.hash,
                                                     c->pretty_ctx->abbrev));
                c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
                return 1;
@@ -1156,7 +1154,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                for (p = commit->parents; p; p = p->next) {
                        if (p != commit->parents)
                                strbuf_addch(sb, ' ');
-                       strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1));
+                       strbuf_addstr(sb, oid_to_hex(&p->item->object.oid));
                }
                return 1;
        case 'p':               /* abbreviated parent hashes */
@@ -1166,7 +1164,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        if (p != commit->parents)
                                strbuf_addch(sb, ' ');
                        strbuf_addstr(sb, find_unique_abbrev(
-                                       p->item->object.sha1,
+                                       p->item->object.oid.hash,
                                        c->pretty_ctx->abbrev));
                }
                c->abbrev_parent_hashes.len = sb->len -
@@ -1179,6 +1177,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                load_ref_decorations(DECORATE_SHORT_REFS);
                format_decorations(sb, commit, c->auto_color);
                return 1;
+       case 'D':
+               load_ref_decorations(DECORATE_SHORT_REFS);
+               format_decorations_extended(sb, commit, c->auto_color, "", ", ", "");
+               return 1;
        case 'g':               /* reflog info */
                switch(placeholder[1]) {
                case 'd':       /* reflog selector */
@@ -1186,7 +1188,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        if (c->pretty_ctx->reflog_info)
                                get_reflog_selector(sb,
                                                    c->pretty_ctx->reflog_info,
-                                                   c->pretty_ctx->date_mode,
+                                                   &c->pretty_ctx->date_mode,
                                                    c->pretty_ctx->date_mode_explicit,
                                                    (placeholder[1] == 'd'));
                        return 2;
@@ -1201,7 +1203,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        return format_reflog_person(sb,
                                                    placeholder[1],
                                                    c->pretty_ctx->reflog_info,
-                                                   c->pretty_ctx->date_mode);
+                                                   &c->pretty_ctx->date_mode);
                }
                return 0;       /* unknown %g placeholder */
        case 'N':
@@ -1252,11 +1254,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
        case 'a':       /* author ... */
                return format_person_part(sb, placeholder[1],
                                   msg + c->author.off, c->author.len,
-                                  c->pretty_ctx->date_mode);
+                                  &c->pretty_ctx->date_mode);
        case 'c':       /* committer ... */
                return format_person_part(sb, placeholder[1],
                                   msg + c->committer.off, c->committer.len,
-                                  c->pretty_ctx->date_mode);
+                                  &c->pretty_ctx->date_mode);
        case 'e':       /* encoding */
                if (c->commit_encoding)
                        strbuf_addstr(sb, c->commit_encoding);
@@ -1377,9 +1379,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
                 * convert it back to chars
                 */
                padding = padding - len + local_sb.len;
-               strbuf_grow(sb, padding);
-               strbuf_setlen(sb, sb_len + padding);
-               memset(sb->buf + sb_len, ' ', sb->len - sb_len);
+               strbuf_addchars(sb, ' ', padding);
                memcpy(sb->buf + sb_len + offset, local_sb.buf,
                       local_sb.len);
        }
@@ -1520,7 +1520,7 @@ static void pp_header(struct pretty_print_context *pp,
        int parents_shown = 0;
 
        for (;;) {
-               const char *line = *msg_p;
+               const char *name, *line = *msg_p;
                int linelen = get_one_line(*msg_p);
 
                if (!linelen)
@@ -1555,14 +1555,14 @@ static void pp_header(struct pretty_print_context *pp,
                 * FULL shows both authors but not dates.
                 * FULLER shows both authors and dates.
                 */
-               if (starts_with(line, "author ")) {
+               if (skip_prefix(line, "author ", &name)) {
                        strbuf_grow(sb, linelen + 80);
-                       pp_user_info(pp, "Author", sb, line + 7, encoding);
+                       pp_user_info(pp, "Author", sb, name, encoding);
                }
-               if (starts_with(line, "committer ") &&
+               if (skip_prefix(line, "committer ", &name) &&
                    (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) {
                        strbuf_grow(sb, linelen + 80);
-                       pp_user_info(pp, "Commit", sb, line + 10, encoding);
+                       pp_user_info(pp, "Commit", sb, name, encoding);
                }
        }
 }
@@ -1631,6 +1631,72 @@ void pp_title_line(struct pretty_print_context *pp,
        strbuf_release(&title);
 }
 
+static int pp_utf8_width(const char *start, const char *end)
+{
+       int width = 0;
+       size_t remain = end - start;
+
+       while (remain) {
+               int n = utf8_width(&start, &remain);
+               if (n < 0 || !start)
+                       return -1;
+               width += n;
+       }
+       return width;
+}
+
+static void strbuf_add_tabexpand(struct strbuf *sb, int tabwidth,
+                                const char *line, int linelen)
+{
+       const char *tab;
+
+       while ((tab = memchr(line, '\t', linelen)) != NULL) {
+               int width = pp_utf8_width(line, tab);
+
+               /*
+                * If it wasn't well-formed utf8, or it
+                * had characters with badly defined
+                * width (control characters etc), just
+                * give up on trying to align things.
+                */
+               if (width < 0)
+                       break;
+
+               /* Output the data .. */
+               strbuf_add(sb, line, tab - line);
+
+               /* .. and the de-tabified tab */
+               strbuf_addchars(sb, ' ', tabwidth - (width % tabwidth));
+
+               /* Skip over the printed part .. */
+               linelen -= tab + 1 - line;
+               line = tab + 1;
+       }
+
+       /*
+        * Print out everything after the last tab without
+        * worrying about width - there's nothing more to
+        * align.
+        */
+       strbuf_add(sb, line, linelen);
+}
+
+/*
+ * pp_handle_indent() prints out the intendation, and
+ * the whole line (without the final newline), after
+ * de-tabifying.
+ */
+static void pp_handle_indent(struct pretty_print_context *pp,
+                            struct strbuf *sb, int indent,
+                            const char *line, int linelen)
+{
+       strbuf_addchars(sb, ' ', indent);
+       if (pp->expand_tabs_in_log)
+               strbuf_add_tabexpand(sb, pp->expand_tabs_in_log, line, linelen);
+       else
+               strbuf_add(sb, line, linelen);
+}
+
 void pp_remainder(struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
@@ -1654,11 +1720,13 @@ void pp_remainder(struct pretty_print_context *pp,
                first = 0;
 
                strbuf_grow(sb, linelen + indent + 20);
-               if (indent) {
-                       memset(sb->buf + sb->len, ' ', indent);
-                       strbuf_setlen(sb, sb->len + indent);
-               }
-               strbuf_add(sb, line, linelen);
+               if (indent)
+                       pp_handle_indent(pp, sb, indent, line, linelen);
+               else if (pp->expand_tabs_in_log)
+                       strbuf_add_tabexpand(sb, pp->expand_tabs_in_log,
+                                            line, linelen);
+               else
+                       strbuf_add(sb, line, linelen);
                strbuf_addch(sb, '\n');
        }
 }