summaryrefslogtreecommitdiff
path: root/pretty.c
diff options
context:
space:
mode:
Diffstat (limited to 'pretty.c')
-rw-r--r--pretty.c456
1 files changed, 270 insertions, 186 deletions
diff --git a/pretty.c b/pretty.c
index 87db08bd74..5e683830d9 100644
--- a/pretty.c
+++ b/pretty.c
@@ -10,12 +10,14 @@
#include "color.h"
#include "reflog-walk.h"
#include "gpg-interface.h"
+#include "trailer.h"
static char *user_format;
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;
@@ -24,6 +26,11 @@ static size_t commit_formats_len;
static size_t commit_formats_alloc;
static struct cmt_fmt_map *find_commit_format(const char *sought);
+int commit_format_is_empty(enum cmit_fmt fmt)
+{
+ return fmt == CMIT_FMT_USERFORMAT && !*user_format;
+}
+
static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
{
free(user_format);
@@ -40,10 +47,9 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
const char *fmt;
int i;
- if (!starts_with(var, "pretty."))
+ if (!skip_prefix(var, "pretty.", &name))
return 0;
- name = var + strlen("pretty.");
for (i = 0; i < builtin_formats_len; i++) {
if (!strcmp(commit_formats[i].name, name))
return 0;
@@ -66,11 +72,12 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
commit_format->name = xstrdup(name);
commit_format->format = CMIT_FMT_USERFORMAT;
- git_config_string(&fmt, var, value);
- 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 (git_config_string(&fmt, var, value))
+ return -1;
+
+ 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;
@@ -82,13 +89,14 @@ 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 },
+ { "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;
@@ -147,16 +155,16 @@ void get_commit_format(const char *arg, struct rev_info *rev)
struct cmt_fmt_map *commit_format;
rev->use_terminator = 0;
- if (!arg || !*arg) {
+ if (!arg) {
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 (strchr(arg, '%')) {
+ if (!*arg || skip_prefix(arg, "tformat:", &arg) || strchr(arg, '%')) {
save_user_format(rev, arg, 1);
return;
}
@@ -167,6 +175,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);
@@ -274,7 +283,7 @@ static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
enum rfc2047_type {
RFC2047_SUBJECT,
- RFC2047_ADDRESS,
+ RFC2047_ADDRESS
};
static int is_rfc2047_special(char ch, enum rfc2047_type type)
@@ -393,16 +402,22 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
strbuf_addstr(sb, "?=");
}
-static const char *show_ident_date(const struct ident_split *ident,
- enum date_mode mode)
+const char *show_ident_date(const struct ident_split *ident,
+ const struct date_mode *mode)
{
unsigned long date = 0;
- int tz = 0;
+ long tz = 0;
if (ident->date_begin && ident->date_end)
date = strtoul(ident->date_begin, NULL, 10);
- if (ident->tz_begin && ident->tz_end)
- tz = strtol(ident->tz_begin, NULL, 10);
+ if (date_overflows(date))
+ date = 0;
+ else {
+ if (ident->tz_begin && ident->tz_end)
+ tz = strtol(ident->tz_begin, NULL, 10);
+ if (tz >= INT_MAX || tz <= INT_MIN)
+ tz = 0;
+ }
return show_date(date, tz, mode);
}
@@ -431,7 +446,7 @@ void pp_user_info(struct pretty_print_context *pp,
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;
@@ -478,15 +493,16 @@ 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:
+ case CMIT_FMT_MBOXRD:
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' */
@@ -494,7 +510,7 @@ void pp_user_info(struct pretty_print_context *pp,
}
}
-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]))
@@ -503,14 +519,14 @@ static int is_empty_line(const char *line, int *len_p)
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;
}
@@ -522,52 +538,29 @@ static void add_merge_info(const struct pretty_print_context *pp,
{
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;
strbuf_addstr(sb, "Merge:");
while (parent) {
- struct commit *p = parent->item;
- const char *hex = NULL;
+ struct object_id *oidp = &parent->item->object.oid;
+ strbuf_addch(sb, ' ');
if (pp->abbrev)
- hex = find_unique_abbrev(p->object.sha1, pp->abbrev);
- if (!hex)
- hex = sha1_to_hex(p->object.sha1);
+ strbuf_add_unique_abbrev(sb, oidp->hash, pp->abbrev);
+ else
+ strbuf_addstr(sb, oid_to_hex(oidp));
parent = parent->next;
-
- strbuf_addf(sb, " %s", hex);
}
strbuf_addch(sb, '\n');
}
-static char *get_header(const struct commit *commit, const char *msg,
- const char *key)
+static char *get_header(const char *msg, const char *key)
{
- int key_len = strlen(key);
- const char *line = msg;
-
- while (line) {
- const char *eol = strchr(line, '\n'), *next;
-
- if (line == eol)
- return NULL;
- if (!eol) {
- warning("malformed commit (header is missing newline): %s",
- sha1_to_hex(commit->object.sha1));
- eol = line + strlen(line);
- next = NULL;
- } else
- next = eol + 1;
- if (eol - line > key_len &&
- !strncmp(line, key, key_len) &&
- line[key_len] == ' ') {
- return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
- }
- line = next;
- }
- return NULL;
+ size_t len;
+ const char *v = find_commit_header(msg, key, &len);
+ return v ? xmemdupz(v, len) : NULL;
}
static char *replace_encoding_header(char *buf, const char *encoding)
@@ -577,7 +570,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;
@@ -601,36 +594,22 @@ static char *replace_encoding_header(char *buf, const char *encoding)
return strbuf_detach(&tmp, NULL);
}
-char *logmsg_reencode(const struct commit *commit,
- char **commit_encoding,
- const char *output_encoding)
+const char *logmsg_reencode(const struct commit *commit,
+ char **commit_encoding,
+ const char *output_encoding)
{
static const char *utf8 = "UTF-8";
const char *use_encoding;
char *encoding;
- char *msg = commit->buffer;
+ const char *msg = get_commit_buffer(commit, NULL);
char *out;
- if (!msg) {
- enum object_type type;
- unsigned long size;
-
- msg = read_sha1_file(commit->object.sha1, &type, &size);
- if (!msg)
- die("Cannot read commit object %s",
- sha1_to_hex(commit->object.sha1));
- if (type != OBJ_COMMIT)
- die("Expected commit for '%s', got %s",
- sha1_to_hex(commit->object.sha1), typename(type));
- }
-
if (!output_encoding || !*output_encoding) {
if (commit_encoding)
- *commit_encoding =
- get_header(commit, msg, "encoding");
+ *commit_encoding = get_header(msg, "encoding");
return msg;
}
- encoding = get_header(commit, msg, "encoding");
+ encoding = get_header(msg, "encoding");
if (commit_encoding)
*commit_encoding = encoding;
use_encoding = encoding ? encoding : utf8;
@@ -647,12 +626,13 @@ char *logmsg_reencode(const struct commit *commit,
* Otherwise, we still want to munge the encoding header in the
* result, which will be done by modifying the buffer. If we
* are using a fresh copy, we can reuse it. But if we are using
- * the cached copy from commit->buffer, we need to duplicate it
- * to avoid munging commit->buffer.
+ * the cached copy from get_commit_buffer, we need to duplicate it
+ * to avoid munging the cached copy.
*/
- out = msg;
- if (out == commit->buffer)
- out = xstrdup(out);
+ if (msg == get_cached_commit_buffer(commit, NULL))
+ out = xstrdup(msg);
+ else
+ out = (char *)msg;
}
else {
/*
@@ -662,8 +642,8 @@ char *logmsg_reencode(const struct commit *commit,
* copy, we can free it.
*/
out = reencode_string(msg, output_encoding, use_encoding);
- if (out && msg != commit->buffer)
- free(msg);
+ if (out)
+ unuse_commit_buffer(commit, msg);
}
/*
@@ -682,12 +662,6 @@ char *logmsg_reencode(const struct commit *commit,
return out ? out : msg;
}
-void logmsg_free(char *msg, const struct commit *commit)
-{
- if (msg != commit->buffer)
- free(msg);
-}
-
static int mailmap_name(const char **email, size_t *email_len,
const char **name, size_t *name_len)
{
@@ -700,7 +674,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;
@@ -740,13 +715,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_MODE(ISO8601)));
return placeholder_len;
- case 'i': /* date, ISO 8601 */
- strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601));
+ case 'I': /* date, ISO 8601 strict */
+ strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(ISO8601_STRICT)));
return placeholder_len;
}
@@ -791,7 +769,7 @@ struct format_commit_context {
struct signature_check signature_check;
enum flush_type flush_type;
enum trunc_type truncate;
- char *message;
+ const char *message;
char *commit_encoding;
size_t width, indent1, indent2;
int auto_color;
@@ -834,18 +812,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;
}
@@ -897,7 +876,7 @@ const char *format_subject(struct strbuf *sb, const char *msg,
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)
@@ -911,16 +890,26 @@ const char *format_subject(struct strbuf *sb, const char *msg,
return msg;
}
+static void format_trailers(struct strbuf *sb, const char *msg)
+{
+ struct trailer_info info;
+
+ trailer_info_get(&info, msg);
+ strbuf_add(sb, info.trailer_start,
+ info.trailer_end - info.trailer_start);
+ trailer_info_release(&info);
+}
+
static void parse_commit_message(struct format_commit_context *c)
{
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;
@@ -958,7 +947,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;
@@ -976,6 +965,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, ')');
@@ -983,31 +974,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,
@@ -1049,9 +1033,15 @@ static size_t parse_padding_placeholder(struct strbuf *sb,
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;
@@ -1084,13 +1074,15 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
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 = 1;
+ c->auto_color = want_color(c->pretty_ctx->color);
+ if (c->auto_color && sb->len)
+ strbuf_addstr(sb, GIT_COLOR_RESET);
return 7; /* consumed 7 bytes, "C(auto)" */
} else {
int ret = parse_color(sb, placeholder, c);
@@ -1108,14 +1100,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
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;
@@ -1148,12 +1137,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 */
@@ -1162,26 +1151,26 @@ 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,
- 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': /* 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,
- 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, sha1_to_hex(p->item->object.sha1));
+ strbuf_addstr(sb, oid_to_hex(&p->item->object.oid));
}
return 1;
case 'p': /* abbreviated parent hashes */
@@ -1190,9 +1179,8 @@ 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, find_unique_abbrev(
- p->item->object.sha1,
- 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;
@@ -1204,6 +1192,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 */
@@ -1211,7 +1203,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;
@@ -1226,7 +1218,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':
@@ -1249,8 +1241,12 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
switch (c->signature_check.result) {
case 'G':
case 'B':
+ case 'E':
case 'U':
case 'N':
+ case 'X':
+ case 'Y':
+ case 'R':
strbuf_addch(sb, c->signature_check.result);
}
break;
@@ -1262,6 +1258,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
if (c->signature_check.key)
strbuf_addstr(sb, c->signature_check.key);
break;
+ default:
+ return 0;
}
return 2;
}
@@ -1275,11 +1273,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);
@@ -1305,6 +1303,12 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
strbuf_addstr(sb, msg + c->body_off);
return 1;
}
+
+ if (starts_with(placeholder, "(trailers)")) {
+ format_trailers(sb, msg + c->subject_off);
+ return strlen("(trailers)");
+ }
+
return 0; /* unknown placeholder */
}
@@ -1320,6 +1324,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
if (!start)
start = sb->buf;
occupied = utf8_strnwidth(start, -1, 1);
+ occupied += c->pretty_ctx->graph_width;
padding = (-padding) - occupied;
}
while (1) {
@@ -1388,7 +1393,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
case trunc_none:
break;
}
- strbuf_addstr(sb, local_sb.buf);
+ strbuf_addbuf(sb, &local_sb);
} else {
int sb_len = sb->len, offset = 0;
if (c->flush_type == flush_left)
@@ -1400,9 +1405,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);
}
@@ -1501,13 +1504,18 @@ void format_commit_message(const struct commit *commit,
context.commit = commit;
context.pretty_ctx = pretty_ctx;
context.wrap_start = sb->len;
+ /*
+ * convert a commit message to UTF-8 first
+ * as far as 'format_commit_item' assumes it in UTF-8
+ */
context.message = logmsg_reencode(commit,
&context.commit_encoding,
- output_enc);
+ utf8);
strbuf_expand(sb, format, format_commit_item, &context);
rewrap_message_tail(sb, &context, 0, 0, 0);
+ /* then convert a commit message to an actual output encoding */
if (output_enc) {
if (same_encoding(utf8, output_enc))
output_enc = NULL;
@@ -1526,9 +1534,7 @@ void format_commit_message(const struct commit *commit,
}
free(context.commit_encoding);
- logmsg_free(context.message, commit);
- free(context.signature_check.gpg_output);
- free(context.signature_check.signer);
+ unuse_commit_buffer(commit, context.message);
}
static void pp_header(struct pretty_print_context *pp,
@@ -1540,7 +1546,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)
@@ -1563,12 +1569,7 @@ static void pp_header(struct pretty_print_context *pp,
}
if (!parents_shown) {
- struct commit_list *parent;
- int num;
- for (parent = commit->parents, num = 0;
- parent;
- parent = parent->next, num++)
- ;
+ unsigned num = commit_list_count(commit->parents);
/* with enough slop */
strbuf_grow(sb, num * 50 + 20);
add_merge_info(pp, sb, commit);
@@ -1580,14 +1581,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);
}
}
}
@@ -1639,7 +1640,7 @@ void pp_title_line(struct pretty_print_context *pp,
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');
}
@@ -1656,6 +1657,82 @@ 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);
+}
+
+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,
@@ -1670,7 +1747,7 @@ void pp_remainder(struct pretty_print_context *pp,
if (!linelen)
break;
- if (is_empty_line(line, &linelen)) {
+ if (is_blank_line(line, &linelen)) {
if (first)
continue;
if (pp->fmt == CMIT_FMT_SHORT)
@@ -1679,11 +1756,18 @@ 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);
+ 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 {
+ if (pp->fmt == CMIT_FMT_MBOXRD &&
+ is_mboxrd_from(line, linelen))
+ strbuf_addch(sb, '>');
+
+ strbuf_add(sb, line, linelen);
}
- strbuf_add(sb, line, linelen);
strbuf_addch(sb, '\n');
}
}
@@ -1695,7 +1779,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
unsigned long beginning_of_body;
int indent = 4;
const char *msg;
- char *reencoded;
+ const char *reencoded;
const char *encoding;
int need_8bit_cte = pp->need_8bit_cte;
@@ -1707,14 +1791,14 @@ void pretty_print_commit(struct pretty_print_context *pp,
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++) {
@@ -1739,10 +1823,10 @@ void pretty_print_commit(struct pretty_print_context *pp,
}
/* 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;
@@ -1759,10 +1843,10 @@ void pretty_print_commit(struct pretty_print_context *pp,
* 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');
- logmsg_free(reencoded, commit);
+ unuse_commit_buffer(commit, reencoded);
}
void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,