Signed-off-by: Jeff Smith <whydoubt@xxxxxxxxx> --- builtin/blame.c | 149 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 93 insertions(+), 56 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index c873cc2..dc7600c 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -62,11 +62,6 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP; #define DEBUG 0 #endif -/* stats */ -static int num_read_blob; -static int num_get_patch; -static int num_commits; - #define PICKAXE_BLAME_MOVE 01 #define PICKAXE_BLAME_COPY 02 #define PICKAXE_BLAME_COPY_HARDER 04 @@ -91,7 +86,7 @@ struct progress_info { }; static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, - xdl_emit_hunk_consume_func_t hunk_func, void *cb_data) + xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts) { xpparam_t xpp = {0}; xdemitconf_t xecfg = {0}; @@ -108,13 +103,13 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, * diff machinery */ static void fill_origin_blob(struct diff_options *opt, - struct origin *o, mmfile_t *file) + struct origin *o, mmfile_t *file, int *num_read_blob) { if (!o->file.ptr) { enum object_type type; unsigned long file_size; - num_read_blob++; + (*num_read_blob)++; if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size)) ; @@ -265,9 +260,36 @@ struct scoreboard { /* look-up a line in the final buffer */ int num_lines; int *lineno; + + /* + * blame for a blame_entry with score lower than these thresholds + * is not passed to the parent using move/copy logic. + */ + unsigned blame_move_score; + unsigned blame_copy_score; + + /* stats */ + int num_read_blob; + int num_get_patch; + int num_commits; + + /* options */ + int show_root; + int reverse; + int xdl_opts; + int no_whole_file_rename; + + /* callbacks */ + void(*sanity_check)(struct scoreboard *); + void(*found_guilty_entry)(struct blame_entry *, void *); + + void *found_guilty_entry_data; }; -static void sanity_check_refcnt(struct scoreboard *); +static void blame_sort_final(struct scoreboard *sb) +{ + sb->ent = blame_sort(sb->ent, compare_blame_final); +} /* * If two blame entries that are next to each other came from @@ -290,8 +312,8 @@ static void coalesce(struct scoreboard *sb) } } - if (DEBUG) /* sanity */ - sanity_check_refcnt(sb); + if (sb->sanity_check) /* sanity */ + sb->sanity_check(sb); } /* @@ -787,11 +809,11 @@ static void pass_blame_to_parent(struct scoreboard *sb, d.offset = 0; d.dstq = &newdest; d.srcq = &target->suspects; - fill_origin_blob(&sb->revs->diffopt, parent, &file_p); - fill_origin_blob(&sb->revs->diffopt, target, &file_o); - num_get_patch++; + fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob); + fill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob); + sb->num_get_patch++; - if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d)) + if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts)) die("unable to generate diff (%s -> %s)", oid_to_hex(&parent->commit->object.oid), oid_to_hex(&target->commit->object.oid)); @@ -940,7 +962,7 @@ static void find_copy_in_blob(struct scoreboard *sb, * file_p partially may match that image. */ memset(split, 0, sizeof(struct blame_entry [3])); - if (diff_hunks(file_p, &file_o, handle_split_cb, &d)) + if (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts)) die("unable to generate diff (%s)", oid_to_hex(&parent->commit->object.oid)); /* remainder, if any, all match the preimage */ @@ -993,7 +1015,7 @@ static void find_move_in_parent(struct scoreboard *sb, if (!unblamed) return; /* nothing remains for this target */ - fill_origin_blob(&sb->revs->diffopt, parent, &file_p); + fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob); if (!file_p.ptr) return; @@ -1009,7 +1031,7 @@ static void find_move_in_parent(struct scoreboard *sb, next = e->next; find_copy_in_blob(sb, e, parent, split, &file_p); if (split[1].suspect && - blame_move_score < ent_score(sb, &split[1])) { + sb->blame_move_score < ent_score(sb, &split[1])) { split_blame(blamed, &unblamedtail, split, e); } else { e->next = leftover; @@ -1018,7 +1040,7 @@ static void find_move_in_parent(struct scoreboard *sb, decref_split(split); } *unblamedtail = NULL; - toosmall = filter_small(sb, toosmall, &unblamed, blame_move_score); + toosmall = filter_small(sb, toosmall, &unblamed, sb->blame_move_score); } while (unblamed); target->suspects = reverse_blame(leftover, NULL); } @@ -1122,7 +1144,7 @@ static void find_copy_in_parent(struct scoreboard *sb, norigin = get_origin(parent, p->one->path); oidcpy(&norigin->blob_oid, &p->one->oid); norigin->mode = p->one->mode; - fill_origin_blob(&sb->revs->diffopt, norigin, &file_p); + fill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob); if (!file_p.ptr) continue; @@ -1139,7 +1161,7 @@ static void find_copy_in_parent(struct scoreboard *sb, for (j = 0; j < num_ents; j++) { struct blame_entry *split = blame_list[j].split; if (split[1].suspect && - blame_copy_score < ent_score(sb, &split[1])) { + sb->blame_copy_score < ent_score(sb, &split[1])) { split_blame(blamed, &unblamedtail, split, blame_list[j].ent); } else { @@ -1150,7 +1172,7 @@ static void find_copy_in_parent(struct scoreboard *sb, } free(blame_list); *unblamedtail = NULL; - toosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score); + toosmall = filter_small(sb, toosmall, &unblamed, sb->blame_copy_score); } while (unblamed); target->suspects = reverse_blame(leftover, NULL); diff_flush(&diff_opts); @@ -1186,7 +1208,8 @@ static void pass_whole_blame(struct scoreboard *sb, * "parent" (and "porigin"), but what we mean is to find scapegoat to * exonerate ourselves. */ -static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit) +static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit, + int reverse) { if (!reverse) { if (revs->first_parent_only && @@ -1200,9 +1223,9 @@ static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit return lookup_decoration(&revs->children, &commit->object); } -static int num_scapegoats(struct rev_info *revs, struct commit *commit) +static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse) { - struct commit_list *l = first_scapegoat(revs, commit); + struct commit_list *l = first_scapegoat(revs, commit, reverse); return commit_list_count(l); } @@ -1240,7 +1263,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) struct blame_entry *toosmall = NULL; struct blame_entry *blames, **blametail = &blames; - num_sg = num_scapegoats(revs, commit); + num_sg = num_scapegoats(revs, commit, sb->reverse); if (!num_sg) goto finish; else if (num_sg < ARRAY_SIZE(sg_buf)) @@ -1252,12 +1275,12 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) * The first pass looks for unrenamed path to optimize for * common cases, then we look for renames in the second pass. */ - for (pass = 0; pass < 2 - no_whole_file_rename; pass++) { + for (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) { struct origin *(*find)(struct scoreboard *, struct commit *, struct origin *); find = pass ? find_rename : find_origin; - for (i = 0, sg = first_scapegoat(revs, commit); + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct commit *p = sg->item; @@ -1288,8 +1311,8 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) } } - num_commits++; - for (i = 0, sg = first_scapegoat(revs, commit); + sb->num_commits++; + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct origin *porigin = sg_origin[i]; @@ -1308,9 +1331,9 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) * Optionally find moves in parents' files. */ if (opt & PICKAXE_BLAME_MOVE) { - filter_small(sb, &toosmall, &origin->suspects, blame_move_score); + filter_small(sb, &toosmall, &origin->suspects, sb->blame_move_score); if (origin->suspects) { - for (i = 0, sg = first_scapegoat(revs, commit); + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct origin *porigin = sg_origin[i]; @@ -1327,17 +1350,17 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) * Optionally find copies from parents' files. */ if (opt & PICKAXE_BLAME_COPY) { - if (blame_copy_score > blame_move_score) - filter_small(sb, &toosmall, &origin->suspects, blame_copy_score); - else if (blame_copy_score < blame_move_score) { + if (sb->blame_copy_score > sb->blame_move_score) + filter_small(sb, &toosmall, &origin->suspects, sb->blame_copy_score); + else if (sb->blame_copy_score < sb->blame_move_score) { origin->suspects = blame_merge(origin->suspects, toosmall); toosmall = NULL; - filter_small(sb, &toosmall, &origin->suspects, blame_copy_score); + filter_small(sb, &toosmall, &origin->suspects, sb->blame_copy_score); } if (!origin->suspects) goto finish; - for (i = 0, sg = first_scapegoat(revs, commit); + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct origin *porigin = sg_origin[i]; @@ -1566,9 +1589,10 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat) * The blame_entry is found to be guilty for the range. * Show it in incremental output. */ -static void found_guilty_entry(struct blame_entry *ent, - struct progress_info *pi) +static void found_guilty_entry(struct blame_entry *ent, void *data) { + struct progress_info *pi = (struct progress_info *)data; + if (incremental) { struct origin *suspect = ent->suspect; @@ -1591,11 +1615,6 @@ static void assign_blame(struct scoreboard *sb, int opt) { struct rev_info *revs = sb->revs; struct commit *commit = prio_queue_get(&sb->commits); - struct progress_info pi = { NULL, 0 }; - - if (show_progress) - pi.progress = start_progress_delay(_("Blaming lines"), - sb->num_lines, 50, 1); while (commit) { struct blame_entry *ent; @@ -1618,7 +1637,7 @@ static void assign_blame(struct scoreboard *sb, int opt) */ origin_incref(suspect); parse_commit(commit); - if (reverse || + if (sb->reverse || (!(commit->object.flags & UNINTERESTING) && !(revs->max_age != -1 && commit->date < revs->max_age))) pass_blame(sb, suspect, opt); @@ -1628,7 +1647,7 @@ static void assign_blame(struct scoreboard *sb, int opt) mark_parents_uninteresting(commit); } /* treat root commit as boundary */ - if (!commit->parents && !show_root) + if (!commit->parents && !sb->show_root) commit->object.flags |= UNINTERESTING; /* Take responsibility for the remaining entries */ @@ -1637,7 +1656,8 @@ static void assign_blame(struct scoreboard *sb, int opt) suspect->guilty = 1; for (;;) { struct blame_entry *next = ent->next; - found_guilty_entry(ent, &pi); + if (sb->found_guilty_entry) + sb->found_guilty_entry(ent, sb->found_guilty_entry_data); if (next) { ent = next; continue; @@ -1650,11 +1670,9 @@ static void assign_blame(struct scoreboard *sb, int opt) } origin_decref(suspect); - if (DEBUG) /* sanity */ - sanity_check_refcnt(sb); + if (sb->sanity_check) /* sanity */ + sb->sanity_check(sb); } - - stop_progress(&pi.progress); } static const char *format_time(unsigned long time, const char *tz_str, @@ -2187,6 +2205,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) char *final_commit_name = NULL; enum object_type type; struct commit *final_commit = NULL; + struct progress_info pi = { NULL, 0 }; struct string_list range_list = STRING_LIST_INIT_NODUP; int output_option = 0, opt = 0; @@ -2476,7 +2495,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) oid_to_hex(&o->blob_oid), path); } - num_read_blob++; + sb.num_read_blob++; lno = prepare_lines(&sb); if (lno && !range_list.nr) @@ -2529,10 +2548,28 @@ int cmd_blame(int argc, const char **argv, const char *prefix) sb.ent = NULL; sb.path = path; + sb.blame_move_score = blame_move_score; + sb.blame_copy_score = blame_copy_score; + + sb.sanity_check = DEBUG ? &sanity_check_refcnt : NULL; + + sb.show_root = show_root; + sb.reverse = reverse; + sb.xdl_opts = xdl_opts; + sb.no_whole_file_rename = no_whole_file_rename; + read_mailmap(&mailmap, NULL); + sb.found_guilty_entry = &found_guilty_entry; + sb.found_guilty_entry_data = π + if (show_progress) + pi.progress = start_progress_delay(_("Blaming lines"), + sb.num_lines, 50, 1); + assign_blame(&sb, opt); + stop_progress(&pi.progress); + if (!incremental) setup_pager(); @@ -2541,7 +2578,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) if (incremental) return 0; - sb.ent = blame_sort(sb.ent, compare_blame_final); + blame_sort_final(&sb); coalesce(&sb); @@ -2557,9 +2594,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix) } if (show_stats) { - printf("num read blob: %d\n", num_read_blob); - printf("num get patch: %d\n", num_get_patch); - printf("num commits: %d\n", num_commits); + printf("num read blob: %d\n", sb.num_read_blob); + printf("num get patch: %d\n", sb.num_get_patch); + printf("num commits: %d\n", sb.num_commits); } return 0; } -- 2.9.3