From: Junio C Hamano Date: Thu, 7 Mar 2019 00:59:52 +0000 (+0900) Subject: Merge branch 'aw/pretty-trailers' X-Git-Tag: v2.22.0-rc0~179 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/42977bf5c7306e4c2dded5353f39cf087cf23a1f?ds=inline;hp=-c 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 --- 42977bf5c7306e4c2dded5353f39cf087cf23a1f diff --combined Documentation/pretty-formats.txt index 7bfffae765,4cfea4c9d6..079598307a --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@@ -102,120 -102,158 +102,160 @@@ The title was >>t4119: test autocomputi + The placeholders are: - - '%H': commit hash - - '%h': abbreviated commit hash - - '%T': tree hash - - '%t': abbreviated tree hash - - '%P': parent hashes - - '%p': abbreviated parent hashes - - '%an': author name - - '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1] - or linkgit:git-blame[1]) - - '%ae': author email - - '%aE': author email (respecting .mailmap, see - linkgit:git-shortlog[1] or linkgit:git-blame[1]) - - '%ad': author date (format respects --date= option) - - '%aD': author date, RFC2822 style - - '%ar': author date, relative - - '%at': author date, UNIX timestamp - - '%ai': author date, ISO 8601-like format - - '%aI': author date, strict ISO 8601 format - - '%cn': committer name - - '%cN': committer name (respecting .mailmap, see - linkgit:git-shortlog[1] or linkgit:git-blame[1]) - - '%ce': committer email - - '%cE': committer email (respecting .mailmap, see - linkgit:git-shortlog[1] or linkgit:git-blame[1]) - - '%cd': committer date (format respects --date= option) - - '%cD': committer date, RFC2822 style - - '%cr': committer date, relative - - '%ct': committer date, UNIX timestamp - - '%ci': committer date, ISO 8601-like format - - '%cI': committer date, strict ISO 8601 format - - '%d': ref names, like the --decorate option of linkgit:git-log[1] - - '%D': ref names without the " (", ")" wrapping. - - '%S': ref name given on the command line by which the commit was reached - (like `git log --source`), only works with `git log` - - '%e': encoding - - '%s': subject - - '%f': sanitized subject line, suitable for a filename - - '%b': body - - '%B': raw body (unwrapped subject and body) + - Placeholders that expand to a single literal character: + '%n':: newline + '%%':: a raw '%' + '%x00':: print a byte from a hex code + + - Placeholders that affect formatting of later placeholders: + '%Cred':: switch color to red + '%Cgreen':: switch color to green + '%Cblue':: switch color to blue + '%Creset':: reset color + '%C(...)':: color specification, as described under Values in the + "CONFIGURATION FILE" section of linkgit:git-config[1]. By + default, colors are shown only when enabled for log output + (by `color.diff`, `color.ui`, or `--color`, and respecting + the `auto` settings of the former if we are going to a + terminal). `%C(auto,...)` is accepted as a historical + synonym for the default (e.g., `%C(auto,red)`). Specifying - `%C(always,...) will show the colors even when color is ++ `%C(always,...)` will show the colors even when color is + not otherwise enabled (though consider just using + `--color=always` to enable color for the whole output, + including this format and anything else git might color). + `auto` alone (i.e. `%C(auto)`) will turn on auto coloring + on the next placeholders until the color is switched + again. + '%m':: left (`<`), right (`>`) or boundary (`-`) mark + '%w([[,[,]]])':: switch line wrapping, like the -w option of + linkgit:git-shortlog[1]. + '%<([,trunc|ltrunc|mtrunc])':: make the next placeholder take at + least N columns, padding spaces on + the right if necessary. Optionally + truncate at the beginning (ltrunc), + the middle (mtrunc) or the end + (trunc) if the output is longer than + N columns. Note that truncating + only works correctly with N >= 2. + '%<|()':: make the next placeholder take at least until Nth + columns, padding spaces on the right if necessary + '%>()', '%>|()':: similar to '%<()', '%<|()' respectively, + but padding spaces on the left + '%>>()', '%>>|()':: similar to '%>()', '%>|()' + respectively, except that if the next + placeholder takes more spaces than given and + there are spaces on its left, use those + spaces + '%><()', '%><|()':: similar to '%<()', '%<|()' + respectively, but padding both sides + (i.e. the text is centered) + + - Placeholders that expand to information extracted from the commit: + '%H':: commit hash + '%h':: abbreviated commit hash + '%T':: tree hash + '%t':: abbreviated tree hash + '%P':: parent hashes + '%p':: abbreviated parent hashes + '%an':: author name + '%aN':: author name (respecting .mailmap, see linkgit:git-shortlog[1] + or linkgit:git-blame[1]) + '%ae':: author email + '%aE':: author email (respecting .mailmap, see linkgit:git-shortlog[1] + or linkgit:git-blame[1]) + '%ad':: author date (format respects --date= option) + '%aD':: author date, RFC2822 style + '%ar':: author date, relative + '%at':: author date, UNIX timestamp + '%ai':: author date, ISO 8601-like format + '%aI':: author date, strict ISO 8601 format + '%cn':: committer name + '%cN':: committer name (respecting .mailmap, see + linkgit:git-shortlog[1] or linkgit:git-blame[1]) + '%ce':: committer email + '%cE':: committer email (respecting .mailmap, see + linkgit:git-shortlog[1] or linkgit:git-blame[1]) + '%cd':: committer date (format respects --date= option) + '%cD':: committer date, RFC2822 style + '%cr':: committer date, relative + '%ct':: committer date, UNIX timestamp + '%ci':: committer date, ISO 8601-like format + '%cI':: committer date, strict ISO 8601 format + '%d':: ref names, like the --decorate option of linkgit:git-log[1] + '%D':: ref names without the " (", ")" wrapping. ++'%S':: ref name given on the command line by which the commit was reached ++ (like `git log --source`), only works with `git log` + '%e':: encoding + '%s':: subject + '%f':: sanitized subject line, suitable for a filename + '%b':: body + '%B':: raw body (unwrapped subject and body) ifndef::git-rev-list[] - - '%N': commit notes + '%N':: commit notes endif::git-rev-list[] - - '%GG': raw verification message from GPG for a signed commit - - '%G?': show "G" for a good (valid) signature, - "B" for a bad signature, - "U" for a good signature with unknown validity, - "X" for a good signature that has expired, - "Y" for a good signature made by an expired key, - "R" for a good signature made by a revoked key, - "E" if the signature cannot be checked (e.g. missing key) - and "N" for no signature - - '%GS': show the name of the signer for a signed commit - - '%GK': show the key used to sign a signed commit - - '%GF': show the fingerprint of the key used to sign a signed commit - - '%GP': show the fingerprint of the primary key whose subkey was used - to sign a signed commit - - '%gD': reflog selector, e.g., `refs/stash@{1}` or - `refs/stash@{2 minutes ago`}; the format follows the rules described - for the `-g` option. The portion before the `@` is the refname as - given on the command line (so `git log -g refs/heads/master` would - yield `refs/heads/master@{0}`). - - '%gd': shortened reflog selector; same as `%gD`, but the refname - portion is shortened for human readability (so `refs/heads/master` - becomes just `master`). - - '%gn': reflog identity name - - '%gN': reflog identity name (respecting .mailmap, see - linkgit:git-shortlog[1] or linkgit:git-blame[1]) - - '%ge': reflog identity email - - '%gE': reflog identity email (respecting .mailmap, see - linkgit:git-shortlog[1] or linkgit:git-blame[1]) - - '%gs': reflog subject - - '%Cred': switch color to red - - '%Cgreen': switch color to green - - '%Cblue': switch color to blue - - '%Creset': reset color - - '%C(...)': color specification, as described under Values in the - "CONFIGURATION FILE" section of linkgit:git-config[1]. - By default, colors are shown only when enabled for log output (by - `color.diff`, `color.ui`, or `--color`, and respecting the `auto` - settings of the former if we are going to a terminal). `%C(auto,...)` - is accepted as a historical synonym for the default (e.g., - `%C(auto,red)`). Specifying `%C(always,...)` will show the colors - even when color is not otherwise enabled (though consider - just using `--color=always` to enable color for the whole output, - including this format and anything else git might color). `auto` - alone (i.e. `%C(auto)`) will turn on auto coloring on the next - placeholders until the color is switched again. - - '%m': left (`<`), right (`>`) or boundary (`-`) mark - - '%n': newline - - '%%': a raw '%' - - '%x00': print a byte from a hex code - - '%w([[,[,]]])': switch line wrapping, like the -w option of - linkgit:git-shortlog[1]. - - '%<([,trunc|ltrunc|mtrunc])': make the next placeholder take at - least N columns, padding spaces on the right if necessary. - Optionally truncate at the beginning (ltrunc), the middle (mtrunc) - or the end (trunc) if the output is longer than N columns. - Note that truncating only works correctly with N >= 2. - - '%<|()': make the next placeholder take at least until Nth - columns, padding spaces on the right if necessary - - '%>()', '%>|()': similar to '%<()', '%<|()' - respectively, but padding spaces on the left - - '%>>()', '%>>|()': similar to '%>()', '%>|()' - respectively, except that if the next placeholder takes more spaces - than given and there are spaces on its left, use those spaces - - '%><()', '%><|()': similar to '%<()', '%<|()' - respectively, but padding both sides (i.e. the text is centered) - - %(trailers[:options]): display the trailers of the body as interpreted - by linkgit:git-interpret-trailers[1]. The `trailers` string may be - followed by a colon and zero or more comma-separated options. If the - `only` option is given, omit non-trailer lines from the trailer block. - If the `unfold` option is given, behave as if interpret-trailer's - `--unfold` option was given. E.g., `%(trailers:only,unfold)` to do - both. + '%GG':: raw verification message from GPG for a signed commit + '%G?':: show "G" for a good (valid) signature, + "B" for a bad signature, + "U" for a good signature with unknown validity, + "X" for a good signature that has expired, + "Y" for a good signature made by an expired key, + "R" for a good signature made by a revoked key, + "E" if the signature cannot be checked (e.g. missing key) + and "N" for no signature + '%GS':: show the name of the signer for a signed commit + '%GK':: show the key used to sign a signed commit + '%GF':: show the fingerprint of the key used to sign a signed commit + '%GP':: show the fingerprint of the primary key whose subkey was used + to sign a signed commit + '%gD':: reflog selector, e.g., `refs/stash@{1}` or `refs/stash@{2 + minutes ago`}; the format follows the rules described for the + `-g` option. The portion before the `@` is the refname as + given on the command line (so `git log -g refs/heads/master` + would yield `refs/heads/master@{0}`). + '%gd':: shortened reflog selector; same as `%gD`, but the refname + portion is shortened for human readability (so + `refs/heads/master` becomes just `master`). + '%gn':: reflog identity name + '%gN':: reflog identity name (respecting .mailmap, see + linkgit:git-shortlog[1] or linkgit:git-blame[1]) + '%ge':: reflog identity email + '%gE':: reflog identity email (respecting .mailmap, see + linkgit:git-shortlog[1] or linkgit:git-blame[1]) + '%gs':: reflog subject + '%(trailers[:options])':: display the trailers of the body as + interpreted by + linkgit:git-interpret-trailers[1]. The + `trailers` string may be followed by a colon + and zero or more comma-separated options: + ** 'key=': only show trailers with specified key. Matching is done + case-insensitively and trailing colon is optional. If option is + given multiple times trailer lines matching any of the keys are + shown. This option automatically enables the `only` option so that + non-trailer lines in the trailer block are hidden. If that is not + desired it can be disabled with `only=false`. E.g., + `%(trailers:key=Reviewed-by)` shows trailer lines with key + `Reviewed-by`. + ** 'only[=val]': select whether non-trailer lines from the trailer + block should be included. The `only` keyword may optionally be + followed by an equal sign and one of `true`, `on`, `yes` to omit or + `false`, `off`, `no` to show the non-trailer lines. If option is + given without value it is enabled. If given multiple times the last + value is used. + ** 'separator=': specify a separator inserted between trailer + lines. When this option is not given each trailer line is + terminated with a line feed character. The string SEP may contain + the literal formatting codes described above. To use comma as + separator one must use `%x2C` as it would otherwise be parsed as + next option. If separator option is given multiple times only the + last one is used. E.g., `%(trailers:key=Ticket,separator=%x2C )` + shows all trailer lines whose key is "Ticket" separated by a comma + and a space. + ** 'unfold[=val]': make it behave as if interpret-trailer's `--unfold` + option was given. In same way as to for `only` it can be followed + by an equal sign and explicit value. E.g., + `%(trailers:only,unfold=true)` unfolds and shows all trailer lines. + ** 'valueonly[=val]': skip over the key part of the trailer line and only + show the value part. Also this optionally allows explicit value. NOTE: Some placeholders may depend on other options given to the revision traversal engine. For example, the `%g*` reflog options will diff --combined pretty.c index 0ab45d10d7,6d58b21aff..d2995b13ed --- a/pretty.c +++ b/pretty.c @@@ -595,15 -595,14 +595,15 @@@ static char *replace_encoding_header(ch return strbuf_detach(&tmp, NULL); } -const char *logmsg_reencode(const struct commit *commit, - char **commit_encoding, - const char *output_encoding) +const char *repo_logmsg_reencode(struct repository *r, + const struct commit *commit, + char **commit_encoding, + const char *output_encoding) { static const char *utf8 = "UTF-8"; const char *use_encoding; char *encoding; - const char *msg = get_commit_buffer(commit, NULL); + const char *msg = repo_get_commit_buffer(r, commit, NULL); char *out; if (!output_encoding || !*output_encoding) { @@@ -631,7 -630,7 +631,7 @@@ * the cached copy from get_commit_buffer, we need to duplicate it * to avoid munging the cached copy. */ - if (msg == get_cached_commit_buffer(the_repository, commit, NULL)) + if (msg == get_cached_commit_buffer(r, commit, NULL)) out = xstrdup(msg); else out = (char *)msg; @@@ -645,7 -644,7 +645,7 @@@ */ out = reencode_string(msg, output_encoding, use_encoding); if (out) - unuse_commit_buffer(commit, msg); + repo_unuse_commit_buffer(r, commit, msg); } /* @@@ -1057,13 -1056,26 +1057,26 @@@ static size_t parse_padding_placeholder 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 -1087,47 +1088,47 @@@ 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 -1137,13 +1138,14 @@@ 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 -1162,6 +1164,6 @@@ */ 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; @@@ -1196,14 -1242,6 +1244,14 @@@ load_ref_decorations(NULL, DECORATE_SHORT_REFS); format_decorations_extended(sb, commit, c->auto_color, "", ", ", ""); return 1; + case 'S': /* tag/branch like --source */ + if (!(c->pretty_ctx->rev && c->pretty_ctx->rev->sources)) + return 0; + slot = revision_sources_at(c->pretty_ctx->rev->sources, commit); + if (!(slot && *slot)) + return 0; + strbuf_addstr(sb, *slot); + return 1; case 'g': /* reflog info */ switch(placeholder[1]) { case 'd': /* reflog selector */ @@@ -1322,24 -1360,53 +1370,53 @@@ 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 */ @@@ -1508,9 -1575,6 +1585,9 @@@ static size_t userformat_want_item(stru case 'N': w->notes = 1; break; + case 'S': + w->source = 1; + break; } return 0; } @@@ -1528,10 -1592,9 +1605,10 @@@ void userformat_find_requirements(cons strbuf_release(&dummy); } -void format_commit_message(const struct commit *commit, - const char *format, struct strbuf *sb, - const struct pretty_print_context *pretty_ctx) +void repo_format_commit_message(struct repository *r, + const struct commit *commit, + const char *format, struct strbuf *sb, + const struct pretty_print_context *pretty_ctx) { struct format_commit_context context; const char *output_enc = pretty_ctx->output_encoding; @@@ -1545,9 -1608,9 +1622,9 @@@ * 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, - utf8); + 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); @@@ -1571,7 -1634,7 +1648,7 @@@ } free(context.commit_encoding); - unuse_commit_buffer(commit, context.message); + repo_unuse_commit_buffer(r, commit, context.message); } static void pp_header(struct pretty_print_context *pp, diff --combined t/t4205-log-pretty-formats.sh index 7df8c3d4ec,99f50fa401..f42a69faa2 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@@ -578,6 -578,24 +578,24 @@@ test_expect_success '%(trailers:only) s test_cmp expect actual ' + test_expect_success '%(trailers:only=yes) shows only "key: value" trailers' ' + git log --no-walk --pretty=format:"%(trailers:only=yes)" >actual && + grep -v patch.description expect && + test_cmp expect actual + ' + + test_expect_success '%(trailers:only=no) shows all trailers' ' + git log --no-walk --pretty=format:"%(trailers:only=no)" >actual && + cat trailers >expect && + test_cmp expect actual + ' + + test_expect_success '%(trailers:only=no,only=true) shows only "key: value" trailers' ' + git log --no-walk --pretty=format:"%(trailers:only=yes)" >actual && + grep -v patch.description expect && + test_cmp expect actual + ' + test_expect_success '%(trailers:unfold) unfolds trailers' ' git log --no-walk --pretty="%(trailers:unfold)" >actual && { @@@ -598,6 -616,105 +616,105 @@@ test_expect_success ':only and :unfold test_cmp expect actual ' + test_expect_success 'pretty format %(trailers:key=foo) shows that trailer' ' + git log --no-walk --pretty="format:%(trailers:key=Acked-by)" >actual && + echo "Acked-by: A U Thor " >expect && + test_cmp expect actual + ' + + test_expect_success 'pretty format %(trailers:key=foo) is case insensitive' ' + git log --no-walk --pretty="format:%(trailers:key=AcKed-bY)" >actual && + echo "Acked-by: A U Thor " >expect && + test_cmp expect actual + ' + + test_expect_success 'pretty format %(trailers:key=foo:) trailing colon also works' ' + git log --no-walk --pretty="format:%(trailers:key=Acked-by:)" >actual && + echo "Acked-by: A U Thor " >expect && + test_cmp expect actual + ' + + test_expect_success 'pretty format %(trailers:key=foo) multiple keys' ' + git log --no-walk --pretty="format:%(trailers:key=Acked-by:,key=Signed-off-By)" >actual && + grep -v patch.description expect && + test_cmp expect actual + ' + + test_expect_success '%(trailers:key=nonexistant) becomes empty' ' + git log --no-walk --pretty="x%(trailers:key=Nacked-by)x" >actual && + echo "xx" >expect && + test_cmp expect actual + ' + + test_expect_success '%(trailers:key=foo) handles multiple lines even if folded' ' + git log --no-walk --pretty="format:%(trailers:key=Signed-Off-by)" >actual && + grep -v patch.description expect && + test_cmp expect actual + ' + + test_expect_success '%(trailers:key=foo,unfold) properly unfolds' ' + git log --no-walk --pretty="format:%(trailers:key=Signed-Off-by,unfold)" >actual && + unfold expect && + test_cmp expect actual + ' + + test_expect_success 'pretty format %(trailers:key=foo,only=no) also includes nontrailer lines' ' + git log --no-walk --pretty="format:%(trailers:key=Acked-by,only=no)" >actual && + { + echo "Acked-by: A U Thor " && + grep patch.description expect && + test_cmp expect actual + ' + + test_expect_success '%(trailers:key) without value is error' ' + git log --no-walk --pretty="tformat:%(trailers:key)" >actual && + echo "%(trailers:key)" >expect && + test_cmp expect actual + ' + + test_expect_success '%(trailers:key=foo,valueonly) shows only value' ' + git log --no-walk --pretty="format:%(trailers:key=Acked-by,valueonly)" >actual && + echo "A U Thor " >expect && + test_cmp expect actual + ' + + test_expect_success 'pretty format %(trailers:separator) changes separator' ' + git log --no-walk --pretty=format:"X%(trailers:separator=%x00,unfold)X" >actual && + printf "XSigned-off-by: A U Thor \0Acked-by: A U Thor \0[ v2 updated patch description ]\0Signed-off-by: A U Thor X" >expect && + test_cmp expect actual + ' + + test_expect_success 'pretty format %(trailers) combining separator/key/valueonly' ' + git commit --allow-empty -F - <<-\EOF && + Important fix + + The fix is explained here + + Closes: #1234 + EOF + + git commit --allow-empty -F - <<-\EOF && + Another fix + + The fix is explained here + + Closes: #567 + Closes: #890 + EOF + + git commit --allow-empty -F - <<-\EOF && + Does not close any tickets + EOF + + git log --pretty="%s% (trailers:separator=%x2c%x20,key=Closes,valueonly)" HEAD~3.. >actual && + test_write_lines \ + "Does not close any tickets" \ + "Another fix #567, #890" \ + "Important fix #1234" >expect && + test_cmp expect actual + ' + test_expect_success 'trailer parsing not fooled by --- line' ' git commit --allow-empty -F - <<-\EOF && this is the subject @@@ -621,54 -738,4 +738,54 @@@ test_cmp expect actual ' +test_expect_success 'set up %S tests' ' + git checkout --orphan source-a && + test_commit one && + test_commit two && + git checkout -b source-b HEAD^ && + test_commit three +' + +test_expect_success 'log --format=%S paints branch names' ' + cat >expect <<-\EOF && + source-b + source-a + source-b + EOF + git log --format=%S source-a source-b >actual && + test_cmp expect actual +' + +test_expect_success 'log --format=%S paints tag names' ' + git tag -m tagged source-tag && + cat >expect <<-\EOF && + source-tag + source-a + source-tag + EOF + git log --format=%S source-tag source-a >actual && + test_cmp expect actual +' + +test_expect_success 'log --format=%S paints symmetric ranges' ' + cat >expect <<-\EOF && + source-b + source-a + EOF + git log --format=%S source-a...source-b >actual && + test_cmp expect actual +' + +test_expect_success '%S in git log --format works with other placeholders (part 1)' ' + git log --format="source-b %h" source-b >expect && + git log --format="%S %h" source-b >actual && + test_cmp expect actual +' + +test_expect_success '%S in git log --format works with other placeholders (part 2)' ' + git log --format="%h source-b" source-b >expect && + git log --format="%h %S" source-b >actual && + test_cmp expect actual +' + test_done