summaryrefslogtreecommitdiff
path: root/pretty.c
diff options
context:
space:
mode:
Diffstat (limited to 'pretty.c')
-rw-r--r--pretty.c277
1 files changed, 213 insertions, 64 deletions
diff --git a/pretty.c b/pretty.c
index b32f036953..9631529c10 100644
--- a/pretty.c
+++ b/pretty.c
@@ -12,6 +12,7 @@
#include "reflog-walk.h"
#include "gpg-interface.h"
#include "trailer.h"
+#include "run-command.h"
static char *user_format;
static struct cmt_fmt_map {
@@ -20,6 +21,7 @@ static struct cmt_fmt_map {
int is_tformat;
int expand_tabs_in_log;
int is_alias;
+ enum date_mode_type default_date_mode_type;
const char *user_format;
} *commit_formats;
static size_t builtin_formats_len;
@@ -97,7 +99,9 @@ static void setup_commit_formats(void)
{ "mboxrd", CMIT_FMT_MBOXRD, 0, 0 },
{ "fuller", CMIT_FMT_FULLER, 0, 8 },
{ "full", CMIT_FMT_FULL, 0, 8 },
- { "oneline", CMIT_FMT_ONELINE, 1, 0 }
+ { "oneline", CMIT_FMT_ONELINE, 1, 0 },
+ { "reference", CMIT_FMT_USERFORMAT, 1, 0,
+ 0, DATE_SHORT, "%C(auto)%h (%s, %ad)" },
/*
* Please update $__git_log_pretty_formats in
* git-completion.bash when you add new formats.
@@ -181,6 +185,8 @@ 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 (!rev->date_mode_explicit && commit_format->default_date_mode_type)
+ rev->date_mode.type = commit_format->default_date_mode_type;
if (commit_format->format == CMIT_FMT_USERFORMAT) {
save_user_format(rev, commit_format->user_format,
commit_format->is_tformat);
@@ -469,7 +475,8 @@ void pp_user_info(struct pretty_print_context *pp,
}
strbuf_addstr(sb, "From: ");
- if (needs_rfc2047_encoding(namebuf, namelen)) {
+ if (pp->encode_email_headers &&
+ needs_rfc2047_encoding(namebuf, namelen)) {
add_rfc2047(sb, namebuf, namelen,
encoding, RFC2047_ADDRESS);
max_length = 76; /* per rfc2047 */
@@ -672,8 +679,8 @@ static int mailmap_name(const char **email, size_t *email_len,
{
static struct string_list *mail_map;
if (!mail_map) {
- mail_map = xcalloc(1, sizeof(*mail_map));
- read_mailmap(mail_map, NULL);
+ CALLOC_ARRAY(mail_map, 1);
+ read_mailmap(mail_map);
}
return mail_map->nr && map_user(mail_map, email, email_len, name, name_len);
}
@@ -696,7 +703,7 @@ static size_t format_person_part(struct strbuf *sb, char part,
mail = s.mail_begin;
maillen = s.mail_end - s.mail_begin;
- if (part == 'N' || part == 'E') /* mailmap lookup */
+ if (part == 'N' || part == 'E' || part == 'L') /* mailmap lookup */
mailmap_name(&mail, &maillen, &name, &namelen);
if (part == 'n' || part == 'N') { /* name */
strbuf_add(sb, name, namelen);
@@ -706,6 +713,13 @@ static size_t format_person_part(struct strbuf *sb, char part,
strbuf_add(sb, mail, maillen);
return placeholder_len;
}
+ if (part == 'l' || part == 'L') { /* local-part */
+ const char *at = memchr(mail, '@', maillen);
+ if (at)
+ maillen = at - mail;
+ strbuf_add(sb, mail, maillen);
+ return placeholder_len;
+ }
if (!s.date_begin)
goto skip;
@@ -731,6 +745,12 @@ static size_t format_person_part(struct strbuf *sb, char part,
case 'I': /* date, ISO 8601 strict */
strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(ISO8601_STRICT)));
return placeholder_len;
+ case 'h': /* date, human */
+ strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(HUMAN)));
+ return placeholder_len;
+ case 's':
+ strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(SHORT)));
+ return placeholder_len;
}
skip:
@@ -767,6 +787,7 @@ enum trunc_type {
};
struct format_commit_context {
+ struct repository *repository;
const struct commit *commit;
const struct pretty_print_context *pretty_ctx;
unsigned commit_header_parsed:1;
@@ -823,21 +844,22 @@ static int istitlechar(char c)
(c >= '0' && c <= '9') || c == '.' || c == '_';
}
-static void format_sanitized_subject(struct strbuf *sb, const char *msg)
+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)
{
size_t trimlen;
size_t start_len = sb->len;
int space = 2;
+ int i;
- for (; *msg && *msg != '\n'; msg++) {
- if (istitlechar(*msg)) {
+ for (i = 0; i < len; i++) {
+ if (istitlechar(msg[i])) {
if (space == 1)
strbuf_addch(sb, '-');
space = 0;
- strbuf_addch(sb, *msg);
- if (*msg == '.')
- while (*(msg+1) == '.')
- msg++;
+ strbuf_addch(sb, msg[i]);
+ if (msg[i] == '.')
+ while (msg[i+1] == '.')
+ i++;
} else
space |= 1;
}
@@ -1131,6 +1153,91 @@ static int format_trailer_match_cb(const struct strbuf *key, void *ud)
return 0;
}
+int format_set_trailers_options(struct process_trailer_options *opts,
+ struct string_list *filter_list,
+ struct strbuf *sepbuf,
+ struct strbuf *kvsepbuf,
+ const char **arg,
+ char **invalid_arg)
+{
+ for (;;) {
+ const char *argval;
+ size_t arglen;
+
+ if (**arg == ')')
+ break;
+
+ if (match_placeholder_arg_value(*arg, "key", arg, &argval, &arglen)) {
+ uintptr_t len = arglen;
+
+ if (!argval)
+ return -1;
+
+ 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_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_arg_value(*arg, "key_value_separator", arg, &argval, &arglen)) {
+ char *fmt;
+
+ strbuf_reset(kvsepbuf);
+ fmt = xstrndup(argval, arglen);
+ strbuf_expand(kvsepbuf, fmt, strbuf_expand_literal_cb, NULL);
+ free(fmt);
+ opts->key_value_separator = kvsepbuf;
+ } 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, "keyonly", arg, &opts->key_only) &&
+ !match_placeholder_bool_arg(*arg, "valueonly", arg, &opts->value_only)) {
+ if (invalid_arg) {
+ size_t len = strcspn(*arg, ",)");
+ *invalid_arg = xstrndup(*arg, len);
+ }
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static size_t parse_describe_args(const char *start, struct strvec *args)
+{
+ const char *options[] = { "match", "exclude" };
+ const char *arg = start;
+
+ for (;;) {
+ const char *matched = NULL;
+ const char *argval;
+ size_t arglen = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(options); i++) {
+ if (match_placeholder_arg_value(arg, options[i], &arg,
+ &argval, &arglen)) {
+ matched = options[i];
+ break;
+ }
+ }
+ if (!matched)
+ break;
+
+ if (!arglen)
+ return 0;
+ strvec_pushf(args, "--%s=%.*s", matched, (int)arglen, argval);
+ }
+ return arg - start;
+}
+
static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
const char *placeholder,
void *context)
@@ -1139,7 +1246,7 @@ 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;
- const char *arg;
+ const char *arg, *eol;
size_t res;
char **slot;
@@ -1196,6 +1303,41 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
return parse_padding_placeholder(placeholder, c);
}
+ if (skip_prefix(placeholder, "(describe", &arg)) {
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ struct strbuf out = STRBUF_INIT;
+ struct strbuf err = STRBUF_INIT;
+ struct pretty_print_describe_status *describe_status;
+
+ describe_status = c->pretty_ctx->describe_status;
+ if (describe_status) {
+ if (!describe_status->max_invocations)
+ return 0;
+ describe_status->max_invocations--;
+ }
+
+ cmd.git_cmd = 1;
+ strvec_push(&cmd.args, "describe");
+
+ if (*arg == ':') {
+ arg++;
+ arg += parse_describe_args(arg, &cmd.args);
+ }
+
+ if (*arg != ')') {
+ child_process_clear(&cmd);
+ return 0;
+ }
+
+ strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
+ pipe_command(&cmd, NULL, 0, &out, 0, &err, 0);
+ strbuf_rtrim(&out);
+ strbuf_addbuf(sb, &out);
+ strbuf_release(&out);
+ strbuf_release(&err);
+ return arg - placeholder + 1;
+ }
+
/* these depend on the commit */
if (!commit->object.parsed)
parse_object(the_repository, &commit->object.oid);
@@ -1296,9 +1438,18 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
case '?':
switch (c->signature_check.result) {
case 'G':
+ switch (c->signature_check.trust_level) {
+ case TRUST_UNDEFINED:
+ case TRUST_NEVER:
+ strbuf_addch(sb, 'U');
+ break;
+ default:
+ strbuf_addch(sb, 'G');
+ break;
+ }
+ break;
case 'B':
case 'E':
- case 'U':
case 'N':
case 'X':
case 'Y':
@@ -1322,16 +1473,38 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
if (c->signature_check.primary_key_fingerprint)
strbuf_addstr(sb, c->signature_check.primary_key_fingerprint);
break;
+ case 'T':
+ switch (c->signature_check.trust_level) {
+ case TRUST_UNDEFINED:
+ strbuf_addstr(sb, "undefined");
+ break;
+ case TRUST_NEVER:
+ strbuf_addstr(sb, "never");
+ break;
+ case TRUST_MARGINAL:
+ strbuf_addstr(sb, "marginal");
+ break;
+ case TRUST_FULLY:
+ strbuf_addstr(sb, "fully");
+ break;
+ case TRUST_ULTIMATE:
+ strbuf_addstr(sb, "ultimate");
+ break;
+ }
+ break;
default:
return 0;
}
return 2;
}
-
/* For the rest we have to parse the commit header. */
- if (!c->commit_header_parsed)
+ if (!c->commit_header_parsed) {
+ msg = c->message =
+ repo_logmsg_reencode(c->repository, commit,
+ &c->commit_encoding, "UTF-8");
parse_commit_header(c);
+ }
switch (placeholder[0]) {
case 'a': /* author ... */
@@ -1361,7 +1534,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
format_subject(sb, msg + c->subject_off, " ");
return 1;
case 'f': /* sanitized subject */
- format_sanitized_subject(sb, msg + c->subject_off);
+ eol = strchrnul(msg + c->subject_off, '\n');
+ format_sanitized_subject(sb, msg + c->subject_off, eol - (msg + c->subject_off));
return 1;
case 'b': /* body */
strbuf_addstr(sb, msg + c->body_off);
@@ -1372,42 +1546,15 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
struct string_list filter_list = STRING_LIST_INIT_NODUP;
struct strbuf sepbuf = STRBUF_INIT;
+ struct strbuf kvsepbuf = STRBUF_INIT;
size_t ret = 0;
opts.no_divider = 1;
if (*arg == ':') {
arg++;
- for (;;) {
- 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_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 (format_set_trailers_options(&opts, &filter_list, &sepbuf, &kvsepbuf, &arg, NULL))
+ goto trailer_out;
}
if (*arg == ')') {
format_trailers_from_commit(sb, msg + c->subject_off, &opts);
@@ -1566,9 +1713,9 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
strbuf_setlen(sb, sb->len - 1);
} else if (orig_len != sb->len) {
if (magic == ADD_LF_BEFORE_NON_EMPTY)
- strbuf_insert(sb, orig_len, "\n", 1);
+ strbuf_insertstr(sb, orig_len, "\n");
else if (magic == ADD_SP_BEFORE_NON_EMPTY)
- strbuf_insert(sb, orig_len, " ", 1);
+ strbuf_insertstr(sb, orig_len, " ");
}
return consumed + 1;
}
@@ -1588,6 +1735,10 @@ static size_t userformat_want_item(struct strbuf *sb, const char *placeholder,
case 'S':
w->source = 1;
break;
+ case 'd':
+ case 'D':
+ w->decorate = 1;
+ break;
}
return 0;
}
@@ -1610,26 +1761,23 @@ void repo_format_commit_message(struct repository *r,
const char *format, struct strbuf *sb,
const struct pretty_print_context *pretty_ctx)
{
- struct format_commit_context context;
+ struct format_commit_context context = {
+ .repository = r,
+ .commit = commit,
+ .pretty_ctx = pretty_ctx,
+ .wrap_start = sb->len
+ };
const char *output_enc = pretty_ctx->output_encoding;
const char *utf8 = "UTF-8";
- memset(&context, 0, sizeof(context));
- 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 = repo_logmsg_reencode(r, commit,
- &context.commit_encoding,
- 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 */
+ /*
+ * Convert output to an actual output encoding; note that
+ * format_commit_item() will always use UTF-8, so we don't
+ * have to bother if that's what the output wants.
+ */
if (output_enc) {
if (same_encoding(utf8, output_enc))
output_enc = NULL;
@@ -1724,7 +1872,8 @@ void pp_title_line(struct pretty_print_context *pp,
if (pp->print_email_subject) {
if (pp->rev)
fmt_output_email_subject(sb, pp->rev);
- if (needs_rfc2047_encoding(title.buf, title.len))
+ if (pp->encode_email_headers &&
+ needs_rfc2047_encoding(title.buf, title.len))
add_rfc2047(sb, title.buf, title.len,
encoding, RFC2047_SUBJECT);
else