Merge branch 'kb/blame-author-email'
authorJunio C Hamano <gitster@pobox.com>
Tue, 30 Nov 2010 01:52:32 +0000 (17:52 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 30 Nov 2010 01:52:32 +0000 (17:52 -0800)
* kb/blame-author-email:
blame: Add option to show author email instead of name

Conflicts:
t/annotate-tests.sh

1  2 
builtin/blame.c
t/annotate-tests.sh
diff --combined builtin/blame.c
index f5fccc1f6767d7386e37f7273c560d0d4491ee91,df45e5cf9720df92df06784c8a84699fd1ec1c7f..cb25ec9ce882ac2b975420bb25735fbd156dc4ab
@@@ -83,7 -83,6 +83,7 @@@ struct origin 
        struct commit *commit;
        mmfile_t file;
        unsigned char blob_sha1[20];
 +      unsigned mode;
        char path[FLEX_ARRAY];
  };
  
@@@ -93,7 -92,6 +93,7 @@@
   * Return 1 if the conversion succeeds, 0 otherwise.
   */
  int textconv_object(const char *path,
 +                  unsigned mode,
                    const unsigned char *sha1,
                    char **buf,
                    unsigned long *buf_size)
        struct userdiff_driver *textconv;
  
        df = alloc_filespec(path);
 -      fill_filespec(df, sha1, S_IFREG | 0664);
 +      fill_filespec(df, sha1, mode);
        textconv = get_textconv(df);
        if (!textconv) {
                free_filespec(df);
@@@ -127,7 -125,7 +127,7 @@@ static void fill_origin_blob(struct dif
  
                num_read_blob++;
                if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 -                  textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size))
 +                  textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
                        ;
                else
                        file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@@@ -315,23 -313,21 +315,23 @@@ static struct origin *get_origin(struc
   * for an origin is also used to pass the blame for the entire file to
   * the parent to detect the case where a child's blob is identical to
   * that of its parent's.
 + *
 + * This also fills origin->mode for corresponding tree path.
   */
 -static int fill_blob_sha1(struct origin *origin)
 +static int fill_blob_sha1_and_mode(struct origin *origin)
  {
 -      unsigned mode;
        if (!is_null_sha1(origin->blob_sha1))
                return 0;
        if (get_tree_entry(origin->commit->object.sha1,
                           origin->path,
 -                         origin->blob_sha1, &mode))
 +                         origin->blob_sha1, &origin->mode))
                goto error_out;
        if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
                goto error_out;
        return 0;
   error_out:
        hashclr(origin->blob_sha1);
 +      origin->mode = S_IFINVALID;
        return -1;
  }
  
