Merge branch 'jc/maint-1.6.0-blame-s' into maint
authorJunio C Hamano <gitster@pobox.com>
Thu, 2 Apr 2009 19:14:37 +0000 (12:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 2 Apr 2009 19:14:37 +0000 (12:14 -0700)
* jc/maint-1.6.0-blame-s:
blame: read custom grafts given by -S before calling setup_revisions()

Conflicts:
builtin-blame.c

1  2 
builtin-blame.c
diff --combined builtin-blame.c
index 114a214ed3fef40ae5cc13737d037f29d6f8acfd,0c241a9ec803a95ff85cf98f2714c74a6503b976..1ead9b48308feacfe253be060c32ec0210475a3d
@@@ -19,7 -19,6 +19,7 @@@
  #include "string-list.h"
  #include "mailmap.h"
  #include "parse-options.h"
 +#include "utf8.h"
  
  static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
  
@@@ -39,6 -38,7 +39,6 @@@ static int show_root
  static int reverse;
  static int blank_boundary;
  static int incremental;
 -static int cmd_is_annotate;
  static int xdl_opts = XDF_NEED_MINIMAL;
  static struct string_list mailmap;
  
@@@ -443,6 -443,135 +443,6 @@@ static struct origin *find_rename(struc
        return porigin;
  }
  
 -/*
 - * Parsing of patch chunks...
 - */
 -struct chunk {
 -      /* line number in postimage; up to but not including this
 -       * line is the same as preimage
 -       */
 -      int same;
 -
 -      /* preimage line number after this chunk */
 -      int p_next;
 -
 -      /* postimage line number after this chunk */
 -      int t_next;
 -};
 -
 -struct patch {
 -      struct chunk *chunks;
 -      int num;
 -};
 -
 -struct blame_diff_state {
 -      struct xdiff_emit_state xm;
 -      struct patch *ret;
 -      unsigned hunk_post_context;
 -      unsigned hunk_in_pre_context : 1;
 -};
 -
 -static void process_u_diff(void *state_, char *line, unsigned long len)
 -{
 -      struct blame_diff_state *state = state_;
 -      struct chunk *chunk;
 -      int off1, off2, len1, len2, num;
 -
 -      num = state->ret->num;
 -      if (len < 4 || line[0] != '@' || line[1] != '@') {
 -              if (state->hunk_in_pre_context && line[0] == ' ')
 -                      state->ret->chunks[num - 1].same++;
 -              else {
 -                      state->hunk_in_pre_context = 0;
 -                      if (line[0] == ' ')
 -                              state->hunk_post_context++;
 -                      else
 -                              state->hunk_post_context = 0;
 -              }
 -              return;
 -      }
 -
 -      if (num && state->hunk_post_context) {
 -              chunk = &state->ret->chunks[num - 1];
 -              chunk->p_next -= state->hunk_post_context;
 -              chunk->t_next -= state->hunk_post_context;
 -      }
 -      state->ret->num = ++num;
 -      state->ret->chunks = xrealloc(state->ret->chunks,
 -                                    sizeof(struct chunk) * num);
 -      chunk = &state->ret->chunks[num - 1];
 -      if (parse_hunk_header(line, len, &off1, &len1, &off2, &len2)) {
 -              state->ret->num--;
 -              return;
 -      }
 -
 -      /* Line numbers in patch output are one based. */
 -      off1--;
 -      off2--;
 -
 -      chunk->same = len2 ? off2 : (off2 + 1);
 -
 -      chunk->p_next = off1 + (len1 ? len1 : 1);
 -      chunk->t_next = chunk->same + len2;
 -      state->hunk_in_pre_context = 1;
 -      state->hunk_post_context = 0;
 -}
 -
 -static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o,
 -                                  int context)
 -{
 -      struct blame_diff_state state;
 -      xpparam_t xpp;
 -      xdemitconf_t xecfg;
 -      xdemitcb_t ecb;
 -
 -      xpp.flags = xdl_opts;
 -      memset(&xecfg, 0, sizeof(xecfg));
 -      xecfg.ctxlen = context;
 -      ecb.outf = xdiff_outf;
 -      ecb.priv = &state;
 -      memset(&state, 0, sizeof(state));
 -      state.xm.consume = process_u_diff;
 -      state.ret = xmalloc(sizeof(struct patch));
 -      state.ret->chunks = NULL;
 -      state.ret->num = 0;
 -
 -      xdi_diff(file_p, file_o, &xpp, &xecfg, &ecb);
 -
 -      if (state.ret->num) {
 -              struct chunk *chunk;
 -              chunk = &state.ret->chunks[state.ret->num - 1];
 -              chunk->p_next -= state.hunk_post_context;
 -              chunk->t_next -= state.hunk_post_context;
 -      }
 -      return state.ret;
 -}
 -
 -/*
 - * Run diff between two origins and grab the patch output, so that
 - * we can pass blame for lines origin is currently suspected for
 - * to its parent.
 - */
 -static struct patch *get_patch(struct origin *parent, struct origin *origin)
 -{
 -      mmfile_t file_p, file_o;
 -      struct patch *patch;
 -
 -      fill_origin_blob(parent, &file_p);
 -      fill_origin_blob(origin, &file_o);
 -      if (!file_p.ptr || !file_o.ptr)
 -              return NULL;
 -      patch = compare_buffer(&file_p, &file_o, 0);
 -      num_get_patch++;
 -      return patch;
 -}
 -
 -static void free_patch(struct patch *p)
 -{
 -      free(p->chunks);
 -      free(p);
 -}
 -
  /*
   * Link in a new blame entry to the scoreboard.  Entries that cover the
   * same line range have been removed from the scoreboard previously.
@@@ -689,22 -818,6 +689,22 @@@ static void blame_chunk(struct scoreboa
        }
  }
  
 +struct blame_chunk_cb_data {
 +      struct scoreboard *sb;
 +      struct origin *target;
 +      struct origin *parent;
 +      long plno;
 +      long tlno;
 +};
 +
 +static void blame_chunk_cb(void *data, long same, long p_next, long t_next)
 +{
 +      struct blame_chunk_cb_data *d = data;
 +      blame_chunk(d->sb, d->tlno, d->plno, same, d->target, d->parent);
 +      d->plno = p_next;
 +      d->tlno = t_next;
 +}
 +
  /*
   * We are looking at the origin 'target' and aiming to pass blame
   * for the lines it is suspected to its parent.  Run diff to find
@@@ -714,28 -827,26 +714,28 @@@ static int pass_blame_to_parent(struct 
                                struct origin *target,
                                struct origin *parent)
  {
 -      int i, last_in_target, plno, tlno;
 -      struct patch *patch;
 +      int last_in_target;
 +      mmfile_t file_p, file_o;
 +      struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
 +      xpparam_t xpp;
 +      xdemitconf_t xecfg;
  
        last_in_target = find_last_in_target(sb, target);
        if (last_in_target < 0)
                return 1; /* nothing remains for this target */
  
 -      patch = get_patch(parent, target);
 -      plno = tlno = 0;
 -      for (i = 0; i < patch->num; i++) {
 -              struct chunk *chunk = &patch->chunks[i];
 +      fill_origin_blob(parent, &file_p);
 +      fill_origin_blob(target, &file_o);
 +      num_get_patch++;
  
 -              blame_chunk(sb, tlno, plno, chunk->same, target, parent);
 -              plno = chunk->p_next;
 -              tlno = chunk->t_next;
 -      }
 +      memset(&xpp, 0, sizeof(xpp));
 +      xpp.flags = xdl_opts;
 +      memset(&xecfg, 0, sizeof(xecfg));
 +      xecfg.ctxlen = 0;
 +      xdi_diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, &xpp, &xecfg);
        /* The rest (i.e. anything after tlno) are the same as the parent */
 -      blame_chunk(sb, tlno, plno, last_in_target, target, parent);
 +      blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
  
 -      free_patch(patch);
        return 0;
  }
  
