Code

log --format: don't ignore %w() at the start of format string
[git.git] / pretty.c
index a0ef356558f4cdb148010f1b47dbd3fcc363d8ba..5e9d1f84b976c7f85e67c6af446d31e54c2388fd 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -86,6 +86,18 @@ int non_ascii(int ch)
        return !isascii(ch) || ch == '\033';
 }
 
+int has_non_ascii(const char *s)
+{
+       int ch;
+       if (!s)
+               return 0;
+       while ((ch = *s++) != '\0') {
+               if (non_ascii(ch))
+                       return 1;
+       }
+       return 0;
+}
+
 static int is_rfc2047_special(char ch)
 {
        return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
@@ -284,7 +296,7 @@ static char *replace_encoding_header(char *buf, const char *encoding)
 static char *logmsg_reencode(const struct commit *commit,
                             const char *output_encoding)
 {
-       static const char *utf8 = "utf-8";
+       static const char *utf8 = "UTF-8";
        const char *use_encoding;
        char *encoding;
        char *out;
@@ -433,6 +445,7 @@ struct format_commit_context {
        enum date_mode dmode;
        unsigned commit_header_parsed:1;
        unsigned commit_message_parsed:1;
+       size_t width, indent1, indent2;
 
        /* These offsets are relative to the start of the commit message. */
        struct chunk author;
@@ -446,6 +459,7 @@ struct format_commit_context {
        struct chunk abbrev_commit_hash;
        struct chunk abbrev_tree_hash;
        struct chunk abbrev_parent_hashes;
+       size_t wrap_start;
 };
 
 static int add_again(struct strbuf *sb, struct chunk *chunk)
@@ -571,7 +585,7 @@ static void format_decoration(struct strbuf *sb, const struct commit *commit)
        struct name_decoration *d;
        const char *prefix = " (";
 
-       load_ref_decorations();
+       load_ref_decorations(DECORATE_SHORT_REFS);
        d = lookup_decoration(&name_decoration, &commit->object);
        while (d) {
                strbuf_addstr(sb, prefix);
@@ -583,6 +597,35 @@ static void format_decoration(struct strbuf *sb, const struct commit *commit)
                strbuf_addch(sb, ')');
 }
 
+static void strbuf_wrap(struct strbuf *sb, size_t pos,
+                       size_t width, size_t indent1, size_t indent2)
+{
+       struct strbuf tmp = STRBUF_INIT;
+
+       if (pos)
+               strbuf_add(&tmp, sb->buf, pos);
+       strbuf_add_wrapped_text(&tmp, sb->buf + pos,
+                               (int) indent1, (int) indent2, (int) width);
+       strbuf_swap(&tmp, sb);
+       strbuf_release(&tmp);
+}
+
+static void rewrap_message_tail(struct strbuf *sb,
+                               struct format_commit_context *c,
+                               size_t new_width, size_t new_indent1,
+                               size_t new_indent2)
+{
+       if (c->width == new_width && c->indent1 == new_indent1 &&
+           c->indent2 == new_indent2)
+               return;
+       if (c->wrap_start < sb->len)
+               strbuf_wrap(sb, c->wrap_start, c->width, c->indent1, c->indent2);
+       c->wrap_start = sb->len;
+       c->width = new_width;
+       c->indent1 = new_indent1;
+       c->indent2 = new_indent2;
+}
+
 static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                                void *context)
 {
@@ -633,6 +676,30 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                        return 3;
                } else
                        return 0;
+       case 'w':
+               if (placeholder[1] == '(') {
+                       unsigned long width = 0, indent1 = 0, indent2 = 0;
+                       char *next;
+                       const char *start = placeholder + 2;
+                       const char *end = strchr(start, ')');
+                       if (!end)
+                               return 0;
+                       if (end > start) {
+                               width = strtoul(start, &next, 10);
+                               if (*next == ',') {
+                                       indent1 = strtoul(next + 1, &next, 10);
+                                       if (*next == ',') {
+                                               indent2 = strtoul(next + 1,
+                                                                &next, 10);
+                                       }
+                               }
+                               if (*next != ')')
+                                       return 0;
+                       }
+                       rewrap_message_tail(sb, c, width, indent1, indent2);
+                       return end - placeholder + 1;
+               } else
+                       return 0;
        }
 
        /* these depend on the commit */
@@ -728,7 +795,7 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
 }
 
 void format_commit_message(const struct commit *commit,
-                          const void *format, struct strbuf *sb,
+                          const char *format, struct strbuf *sb,
                           enum date_mode dmode)
 {
        struct format_commit_context context;
@@ -736,7 +803,9 @@ void format_commit_message(const struct commit *commit,
        memset(&context, 0, sizeof(context));
        context.commit = commit;
        context.dmode = dmode;
+       context.wrap_start = sb->len;
        strbuf_expand(sb, format, format_commit_item, &context);
+       rewrap_message_tail(sb, &context, 0, 0, 0);
 }
 
 static void pp_header(enum cmit_fmt fmt,
@@ -881,7 +950,7 @@ char *reencode_commit_message(const struct commit *commit, const char **encoding
                    ? git_log_output_encoding
                    : git_commit_encoding);
        if (!encoding)
-               encoding = "utf-8";
+               encoding = "UTF-8";
        if (encoding_p)
                *encoding_p = encoding;
        return logmsg_reencode(commit, encoding);