/* * Helper for blame_chunk(). blame_entry e is known to overlap with the patch * hunk; split it and pass blame to the parent. */ static void blame_overlap( git_blame *blame, git_blame__entry *e, size_t tlno, size_t plno, size_t same, git_blame__origin *parent) { git_blame__entry split[3] = {{0}}; split_overlap(split, e, tlno, plno, same, parent); if (split[1].suspect) split_blame(blame, split, e); decref_split(split); }
/* * See if lines currently target is suspected for can be attributed to * parent. */ static void find_move_in_parent(struct blame_scoreboard *sb, struct blame_entry ***blamed, struct blame_entry **toosmall, struct blame_origin *target, struct blame_origin *parent) { struct blame_entry *e, split[3]; struct blame_entry *unblamed = target->suspects; struct blame_entry *leftover = NULL; mmfile_t file_p; if (!unblamed) return; /* nothing remains for this target */ fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob); if (!file_p.ptr) return; /* At each iteration, unblamed has a NULL-terminated list of * entries that have not yet been tested for blame. leftover * contains the reversed list of entries that have been tested * without being assignable to the parent. */ do { struct blame_entry **unblamedtail = &unblamed; struct blame_entry *next; for (e = unblamed; e; e = next) { next = e->next; find_copy_in_blob(sb, e, parent, split, &file_p); if (split[1].suspect && sb->move_score < blame_entry_score(sb, &split[1])) { split_blame(blamed, &unblamedtail, split, e); } else { e->next = leftover; leftover = e; } decref_split(split); } *unblamedtail = NULL; toosmall = filter_small(sb, toosmall, &unblamed, sb->move_score); } while (unblamed); target->suspects = reverse_blame(leftover, NULL); }
/* * For lines target is suspected for, see if we can find code movement * across file boundary from the parent commit. porigin is the path * in the parent we already tried. */ static void find_copy_in_parent(struct blame_scoreboard *sb, struct blame_entry ***blamed, struct blame_entry **toosmall, struct blame_origin *target, struct commit *parent, struct blame_origin *porigin, int opt) { struct diff_options diff_opts; int i, j; struct blame_list *blame_list; int num_ents; struct blame_entry *unblamed = target->suspects; struct blame_entry *leftover = NULL; if (!unblamed) return; /* nothing remains for this target */ diff_setup(&diff_opts); diff_opts.flags.recursive = 1; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&diff_opts); /* Try "find copies harder" on new path if requested; * we do not want to use diffcore_rename() actually to * match things up; find_copies_harder is set only to * force diff_tree_oid() to feed all filepairs to diff_queue, * and this code needs to be after diff_setup_done(), which * usually makes find-copies-harder imply copy detection. */ if ((opt & PICKAXE_BLAME_COPY_HARDEST) || ((opt & PICKAXE_BLAME_COPY_HARDER) && (!porigin || strcmp(target->path, porigin->path)))) diff_opts.flags.find_copies_harder = 1; if (is_null_oid(&target->commit->object.oid)) do_diff_cache(get_commit_tree_oid(parent), &diff_opts); else diff_tree_oid(get_commit_tree_oid(parent), get_commit_tree_oid(target->commit), "", &diff_opts); if (!diff_opts.flags.find_copies_harder) diffcore_std(&diff_opts); do { struct blame_entry **unblamedtail = &unblamed; blame_list = setup_blame_list(unblamed, &num_ents); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; struct blame_origin *norigin; mmfile_t file_p; struct blame_entry potential[3]; 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; 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, &sb->num_read_blob); if (!file_p.ptr) continue; for (j = 0; j < num_ents; j++) { find_copy_in_blob(sb, blame_list[j].ent, norigin, potential, &file_p); copy_split_if_better(sb, blame_list[j].split, potential); decref_split(potential); } blame_origin_decref(norigin); } for (j = 0; j < num_ents; j++) { struct blame_entry *split = blame_list[j].split; if (split[1].suspect && sb->copy_score < blame_entry_score(sb, &split[1])) { split_blame(blamed, &unblamedtail, split, blame_list[j].ent); } else { blame_list[j].ent->next = leftover; leftover = blame_list[j].ent; } decref_split(split); } free(blame_list); *unblamedtail = NULL; toosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score); } while (unblamed); target->suspects = reverse_blame(leftover, NULL); diff_flush(&diff_opts); clear_pathspec(&diff_opts.pathspec); }