Merge branch 'el/blame-date'
authorJunio C Hamano <gitster@pobox.com>
Wed, 11 Mar 2009 20:47:12 +0000 (13:47 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 11 Mar 2009 20:47:12 +0000 (13:47 -0700)
* el/blame-date:
Make git blame's date output format configurable, like git log

1  2 
Documentation/blame-options.txt
builtin-blame.c
index df2a7c164130743b28f110bda3d323e978897bce,e6717af44ed6d8812466e17486d1884bcb7fbc1b..63fc197fbe768e39fde653a24f77e6d8f03af114
@@@ -70,11 -70,19 +70,19 @@@ of lines before or after the line give
        tree copy has the contents of the named file (specify
        `-` to make the command read from the standard input).
  
+ --date <format>::
+       The value is one of the following alternatives:
+       {relative,local,default,iso,rfc,short}. If --date is not
+       provided, the value of the blame.date config variable is
+       used. If the blame.date config variable is also not set, the
+       iso format is used. For more information, See the discussion
+       of the --date option at linkgit:git-log[1].
  -M|<num>|::
        Detect moving lines in the file as well.  When a commit
        moves a block of lines in a file (e.g. the original file
        has A and then B, and the commit changes it to B and
 -      then A), traditional 'blame' algorithm typically blames
 +      then A), the traditional 'blame' algorithm typically blames
        the lines that were moved up (i.e. B) to the parent and
        assigns blame to the lines that were moved down (i.e. A)
        to the child commit.  With this option, both groups of lines
@@@ -90,8 -98,8 +98,8 @@@ commit
        files that were modified in the same commit.  This is
        useful when you reorganize your program and move code
        around across files.  When this option is given twice,
 -      the command looks for copies from all other files in the
 -      parent for the commit that creates the file in addition.
 +      the command additionally looks for copies from all other
 +      files in the parent for the commit that creates the file.
  +
  <num> is optional but it is the lower bound on the number of
  alphanumeric characters that git must detect as moving
diff --combined builtin-blame.c
index 971126a80d3919c07b068b15540e297f64222248,0c6ad98d19cba2459169653cdfeedd1caef03fe9..2aedd17c39fdfa12b72551d61af559f534dd65a7
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Pickaxe
+  * Blame
   *
   * Copyright (c) 2006, Junio C Hamano
   */
@@@ -40,6 -40,10 +40,10 @@@ static int reverse
  static int blank_boundary;
  static int incremental;
  static int xdl_opts = XDF_NEED_MINIMAL;
+ static enum date_mode blame_date_mode = DATE_ISO8601;
+ static size_t blame_date_width;
  static struct string_list mailmap;
  
  #ifndef DEBUG
