From: Junio C Hamano Date: Mon, 19 Dec 2016 22:45:34 +0000 (-0800) Subject: Merge branch 'jk/trailers-placeholder-in-pretty' X-Git-Tag: v2.12.0-rc0~131 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/f008159fc2dec7b98e74736751d3e6ad8d9c060f?hp=-c Merge branch 'jk/trailers-placeholder-in-pretty' In addition to %(subject), %(body), "log --pretty=format:..." learned a new placeholder %(trailers). * jk/trailers-placeholder-in-pretty: ref-filter: add support to display trailers as part of contents pretty: add %(trailers) format for displaying trailers of a commit message --- f008159fc2dec7b98e74736751d3e6ad8d9c060f diff --combined Documentation/git-for-each-ref.txt index 6d22974da6,e5807eede7..abe13f3bed --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@@ -79,9 -79,6 +79,9 @@@ OPTION Only list refs which contain the specified commit (HEAD if not specified). +--ignore-case:: + Sorting and filtering refs are case insensitive. + FIELD NAMES ----------- @@@ -168,6 -165,8 +168,8 @@@ of all lines of the commit message up t 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'. For sorting purposes, fields with numeric values sort in numeric order (`objectsize`, `authordate`, `committerdate`, `creatordate`, `taggerdate`). diff --combined ref-filter.c index bd9801024c,b6f1bb73ed..1a978405e6 --- a/ref-filter.c +++ b/ref-filter.c @@@ -13,6 -13,7 +13,7 @@@ #include "utf8.h" #include "git-compat-util.h" #include "version.h" + #include "trailer.h" typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; @@@ -40,7 -41,7 +41,7 @@@ static struct used_atom enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } remote_ref; struct { - enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option; + enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option; unsigned int nlines; } contents; enum { O_FULL, O_SHORT } objectname; @@@ -85,6 -86,13 +86,13 @@@ static void subject_atom_parser(struct atom->u.contents.option = C_SUB; } + static void trailers_atom_parser(struct used_atom *atom, const char *arg) + { + if (arg) + die(_("%%(trailers) does not take arguments")); + atom->u.contents.option = C_TRAILERS; + } + static void contents_atom_parser(struct used_atom *atom, const char *arg) { if (!arg) @@@ -95,6 -103,8 +103,8 @@@ 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)) { atom->u.contents.option = C_LINES; if (strtoul_ui(arg, 10, &atom->u.contents.nlines)) @@@ -194,6 -204,7 +204,7 @@@ static struct { "creatordate", FIELD_TIME }, { "subject", FIELD_STR, subject_atom_parser }, { "body", FIELD_STR, body_atom_parser }, + { "trailers", FIELD_STR, trailers_atom_parser }, { "contents", FIELD_STR, contents_atom_parser }, { "upstream", FIELD_STR, remote_ref_atom_parser }, { "push", FIELD_STR, remote_ref_atom_parser }, @@@ -785,6 -796,7 +796,7 @@@ static void grab_sub_body_contents(stru name++; if (strcmp(name, "subject") && strcmp(name, "body") && + strcmp(name, "trailers") && !starts_with(name, "contents")) continue; if (!subpos) @@@ -808,6 -820,14 +820,14 @@@ /* Size is the length of the message after removing the signature */ 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; + + /* 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); } else if (atom->u.contents.option == C_BARE) v->s = xstrdup(subpos); } @@@ -1017,7 -1037,7 +1037,7 @@@ static void populate_value(struct ref_a head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, sha1, NULL); - if (!strcmp(ref->refname, head)) + if (head && !strcmp(ref->refname, head)) v->s = "*"; else v->s = " "; @@@ -1231,14 -1251,8 +1251,14 @@@ static int commit_contains(struct ref_f * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref * matches "refs/heads/mas*", too). */ -static int match_pattern(const char **patterns, const char *refname) +static int match_pattern(const struct ref_filter *filter, const char *refname) { + const char **patterns = filter->name_patterns; + unsigned flags = 0; + + if (filter->ignore_case) + flags |= WM_CASEFOLD; + /* * When no '--format' option is given we need to skip the prefix * for matching refs of tags and branches. @@@ -1249,7 -1263,7 +1269,7 @@@ skip_prefix(refname, "refs/", &refname)); for (; *patterns; patterns++) { - if (!wildmatch(*patterns, refname, 0, NULL)) + if (!wildmatch(*patterns, refname, flags, NULL)) return 1; } return 0; @@@ -1261,15 -1275,9 +1281,15 @@@ * matches a pattern "refs/heads/" but not "refs/heads/m") or a * wildcard (e.g. the same ref matches "refs/heads/m*", too). */ -static int match_name_as_path(const char **pattern, const char *refname) +static int match_name_as_path(const struct ref_filter *filter, const char *refname) { + const char **pattern = filter->name_patterns; int namelen = strlen(refname); + unsigned flags = WM_PATHNAME; + + if (filter->ignore_case) + flags |= WM_CASEFOLD; + for (; *pattern; pattern++) { const char *p = *pattern; int plen = strlen(p); @@@ -1292,8 -1300,8 +1312,8 @@@ static int filter_pattern_match(struct if (!*filter->name_patterns) return 1; /* No pattern always matches */ if (filter->match_as_path) - return match_name_as_path(filter->name_patterns, refname); - return match_pattern(filter->name_patterns, refname); + return match_name_as_path(filter, refname); + return match_pattern(filter, refname); } /* @@@ -1548,20 -1556,18 +1568,20 @@@ static int cmp_ref_sorting(struct ref_s struct atom_value *va, *vb; int cmp; cmp_type cmp_type = used_atom[s->atom].type; + int (*cmp_fn)(const char *, const char *); get_ref_atom_value(a, s->atom, &va); get_ref_atom_value(b, s->atom, &vb); + cmp_fn = s->ignore_case ? strcasecmp : strcmp; if (s->version) cmp = versioncmp(va->s, vb->s); else if (cmp_type == FIELD_STR) - cmp = strcmp(va->s, vb->s); + cmp = cmp_fn(va->s, vb->s); else { if (va->ul < vb->ul) cmp = -1; else if (va->ul == vb->ul) - cmp = strcmp(a->refname, b->refname); + cmp = cmp_fn(a->refname, b->refname); else cmp = 1; } diff --combined t/t6300-for-each-ref.sh index 039509a9cb,eb4bac0fe4..aea1dfc714 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@@ -554,13 -554,29 +554,38 @@@ test_expect_success 'Verify sort with m test_cmp expected actual ' +test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' ' + test_when_finished "git checkout master" && + git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && + sed -e "s/^\* / /" actual >expect && + git checkout --orphan HEAD && + git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && + test_cmp expect actual +' + + cat >trailers < + Signed-off-by: A U Thor + EOF + + test_expect_success 'basic atom: head contents:trailers' ' + echo "Some contents" > two && + git add two && + git commit -F - <<-EOF && + trailers: this commit message has trailers + + Some message contents + + $(cat trailers) + EOF + git for-each-ref --format="%(contents:trailers)" refs/heads/master >actual && + sanitize_pgp actual.clean && + # git for-each-ref ends with a blank line + cat >expect <<-EOF && + $(cat trailers) + + EOF + test_cmp expect actual.clean + ' + test_done