void git_blame__like_git(git_blame *blame, uint32_t opt) { while (true) { git_blame__entry *ent; git_blame__origin *suspect = NULL; /* Find a suspect to break down */ for (ent = blame->ent; !suspect && ent; ent = ent->next) if (!ent->guilty) suspect = ent->suspect; if (!suspect) return; /* all done */ /* We'll use this suspect later in the loop, so hold on to it for now. */ origin_incref(suspect); pass_blame(blame, suspect, opt); /* Take responsibility for the remaining entries */ for (ent = blame->ent; ent; ent = ent->next) { if (same_suspect(ent->suspect, suspect)) { ent->guilty = true; ent->is_boundary = !git_oid_cmp( git_commit_id(suspect->commit), &blame->options.oldest_commit); } } origin_decref(suspect); } coalesce(blame); }
/* * 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); } }