@@@ -827,23 -938,6 +827,23 @@@ static void handle_split(struct scorebo
        }
  }
  
 +struct handle_split_cb_data {
 +      struct scoreboard *sb;
 +      struct blame_entry *ent;
 +      struct origin *parent;
 +      struct blame_entry *split;
 +      long plno;
 +      long tlno;
 +};
 +
 +static void handle_split_cb(void *data, long same, long p_next, long t_next)
 +{
 +      struct handle_split_cb_data *d = data;
 +      handle_split(d->sb, d->ent, d->tlno, d->plno, same, d->parent, d->split);
 +      d->plno = p_next;
 +      d->tlno = t_next;
 +}
 +
  /*
   * Find the lines from parent that are the same as ent so that
   * we can pass blames to it.  file_p has the blob contents for
@@@ -858,9 -952,8 +858,9 @@@ static void find_copy_in_blob(struct sc
        const char *cp;
        int cnt;
        mmfile_t file_o;
 -      struct patch *patch;
 -      int i, plno, tlno;
 +      struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
 +      xpparam_t xpp;
 +      xdemitconf_t xecfg;
  
        /*
         * Prepare mmfile that contains only the lines in ent.
        }
        file_o.size = cp - file_o.ptr;
  
 -      patch = compare_buffer(file_p, &file_o, 1);
 -
        /*
         * file_o is a part of final image we are annotating.
         * file_p partially may match that image.
         */
 +      memset(&xpp, 0, sizeof(xpp));
 +      xpp.flags = xdl_opts;
 +      memset(&xecfg, 0, sizeof(xecfg));
 +      xecfg.ctxlen = 1;
        memset(split, 0, sizeof(struct blame_entry [3]));
 -      plno = tlno = 0;
 -      for (i = 0; i < patch->num; i++) {
 -              struct chunk *chunk = &patch->chunks[i];
 -
 -              handle_split(sb, ent, tlno, plno, chunk->same, parent, split);
 -              plno = chunk->p_next;
 -              tlno = chunk->t_next;
 -      }
 +      xdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg);
        /* remainder, if any, all match the preimage */
 -      handle_split(sb, ent, tlno, plno, ent->num_lines, parent, split);
 -      free_patch(patch);
 +      handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
  }
  
  /*
@@@ -1038,8 -1137,6 +1038,8 @@@ static int find_copy_in_parent(struct s
  
                        if (!DIFF_FILE_VALID(p->one))
                                continue; /* does not exist in parent */
 +                      if (S_ISGITLINK(p->one->mode))
 +                              continue; /* ignore git links */
                        if (porigin && !strcmp(p->one->path, porigin->path))
                                /* find_move already dealt with this path */
                                continue;