@@@ -74,7 -78,6 +78,7 @@@ static unsigned blame_copy_score
   */
  struct origin {
        int refcnt;
 +      struct origin *previous;
        struct commit *commit;
        mmfile_t file;
        unsigned char blob_sha1[20];
@@@ -116,8 -119,6 +120,8 @@@ static inline struct origin *origin_inc
  static void origin_decref(struct origin *o)
  {
        if (o && --o->refcnt <= 0) {
 +              if (o->previous)
 +                      origin_decref(o->previous);
                free(o->file.ptr);
                free(o);
        }
@@@ -1201,10 -1202,6 +1205,10 @@@ static void pass_blame(struct scoreboar
                struct origin *porigin = sg_origin[i];
                if (!porigin)
                        continue;
 +              if (!origin->previous) {
 +                      origin_incref(porigin);
 +                      origin->previous = porigin;
 +              }
                if (pass_blame_to_parent(sb, origin, porigin))
                        goto finish;
        }
@@@ -1421,39 -1418,6 +1425,39 @@@ static void write_filename_info(const c
        write_name_quoted(path, stdout, '\n');
  }
  
 +/*
 + * Porcelain/Incremental format wants to show a lot of details per
 + * commit.  Instead of repeating this every line, emit it only once,
 + * the first time each commit appears in the output.
 + */
 +static int emit_one_suspect_detail(struct origin *suspect)
 +{
 +      struct commit_info ci;
 +
 +      if (suspect->commit->object.flags & METAINFO_SHOWN)
 +              return 0;
 +
 +      suspect->commit->object.flags |= METAINFO_SHOWN;
 +      get_commit_info(suspect->commit, &ci, 1);
 +      printf("author %s\n", ci.author);
 +      printf("author-mail %s\n", ci.author_mail);
 +      printf("author-time %lu\n", ci.author_time);
 +      printf("author-tz %s\n", ci.author_tz);
 +      printf("committer %s\n", ci.committer);
 +      printf("committer-mail %s\n", ci.committer_mail);
 +      printf("committer-time %lu\n", ci.committer_time);
 +      printf("committer-tz %s\n", ci.committer_tz);
 +      printf("summary %s\n", ci.summary);
 +      if (suspect->commit->object.flags & UNINTERESTING)
 +              printf("boundary\n");
 +      if (suspect->previous) {
 +              struct origin *prev = suspect->previous;
 +              printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
 +              write_name_quoted(prev->path, stdout, '\n');
 +      }
 +      return 1;
 +}
 +
  /*
   * The blame_entry is found to be guilty for the range.  Mark it
   * as such, and show it in incremental output.
@@@ -1469,7 -1433,22 +1473,7 @@@ static void found_guilty_entry(struct b
                printf("%s %d %d %d\n",
                       sha1_to_hex(suspect->commit->object.sha1),
                       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
 -              if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
 -                      struct commit_info ci;
 -                      suspect->commit->object.flags |= METAINFO_SHOWN;
 -                      get_commit_info(suspect->commit, &ci, 1);
 -                      printf("author %s\n", ci.author);
 -                      printf("author-mail %s\n", ci.author_mail);
 -                      printf("author-time %lu\n", ci.author_time);
 -                      printf("author-tz %s\n", ci.author_tz);
 -                      printf("committer %s\n", ci.committer);
 -                      printf("committer-mail %s\n", ci.committer_mail);
 -                      printf("committer-time %lu\n", ci.committer_time);
 -                      printf("committer-tz %s\n", ci.committer_tz);
 -                      printf("summary %s\n", ci.summary);
 -                      if (suspect->commit->object.flags & UNINTERESTING)
 -                              printf("boundary\n");
 -              }
 +              emit_one_suspect_detail(suspect);
                write_filename_info(suspect->path);
                maybe_flush_or_die(stdout, "stdout");
        }
@@@ -1532,24 -1511,20 +1536,20 @@@ static const char *format_time(unsigne
                               int show_raw_time)
  {
        static char time_buf[128];
-       time_t t = time;
-       int minutes, tz;
-       struct tm *tm;
+       const char *time_str;
+       int time_len;
+       int tz;
  
        if (show_raw_time) {
                sprintf(time_buf, "%lu %s", time, tz_str);
-               return time_buf;
        }
-       tz = atoi(tz_str);
-       minutes = tz < 0 ? -tz : tz;
-       minutes = (minutes / 100)*60 + (minutes % 100);
-       minutes = tz < 0 ? -minutes : minutes;
-       t = time + minutes * 60;
-       tm = gmtime(&t);
-       strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", tm);
-       strcat(time_buf, tz_str);
+       else {
+               tz = atoi(tz_str);
+               time_str = show_date(time, tz, blame_date_mode);
+               time_len = strlen(time_str);
+               memcpy(time_buf, time_str, time_len);
+               memset(time_buf + time_len, ' ', blame_date_width - time_len);
+       }
        return time_buf;
  }
  
@@@ -1576,8 -1551,24 +1576,8 @@@ static void emit_porcelain(struct score
               ent->s_lno + 1,
               ent->lno + 1,
               ent->num_lines);
 -      if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
 -              struct commit_info ci;
 -              suspect->commit->object.flags |= METAINFO_SHOWN;
 -              get_commit_info(suspect->commit, &ci, 1);
 -              printf("author %s\n", ci.author);
 -              printf("author-mail %s\n", ci.author_mail);
 -              printf("author-time %lu\n", ci.author_time);
 -              printf("author-tz %s\n", ci.author_tz);
 -              printf("committer %s\n", ci.committer);
 -              printf("committer-mail %s\n", ci.committer_mail);
 -              printf("committer-time %lu\n", ci.committer_time);
 -              printf("committer-tz %s\n", ci.committer_tz);
 -              write_filename_info(suspect->path);
 -              printf("summary %s\n", ci.summary);
 -              if (suspect->commit->object.flags & UNINTERESTING)
 -                      printf("boundary\n");
 -      }
 -      else if (suspect->commit->object.flags & MORE_THAN_ONE_PATH)
 +      if (emit_one_suspect_detail(suspect) ||
 +          (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
                write_filename_info(suspect->path);
  
        cp = nth_line(sb, ent->lno);
@@@ -1815,6 -1806,36 +1815,6 @@@ static void sanity_check_refcnt(struct 
                        baa = 1;
                }
        }
 -      for (ent = sb->ent; ent; ent = ent->next) {
 -              /* Mark the ones that haven't been checked */
 -              if (0 < ent->suspect->refcnt)
 -                      ent->suspect->refcnt = -ent->suspect->refcnt;
 -      }
 -      for (ent = sb->ent; ent; ent = ent->next) {
 -              /*
 -               * ... then pick each and see if they have the the
 -               * correct refcnt.
 -               */
 -              int found;
 -              struct blame_entry *e;
 -              struct origin *suspect = ent->suspect;
 -
 -              if (0 < suspect->refcnt)
 -                      continue;
 -              suspect->refcnt = -suspect->refcnt; /* Unmark */
 -              for (found = 0, e = sb->ent; e; e = e->next) {
 -                      if (e->suspect != suspect)
 -                              continue;
 -                      found++;
 -              }
 -              if (suspect->refcnt != found) {
 -                      fprintf(stderr, "%s in %s has refcnt %d, not %d\n",
 -                              ent->suspect->path,
 -                              sha1_to_hex(ent->suspect->commit->object.sha1),
 -                              ent->suspect->refcnt, found);
 -                      baa = 2;
 -              }
 -      }
        if (baa) {
                int opt = 0160;
                find_alignment(sb, &opt);
@@@ -1954,6 -1975,12 +1954,12 @@@ static int git_blame_config(const char 
                blank_boundary = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "blame.date")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               blame_date_mode = parse_date_format(value);
+               return 0;
+       }
        return git_default_config(var, value, cb);
  }
  
