Merge branch 'mp/for-each-ref-missing-name-or-email'
[gitweb.git] / builtin / blame.c
index 21cde57e711e337f3c123b35332bb2af0ebdf9cf..b6534d4dea9ad81a34eaf099f7cf9a0a1e56f410 100644 (file)
@@ -53,14 +53,17 @@ static int no_whole_file_rename;
 static int show_progress;
 static char repeated_meta_color[COLOR_MAXLEN];
 static int coloring_mode;
+static struct string_list ignore_revs_file_list = STRING_LIST_INIT_NODUP;
+static int mark_unblamable_lines;
+static int mark_ignored_lines;
 
 static struct date_mode blame_date_mode = { DATE_ISO8601 };
 static size_t blame_date_width;
 
 static struct string_list mailmap = STRING_LIST_INIT_NODUP;
 
-#ifndef DEBUG
-#define DEBUG 0
+#ifndef DEBUG_BLAME
+#define DEBUG_BLAME 0
 #endif
 
 static unsigned blame_move_score;
@@ -480,6 +483,14 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
                        }
                }
 
+               if (mark_unblamable_lines && ent->unblamable) {
+                       length--;
+                       putchar('*');
+               }
+               if (mark_ignored_lines && ent->ignored) {
+                       length--;
+                       putchar('?');
+               }
                printf("%.*s", length, hex);
                if (opt & OUTPUT_ANNOTATE_COMPAT) {
                        const char *name;
@@ -696,6 +707,24 @@ static int git_blame_config(const char *var, const char *value, void *cb)
                parse_date_format(value, &blame_date_mode);
                return 0;
        }
+       if (!strcmp(var, "blame.ignorerevsfile")) {
+               const char *str;
+               int ret;
+
+               ret = git_config_pathname(&str, var, value);
+               if (ret)
+                       return ret;
+               string_list_insert(&ignore_revs_file_list, str);
+               return 0;
+       }
+       if (!strcmp(var, "blame.markunblamablelines")) {
+               mark_unblamable_lines = git_config_bool(var, value);
+               return 0;
+       }
+       if (!strcmp(var, "blame.markignoredlines")) {
+               mark_ignored_lines = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "color.blame.repeatedlines")) {
                if (color_parse_mem(value, strlen(value), repeated_meta_color))
                        warning(_("invalid color '%s' in color.blame.repeatedLines"),
@@ -775,6 +804,27 @@ static int is_a_rev(const char *name)
        return OBJ_NONE < oid_object_info(the_repository, &oid, NULL);
 }
 
+static void build_ignorelist(struct blame_scoreboard *sb,
+                            struct string_list *ignore_revs_file_list,
+                            struct string_list *ignore_rev_list)
+{
+       struct string_list_item *i;
+       struct object_id oid;
+
+       oidset_init(&sb->ignore_list, 0);
+       for_each_string_list_item(i, ignore_revs_file_list) {
+               if (!strcmp(i->string, ""))
+                       oidset_clear(&sb->ignore_list);
+               else
+                       oidset_parse_file(&sb->ignore_list, i->string);
+       }
+       for_each_string_list_item(i, ignore_rev_list) {
+               if (get_oid_committish(i->string, &oid))
+                       die(_("cannot find revision %s to ignore"), i->string);
+               oidset_insert(&sb->ignore_list, &oid);
+       }
+}
+
 int cmd_blame(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
@@ -786,6 +836,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        struct progress_info pi = { NULL, 0 };
 
        struct string_list range_list = STRING_LIST_INIT_NODUP;
+       struct string_list ignore_rev_list = STRING_LIST_INIT_NODUP;
        int output_option = 0, opt = 0;
        int show_stats = 0;
        const char *revs_file = NULL;
@@ -807,6 +858,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
                OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
                OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
+               OPT_STRING_LIST(0, "ignore-rev", &ignore_rev_list, N_("rev"), N_("Ignore <rev> when blaming")),
+               OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("Ignore revisions from <file>")),
                OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE),
                OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR),
 
@@ -1012,6 +1065,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        sb.contents_from = contents_from;
        sb.reverse = reverse;
        sb.repo = the_repository;
+       build_ignorelist(&sb, &ignore_revs_file_list, &ignore_rev_list);
+       string_list_clear(&ignore_revs_file_list, 0);
+       string_list_clear(&ignore_rev_list, 0);
        setup_scoreboard(&sb, path, &o);
        lno = sb.num_lines;
 
@@ -1062,7 +1118,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        if (blame_copy_score)
                sb.copy_score = blame_copy_score;
 
-       sb.debug = DEBUG;
+       sb.debug = DEBUG_BLAME;
        sb.on_sanity_fail = &sanity_check_on_fail;
 
        sb.show_root = show_root;