@@@ -1264,12 -1361,11 +1264,12 @@@ struct commit_inf
   * Parse author/committer line in the commit object buffer
   */
  static void get_ac_line(const char *inbuf, const char *what,
 -                      int bufsz, char *person, const char **mail,
 +                      int person_len, char *person,
 +                      int mail_len, char *mail,
                        unsigned long *time, const char **tz)
  {
        int len, tzlen, maillen;
 -      char *tmp, *endp, *timepos;
 +      char *tmp, *endp, *timepos, *mailpos;
  
        tmp = strstr(inbuf, what);
        if (!tmp)
                len = strlen(tmp);
        else
                len = endp - tmp;
 -      if (bufsz <= len) {
 +      if (person_len <= len) {
        error_out:
                /* Ugh */
 -              *mail = *tz = "(unknown)";
 +              *tz = "(unknown)";
 +              strcpy(mail, *tz);
                *time = 0;
                return;
        }
        *tmp = 0;
        while (*tmp != ' ')
                tmp--;
 -      *mail = tmp + 1;
 +      mailpos = tmp + 1;
        *tmp = 0;
        maillen = timepos - tmp;
 +      memcpy(mail, mailpos, maillen);
  
        if (!mailmap.nr)
                return;
         * mailmap expansion may make the name longer.
         * make room by pushing stuff down.
         */
 -      tmp = person + bufsz - (tzlen + 1);
 +      tmp = person + person_len - (tzlen + 1);
        memmove(tmp, *tz, tzlen);
        tmp[tzlen] = 0;
        *tz = tmp;
  
 -      tmp = tmp - (maillen + 1);
 -      memmove(tmp, *mail, maillen);
 -      tmp[maillen] = 0;
 -      *mail = tmp;
 -
        /*
 -       * Now, convert e-mail using mailmap
 +       * Now, convert both name and e-mail using mailmap
         */
 -      map_email(&mailmap, tmp + 1, person, tmp-person-1);
 +      if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
 +              /* Add a trailing '>' to email, since map_user returns plain emails
 +                 Note: It already has '<', since we replace from mail+1 */
 +              mailpos = memchr(mail, '\0', mail_len);
 +              if (mailpos && mailpos-mail < mail_len - 1) {
 +                      *mailpos = '>';
 +                      *(mailpos+1) = '\0';
 +              }
 +      }
  }
  
  static void get_commit_info(struct commit *commit,
                            int detailed)
  {
        int len;
 -      char *tmp, *endp;
 -      static char author_buf[1024];
 -      static char committer_buf[1024];
 +      char *tmp, *endp, *reencoded, *message;
 +      static char author_name[1024];
 +      static char author_mail[1024];
 +      static char committer_name[1024];
 +      static char committer_mail[1024];
        static char summary_buf[1024];
  
        /*
                        die("Cannot read commit %s",
                            sha1_to_hex(commit->object.sha1));
        }
 -      ret->author = author_buf;
 -      get_ac_line(commit->buffer, "\nauthor ",
 -                  sizeof(author_buf), author_buf, &ret->author_mail,
 +      reencoded = reencode_commit_message(commit, NULL);
 +      message   = reencoded ? reencoded : commit->buffer;
 +      ret->author = author_name;
 +      ret->author_mail = author_mail;
 +      get_ac_line(message, "\nauthor ",
 +                  sizeof(author_name), author_name,
 +                  sizeof(author_mail), author_mail,
                    &ret->author_time, &ret->author_tz);
  
 -      if (!detailed)
 +      if (!detailed) {
 +              free(reencoded);
                return;
 +      }
  
 -      ret->committer = committer_buf;
 -      get_ac_line(commit->buffer, "\ncommitter ",
 -                  sizeof(committer_buf), committer_buf, &ret->committer_mail,
 +      ret->committer = committer_name;
 +      ret->committer_mail = committer_mail;
 +      get_ac_line(message, "\ncommitter ",
 +                  sizeof(committer_name), committer_name,
 +                  sizeof(committer_mail), committer_mail,
                    &ret->committer_time, &ret->committer_tz);
  
        ret->summary = summary_buf;
 -      tmp = strstr(commit->buffer, "\n\n");
 +      tmp = strstr(message, "\n\n");
        if (!tmp) {
        error_out:
                sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
 +              free(reencoded);
                return;
        }
        tmp += 2;
                goto error_out;
        memcpy(summary_buf, tmp, len);
        summary_buf[len] = 0;
 +      free(reencoded);
  }
  
  /*
@@@ -1607,7 -1686,7 +1607,7 @@@ static void emit_other(struct scoreboar
                if (suspect->commit->object.flags & UNINTERESTING) {
                        if (blank_boundary)
                                memset(hex, ' ', length);
 -                      else if (!cmd_is_annotate) {
 +                      else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {
                                length--;
                                putchar('^');
                        }
                                printf(" %*d", max_orig_digits,
                                       ent->s_lno + 1 + cnt);
  
 -                      if (!(opt & OUTPUT_NO_AUTHOR))
 -                              printf(" (%-*.*s %10s",
 -                                     longest_author, longest_author,
 -                                     ci.author,
 +                      if (!(opt & OUTPUT_NO_AUTHOR)) {
 +                              int pad = longest_author - utf8_strwidth(ci.author);
 +                              printf(" (%s%*s %10s",
 +                                     ci.author, pad, "",
                                       format_time(ci.author_time,
                                                   ci.author_tz,
                                                   show_raw_time));
 +                      }
                        printf(" %*d) ",
                               max_digits, ent->lno + 1 + cnt);
                }
@@@ -1713,7 -1791,7 +1713,7 @@@ static int prepare_lines(struct scorebo
  
  /*
   * Add phony grafts for use with -S; this is primarily to
 - * support git-cvsserver that wants to give a linear history
 + * support git's cvsserver that wants to give a linear history
   * to its clients.
   */
  static int read_ancestry(const char *graft_file)
@@@ -1769,7 -1847,7 +1769,7 @@@ 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 = strlen(ci.author);
 +                      num = utf8_strwidth(ci.author);
                        if (longest_author < num)
                                longest_author = num;
                }
