Merge updated git-gui and gitk that call each other
[gitweb.git] / builtin-blame.c
index 06c7de429799f4af5303253775f21b7f37723b94..6b7b9f4466989ca02d3a5e53caac9ff5c7e8a922 100644 (file)
 #include "quote.h"
 #include "xdiff-interface.h"
 #include "cache-tree.h"
-#include "path-list.h"
+#include "string-list.h"
 #include "mailmap.h"
 #include "parse-options.h"
 
-static char blame_usage[] = "git-blame [options] [rev-opts] [rev] [--] file";
+static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
 
 static const char *blame_opt_usage[] = {
        blame_usage,
@@ -38,9 +38,8 @@ 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 path_list mailmap;
+static struct string_list mailmap;
 
 #ifndef DEBUG
 #define DEBUG 0
@@ -153,6 +152,10 @@ struct blame_entry {
         */
        char guilty;
 
+       /* true if the entry has been scanned for copies in the current parent
+        */
+       char scanned;
+
        /* the line number of the first line of this group in the
         * suspect's file; internally all line numbers are 0 based.
         */
@@ -461,7 +464,6 @@ struct patch {
 };
 
 struct blame_diff_state {
-       struct xdiff_emit_state xm;
        struct patch *ret;
        unsigned hunk_post_context;
        unsigned hunk_in_pre_context : 1;
@@ -524,15 +526,12 @@ static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o,
        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);
+       xdi_diff_outf(file_p, file_o, process_u_diff, &state, &xpp, &xecfg, &ecb);
 
        if (state.ret->num) {
                struct chunk *chunk;
@@ -1008,7 +1007,8 @@ static int find_move_in_parent(struct scoreboard *sb,
        while (made_progress) {
                made_progress = 0;
                for (e = sb->ent; e; e = e->next) {
-                       if (e->guilty || !same_suspect(e->suspect, target))
+                       if (e->guilty || !same_suspect(e->suspect, target) ||
+                           ent_score(sb, e) < blame_move_score)
                                continue;
                        find_copy_in_blob(sb, e, parent, split, &file_p);
                        if (split[1].suspect &&
@@ -1033,6 +1033,7 @@ struct blame_list {
  */
 static struct blame_list *setup_blame_list(struct scoreboard *sb,
                                           struct origin *target,
+                                          int min_score,
                                           int *num_ents_p)
 {
        struct blame_entry *e;
@@ -1040,18 +1041,32 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb,
        struct blame_list *blame_list = NULL;
 
        for (e = sb->ent, num_ents = 0; e; e = e->next)
-               if (!e->guilty && same_suspect(e->suspect, target))
+               if (!e->scanned && !e->guilty &&
+                   same_suspect(e->suspect, target) &&
+                   min_score < ent_score(sb, e))
                        num_ents++;
        if (num_ents) {
                blame_list = xcalloc(num_ents, sizeof(struct blame_list));
                for (e = sb->ent, i = 0; e; e = e->next)
-                       if (!e->guilty && same_suspect(e->suspect, target))
+                       if (!e->scanned && !e->guilty &&
+                           same_suspect(e->suspect, target) &&
+                           min_score < ent_score(sb, e))
                                blame_list[i++].ent = e;
        }
        *num_ents_p = num_ents;
        return blame_list;
 }
 
+/*
+ * Reset the scanned status on all entries.
+ */
+static void reset_scanned_flag(struct scoreboard *sb)
+{
+       struct blame_entry *e;
+       for (e = sb->ent; e; e = e->next)
+               e->scanned = 0;
+}
+
 /*
  * For lines target is suspected for, see if we can find code movement
  * across file boundary from the parent commit.  porigin is the path
@@ -1070,7 +1085,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
        struct blame_list *blame_list;
        int num_ents;
 
-       blame_list = setup_blame_list(sb, target, &num_ents);
+       blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
        if (!blame_list)
                return 1; /* nothing remains for this target */
 
@@ -1144,18 +1159,21 @@ static int find_copy_in_parent(struct scoreboard *sb,
                                split_blame(sb, split, blame_list[j].ent);
                                made_progress = 1;
                        }
+                       else
+                               blame_list[j].ent->scanned = 1;
                        decref_split(split);
                }
                free(blame_list);
 
                if (!made_progress)
                        break;
-               blame_list = setup_blame_list(sb, target, &num_ents);
+               blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
                if (!blame_list) {
                        retval = 1;
                        break;
                }
        }
+       reset_scanned_flag(sb);
        diff_flush(&diff_opts);
        diff_tree_release_paths(&diff_opts);
        return retval;
@@ -1663,7 +1681,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
                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('^');
                        }
@@ -1768,7 +1786,7 @@ static int prepare_lines(struct scoreboard *sb)
 
 /*
  * 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)
@@ -1903,7 +1921,7 @@ static void sanity_check_refcnt(struct scoreboard *sb)
  * Used for the command line parsing; check if the path exists
  * in the working tree.
  */
-static int has_path_in_work_tree(const char *path)
+static int has_string_in_work_tree(const char *path)
 {
        struct stat st;
        return !lstat(path, &st);
@@ -2294,8 +2312,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        };
 
        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);
@@ -2323,6 +2340,13 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 parse_done:
        argc = parse_options_end(&ctx);
 
+       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);
+
        if (!blame_move_score)
                blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
        if (!blame_copy_score)
@@ -2367,14 +2391,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                if (argc < 2)
                        usage_with_options(blame_opt_usage, options);
                path = add_prefix(prefix, argv[argc - 1]);
-               if (argc == 3 && !has_path_in_work_tree(path)) { /* (2b) */
+               if (argc == 3 && !has_string_in_work_tree(path)) { /* (2b) */
                        path = add_prefix(prefix, argv[1]);
                        argv[1] = argv[2];
                }
                argv[argc - 1] = "--";
 
                setup_work_tree();
-               if (!has_path_in_work_tree(path))
+               if (!has_string_in_work_tree(path))
                        die("cannot stat path %s: %s", path, strerror(errno));
        }