@@@ -364,14 -360,12 +364,14 @@@ static struct origin *find_origin(struc
                        /*
                         * If the origin was newly created (i.e. get_origin
                         * would call make_origin if none is found in the
 -                       * scoreboard), it does not know the blob_sha1,
 +                       * scoreboard), it does not know the blob_sha1/mode,
                         * so copy it.  Otherwise porigin was in the
 -                       * scoreboard and already knows blob_sha1.
 +                       * scoreboard and already knows blob_sha1/mode.
                         */
 -                      if (porigin->refcnt == 1)
 +                      if (porigin->refcnt == 1) {
                                hashcpy(porigin->blob_sha1, cached->blob_sha1);
 +                              porigin->mode = cached->mode;
 +                      }
                        return porigin;
                }
                /* otherwise it was not very useful; free it */
                /* The path is the same as parent */
                porigin = get_origin(sb, parent, origin->path);
                hashcpy(porigin->blob_sha1, origin->blob_sha1);
 +              porigin->mode = origin->mode;
        } else {
                /*
                 * Since origin->path is a pathspec, if the parent
                case 'M':
                        porigin = get_origin(sb, parent, origin->path);
                        hashcpy(porigin->blob_sha1, p->one->sha1);
 +                      porigin->mode = p->one->mode;
                        break;
                case 'A':
                case 'T':
  
                cached = make_origin(porigin->commit, porigin->path);
                hashcpy(cached->blob_sha1, porigin->blob_sha1);
 +              cached->mode = porigin->mode;
                parent->util = cached;
        }
        return porigin;
@@@ -495,7 -486,6 +495,7 @@@ static struct origin *find_rename(struc
                    !strcmp(p->two->path, origin->path)) {
                        porigin = get_origin(sb, parent, p->one->path);
                        hashcpy(porigin->blob_sha1, p->one->sha1);
 +                      porigin->mode = p->one->mode;
                        break;
                }
        }
@@@ -1109,7 -1099,6 +1109,7 @@@ static int find_copy_in_parent(struct s
  
                        norigin = get_origin(sb, parent, p->one->path);
                        hashcpy(norigin->blob_sha1, p->one->sha1);
 +                      norigin->mode = p->one->mode;
                        fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
                        if (!file_p.ptr)
                                continue;
@@@ -1617,6 -1606,7 +1617,7 @@@ static const char *format_time(unsigne
  #define OUTPUT_SHOW_NUMBER    040
  #define OUTPUT_SHOW_SCORE      0100
  #define OUTPUT_NO_AUTHOR       0200
+ #define OUTPUT_SHOW_EMAIL     0400
  
  static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
  {
@@@ -1682,12 -1672,17 +1683,17 @@@ static void emit_other(struct scoreboar
                }
  
                printf("%.*s", length, hex);
-               if (opt & OUTPUT_ANNOTATE_COMPAT)
-                       printf("\t(%10s\t%10s\t%d)", ci.author,
+               if (opt & OUTPUT_ANNOTATE_COMPAT) {
+                       const char *name;
+                       if (opt & OUTPUT_SHOW_EMAIL)
+                               name = ci.author_mail;
+                       else
+                               name = ci.author;
+                       printf("\t(%10s\t%10s\t%d)", name,
                               format_time(ci.author_time, ci.author_tz,
                                           show_raw_time),
                               ent->lno + 1 + cnt);
-               else {
+               else {
                        if (opt & OUTPUT_SHOW_SCORE)
                                printf(" %*d %02d",
                                       max_score_digits, ent->score,
                                       ent->s_lno + 1 + cnt);
  
                        if (!(opt & OUTPUT_NO_AUTHOR)) {
-                               int pad = longest_author - utf8_strwidth(ci.author);
+                               const char *name;
+                               int pad;
+                               if (opt & OUTPUT_SHOW_EMAIL)
+                                       name = ci.author_mail;
+                               else
+                                       name = ci.author;
+                               pad = longest_author - utf8_strwidth(name);
                                printf(" (%s%*s %10s",
-                                      ci.author, pad, "",
+                                      name, pad, "",
                                       format_time(ci.author_time,
                                                   ci.author_tz,
                                                   show_raw_time));
@@@ -1840,7 -1841,10 +1852,10 @@@ static void find_alignment(struct score
                if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
                        suspect->commit->object.flags |= METAINFO_SHOWN;
                        get_commit_info(suspect->commit, &ci, 1);
-                       num = utf8_strwidth(ci.author);
+                       if (*option & OUTPUT_SHOW_EMAIL)
+                               num = utf8_strwidth(ci.author_mail);
+                       else
+                               num = utf8_strwidth(ci.author);
                        if (longest_author < num)
                                longest_author = num;
                }
@@@ -2086,7 -2090,7 +2101,7 @@@ static struct commit *fake_working_tree
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 -                          textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
 +                          textconv_object(read_from, mode, null_sha1, &buf.buf, &buf_len))
                                buf.len = buf_len;
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
@@@ -2289,6 -2293,7 +2304,7 @@@ int cmd_blame(int argc, const char **ar
                OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
                OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
                OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR),
+               OPT_BIT('e', "show-email", &output_option, "Show author email instead of name (Default: off)", OUTPUT_SHOW_EMAIL),
                OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE),
                OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"),
                OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"),
@@@ -2466,11 -2471,11 +2482,11 @@@ parse_done
        }
        else {
                o = get_origin(&sb, sb.final, path);
 -              if (fill_blob_sha1(o))
 +              if (fill_blob_sha1_and_mode(o))
                        die("no such path %s in %s", path, final_commit_name);
  
                if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
 -                  textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
 +                  textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
                                    &sb.final_buf_size))
                        ;
                else
diff --combined t/annotate-tests.sh
index 212bd605b5193d26c38efda3bb2ed9fd95b8b462,0eeeda3f3b164b86bb3d44693669d20c360565ca..d34208cc27623a6105f6b12f863c648429d2d34d
@@@ -8,27 -8,27 +8,27 @@@ check_count () 
        $PROG file $head >.result || return 1
        cat .result | perl -e '
                my %expect = (@ARGV);
 -              my %count = ();
 +              my %count = map { $_ => 0 } keys %expect;
                while (<STDIN>) {
                        if (/^[0-9a-f]+\t\(([^\t]+)\t/) {
                                my $author = $1;
                                for ($author) { s/^\s*//; s/\s*$//; }
 -                              if (exists $expect{$author}) {
 -                                      $count{$author}++;
 -                              }
 +                              $count{$author}++;
                        }
                }
                my $bad = 0;
                while (my ($author, $count) = each %count) {
                        my $ok;
 -                      if ($expect{$author} != $count) {
 +                      my $value = 0;
 +                      $value = $expect{$author} if defined $expect{$author};
 +                      if ($value != $count) {
                                $bad = 1;
                                $ok = "bad";
                        }
                        else {
                                $ok = "good";
                        }
 -                      print STDERR "Author $author (expected $expect{$author}, attributed $count) $ok\n";
 +                      print STDERR "Author $author (expected $value, attributed $count) $ok\n";
                }
                exit($bad);
        ' "$@"
@@@ -38,8 -38,8 +38,8 @@@ test_expect_success 
      'prepare reference tree' \
      'echo "1A quick brown fox jumps over the" >file &&
       echo "lazy dog" >>file &&
 -     git add file
 +     git add file &&
-      GIT_AUTHOR_NAME="A" git commit -a -m "Initial."'
+      GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" git commit -a -m "Initial."'
  
  test_expect_success \
      'check all lines blamed on A' \
@@@ -49,7 -49,7 +49,7 @@@ test_expect_success 
      'Setup new lines blamed on B' \
      'echo "2A quick brown fox jumps over the" >>file &&
       echo "lazy dog" >> file &&
-      GIT_AUTHOR_NAME="B" git commit -a -m "Second."'
+      GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" git commit -a -m "Second."'
  
  test_expect_success \
      'Two lines blamed on A, two on B' \
@@@ -60,7 -60,7 +60,7 @@@ test_expect_success 
      'git checkout -b branch1 master &&
       echo "3A slow green fox jumps into the" >> file &&
       echo "well." >> file &&
-      GIT_AUTHOR_NAME="B1" git commit -a -m "Branch1-1"'
+      GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" git commit -a -m "Branch1-1"'
  
  test_expect_success \
      'Two lines blamed on A, two on B, two on B1' \
@@@ -71,7 -71,7 +71,7 @@@ test_expect_success 
      'git checkout -b branch2 master &&
       sed -e "s/2A quick brown/4A quick brown lazy dog/" < file > file.new &&
       mv file.new file &&
-      GIT_AUTHOR_NAME="B2" git commit -a -m "Branch2-1"'
+      GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" git commit -a -m "Branch2-1"'
  
  test_expect_success \
      'Two lines blamed on A, one on B, one on B2' \
@@@ -105,7 -105,7 +105,7 @@@ test_expect_success 
  test_expect_success \
      'an incomplete line added' \
      'echo "incomplete" | tr -d "\\012" >>file &&
-     GIT_AUTHOR_NAME="C" git commit -a -m "Incomplete"'
+     GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" git commit -a -m "Incomplete"'
  
  test_expect_success \
      'With incomplete lines.' \
@@@ -119,7 -119,7 +119,7 @@@ test_expect_success 
        echo
      } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" > file &&
      echo "incomplete" | tr -d "\\012" >>file &&
-     GIT_AUTHOR_NAME="D" git commit -a -m "edit"'
+     GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" git commit -a -m "edit"'
  
  test_expect_success \
      'some edit' \