Code

Merge branch 'ss/blame-textconv-fake-working-tree'
authorJunio C Hamano <gitster@pobox.com>
Tue, 1 Nov 2011 22:20:28 +0000 (15:20 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 1 Nov 2011 22:20:28 +0000 (15:20 -0700)
* ss/blame-textconv-fake-working-tree:
  (squash) test for previous
  blame.c: Properly initialize strbuf after calling, textconv_object()

Conflicts:
t/t8006-blame-textconv.sh

1  2 
builtin/blame.c
t/t8006-blame-textconv.sh

diff --combined builtin/blame.c
index 26a5d424b8ceb0fd403a492e46e3637fd35068ba,e39d9865e2d3f8db10ef2f43b9a2d07ad0b61df9..86c0537cbb50c0e5d6169d6d316ac37b5cc5b856
@@@ -41,7 -41,6 +41,7 @@@ static int reverse
  static int blank_boundary;
  static int incremental;
  static int xdl_opts;
 +static int abbrev = -1;
  
  static enum date_mode blame_date_mode = DATE_ISO8601;
  static size_t blame_date_width;
@@@ -1313,7 -1312,8 +1313,7 @@@ static void pass_blame(struct scoreboar
  /*
   * Information on commits, used for output.
   */
 -struct commit_info
 -{
 +struct commit_info {
        const char *author;
        const char *author_mail;
        unsigned long author_time;
@@@ -1378,7 -1378,7 +1378,7 @@@ static void get_ac_line(const char *inb
        timepos = tmp;
  
        *tmp = 0;
 -      while (person < tmp && *tmp != ' ')
 +      while (person < tmp && !(*tmp == ' ' && tmp[1] == '<'))
                tmp--;
        if (tmp <= person)
                return;
@@@ -1418,8 -1418,7 +1418,8 @@@ static void get_commit_info(struct comm
                            int detailed)
  {
        int len;
 -      char *tmp, *endp, *reencoded, *message;
 +      const char *subject;
 +      char *reencoded, *message;
        static char author_name[1024];
        static char author_mail[1024];
        static char committer_name[1024];
                    &ret->committer_time, &ret->committer_tz);
  
        ret->summary = summary_buf;
 -      tmp = strstr(message, "\n\n");
 -      if (!tmp) {
 -      error_out:
 +      len = find_commit_subject(message, &subject);
 +      if (len && len < sizeof(summary_buf)) {
 +              memcpy(summary_buf, subject, len);
 +              summary_buf[len] = 0;
 +      } else {
                sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
 -              free(reencoded);
 -              return;
        }
 -      tmp += 2;
 -      endp = strchr(tmp, '\n');
 -      if (!endp)
 -              endp = tmp + strlen(tmp);
 -      len = endp - tmp;
 -      if (len >= sizeof(summary_buf) || len == 0)
 -              goto error_out;
 -      memcpy(summary_buf, tmp, len);
 -      summary_buf[len] = 0;
        free(reencoded);
  }
  
@@@ -1484,14 -1492,13 +1484,14 @@@ static void write_filename_info(const c
  /*
   * 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.
 + * the first time each commit appears in the output (unless the
 + * user has specifically asked for us to repeat).
   */
 -static int emit_one_suspect_detail(struct origin *suspect)
 +static int emit_one_suspect_detail(struct origin *suspect, int repeat)
  {
        struct commit_info ci;
  
 -      if (suspect->commit->object.flags & METAINFO_SHOWN)
 +      if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
                return 0;
  
        suspect->commit->object.flags |= METAINFO_SHOWN;
@@@ -1530,7 -1537,7 +1530,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);
 -              emit_one_suspect_detail(suspect);
 +              emit_one_suspect_detail(suspect, 0);
                write_filename_info(suspect->path);
                maybe_flush_or_die(stdout, "stdout");
        }
@@@ -1618,20 -1625,9 +1618,20 @@@ 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
 +#define OUTPUT_LINE_PORCELAIN 01000
 +
 +static void emit_porcelain_details(struct origin *suspect, int repeat)
 +{
 +      if (emit_one_suspect_detail(suspect, repeat) ||
 +          (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
 +              write_filename_info(suspect->path);
 +}
  
 -static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
 +static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
 +                         int opt)
  {
 +      int repeat = opt & OUTPUT_LINE_PORCELAIN;
        int cnt;
        const char *cp;
        struct origin *suspect = ent->suspect;
               ent->s_lno + 1,
               ent->lno + 1,
               ent->num_lines);
 -      if (emit_one_suspect_detail(suspect) ||
 -          (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
 -              write_filename_info(suspect->path);
 +      emit_porcelain_details(suspect, repeat);
  
        cp = nth_line(sb, ent->lno);
        for (cnt = 0; cnt < ent->num_lines; cnt++) {
                char ch;
 -              if (cnt)
 +              if (cnt) {
                        printf("%s %d %d\n", hex,
                               ent->s_lno + 1 + cnt,
                               ent->lno + 1 + cnt);
 +                      if (repeat)
 +                              emit_porcelain_details(suspect, 1);
 +              }
                putchar('\t');
                do {
                        ch = *cp++;
@@@ -1683,7 -1678,7 +1683,7 @@@ static void emit_other(struct scoreboar
        cp = nth_line(sb, ent->lno);
        for (cnt = 0; cnt < ent->num_lines; cnt++) {
                char ch;
 -              int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;
 +              int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : abbrev;
  
                if (suspect->commit->object.flags & UNINTERESTING) {
                        if (blank_boundary)
                }
  
                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));
@@@ -1768,7 -1752,7 +1768,7 @@@ static void output(struct scoreboard *s
  
        for (ent = sb->ent; ent; ent = ent->next) {
                if (option & OUTPUT_PORCELAIN)
 -                      emit_porcelain(sb, ent);
 +                      emit_porcelain(sb, ent, option);
                else {
                        emit_other(sb, ent, option);
                }
@@@ -1864,10 -1848,7 +1864,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;
                }
@@@ -2113,8 -2094,10 +2113,10 @@@ 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, mode, null_sha1, &buf.buf, &buf_len))
+                           textconv_object(read_from, mode, null_sha1, &buf.buf, &buf_len)) {
+                               buf.alloc = 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);
                        break;
@@@ -2312,19 -2295,16 +2314,19 @@@ int cmd_blame(int argc, const char **ar
                OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
                OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
                OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
 +              OPT_BIT(0, "line-porcelain", &output_option, "Show porcelain format with per-line commit information", OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
                OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
                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"),
                { OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
                { OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback },
                OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback),
 +              OPT__ABBREV(&abbrev),
                OPT_END()
        };
  
        save_commit_buffer = 0;
        dashdash_pos = 0;
  
 -      parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
 -                          PARSE_OPT_KEEP_ARGV0);
 +      parse_options_start(&ctx, argc, argv, prefix, options,
 +                          PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
        for (;;) {
                switch (parse_options_step(&ctx, options, blame_opt_usage)) {
                case PARSE_OPT_HELP:
  parse_done:
        argc = parse_options_end(&ctx);
  
 +      if (abbrev == -1)
 +              abbrev = default_abbrev;
 +      /* one more abbrev length is needed for the boundary commit */
 +      abbrev++;
 +
        if (revs_file && read_ancestry(revs_file))
                die_errno("reading graft file '%s' failed", revs_file);
  
index 32ec82ad678d56bbf27f525fc8588b3391d9117d,53905a222738d9fef0cdc79108a241ec3ee5a042..4ee42f12f0af6bc7e4b072350f88988b85e40cbb
@@@ -15,6 -15,7 +15,7 @@@ EO
  chmod +x helper
  
  test_expect_success 'setup ' '
+       echo "bin: test number 0" >zero.bin &&
        echo "bin: test 1" >one.bin &&
        echo "bin: test number 2" >two.bin &&
        if test_have_prereq SYMLINKS; then
@@@ -25,8 -26,7 +26,8 @@@
        echo "bin: test 1 version 2" >one.bin &&
        echo "bin: test number 2 version 2" >>two.bin &&
        if test_have_prereq SYMLINKS; then
 -              ln -sf two.bin symlink.bin
 +              rm symlink.bin &&
 +              ln -s two.bin symlink.bin
        fi &&
        GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
  '
@@@ -43,6 -43,7 +44,7 @@@ test_expect_success 'no filter specifie
  
  test_expect_success 'setup textconv filters' '
        echo "*.bin diff=test" >.gitattributes &&
+       echo "zero.bin eol=crlf" >>.gitattributes &&
        git config diff.test.textconv ./helper &&
        git config diff.test.cachetextconv false
  '
@@@ -74,27 -75,15 +76,36 @@@ test_expect_success 'blame --textconv g
        test_cmp expected result
  '
  
+ test_expect_success 'blame --textconv with local changes' '
+       test_when_finished "git checkout zero.bin" &&
+       printf "bin: updated number 0\015" >zero.bin &&
+       git blame --textconv zero.bin >blame &&
+       expect="(Not Committed Yet ....-..-.. ..:..:.. +0000 1)" &&
+       expect="$expect converted: updated number 0" &&
+       expr "$(find_blame <blame)" : "^$expect"
+ '
 +test_expect_success 'setup +cachetextconv' '
 +      git config diff.test.cachetextconv true
 +'
 +
 +cat >expected_one <<EOF
 +(Number2 2010-01-01 20:00:00 +0000 1) converted: test 1 version 2
 +EOF
 +
 +test_expect_success 'blame --textconv works with textconvcache' '
 +      git blame --textconv two.bin >blame &&
 +      find_blame <blame >result &&
 +      test_cmp expected result &&
 +      git blame --textconv one.bin >blame &&
 +      find_blame  <blame >result &&
 +      test_cmp expected_one result
 +'
 +
 +test_expect_success 'setup -cachetextconv' '
 +      git config diff.test.cachetextconv false
 +'
 +
  test_expect_success 'make a new commit' '
        echo "bin: test number 2 version 3" >>two.bin &&
        GIT_AUTHOR_NAME=Number3 git commit -a -m Third --date="2010-01-01 22:00:00"