Merge branch 'jk/trailers-placeholder-in-pretty'
authorJunio C Hamano <gitster@pobox.com>
Mon, 19 Dec 2016 22:45:34 +0000 (14:45 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 19 Dec 2016 22:45:34 +0000 (14:45 -0800)
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

1  2 
Documentation/git-for-each-ref.txt
ref-filter.c
t/t6300-for-each-ref.sh
index 6d22974da6e8fea45881d6a2ce08e47cc2ed78b7,e5807eede787dec3482d1b17e4d7149bcc585059..abe13f3bedaf55dbefc03d26b934351107ff50af
@@@ -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 bd9801024c9cc95b2846ae5ec221c4e9d18d5e94,b6f1bb73ed374178173906b7078a1fca5a6f84d0..1a978405e6b97d1f57d75b867ddeae30f091ef19
@@@ -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)
                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)
                        /*  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.
               skip_prefix(refname, "refs/", &refname));
  
        for (; *patterns; patterns++) {
 -              if (!wildmatch(*patterns, refname, 0, NULL))
 +              if (!wildmatch(*patterns, refname, flags, NULL))
                        return 1;
        }
        return 0;
   * 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 039509a9cb94ef5c653df09e0453ac83157bf184,eb4bac0fe47785164e0f0bc3d1d0ea4aa5f452ff..aea1dfc7148e4885e52a579ca60ee622c49b5693
@@@ -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 <<EOF
+ Reviewed-by: A U Thor <author@example.com>
+ Signed-off-by: A U Thor <author@example.com>
+ 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 >actual.clean &&
+       # git for-each-ref ends with a blank line
+       cat >expect <<-EOF &&
+       $(cat trailers)
+       EOF
+       test_cmp expect actual.clean
+ '
  test_done