/* * src typically is on-stack; we want to copy the information in it to * a malloced blame_entry that gets added to the given queue. The * origin of dst loses a refcnt. */ static void dup_entry(struct blame_entry ***queue, struct blame_entry *dst, struct blame_entry *src) { blame_origin_incref(src->suspect); blame_origin_decref(dst->suspect); memcpy(dst, src, sizeof(*src)); dst->next = **queue; **queue = dst; *queue = &dst->next; }
/* * It is known that lines between tlno to same came from parent, and e * has an overlap with that range. it also is known that parent's * line plno corresponds to e's line tlno. * * <---- e -----> * <------> * <------------> * <------------> * <------------------> * * Split e into potentially three parts; before this chunk, the chunk * to be blamed for the parent, and after that portion. */ static void split_overlap(struct blame_entry *split, struct blame_entry *e, int tlno, int plno, int same, struct blame_origin *parent) { int chunk_end_lno; memset(split, 0, sizeof(struct blame_entry [3])); if (e->s_lno < tlno) { /* there is a pre-chunk part not blamed on parent */ split[0].suspect = blame_origin_incref(e->suspect); split[0].lno = e->lno; split[0].s_lno = e->s_lno; split[0].num_lines = tlno - e->s_lno; split[1].lno = e->lno + tlno - e->s_lno; split[1].s_lno = plno; } else { split[1].lno = e->lno; split[1].s_lno = plno + (e->s_lno - tlno); } if (same < e->s_lno + e->num_lines) { /* there is a post-chunk part not blamed on parent */ split[2].suspect = blame_origin_incref(e->suspect); split[2].lno = e->lno + (same - e->s_lno); split[2].s_lno = e->s_lno + (same - e->s_lno); split[2].num_lines = e->s_lno + e->num_lines - same; chunk_end_lno = split[2].lno; } else chunk_end_lno = e->lno + e->num_lines; split[1].num_lines = chunk_end_lno - split[1].lno; /* * if it turns out there is nothing to blame the parent for, * forget about the splitting. !split[1].suspect signals this. */ if (split[1].num_lines < 1) return; split[1].suspect = blame_origin_incref(parent); }
/* * Append a new blame entry to a given output queue. */ static void add_blame_entry(struct blame_entry ***queue, const struct blame_entry *src) { struct blame_entry *e = xmalloc(sizeof(*e)); memcpy(e, src, sizeof(*e)); blame_origin_incref(e->suspect); e->next = **queue; **queue = e; *queue = &e->next; }
/* * Locate an existing origin or create a new one. * This moves the origin to front position in the commit util list. */ static struct blame_origin *get_origin(struct commit *commit, const char *path) { struct blame_origin *o, *l; for (o = commit->util, l = NULL; o; l = o, o = o->next) { if (!strcmp(o->path, path)) { /* bump to front */ if (l) { l->next = o->next; o->next = commit->util; commit->util = o; } return blame_origin_incref(o); } } return make_origin(commit, path); }
/* * The blobs of origin and porigin exactly match, so everything * origin is suspected for can be blamed on the parent. */ static void pass_whole_blame(struct blame_scoreboard *sb, struct blame_origin *origin, struct blame_origin *porigin) { struct blame_entry *e, *suspects; if (!porigin->file.ptr && origin->file.ptr) { /* Steal its file */ porigin->file = origin->file; origin->file.ptr = NULL; } suspects = origin->suspects; origin->suspects = NULL; for (e = suspects; e; e = e->next) { blame_origin_incref(porigin); blame_origin_decref(e->suspect); e->suspect = porigin; } queue_blames(sb, porigin, suspects); }
/* * best_so_far[] and potential[] are both a split of an existing blame_entry * that passes blame to the parent. Maintain best_so_far the best split so * far, by comparing potential and best_so_far and copying potential into * bst_so_far as needed. */ static void copy_split_if_better(struct blame_scoreboard *sb, struct blame_entry *best_so_far, struct blame_entry *potential) { int i; if (!potential[1].suspect) return; if (best_so_far[1].suspect) { if (blame_entry_score(sb, &potential[1]) < blame_entry_score(sb, &best_so_far[1])) return; } for (i = 0; i < 3; i++) blame_origin_incref(potential[i].suspect); decref_split(best_so_far); memcpy(best_so_far, potential, sizeof(struct blame_entry[3])); }
/* * Process one hunk from the patch between the current suspect for * blame_entry e and its parent. This first blames any unfinished * entries before the chunk (which is where target and parent start * differing) on the parent, and then splits blame entries at the * start and at the end of the difference region. Since use of -M and * -C options may lead to overlapping/duplicate source line number * ranges, all we can rely on from sorting/merging is the order of the * first suspect line number. */ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq, int tlno, int offset, int same, struct blame_origin *parent) { struct blame_entry *e = **srcq; struct blame_entry *samep = NULL, *diffp = NULL; while (e && e->s_lno < tlno) { struct blame_entry *next = e->next; /* * current record starts before differing portion. If * it reaches into it, we need to split it up and * examine the second part separately. */ if (e->s_lno + e->num_lines > tlno) { /* Move second half to a new record */ int len = tlno - e->s_lno; struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry)); n->suspect = e->suspect; n->lno = e->lno + len; n->s_lno = e->s_lno + len; n->num_lines = e->num_lines - len; e->num_lines = len; e->score = 0; /* Push new record to diffp */ n->next = diffp; diffp = n; } else blame_origin_decref(e->suspect); /* Pass blame for everything before the differing * chunk to the parent */ e->suspect = blame_origin_incref(parent); e->s_lno += offset; e->next = samep; samep = e; e = next; } /* * As we don't know how much of a common stretch after this * diff will occur, the currently blamed parts are all that we * can assign to the parent for now. */ if (samep) { **dstq = reverse_blame(samep, **dstq); *dstq = &samep->next; } /* * Prepend the split off portions: everything after e starts * after the blameable portion. */ e = reverse_blame(diffp, e); /* * Now retain records on the target while parts are different * from the parent. */ samep = NULL; diffp = NULL; while (e && e->s_lno < same) { struct blame_entry *next = e->next; /* * If current record extends into sameness, need to split. */ if (e->s_lno + e->num_lines > same) { /* * Move second half to a new record to be * processed by later chunks */ int len = same - e->s_lno; struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry)); n->suspect = blame_origin_incref(e->suspect); n->lno = e->lno + len; n->s_lno = e->s_lno + len; n->num_lines = e->num_lines - len; e->num_lines = len; e->score = 0; /* Push new record to samep */ n->next = samep; samep = n; } e->next = diffp; diffp = e; e = next; } **srcq = reverse_blame(diffp, reverse_blame(samep, e)); /* Move across elements that are in the unblamable portion */ if (diffp) *srcq = &diffp->next; }
/* * We have an origin -- check if the same path exists in the * parent and return an origin structure to represent it. */ static struct blame_origin *find_origin(struct commit *parent, struct blame_origin *origin) { struct blame_origin *porigin; struct diff_options diff_opts; const char *paths[2]; /* First check any existing origins */ for (porigin = parent->util; porigin; porigin = porigin->next) if (!strcmp(porigin->path, origin->path)) { /* * The same path between origin and its parent * without renaming -- the most common case. */ return blame_origin_incref (porigin); } /* See if the origin->path is different between parent * and origin first. Most of the time they are the * same and diff-tree is fairly efficient about this. */ diff_setup(&diff_opts); DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = 0; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; paths[0] = origin->path; paths[1] = NULL; parse_pathspec(&diff_opts.pathspec, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", paths); diff_setup_done(&diff_opts); if (is_null_oid(&origin->commit->object.oid)) do_diff_cache(&parent->tree->object.oid, &diff_opts); else diff_tree_oid(&parent->tree->object.oid, &origin->commit->tree->object.oid, "", &diff_opts); diffcore_std(&diff_opts); if (!diff_queued_diff.nr) { /* The path is the same as parent */ porigin = get_origin(parent, origin->path); oidcpy(&porigin->blob_oid, &origin->blob_oid); porigin->mode = origin->mode; } else { /* * Since origin->path is a pathspec, if the parent * commit had it as a directory, we will see a whole * bunch of deletion of files in the directory that we * do not care about. */ int i; struct diff_filepair *p = NULL; for (i = 0; i < diff_queued_diff.nr; i++) { const char *name; p = diff_queued_diff.queue[i]; name = p->one->path ? p->one->path : p->two->path; if (!strcmp(name, origin->path)) break; } if (!p) die("internal error in blame::find_origin"); switch (p->status) { default: die("internal error in blame::find_origin (%c)", p->status); case 'M': porigin = get_origin(parent, origin->path); oidcpy(&porigin->blob_oid, &p->one->oid); porigin->mode = p->one->mode; break; case 'A': case 'T': /* Did not exist in parent, or type changed */ break; } } diff_flush(&diff_opts); clear_pathspec(&diff_opts.pathspec); return porigin; }
/* * The main loop -- while we have blobs with lines whose true origin * is still unknown, pick one blob, and allow its lines to pass blames * to its parents. */ void assign_blame(struct blame_scoreboard *sb, int opt) { struct rev_info *revs = sb->revs; struct commit *commit = prio_queue_get(&sb->commits); while (commit) { struct blame_entry *ent; struct blame_origin *suspect = commit->util; /* find one suspect to break down */ while (suspect && !suspect->suspects) suspect = suspect->next; if (!suspect) { commit = prio_queue_get(&sb->commits); continue; } assert(commit == suspect->commit); /* * We will use this suspect later in the loop, * so hold onto it in the meantime. */ blame_origin_incref(suspect); parse_commit(commit); if (sb->reverse || (!(commit->object.flags & UNINTERESTING) && !(revs->max_age != -1 && commit->date < revs->max_age))) pass_blame(sb, suspect, opt); else { commit->object.flags |= UNINTERESTING; if (commit->object.parsed) mark_parents_uninteresting(commit); } /* treat root commit as boundary */ if (!commit->parents && !sb->show_root) commit->object.flags |= UNINTERESTING; /* Take responsibility for the remaining entries */ ent = suspect->suspects; if (ent) { suspect->guilty = 1; for (;;) { struct blame_entry *next = ent->next; if (sb->found_guilty_entry) sb->found_guilty_entry(ent, sb->found_guilty_entry_data); if (next) { ent = next; continue; } ent->next = sb->ent; sb->ent = suspect->suspects; suspect->suspects = NULL; break; } } blame_origin_decref(suspect); if (sb->debug) /* sanity */ sanity_check_refcnt(sb); } }
static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, int opt) { struct rev_info *revs = sb->revs; int i, pass, num_sg; struct commit *commit = origin->commit; struct commit_list *sg; struct blame_origin *sg_buf[MAXSG]; struct blame_origin *porigin, **sg_origin = sg_buf; struct blame_entry *toosmall = NULL; struct blame_entry *blames, **blametail = &blames; num_sg = num_scapegoats(revs, commit, sb->reverse); if (!num_sg) goto finish; else if (num_sg < ARRAY_SIZE(sg_buf)) memset(sg_buf, 0, sizeof(sg_buf)); else sg_origin = xcalloc(num_sg, sizeof(*sg_origin)); /* * 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 - sb->no_whole_file_rename; pass++) { struct blame_origin *(*find)(struct commit *, struct blame_origin *); find = pass ? find_rename : find_origin; for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct commit *p = sg->item; int j, same; if (sg_origin[i]) continue; if (parse_commit(p)) continue; porigin = find(p, origin); if (!porigin) continue; if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) { pass_whole_blame(sb, origin, porigin); blame_origin_decref(porigin); goto finish; } for (j = same = 0; j < i; j++) if (sg_origin[j] && !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) { same = 1; break; } if (!same) sg_origin[i] = porigin; else blame_origin_decref(porigin); } } sb->num_commits++; for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct blame_origin *porigin = sg_origin[i]; if (!porigin) continue; if (!origin->previous) { blame_origin_incref(porigin); origin->previous = porigin; } pass_blame_to_parent(sb, origin, porigin); if (!origin->suspects) goto finish; } /* * Optionally find moves in parents' files. */ if (opt & PICKAXE_BLAME_MOVE) { filter_small(sb, &toosmall, &origin->suspects, sb->move_score); if (origin->suspects) { for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct blame_origin *porigin = sg_origin[i]; if (!porigin) continue; find_move_in_parent(sb, &blametail, &toosmall, origin, porigin); if (!origin->suspects) break; } } } /* * Optionally find copies from parents' files. */ if (opt & PICKAXE_BLAME_COPY) { if (sb->copy_score > sb->move_score) filter_small(sb, &toosmall, &origin->suspects, sb->copy_score); else if (sb->copy_score < sb->move_score) { origin->suspects = blame_merge(origin->suspects, toosmall); toosmall = NULL; filter_small(sb, &toosmall, &origin->suspects, sb->copy_score); } if (!origin->suspects) goto finish; for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct blame_origin *porigin = sg_origin[i]; find_copy_in_parent(sb, &blametail, &toosmall, origin, sg->item, porigin, opt); if (!origin->suspects) goto finish; } } finish: *blametail = NULL; distribute_blame(sb, blames); /* * prepend toosmall to origin->suspects * * There is no point in sorting: this ends up on a big * unsorted list in the caller anyway. */ if (toosmall) { struct blame_entry **tail = &toosmall; while (*tail) tail = &(*tail)->next; *tail = origin->suspects; origin->suspects = toosmall; } for (i = 0; i < num_sg; i++) { if (sg_origin[i]) { drop_origin_blob(sg_origin[i]); blame_origin_decref(sg_origin[i]); } } drop_origin_blob(origin); if (sg_buf != sg_origin) free(sg_origin); }