Merge branch 'rs/c-auto-resets-attributes'
authorJunio C Hamano <gitster@pobox.com>
Mon, 26 Sep 2016 23:09:15 +0000 (16:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 26 Sep 2016 23:09:15 +0000 (16:09 -0700)
The pretty-format specifier "%C(auto)" used by the "log" family of
commands to enable coloring of the output is taught to also issue a
color-reset sequence to the output.

* rs/c-auto-resets-attributes:
pretty: let %C(auto) reset all attributes

1  2 
pretty.c
diff --combined pretty.c
index 9788bd8f3f0ece03a6b87778a2d43bfaf3c14a9d,b86c5bb907ee8607d054f8afde6ab15f8c90a183..493edb0a446ec0019e39e8f4f2ca8a4e108c40f5
+++ b/pretty.c
@@@ -16,7 -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;
@@@ -88,14 -87,13 +88,14 @@@ static int git_pretty_formats_config(co
  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 },
 +              { "mboxrd",     CMIT_FMT_MBOXRD,        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;
@@@ -174,7 -172,6 +174,7 @@@ void get_commit_format(const char *arg
  
        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);
@@@ -445,7 -442,7 +445,7 @@@ void pp_user_info(struct pretty_print_c
        if (pp->mailmap)
                map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
  
 -      if (pp->fmt == CMIT_FMT_EMAIL) {
 +      if (cmit_fmt_is_mail(pp->fmt)) {
                if (pp->from_ident && ident_cmp(pp->from_ident, &ident)) {
                        struct strbuf buf = STRBUF_INIT;
  
                            show_ident_date(&ident, &pp->date_mode));
                break;
        case CMIT_FMT_EMAIL:
 +      case CMIT_FMT_MBOXRD:
                strbuf_addf(sb, "Date: %s\n",
                            show_ident_date(&ident, DATE_MODE(RFC2822)));
                break;
        }
  }
  
 -static int is_empty_line(const char *line, int *len_p)
 +static int is_blank_line(const char *line, int *len_p)
  {
        int len = *len_p;
        while (len && isspace(line[len - 1]))
        return !len;
  }
  
 -static const char *skip_empty_lines(const char *msg)
 +const char *skip_blank_lines(const char *msg)
  {
        for (;;) {
                int linelen = get_one_line(msg);
                int ll = linelen;
                if (!linelen)
                        break;
 -              if (!is_empty_line(msg, &ll))
 +              if (!is_blank_line(msg, &ll))
                        break;
                msg += linelen;
        }
@@@ -537,7 -533,7 +537,7 @@@ static void add_merge_info(const struc
  {
        struct commit_list *parent = commit->parents;
  
 -      if ((pp->fmt == CMIT_FMT_ONELINE) || (pp->fmt == CMIT_FMT_EMAIL) ||
 +      if ((pp->fmt == CMIT_FMT_ONELINE) || (cmit_fmt_is_mail(pp->fmt)) ||
            !parent || !parent->next)
                return;
  
@@@ -877,7 -873,7 +877,7 @@@ const char *format_subject(struct strbu
                int linelen = get_one_line(line);
  
                msg += linelen;
 -              if (!linelen || is_empty_line(line, &linelen))
 +              if (!linelen || is_blank_line(line, &linelen))
                        break;
  
                if (!sb)
@@@ -896,11 -892,11 +896,11 @@@ static void parse_commit_message(struc
        const char *msg = c->message + c->message_off;
        const char *start = c->message;
  
 -      msg = skip_empty_lines(msg);
 +      msg = skip_blank_lines(msg);
        c->subject_off = msg - start;
  
        msg = format_subject(NULL, msg, NULL);
 -      msg = skip_empty_lines(msg);
 +      msg = skip_blank_lines(msg);
        c->body_off = msg - start;
  
        c->commit_message_parsed = 1;
@@@ -1024,15 -1020,9 +1024,15 @@@ static size_t parse_padding_placeholder
                int width;
                if (!end || end == start)
                        return 0;
 -              width = strtoul(start, &next, 10);
 +              width = strtol(start, &next, 10);
                if (next == start || width == 0)
                        return 0;
 +              if (width < 0) {
 +                      if (to_column)
 +                              width += term_columns();
 +                      if (width < 0)
 +                              return 0;
 +              }
                c->padding = to_column ? -width : width;
                c->flush_type = flush_type;
  
@@@ -1065,13 -1055,15 +1065,15 @@@ static size_t format_commit_one(struct 
        const struct commit *commit = c->commit;
        const char *msg = c->message;
        struct commit_list *p;
 -      int h1, h2;
 +      int ch;
  
        /* these are independent of the commit */
        switch (placeholder[0]) {
        case 'C':
                if (starts_with(placeholder + 1, "(auto)")) {
                        c->auto_color = want_color(c->pretty_ctx->color);
+                       if (c->auto_color)
+                               strbuf_addstr(sb, GIT_COLOR_RESET);
                        return 7; /* consumed 7 bytes, "C(auto)" */
                } else {
                        int ret = parse_color(sb, placeholder, c);
                return 1;
        case 'x':
                /* %x00 == NUL, %x0a == LF, etc. */
 -              if (0 <= (h1 = hexval_table[0xff & placeholder[1]]) &&
 -                  h1 <= 16 &&
 -                  0 <= (h2 = hexval_table[0xff & placeholder[2]]) &&
 -                  h2 <= 16) {
 -                      strbuf_addch(sb, (h1<<4)|h2);
 -                      return 3;
 -              } else
 +              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;
                        strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
                        return 1;
                }
 -              strbuf_addstr(sb, find_unique_abbrev(commit->object.oid.hash,
 -                                                   c->pretty_ctx->abbrev));
 +              strbuf_add_unique_abbrev(sb, 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':               /* abbreviated tree hash */
                if (add_again(sb, &c->abbrev_tree_hash))
                        return 1;
 -              strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.oid.hash,
 -                                                   c->pretty_ctx->abbrev));
 +              strbuf_add_unique_abbrev(sb, commit->tree->object.oid.hash,
 +                                       c->pretty_ctx->abbrev);
                c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
                return 1;
        case 'P':               /* parent hashes */
                for (p = commit->parents; p; p = p->next) {
                        if (p != commit->parents)
                                strbuf_addch(sb, ' ');
 -                      strbuf_addstr(sb, find_unique_abbrev(
 -                                      p->item->object.oid.hash,
 -                                      c->pretty_ctx->abbrev));
 +                      strbuf_add_unique_abbrev(sb, p->item->object.oid.hash,
 +                                               c->pretty_ctx->abbrev);
                }
                c->abbrev_parent_hashes.len = sb->len -
                                              c->abbrev_parent_hashes.off;
@@@ -1303,7 -1299,6 +1305,7 @@@ static size_t format_and_pad_commit(str
                if (!start)
                        start = sb->buf;
                occupied = utf8_strnwidth(start, -1, 1);
 +              occupied += c->pretty_ctx->graph_width;
                padding = (-padding) - occupied;
        }
        while (1) {
@@@ -1619,7 -1614,7 +1621,7 @@@ void pp_title_line(struct pretty_print_
        if (pp->after_subject) {
                strbuf_addstr(sb, pp->after_subject);
        }
 -      if (pp->fmt == CMIT_FMT_EMAIL) {
 +      if (cmit_fmt_is_mail(pp->fmt)) {
                strbuf_addch(sb, '\n');
        }
  
        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);
 +}
 +
 +static int is_mboxrd_from(const char *line, int len)
 +{
 +      /*
 +       * a line matching /^From $/ here would only have len == 4
 +       * at this point because is_empty_line would've trimmed all
 +       * trailing space
 +       */
 +      return len > 4 && starts_with(line + strspn(line, ">"), "From ");
 +}
 +
  void pp_remainder(struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
                if (!linelen)
                        break;
  
 -              if (is_empty_line(line, &linelen)) {
 +              if (is_blank_line(line, &linelen)) {
                        if (first)
                                continue;
                        if (pp->fmt == CMIT_FMT_SHORT)
  
                strbuf_grow(sb, linelen + indent + 20);
                if (indent)
 -                      strbuf_addchars(sb, ' ', indent);
 -              strbuf_add(sb, line, linelen);
 +                      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 {
 +                      if (pp->fmt == CMIT_FMT_MBOXRD &&
 +                                      is_mboxrd_from(line, linelen))
 +                              strbuf_addch(sb, '>');
 +
 +                      strbuf_add(sb, line, linelen);
 +              }
                strbuf_addch(sb, '\n');
        }
  }
@@@ -1770,14 -1680,14 +1772,14 @@@ void pretty_print_commit(struct pretty_
        encoding = get_log_output_encoding();
        msg = reencoded = logmsg_reencode(commit, NULL, encoding);
  
 -      if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
 +      if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
                indent = 0;
  
        /*
         * We need to check and emit Content-type: to mark it
         * as 8-bit if we haven't done so.
         */
 -      if (pp->fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
 +      if (cmit_fmt_is_mail(pp->fmt) && need_8bit_cte == 0) {
                int i, ch, in_body;
  
                for (in_body = i = 0; (ch = msg[i]); i++) {
        }
  
        /* Skip excess blank lines at the beginning of body, if any... */
 -      msg = skip_empty_lines(msg);
 +      msg = skip_blank_lines(msg);
  
        /* These formats treat the title line specially. */
 -      if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
 +      if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
                pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
  
        beginning_of_body = sb->len;
         * format.  Make sure we did not strip the blank line
         * between the header and the body.
         */
 -      if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
 +      if (cmit_fmt_is_mail(pp->fmt) && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
  
        unuse_commit_buffer(commit, reencoded);