Merge branch 'jc/maint-blame-unique-abbrev'
authorJunio C Hamano <gitster@pobox.com>
Mon, 9 Jul 2012 16:01:37 +0000 (09:01 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 9 Jul 2012 16:01:38 +0000 (09:01 -0700)
"git blame" did not try to make sure the abbreviated commit object
names in its output are unique.

* jc/maint-blame-unique-abbrev:
blame: compute abbreviation width that ensures uniqueness

1  2 
builtin/blame.c
diff --combined builtin/blame.c
index 24d3dd52920542b993d4945ccb2642a74da5f759,f13ec327f6707ec832214ff4ffca7d630048ca94..960c58d855a6f1a04ad1d08637fc75c3da240a30
@@@ -88,20 -88,6 +88,20 @@@ struct origin 
        char path[FLEX_ARRAY];
  };
  
 +static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
 +                    xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
 +{
 +      xpparam_t xpp = {0};
 +      xdemitconf_t xecfg = {0};
 +      xdemitcb_t ecb = {NULL};
 +
 +      xpp.flags = xdl_opts;
 +      xecfg.ctxlen = ctxlen;
 +      xecfg.hunk_func = hunk_func;
 +      ecb.priv = cb_data;
 +      return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
 +}
 +
  /*
   * Prepare diff_filespec and convert it using diff textconv API
   * if the textconv driver exists.
@@@ -773,14 -759,12 +773,14 @@@ struct blame_chunk_cb_data 
        long tlno;
  };
  
 -static void blame_chunk_cb(void *data, long same, long p_next, long t_next)
 +static int blame_chunk_cb(long start_a, long count_a,
 +                        long start_b, long count_b, void *data)
  {
        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;
 +      blame_chunk(d->sb, d->tlno, d->plno, start_b, d->target, d->parent);
 +      d->plno = start_a + count_a;
 +      d->tlno = start_b + count_b;
 +      return 0;
  }
  
  /*
@@@ -795,7 -779,8 +795,7 @@@ static int pass_blame_to_parent(struct 
        int last_in_target;
        mmfile_t file_p, file_o;
        struct blame_chunk_cb_data d;
 -      xpparam_t xpp;
 -      xdemitconf_t xecfg;
 +
        memset(&d, 0, sizeof(d));
        d.sb = sb; d.target = target; d.parent = parent;
        last_in_target = find_last_in_target(sb, target);
        fill_origin_blob(&sb->revs->diffopt, target, &file_o);
        num_get_patch++;
  
 -      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);
 +      diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d);
        /* The rest (i.e. anything after tlno) are the same as the parent */
        blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
  
@@@ -910,15 -899,12 +910,15 @@@ struct handle_split_cb_data 
        long tlno;
  };
  
 -static void handle_split_cb(void *data, long same, long p_next, long t_next)
 +static int handle_split_cb(long start_a, long count_a,
 +                         long start_b, long count_b, void *data)
  {
        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;
 +      handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,
 +                   d->split);
 +      d->plno = start_a + count_a;
 +      d->tlno = start_b + count_b;
 +      return 0;
  }
  
  /*
@@@ -936,7 -922,8 +936,7 @@@ static void find_copy_in_blob(struct sc
        int cnt;
        mmfile_t file_o;
        struct handle_split_cb_data d;
 -      xpparam_t xpp;
 -      xdemitconf_t xecfg;
 +
        memset(&d, 0, sizeof(d));
        d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
        /*
         * 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]));
 -      xdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg);
 +      diff_hunks(file_p, &file_o, 1, handle_split_cb, &d);
        /* remainder, if any, all match the preimage */
        handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
  }
@@@ -1837,6 -1828,28 +1837,16 @@@ static int read_ancestry(const char *gr
        return 0;
  }
  
 -/*
 - * How many columns do we need to show line numbers in decimal?
 - */
 -static int lineno_width(int lines)
 -{
 -      int i, width;
 -
 -      for (width = 1, i = 10; i <= lines; width++)
 -              i *= 10;
 -      return width;
 -}
 -
+ static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
+ {
+       const char *uniq = find_unique_abbrev(suspect->commit->object.sha1,
+                                             auto_abbrev);
+       int len = strlen(uniq);
+       if (auto_abbrev < len)
+               return len;
+       return auto_abbrev;
+ }
  /*
   * How many columns do we need to show line numbers, authors,
   * and filenames?
@@@ -1847,12 -1860,16 +1857,16 @@@ static void find_alignment(struct score
        int longest_dst_lines = 0;
        unsigned largest_score = 0;
        struct blame_entry *e;
+       int compute_auto_abbrev = (abbrev < 0);
+       int auto_abbrev = default_abbrev;
  
        for (e = sb->ent; e; e = e->next) {
                struct origin *suspect = e->suspect;
                struct commit_info ci;
                int num;
  
+               if (compute_auto_abbrev)
+                       auto_abbrev = update_auto_abbrev(auto_abbrev, suspect);
                if (strcmp(suspect->path, sb->path))
                        *option |= OUTPUT_SHOW_NAME;
                num = strlen(suspect->path);
                if (largest_score < ent_score(sb, e))
                        largest_score = ent_score(sb, e);
        }
 -      max_orig_digits = lineno_width(longest_src_lines);
 -      max_digits = lineno_width(longest_dst_lines);
 -      max_score_digits = lineno_width(largest_score);
 +      max_orig_digits = decimal_width(longest_src_lines);
 +      max_digits = decimal_width(longest_dst_lines);
 +      max_score_digits = decimal_width(largest_score);
+       if (compute_auto_abbrev)
+               /* one more abbrev length is needed for the boundary commit */
+               abbrev = auto_abbrev + 1;
  }
  
  /*
@@@ -2047,8 -2068,14 +2065,8 @@@ static int git_blame_config(const char 
                return 0;
        }
  
 -      switch (userdiff_config(var, value)) {
 -      case 0:
 -              break;
 -      case -1:
 +      if (userdiff_config(var, value) < 0)
                return -1;
 -      default:
 -              return 0;
 -      }
  
        return git_default_config(var, value, cb);
  }
@@@ -2087,7 -2114,6 +2105,7 @@@ static struct commit *fake_working_tree
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
                const char *read_from;
 +              char *buf_ptr;
                unsigned long buf_len;
  
                if (contents_from) {
                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))
 -                              buf.len = buf_len;
 +                          textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
 +                              strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
                        break;
@@@ -2311,7 -2337,6 +2329,7 @@@ int cmd_blame(int argc, const char **ar
                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_BIT(0, "minimal", &xdl_opts, "Spend extra cycles to find better match", XDF_NEED_MINIMAL),
                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 },
  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 (0 < 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);