Merge branch 'aw/pretty-trailers'
The %(trailers) formatter in "git log --format=..." now allows to optionally pick trailers selectively by keyword, show only values, etc. * aw/pretty-trailers: pretty: add support for separator option in %(trailers) strbuf: separate callback for strbuf_expand:ing literals pretty: add support for "valueonly" option in %(trailers) pretty: allow showing specific trailers pretty: single return path in %(trailers) handling pretty: allow %(trailers) options with explicit value doc: group pretty-format.txt placeholders descriptions
This commit is contained in:
113
pretty.c
113
pretty.c
@ -1057,13 +1057,26 @@ static size_t parse_padding_placeholder(struct strbuf *sb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_placeholder_arg(const char *to_parse, const char *candidate,
|
||||
const char **end)
|
||||
static int match_placeholder_arg_value(const char *to_parse, const char *candidate,
|
||||
const char **end, const char **valuestart,
|
||||
size_t *valuelen)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
if (!(skip_prefix(to_parse, candidate, &p)))
|
||||
return 0;
|
||||
if (valuestart) {
|
||||
if (*p == '=') {
|
||||
*valuestart = p + 1;
|
||||
*valuelen = strcspn(*valuestart, ",)");
|
||||
p = *valuestart + *valuelen;
|
||||
} else {
|
||||
if (*p != ',' && *p != ')')
|
||||
return 0;
|
||||
*valuestart = NULL;
|
||||
*valuelen = 0;
|
||||
}
|
||||
}
|
||||
if (*p == ',') {
|
||||
*end = p + 1;
|
||||
return 1;
|
||||
@ -1075,6 +1088,47 @@ static int match_placeholder_arg(const char *to_parse, const char *candidate,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_placeholder_bool_arg(const char *to_parse, const char *candidate,
|
||||
const char **end, int *val)
|
||||
{
|
||||
const char *argval;
|
||||
char *strval;
|
||||
size_t arglen;
|
||||
int v;
|
||||
|
||||
if (!match_placeholder_arg_value(to_parse, candidate, end, &argval, &arglen))
|
||||
return 0;
|
||||
|
||||
if (!argval) {
|
||||
*val = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
strval = xstrndup(argval, arglen);
|
||||
v = git_parse_maybe_bool(strval);
|
||||
free(strval);
|
||||
|
||||
if (v == -1)
|
||||
return 0;
|
||||
|
||||
*val = v;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int format_trailer_match_cb(const struct strbuf *key, void *ud)
|
||||
{
|
||||
const struct string_list *list = ud;
|
||||
const struct string_list_item *item;
|
||||
|
||||
for_each_string_list_item (item, list) {
|
||||
if (key->len == (uintptr_t)item->util &&
|
||||
!strncasecmp(item->string, key->buf, key->len))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
|
||||
const char *placeholder,
|
||||
void *context)
|
||||
@ -1084,10 +1138,14 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
|
||||
const char *msg = c->message;
|
||||
struct commit_list *p;
|
||||
const char *arg;
|
||||
int ch;
|
||||
size_t res;
|
||||
char **slot;
|
||||
|
||||
/* these are independent of the commit */
|
||||
res = strbuf_expand_literal_cb(sb, placeholder, NULL);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
switch (placeholder[0]) {
|
||||
case 'C':
|
||||
if (starts_with(placeholder + 1, "(auto)")) {
|
||||
@ -1106,16 +1164,6 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
case 'n': /* newline */
|
||||
strbuf_addch(sb, '\n');
|
||||
return 1;
|
||||
case 'x':
|
||||
/* %x00 == NUL, %x0a == LF, etc. */
|
||||
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;
|
||||
@ -1322,24 +1370,53 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
|
||||
|
||||
if (skip_prefix(placeholder, "(trailers", &arg)) {
|
||||
struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
|
||||
struct string_list filter_list = STRING_LIST_INIT_NODUP;
|
||||
struct strbuf sepbuf = STRBUF_INIT;
|
||||
size_t ret = 0;
|
||||
|
||||
opts.no_divider = 1;
|
||||
|
||||
if (*arg == ':') {
|
||||
arg++;
|
||||
for (;;) {
|
||||
if (match_placeholder_arg(arg, "only", &arg))
|
||||
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(arg, "unfold", &arg))
|
||||
opts.unfold = 1;
|
||||
else
|
||||
} 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 (*arg == ')') {
|
||||
format_trailers_from_commit(sb, msg + c->subject_off, &opts);
|
||||
return arg - placeholder + 1;
|
||||
ret = arg - placeholder + 1;
|
||||
}
|
||||
trailer_out:
|
||||
string_list_clear(&filter_list, 0);
|
||||
strbuf_release(&sepbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0; /* unknown placeholder */
|
||||
|
Reference in New Issue
Block a user