@@@ -1987,7 -2065,7 +1987,7 @@@ static struct commit *fake_working_tree
        struct commit *commit;
        struct origin *origin;
        unsigned char head_sha1[20];
 -      struct strbuf buf;
 +      struct strbuf buf = STRBUF_INIT;
        const char *ident;
        time_t now;
        int size, len;
  
        origin = make_origin(commit, path);
  
 -      strbuf_init(&buf, 0);
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
                const char *read_from;
 -              unsigned long fin_size;
  
                if (contents_from) {
                        if (stat(contents_from, &st) < 0)
                                die("Cannot lstat %s", path);
                        read_from = path;
                }
 -              fin_size = xsize_t(st.st_size);
                mode = canon_mode(st.st_mode);
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                                die("cannot open or read %s", read_from);
                        break;
                case S_IFLNK:
 -                      if (readlink(read_from, buf.buf, buf.alloc) != fin_size)
 +                      if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
                                die("cannot readlink %s", read_from);
 -                      buf.len = fin_size;
                        break;
                default:
                        die("unsupported file type %s", read_from);
@@@ -2235,7 -2317,8 +2235,7 @@@ int cmd_blame(int argc, const char **ar
        };
  
        struct parse_opt_ctx_t ctx;
 -
 -      cmd_is_annotate = !strcmp(argv[0], "annotate");
 +      int cmd_is_annotate = !strcmp(argv[0], "annotate");
  
        git_config(git_blame_config, NULL);
        init_revisions(&revs, NULL);
  parse_done:
        argc = parse_options_end(&ctx);
  
+       if (revs_file && read_ancestry(revs_file))
+               die("reading graft file %s failed: %s",
+                   revs_file, strerror(errno));
 +      if (cmd_is_annotate)
 +              output_option |= OUTPUT_ANNOTATE_COMPAT;
 +
        if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
                opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
                        PICKAXE_BLAME_COPY_HARDER);
        sb.ent = ent;
        sb.path = path;
  
-       if (revs_file && read_ancestry(revs_file))
-               die("reading graft file %s failed: %s",
-                   revs_file, strerror(errno));
 -      read_mailmap(&mailmap, ".mailmap", NULL);
 +      read_mailmap(&mailmap, NULL);
  
        if (!incremental)
                setup_pager();