@@@ -2218,6 -2245,8 +2224,8 @@@ int cmd_blame(int argc, const char **ar
  
        git_config(git_blame_config, NULL);
        init_revisions(&revs, NULL);
+       revs.date_mode = blame_date_mode;
        save_commit_buffer = 0;
        dashdash_pos = 0;
  
  parse_done:
        argc = parse_options_end(&ctx);
  
-       if (cmd_is_annotate)
+       if (cmd_is_annotate) {
                output_option |= OUTPUT_ANNOTATE_COMPAT;
+               blame_date_mode = DATE_ISO8601;
+       } else {
+               blame_date_mode = revs.date_mode;
+       }
+       /* The maximum width used to show the dates */
+       switch (blame_date_mode) {
+       case DATE_RFC2822:
+               blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
+               break;
+       case DATE_ISO8601:
+               blame_date_width = sizeof("2006-10-19 16:00:04 -0700");
+               break;
+       case DATE_RAW:
+               blame_date_width = sizeof("1161298804 -0700");
+               break;
+       case DATE_SHORT:
+               blame_date_width = sizeof("2006-10-19");
+               break;
+       case DATE_RELATIVE:
+               /* "normal" is used as the fallback for "relative" */
+       case DATE_LOCAL:
+       case DATE_NORMAL:
+               blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
+               break;
+       }
+       blame_date_width -= 1; /* strip the null */
  
        if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
                opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |