Merge branch 'jk/maint-reflog-walk-count-vs-time' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 14 May 2012 18:46:16 +0000 (11:46 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 14 May 2012 18:46:16 +0000 (11:46 -0700)
Gives a better DWIM behaviour for --pretty=format:%gd, "stash list", and
"log -g", depending on how the starting point ("master" vs "master@{0}" vs
"master@{now}") and date formatting options (e.g. "--date=iso") are given
on the command line.

By Jeff King (4) and Junio C Hamano (1)
* jk/maint-reflog-walk-count-vs-time:
reflog-walk: tell explicit --date=default from not having --date at all
reflog-walk: always make HEAD@{0} show indexed selectors
reflog-walk: clean up "flag" field of commit_reflog struct
log: respect date_mode_explicit with --format:%gd
t1411: add more selector index/date tests

1  2 
builtin/rev-list.c
commit.h
log-tree.c
pretty.c
reflog-walk.c
reflog-walk.h
diff --combined builtin/rev-list.c
index 4c4d404afc7321a222d3dc136b570ada3d3c2317,fe0fb20d2d9069766929eddf1dbce4c88fa217e0..ff5a38372d76cc50167a8f7e7862a67cc9d419ea
@@@ -52,11 -52,6 +52,11 @@@ static void show_commit(struct commit *
        struct rev_list_info *info = data;
        struct rev_info *revs = info->revs;
  
 +      if (info->flags & REV_LIST_QUIET) {
 +              finish_commit(commit, data);
 +              return;
 +      }
 +
        graph_show_commit(revs->graph);
  
        if (revs->count) {
                struct pretty_print_context ctx = {0};
                ctx.abbrev = revs->abbrev;
                ctx.date_mode = revs->date_mode;
+               ctx.date_mode_explicit = revs->date_mode_explicit;
                ctx.fmt = revs->commit_format;
                pretty_print_commit(&ctx, commit, &buf);
                if (revs->graph) {
@@@ -173,26 -169,29 +174,26 @@@ static void finish_commit(struct commi
        commit->buffer = NULL;
  }
  
 -static void finish_object(struct object *obj, const struct name_path *path, const char *name)
 +static void finish_object(struct object *obj,
 +                        const struct name_path *path, const char *name,
 +                        void *cb_data)
  {
 +      struct rev_list_info *info = cb_data;
        if (obj->type == OBJ_BLOB && !has_sha1_file(obj->sha1))
                die("missing blob object '%s'", sha1_to_hex(obj->sha1));
 +      if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
 +              parse_object(obj->sha1);
  }
  
 -static void show_object(struct object *obj, const struct name_path *path, const char *component)
 +static void show_object(struct object *obj,
 +                      const struct name_path *path, const char *component,
 +                      void *cb_data)
  {
 -      char *name = path_name(path, component);
 -      /* An object with name "foo\n0000000..." can be used to
 -       * confuse downstream "git pack-objects" very badly.
 -       */
 -      const char *ep = strchr(name, '\n');
 -
 -      finish_object(obj, path, name);
 -      if (ep) {
 -              printf("%s %.*s\n", sha1_to_hex(obj->sha1),
 -                     (int) (ep - name),
 -                     name);
 -      }
 -      else
 -              printf("%s %s\n", sha1_to_hex(obj->sha1), name);
 -      free(name);
 +      struct rev_list_info *info = cb_data;
 +      finish_object(obj, path, component, cb_data);
 +      if (info->flags & REV_LIST_QUIET)
 +              return;
 +      show_object_with_name(stdout, obj, path, component);
  }
  
  static void show_edge(struct commit *commit)
@@@ -249,6 -248,13 +250,6 @@@ void print_commit_list(struct commit_li
        }
  }
  
 -static void show_tried_revs(struct commit_list *tried)
 -{
 -      printf("bisect_tried='");
 -      print_commit_list(tried, "%s|", "%s");
 -      printf("'\n");
 -}
 -
  static void print_var_str(const char *var, const char *val)
  {
        printf("%s='%s'\n", var, val);
@@@ -261,12 -267,12 +262,12 @@@ static void print_var_int(const char *v
  
  static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
  {
 -      int cnt, flags = info->bisect_show_flags;
 +      int cnt, flags = info->flags;
        char hex[41] = "";
        struct commit_list *tried;
        struct rev_info *revs = info->revs;
  
 -      if (!revs->commits && !(flags & BISECT_SHOW_TRIED))
 +      if (!revs->commits)
                return 1;
  
        revs->commits = filter_skipped(revs->commits, &tried,
                printf("------\n");
        }
  
 -      if (flags & BISECT_SHOW_TRIED)
 -              show_tried_revs(tried);
 -
        print_var_str("bisect_rev", hex);
        print_var_int("bisect_nr", cnt - 1);
        print_var_int("bisect_good", all - reaches - 1);
@@@ -312,6 -321,7 +313,6 @@@ int cmd_rev_list(int argc, const char *
        int bisect_list = 0;
        int bisect_show_vars = 0;
        int bisect_find_all = 0;
 -      int quiet = 0;
  
        git_config(git_default_config, NULL);
        init_revisions(&revs, prefix);
        if (revs.bisect)
                bisect_list = 1;
  
 -      quiet = DIFF_OPT_TST(&revs.diffopt, QUICK);
 +      if (DIFF_OPT_TST(&revs.diffopt, QUICK))
 +              info.flags |= REV_LIST_QUIET;
        for (i = 1 ; i < argc; i++) {
                const char *arg = argv[i];
  
                if (!strcmp(arg, "--bisect-all")) {
                        bisect_list = 1;
                        bisect_find_all = 1;
 -                      info.bisect_show_flags = BISECT_SHOW_ALL;
 +                      info.flags |= BISECT_SHOW_ALL;
                        revs.show_decorations = 1;
                        continue;
                }
                        return show_bisect_vars(&info, reaches, all);
        }
  
 -      traverse_commit_list(&revs,
 -                           quiet ? finish_commit : show_commit,
 -                           quiet ? finish_object : show_object,
 -                           &info);
 +      traverse_commit_list(&revs, show_commit, show_object, &info);
  
        if (revs.count) {
                if (revs.left_right && revs.cherry_mark)
diff --combined commit.h
index 154c0e34ff7d2dbaddcfb66b74d26697ffba6381,048f31ed93603a3df4ca4e36cf1fd12a2198a858..205eea3382161672ae2206a3d858eb2075fcb0a2
+++ b/commit.h
@@@ -82,6 -82,7 +82,7 @@@ struct pretty_print_context 
        const char *after_subject;
        int preserve_subject;
        enum date_mode date_mode;
+       unsigned date_mode_explicit:1;
        int need_8bit_cte;
        int show_notes;
        struct reflog_walk_info *reflog_info;
@@@ -133,7 -134,6 +134,7 @@@ struct commit *pop_most_recent_commit(s
  struct commit *pop_commit(struct commit_list **stack);
  
  void clear_commit_marks(struct commit *commit, unsigned int mark);
 +void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
  
  /*
   * Performs an in-place topological sort of list supplied.
@@@ -181,43 -181,8 +182,43 @@@ static inline int single_parent(struct 
  
  struct commit_list *reduce_heads(struct commit_list *heads);
  
 -extern int commit_tree(const char *msg, unsigned char *tree,
 -              struct commit_list *parents, unsigned char *ret,
 -              const char *author);
 +struct commit_extra_header {
 +      struct commit_extra_header *next;
 +      char *key;
 +      char *value;
 +      size_t len;
 +};
 +
 +extern void append_merge_tag_headers(struct commit_list *parents,
 +                                   struct commit_extra_header ***tail);
 +
 +extern int commit_tree(const struct strbuf *msg, unsigned char *tree,
 +                     struct commit_list *parents, unsigned char *ret,
 +                     const char *author, const char *sign_commit);
 +
 +extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
 +                              struct commit_list *parents, unsigned char *ret,
 +                              const char *author, const char *sign_commit,
 +                              struct commit_extra_header *);
 +
 +extern struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
 +extern struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 +
 +extern void free_commit_extra_headers(struct commit_extra_header *extra);
 +
 +struct merge_remote_desc {
 +      struct object *obj; /* the named object, could be a tag */
 +      const char *name;
 +};
 +#define merge_remote_util(commit) ((struct merge_remote_desc *)((commit)->util))
 +
 +/*
 + * Given "name" from the command line to merge, find the commit object
 + * and return it, while storing merge_remote_desc in its ->util field,
 + * to allow callers to tell if we are told to merge a tag.
 + */
 +struct commit *get_merge_parent(const char *name);
  
 +extern int parse_signed_commit(const unsigned char *sha1,
 +                             struct strbuf *message, struct strbuf *signature);
  #endif /* COMMIT_H */
diff --combined log-tree.c
index 44f02683722c5b9181f94f1a6380f8ad191eb08f,588117e013e4d6c4362cb67ec1e5bf1768b4ccb9..376d97317674dfe6dcffec3772c899c4fc96f82f
@@@ -8,7 -8,6 +8,7 @@@
  #include "refs.h"
  #include "string-list.h"
  #include "color.h"
 +#include "gpg-interface.h"
  
  struct decoration name_decoration = { "object names" };
  
@@@ -120,9 -119,9 +120,9 @@@ static int add_ref_decoration(const cha
                type = DECORATION_REF_REMOTE;
        else if (!prefixcmp(refname, "refs/tags/"))
                type = DECORATION_REF_TAG;
 -      else if (!prefixcmp(refname, "refs/stash"))
 +      else if (!strcmp(refname, "refs/stash"))
                type = DECORATION_REF_STASH;
 -      else if (!prefixcmp(refname, "HEAD"))
 +      else if (!strcmp(refname, "HEAD"))
                type = DECORATION_REF_HEAD;
  
        if (!cb_data || *(int *)cb_data == DECORATE_SHORT_REFS)
@@@ -166,14 -165,6 +166,14 @@@ static void show_parents(struct commit 
        }
  }
  
 +static void show_children(struct rev_info *opt, struct commit *commit, int abbrev)
 +{
 +      struct commit_list *p = lookup_decoration(&opt->children, &commit->object);
 +      for ( ; p; p = p->next) {
 +              printf(" %s", find_unique_abbrev(p->item->object.sha1, abbrev));
 +      }
 +}
 +
  void show_decorations(struct rev_info *opt, struct commit *commit)
  {
        const char *prefix;
@@@ -404,129 -395,6 +404,129 @@@ void log_write_email_headers(struct rev
        *extra_headers_p = extra_headers;
  }
  
 +static void show_sig_lines(struct rev_info *opt, int status, const char *bol)
 +{
 +      const char *color, *reset, *eol;
 +
 +      color = diff_get_color_opt(&opt->diffopt,
 +                                 status ? DIFF_WHITESPACE : DIFF_FRAGINFO);
 +      reset = diff_get_color_opt(&opt->diffopt, DIFF_RESET);
 +      while (*bol) {
 +              eol = strchrnul(bol, '\n');
 +              printf("%s%.*s%s%s", color, (int)(eol - bol), bol, reset,
 +                     *eol ? "\n" : "");
 +              bol = (*eol) ? (eol + 1) : eol;
 +      }
 +}
 +
 +static void show_signature(struct rev_info *opt, struct commit *commit)
 +{
 +      struct strbuf payload = STRBUF_INIT;
 +      struct strbuf signature = STRBUF_INIT;
 +      struct strbuf gpg_output = STRBUF_INIT;
 +      int status;
 +
 +      if (parse_signed_commit(commit->object.sha1, &payload, &signature) <= 0)
 +              goto out;
 +
 +      status = verify_signed_buffer(payload.buf, payload.len,
 +                                    signature.buf, signature.len,
 +                                    &gpg_output);
 +      if (status && !gpg_output.len)
 +              strbuf_addstr(&gpg_output, "No signature\n");
 +
 +      show_sig_lines(opt, status, gpg_output.buf);
 +
 + out:
 +      strbuf_release(&gpg_output);
 +      strbuf_release(&payload);
 +      strbuf_release(&signature);
 +}
 +
 +static int which_parent(const unsigned char *sha1, const struct commit *commit)
 +{
 +      int nth;
 +      const struct commit_list *parent;
 +
 +      for (nth = 0, parent = commit->parents; parent; parent = parent->next) {
 +              if (!hashcmp(parent->item->object.sha1, sha1))
 +                      return nth;
 +              nth++;
 +      }
 +      return -1;
 +}
 +
 +static int is_common_merge(const struct commit *commit)
 +{
 +      return (commit->parents
 +              && commit->parents->next
 +              && !commit->parents->next->next);
 +}
 +
 +static void show_one_mergetag(struct rev_info *opt,
 +                            struct commit_extra_header *extra,
 +                            struct commit *commit)
 +{
 +      unsigned char sha1[20];
 +      struct tag *tag;
 +      struct strbuf verify_message;
 +      int status, nth;
 +      size_t payload_size, gpg_message_offset;
 +
 +      hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), sha1);
 +      tag = lookup_tag(sha1);
 +      if (!tag)
 +              return; /* error message already given */
 +
 +      strbuf_init(&verify_message, 256);
 +      if (parse_tag_buffer(tag, extra->value, extra->len))
 +              strbuf_addstr(&verify_message, "malformed mergetag\n");
 +      else if (is_common_merge(commit) &&
 +               !hashcmp(tag->tagged->sha1,
 +                        commit->parents->next->item->object.sha1))
 +              strbuf_addf(&verify_message,
 +                          "merged tag '%s'\n", tag->tag);
 +      else if ((nth = which_parent(tag->tagged->sha1, commit)) < 0)
 +              strbuf_addf(&verify_message, "tag %s names a non-parent %s\n",
 +                                  tag->tag, tag->tagged->sha1);
 +      else
 +              strbuf_addf(&verify_message,
 +                          "parent #%d, tagged '%s'\n", nth + 1, tag->tag);
 +      gpg_message_offset = verify_message.len;
 +
 +      payload_size = parse_signature(extra->value, extra->len);
 +      if ((extra->len <= payload_size) ||
 +          (verify_signed_buffer(extra->value, payload_size,
 +                                extra->value + payload_size,
 +                                extra->len - payload_size,
 +                                &verify_message) &&
 +           verify_message.len <= gpg_message_offset)) {
 +              strbuf_addstr(&verify_message, "No signature\n");
 +              status = -1;
 +      }
 +      else if (strstr(verify_message.buf + gpg_message_offset,
 +                      ": Good signature from "))
 +              status = 0;
 +      else
 +              status = -1;
 +
 +      show_sig_lines(opt, status, verify_message.buf);
 +      strbuf_release(&verify_message);
 +}
 +
 +static void show_mergetag(struct rev_info *opt, struct commit *commit)
 +{
 +      struct commit_extra_header *extra, *to_free;
 +
 +      to_free = read_commit_extra_headers(commit, NULL);
 +      for (extra = to_free; extra; extra = extra->next) {
 +              if (strcmp(extra->key, "mergetag"))
 +                      continue; /* not a merge tag */
 +              show_one_mergetag(opt, extra, commit);
 +      }
 +      free_commit_extra_headers(to_free);
 +}
 +
  void show_log(struct rev_info *opt)
  {
        struct strbuf msgbuf = STRBUF_INIT;
                fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
 +              if (opt->children.name)
 +                      show_children(opt, commit, abbrev_commit);
                show_decorations(opt, commit);
                if (opt->graph && !graph_is_commit_finished(opt->graph)) {
                        putchar('\n');
                      stdout);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
 +              if (opt->children.name)
 +                      show_children(opt, commit, abbrev_commit);
                if (parent)
                        printf(" (from %s)",
                               find_unique_abbrev(parent->object.sha1,
                         * graph info here.
                         */
                        show_reflog_message(opt->reflog_info,
-                                   opt->commit_format == CMIT_FMT_ONELINE,
-                                   opt->date_mode_explicit ?
-                                       opt->date_mode :
-                                       DATE_NORMAL);
+                                           opt->commit_format == CMIT_FMT_ONELINE,
+                                           opt->date_mode,
+                                           opt->date_mode_explicit);
                        if (opt->commit_format == CMIT_FMT_ONELINE)
                                return;
                }
        }
  
 +      if (opt->show_signature) {
 +              show_signature(opt, commit);
 +              show_mergetag(opt, commit);
 +      }
 +
        if (!commit->buffer)
                return;
  
        if (ctx.need_8bit_cte >= 0)
                ctx.need_8bit_cte = has_non_ascii(opt->add_signoff);
        ctx.date_mode = opt->date_mode;
+       ctx.date_mode_explicit = opt->date_mode_explicit;
        ctx.abbrev = opt->diffopt.abbrev;
        ctx.after_subject = extra_headers;
        ctx.preserve_subject = opt->preserve_subject;
        if (opt->use_terminator) {
                if (!opt->missing_newline)
                        graph_show_padding(opt->graph);
 -              putchar('\n');
 +              putchar(opt->diffopt.line_termination);
        }
  
        strbuf_release(&msgbuf);
@@@ -711,15 -570,14 +711,15 @@@ int log_tree_diff_flush(struct rev_inf
                    opt->verbose_header &&
                    opt->commit_format != CMIT_FMT_ONELINE) {
                        int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
 -                      if ((pch & opt->diffopt.output_format) == pch)
 -                              printf("---");
                        if (opt->diffopt.output_prefix) {
                                struct strbuf *msg = NULL;
                                msg = opt->diffopt.output_prefix(&opt->diffopt,
                                        opt->diffopt.output_prefix_data);
                                fwrite(msg->buf, msg->len, 1, stdout);
                        }
 +                      if ((pch & opt->diffopt.output_format) == pch) {
 +                              printf("---");
 +                      }
                        putchar('\n');
                }
        }
  
  static int do_diff_combined(struct rev_info *opt, struct commit *commit)
  {
 -      unsigned const char *sha1 = commit->object.sha1;
 -
 -      diff_tree_combined_merge(sha1, opt->dense_combined_merges, opt);
 +      diff_tree_combined_merge(commit, opt->dense_combined_merges, opt);
        return !opt->loginfo;
  }
  
diff --combined pretty.c
index f2dee308b887efc9a23ee1b2dcda9119f23cc9a4,25944de034e1b352a7264b5ae66bf8e201405880..02a0a2bb43570fec656c4152454fa67743111a2c
+++ b/pretty.c
@@@ -9,7 -9,6 +9,7 @@@
  #include "notes.h"
  #include "color.h"
  #include "reflog-walk.h"
 +#include "gpg-interface.h"
  
  static char *user_format;
  static struct cmt_fmt_map {
@@@ -531,24 -530,41 +531,24 @@@ static size_t format_person_part(struc
  {
        /* currently all placeholders have same length */
        const int placeholder_len = 2;
 -      int start, end, tz = 0;
 +      int tz;
        unsigned long date = 0;
 -      char *ep;
 -      const char *name_start, *name_end, *mail_start, *mail_end, *msg_end = msg+len;
        char person_name[1024];
        char person_mail[1024];
 +      struct ident_split s;
 +      const char *name_start, *name_end, *mail_start, *mail_end;
  
 -      /* advance 'end' to point to email start delimiter */
 -      for (end = 0; end < len && msg[end] != '<'; end++)
 -              ; /* do nothing */
 -
 -      /*
 -       * When end points at the '<' that we found, it should have
 -       * matching '>' later, which means 'end' must be strictly
 -       * below len - 1.
 -       */
 -      if (end >= len - 2)
 +      if (split_ident_line(&s, msg, len) < 0)
                goto skip;
  
 -      /* Seek for both name and email part */
 -      name_start = msg;
 -      name_end = msg+end;
 -      while (name_end > name_start && isspace(*(name_end-1)))
 -              name_end--;
 -      mail_start = msg+end+1;
 -      mail_end = mail_start;
 -      while (mail_end < msg_end && *mail_end != '>')
 -              mail_end++;
 -      if (mail_end == msg_end)
 -              goto skip;
 -      end = mail_end-msg;
 +      name_start = s.name_begin;
 +      name_end = s.name_end;
 +      mail_start = s.mail_begin;
 +      mail_end = s.mail_end;
  
        if (part == 'N' || part == 'E') { /* mailmap lookup */
 -              strlcpy(person_name, name_start, name_end-name_start+1);
 -              strlcpy(person_mail, mail_start, mail_end-mail_start+1);
 +              strlcpy(person_name, name_start, name_end - name_start + 1);
 +              strlcpy(person_mail, mail_start, mail_end - mail_start + 1);
                mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
                name_start = person_name;
                name_end = name_start + strlen(person_name);
                return placeholder_len;
        }
  
 -      /* advance 'start' to point to date start delimiter */
 -      for (start = end + 1; start < len && isspace(msg[start]); start++)
 -              ; /* do nothing */
 -      if (start >= len)
 -              goto skip;
 -      date = strtoul(msg + start, &ep, 10);
 -      if (msg + start == ep)
 +      if (!s.date_begin)
                goto skip;
  
 +      date = strtoul(s.date_begin, NULL, 10);
 +
        if (part == 't') {      /* date, UNIX timestamp */
 -              strbuf_add(sb, msg + start, ep - (msg + start));
 +              strbuf_add(sb, s.date_begin, s.date_end - s.date_begin);
                return placeholder_len;
        }
  
        /* parse tz */
 -      for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
 -              ; /* do nothing */
 -      if (start + 1 < len) {
 -              tz = strtoul(msg + start + 1, NULL, 10);
 -              if (msg[start] == '-')
 -                      tz = -tz;
 -      }
 +      tz = strtoul(s.tz_begin + 1, NULL, 10);
 +      if (*s.tz_begin == '-')
 +              tz = -tz;
  
        switch (part) {
        case 'd':       /* date */
  
  skip:
        /*
 -       * bogus commit, 'sb' cannot be updated, but we still need to
 -       * compute a valid return value.
 +       * reading from either a bogus commit, or a reflog entry with
 +       * %gn, %ge, etc.; 'sb' cannot be updated, but we still need
 +       * to compute a valid return value.
         */
        if (part == 'n' || part == 'e' || part == 't' || part == 'd'
            || part == 'D' || part == 'r' || part == 'i')
@@@ -617,12 -640,6 +617,12 @@@ struct format_commit_context 
        const struct pretty_print_context *pretty_ctx;
        unsigned commit_header_parsed:1;
        unsigned commit_message_parsed:1;
 +      unsigned commit_signature_parsed:1;
 +      struct {
 +              char *gpg_output;
 +              char good_bad;
 +              char *signer;
 +      } signature;
        char *message;
        size_t width, indent1, indent2;
  
@@@ -805,76 -822,6 +805,76 @@@ static void rewrap_message_tail(struct 
        c->indent2 = new_indent2;
  }
  
 +static struct {
 +      char result;
 +      const char *check;
 +} signature_check[] = {
 +      { 'G', ": Good signature from " },
 +      { 'B', ": BAD signature from " },
 +};
 +
 +static void parse_signature_lines(struct format_commit_context *ctx)
 +{
 +      const char *buf = ctx->signature.gpg_output;
 +      int i;
 +
 +      for (i = 0; i < ARRAY_SIZE(signature_check); i++) {
 +              const char *found = strstr(buf, signature_check[i].check);
 +              const char *next;
 +              if (!found)
 +                      continue;
 +              ctx->signature.good_bad = signature_check[i].result;
 +              found += strlen(signature_check[i].check);
 +              next = strchrnul(found, '\n');
 +              ctx->signature.signer = xmemdupz(found, next - found);
 +              break;
 +      }
 +}
 +
 +static void parse_commit_signature(struct format_commit_context *ctx)
 +{
 +      struct strbuf payload = STRBUF_INIT;
 +      struct strbuf signature = STRBUF_INIT;
 +      struct strbuf gpg_output = STRBUF_INIT;
 +      int status;
 +
 +      ctx->commit_signature_parsed = 1;
 +
 +      if (parse_signed_commit(ctx->commit->object.sha1,
 +                              &payload, &signature) <= 0)
 +              goto out;
 +      status = verify_signed_buffer(payload.buf, payload.len,
 +                                    signature.buf, signature.len,
 +                                    &gpg_output);
 +      if (status && !gpg_output.len)
 +              goto out;
 +      ctx->signature.gpg_output = strbuf_detach(&gpg_output, NULL);
 +      parse_signature_lines(ctx);
 +
 + out:
 +      strbuf_release(&gpg_output);
 +      strbuf_release(&payload);
 +      strbuf_release(&signature);
 +}
 +
 +
 +static int format_reflog_person(struct strbuf *sb,
 +                              char part,
 +                              struct reflog_walk_info *log,
 +                              enum date_mode dmode)
 +{
 +      const char *ident;
 +
 +      if (!log)
 +              return 2;
 +
 +      ident = get_reflog_ident(log);
 +      if (!ident)
 +              return 2;
 +
 +      return format_person_part(sb, part, ident, strlen(ident), dmode);
 +}
 +
  static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                                void *context)
  {
                                get_reflog_selector(sb,
                                                    c->pretty_ctx->reflog_info,
                                                    c->pretty_ctx->date_mode,
+                                                   c->pretty_ctx->date_mode_explicit,
                                                    (placeholder[1] == 'd'));
                        return 2;
                case 's':       /* reflog message */
                        if (c->pretty_ctx->reflog_info)
                                get_reflog_message(sb, c->pretty_ctx->reflog_info);
                        return 2;
 +              case 'n':
 +              case 'N':
 +              case 'e':
 +              case 'E':
 +                      return format_reflog_person(sb,
 +                                                  placeholder[1],
 +                                                  c->pretty_ctx->reflog_info,
 +                                                  c->pretty_ctx->date_mode);
                }
                return 0;       /* unknown %g placeholder */
        case 'N':
                return 0;
        }
  
 +      if (placeholder[0] == 'G') {
 +              if (!c->commit_signature_parsed)
 +                      parse_commit_signature(c);
 +              switch (placeholder[1]) {
 +              case 'G':
 +                      if (c->signature.gpg_output)
 +                              strbuf_addstr(sb, c->signature.gpg_output);
 +                      break;
 +              case '?':
 +                      switch (c->signature.good_bad) {
 +                      case 'G':
 +                      case 'B':
 +                              strbuf_addch(sb, c->signature.good_bad);
 +                      }
 +                      break;
 +              case 'S':
 +                      if (c->signature.signer)
 +                              strbuf_addstr(sb, c->signature.signer);
 +                      break;
 +              }
 +              return 2;
 +      }
 +
 +
        /* For the rest we have to parse the commit header. */
        if (!c->commit_header_parsed)
                parse_commit_header(c);
@@@ -1179,6 -1095,7 +1180,6 @@@ void format_commit_message(const struc
  {
        struct format_commit_context context;
        static const char utf8[] = "UTF-8";
 -      const char *enc;
        const char *output_enc = pretty_ctx->output_encoding;
  
        memset(&context, 0, sizeof(context));
        context.wrap_start = sb->len;
        context.message = commit->buffer;
        if (output_enc) {
 -              enc = get_header(commit, "encoding");
 -              enc = enc ? enc : utf8;
 -              if (strcmp(enc, output_enc))
 +              char *enc = get_header(commit, "encoding");
 +              if (strcmp(enc ? enc : utf8, output_enc)) {
                        context.message = logmsg_reencode(commit, output_enc);
 +                      if (!context.message)
 +                              context.message = commit->buffer;
 +              }
 +              free(enc);
        }
  
        strbuf_expand(sb, format, format_commit_item, &context);
  
        if (context.message != commit->buffer)
                free(context.message);
 +      free(context.signature.gpg_output);
 +      free(context.signature.signer);
  }
  
  static void pp_header(const struct pretty_print_context *pp,
diff --combined reflog-walk.c
index 86d18843f52046d87741bffa9f865ec973a2ae73,0c904fb2d1637f63b28a44caafd3d40e437266a9..b2fbdb2392f80a531d5f9a21630a036d45c0a827
@@@ -50,13 -50,9 +50,13 @@@ static struct complete_reflogs *read_co
        for_each_reflog_ent(ref, read_one_reflog, reflogs);
        if (reflogs->nr == 0) {
                unsigned char sha1[20];
 -              const char *name = resolve_ref(ref, sha1, 1, NULL);
 -              if (name)
 +              const char *name;
 +              void *name_to_free;
 +              name = name_to_free = resolve_refdup(ref, sha1, 1, NULL);
 +              if (name) {
                        for_each_reflog_ent(name, read_one_reflog, reflogs);
 +                      free(name_to_free);
 +              }
        }
        if (reflogs->nr == 0) {
                int len = strlen(ref);
@@@ -126,7 -122,12 +126,12 @@@ static void add_commit_info(struct comm
  }
  
  struct commit_reflog {
-       int flag, recno;
+       int recno;
+       enum selector_type {
+               SELECTOR_NONE,
+               SELECTOR_INDEX,
+               SELECTOR_DATE
+       } selector;
        struct complete_reflogs *reflogs;
  };
  
@@@ -150,6 -151,7 +155,7 @@@ int add_reflog_for_walk(struct reflog_w
        struct complete_reflogs *reflogs;
        char *branch, *at = strchr(name, '@');
        struct commit_reflog *commit_reflog;
+       enum selector_type selector = SELECTOR_NONE;
  
        if (commit->object.flags & UNINTERESTING)
                die ("Cannot walk reflogs for %s", name);
                if (*ep != '}') {
                        recno = -1;
                        timestamp = approxidate(at + 2);
+                       selector = SELECTOR_DATE;
                }
+               else
+                       selector = SELECTOR_INDEX;
        } else
                recno = 0;
  
        else {
                if (*branch == '\0') {
                        unsigned char sha1[20];
 -                      const char *head = resolve_ref("HEAD", sha1, 0, NULL);
 -                      if (!head)
 -                              die ("No current branch");
                        free(branch);
 -                      branch = xstrdup(head);
 +                      branch = resolve_refdup("HEAD", sha1, 0, NULL);
 +                      if (!branch)
 +                              die ("No current branch");
 +
                }
                reflogs = read_complete_reflog(branch);
                if (!reflogs || reflogs->nr == 0) {
  
        commit_reflog = xcalloc(sizeof(struct commit_reflog), 1);
        if (recno < 0) {
-               commit_reflog->flag = 1;
                commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp);
                if (commit_reflog->recno < 0) {
                        free(branch);
                }
        } else
                commit_reflog->recno = reflogs->nr - recno - 1;
+       commit_reflog->selector = selector;
        commit_reflog->reflogs = reflogs;
  
        add_commit_info(commit, commit_reflog, &info->reflogs);
@@@ -247,7 -252,7 +256,7 @@@ void fake_reflog_parent(struct reflog_w
  
  void get_reflog_selector(struct strbuf *sb,
                         struct reflog_walk_info *reflog_info,
-                        enum date_mode dmode,
+                        enum date_mode dmode, int force_date,
                         int shorten)
  {
        struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
        }
  
        strbuf_addf(sb, "%s@{", printed_ref);
-       if (commit_reflog->flag || dmode) {
+       if (commit_reflog->selector == SELECTOR_DATE ||
+           (commit_reflog->selector == SELECTOR_NONE && force_date)) {
                info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
                strbuf_addstr(sb, show_date(info->timestamp, info->tz, dmode));
        } else {
@@@ -295,20 -301,8 +305,20 @@@ void get_reflog_message(struct strbuf *
        strbuf_add(sb, info->message, len);
  }
  
 +const char *get_reflog_ident(struct reflog_walk_info *reflog_info)
 +{
 +      struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
 +      struct reflog_info *info;
 +
 +      if (!commit_reflog)
 +              return NULL;
 +
 +      info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
 +      return info->email;
 +}
 +
  void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
-       enum date_mode dmode)
+                        enum date_mode dmode, int force_date)
  {
        if (reflog_info && reflog_info->last_commit_reflog) {
                struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
                struct strbuf selector = STRBUF_INIT;
  
                info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
-               get_reflog_selector(&selector, reflog_info, dmode, 0);
+               get_reflog_selector(&selector, reflog_info, dmode, force_date, 0);
                if (oneline) {
                        printf("%s: %s", selector.buf, info->message);
                }
diff --combined reflog-walk.h
index afb1ae3fde93a5c61def211be56b0554a25dd45e,3adccb018bd996c3222bdbc89843265d800a02b4..50265f51c56431025b665abf34ac29fdce3d302b
@@@ -11,13 -11,12 +11,13 @@@ extern int add_reflog_for_walk(struct r
  extern void fake_reflog_parent(struct reflog_walk_info *info,
                struct commit *commit);
  extern void show_reflog_message(struct reflog_walk_info *info, int,
-               enum date_mode);
+                               enum date_mode, int force_date);
  extern void get_reflog_message(struct strbuf *sb,
                struct reflog_walk_info *reflog_info);
 +extern const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
  extern void get_reflog_selector(struct strbuf *sb,
                struct reflog_walk_info *reflog_info,
-               enum date_mode dmode,
+               enum date_mode dmode, int force_date,
                int shorten);
  
  #endif