From: Junio C Hamano Date: Wed, 11 Oct 2017 05:52:22 +0000 (+0900) Subject: Merge branch 'tb/show-trailers-in-ref-filter' X-Git-Tag: v2.15.0-rc1~10 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/b03cd16613477d3f9ce2e82f3a9fdb5fa99a8b01?hp=-c Merge branch 'tb/show-trailers-in-ref-filter' "git for-each-ref --format=..." learned a new format element, %(trailers), to show only the commit log trailer part of the log message. * tb/show-trailers-in-ref-filter: ref-filter.c: parse trailers arguments with %(contents) atom ref-filter.c: use trailer_opts to format trailers t6300: refactor %(trailers) tests doc: use "``"-style quoting for literal strings doc: 'trailers' is the preferred way to format trailers t4205: unfold across multiple lines --- b03cd16613477d3f9ce2e82f3a9fdb5fa99a8b01 diff --combined Documentation/git-for-each-ref.txt index cbd0a6212a,4a2c851e6c..1d420e4cde --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@@ -57,11 -57,6 +57,11 @@@ OPTION `xx`; for example `%00` interpolates to `\0` (NUL), `%09` to `\t` (TAB) and `%0a` to `\n` (LF). +--color[=]: + Respect any colors specified in the `--format` option. The + `` field must be one of `always`, `never`, or `auto` (if + `` is absent, behave as if `always` was given). + --shell:: --perl:: --python:: @@@ -218,11 -213,15 +218,15 @@@ and `date` to extract the named compone The complete message in a commit and tag object is `contents`. Its first line is `contents:subject`, where subject is the concatenation of all lines of the commit message up to the first blank line. The next - line is 'contents:body', where body is all of the lines after the first + line is `contents:body`, where body is all of the lines after the first blank line. The optional GPG signature is `contents:signature`. The first `N` lines of the message is obtained using `contents:lines=N`. Additionally, the trailers as interpreted by linkgit:git-interpret-trailers[1] - are obtained as 'contents:trailers'. + are obtained as `trailers` (or by using the historical alias + `contents:trailers`). Non-trailer lines from the trailer block can be omitted + with `trailers:only`. Whitespace-continuations can be removed from trailers so + that each trailer appears on a line by itself with its full content with + `trailers:unfold`. Both can be used together as `trailers:unfold,only`. For sorting purposes, fields with numeric values sort in numeric order (`objectsize`, `authordate`, `committerdate`, `creatordate`, `taggerdate`). diff --combined ref-filter.c index 45a3be8340,85625fe2d2..e728b15b3a --- a/ref-filter.c +++ b/ref-filter.c @@@ -82,6 -82,7 +82,7 @@@ static struct used_atom } remote_ref; struct { enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option; + struct process_trailer_options trailer_opts; unsigned int nlines; } contents; struct { @@@ -182,9 -183,23 +183,23 @@@ static void subject_atom_parser(const s static void trailers_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg) { - if (arg) - die(_("%%(trailers) does not take arguments")); + struct string_list params = STRING_LIST_INIT_DUP; + int i; + + if (arg) { + string_list_split(¶ms, arg, ',', -1); + for (i = 0; i < params.nr; i++) { + const char *s = params.items[i].string; + if (!strcmp(s, "unfold")) + atom->u.contents.trailer_opts.unfold = 1; + else if (!strcmp(s, "only")) + atom->u.contents.trailer_opts.only_trailers = 1; + else + die(_("unknown %%(trailers) argument: %s"), s); + } + } atom->u.contents.option = C_TRAILERS; + string_list_clear(¶ms, 0); } static void contents_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg) @@@ -197,9 -212,10 +212,10 @@@ atom->u.contents.option = C_SIG; else if (!strcmp(arg, "subject")) atom->u.contents.option = C_SUB; - else if (!strcmp(arg, "trailers")) - atom->u.contents.option = C_TRAILERS; - else if (skip_prefix(arg, "lines=", &arg)) { + else if (skip_prefix(arg, "trailers", &arg)) { + skip_prefix(arg, ":", &arg); + trailers_atom_parser(format, atom, *arg ? arg : NULL); + } else if (skip_prefix(arg, "lines=", &arg)) { atom->u.contents.option = C_LINES; if (strtoul_ui(arg, 10, &atom->u.contents.nlines)) die(_("positive value expected contents:lines=%s"), arg); @@@ -295,7 -311,9 +311,7 @@@ static void if_atom_parser(const struc static void head_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg) { - struct object_id unused; - - atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, unused.hash, NULL); + atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL); } static struct { @@@ -413,16 -431,8 +429,16 @@@ static int parse_ref_filter_atom(const REALLOC_ARRAY(used_atom, used_atom_cnt); used_atom[at].name = xmemdupz(atom, ep - atom); used_atom[at].type = valid_atom[i].cmp_type; - if (arg) + if (arg) { arg = used_atom[at].name + (arg - atom) + 1; + if (!*arg) { + /* + * Treat empty sub-arguments list as NULL (i.e., + * "%(atom:)" is equivalent to "%(atom)"). + */ + arg = NULL; + } + } memset(&used_atom[at].u, 0, sizeof(used_atom[at].u)); if (valid_atom[i].parser) valid_atom[i].parser(format, &used_atom[at], arg); @@@ -1048,7 -1058,7 +1064,7 @@@ static void grab_sub_body_contents(stru name++; if (strcmp(name, "subject") && strcmp(name, "body") && - strcmp(name, "trailers") && + !starts_with(name, "trailers") && !starts_with(name, "contents")) continue; if (!subpos) @@@ -1073,13 -1083,12 +1089,12 @@@ append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines); v->s = strbuf_detach(&s, NULL); } else if (atom->u.contents.option == C_TRAILERS) { - struct trailer_info info; + struct strbuf s = STRBUF_INIT; - /* Search for trailer info */ - trailer_info_get(&info, subpos); - v->s = xmemdupz(info.trailer_start, - info.trailer_end - info.trailer_start); - trailer_info_release(&info); + /* Format the trailer info according to the trailer_opts given */ + format_trailers_from_commit(&s, subpos, &atom->u.contents.trailer_opts); + + v->s = strbuf_detach(&s, NULL); } else if (atom->u.contents.option == C_BARE) v->s = xstrdup(subpos); } @@@ -1323,8 -1332,9 +1338,8 @@@ static void populate_value(struct ref_a ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value)); if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) { - struct object_id unused1; ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING, - unused1.hash, NULL); + NULL, NULL); if (!ref->symref) ref->symref = ""; } diff --combined t/t6300-for-each-ref.sh index a6f51a5003,3bdfa02559..416ff7d0b8 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@@ -51,7 -51,6 +51,7 @@@ test_atom() } test_atom head refname refs/heads/master +test_atom head refname: refs/heads/master test_atom head refname:short master test_atom head refname:lstrip=1 heads/master test_atom head refname:lstrip=2 master @@@ -426,7 -425,8 +426,7 @@@ test_expect_success 'set up color tests ' test_expect_success TTY '%(color) shows color with a tty' ' - test_terminal env TERM=vt100 \ - git for-each-ref --format="$color_format" >actual.raw && + test_terminal git for-each-ref --format="$color_format" >actual.raw && test_decode_color actual && test_cmp expected.color actual ' @@@ -436,8 -436,8 +436,8 @@@ test_expect_success '%(color) does not test_cmp expected.bare actual ' -test_expect_success 'color.ui=always can override tty check' ' - git -c color.ui=always for-each-ref --format="$color_format" >actual.raw && +test_expect_success '--color can override tty check' ' + git for-each-ref --color --format="$color_format" >actual.raw && test_decode_color actual && test_cmp expected.color actual ' @@@ -605,18 -605,104 +605,104 @@@ test_expect_success 'do not dereferenc cat >trailers < Signed-off-by: A U Thor + [ v2 updated patch description ] + Acked-by: A U Thor + EOF - test_expect_success 'basic atom: head contents:trailers' ' + unfold () { + perl -0pe 's/\n\s+/ /g' + } + + test_expect_success 'set up trailers for next test' ' echo "Some contents" > two && git add two && - git commit -F - <<-EOF && + git commit -F - <<-EOF trailers: this commit message has trailers Some message contents $(cat trailers) EOF + ' + + test_expect_success '%(trailers:unfold) unfolds trailers' ' + git for-each-ref --format="%(trailers:unfold)" refs/heads/master >actual && + { + unfold expect && + test_cmp expect actual + ' + + test_expect_success '%(trailers:only) shows only "key: value" trailers' ' + git for-each-ref --format="%(trailers:only)" refs/heads/master >actual && + { + grep -v patch.description expect && + test_cmp expect actual + ' + + test_expect_success '%(trailers:only) and %(trailers:unfold) work together' ' + git for-each-ref --format="%(trailers:only,unfold)" refs/heads/master >actual && + git for-each-ref --format="%(trailers:unfold,only)" refs/heads/master >reverse && + test_cmp actual reverse && + { + grep -v patch.description expect && + test_cmp expect actual + ' + + test_expect_success '%(contents:trailers:unfold) unfolds trailers' ' + git for-each-ref --format="%(contents:trailers:unfold)" refs/heads/master >actual && + { + unfold expect && + test_cmp expect actual + ' + + test_expect_success '%(contents:trailers:only) shows only "key: value" trailers' ' + git for-each-ref --format="%(contents:trailers:only)" refs/heads/master >actual && + { + grep -v patch.description expect && + test_cmp expect actual + ' + + test_expect_success '%(contents:trailers:only) and %(contents:trailers:unfold) work together' ' + git for-each-ref --format="%(contents:trailers:only,unfold)" refs/heads/master >actual && + git for-each-ref --format="%(contents:trailers:unfold,only)" refs/heads/master >reverse && + test_cmp actual reverse && + { + grep -v patch.description expect && + test_cmp expect actual + ' + + test_expect_success '%(trailers) rejects unknown trailers arguments' ' + # error message cannot be checked under i18n + cat >expect <<-EOF && + fatal: unknown %(trailers) argument: unsupported + EOF + test_must_fail git for-each-ref --format="%(trailers:unsupported)" 2>actual && + test_i18ncmp expect actual + ' + + test_expect_success '%(contents:trailers) rejects unknown trailers arguments' ' + # error message cannot be checked under i18n + cat >expect <<-EOF && + fatal: unknown %(trailers) argument: unsupported + EOF + test_must_fail git for-each-ref --format="%(contents:trailers:unsupported)" 2>actual && + test_i18ncmp expect actual + ' + + test_expect_success 'basic atom: head contents:trailers' ' git for-each-ref --format="%(contents:trailers)" refs/heads/master >actual && sanitize_pgp actual.clean && # git for-each-ref ends with a blank line