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); }
/* find the line number of the last line the target is suspected for */ static int find_last_in_target(git_blame *blame, git_blame__origin *target) { git_blame__entry *e; int last_in_target = -1; for (e=blame->ent; e; e=e->next) { if (e->guilty || !same_suspect(e->suspect, target)) continue; if (last_in_target < e->s_lno + e->num_lines) last_in_target = e->s_lno + e->num_lines; } return last_in_target; }
/* * 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(git_blame *blame, git_blame__origin *origin, git_blame__origin *porigin) { git_blame__entry *e; if (!porigin->blob) git_object_lookup((git_object**)&porigin->blob, blame->repository, git_blob_id(origin->blob), GIT_OBJ_BLOB); for (e=blame->ent; e; e=e->next) { if (!same_suspect(e->suspect, origin)) continue; origin_incref(porigin); origin_decref(e->suspect); e->suspect = porigin; } }
/* find the line number of the last line the target is suspected for */ static bool find_last_in_target(size_t *out, git_blame *blame, git_blame__origin *target) { git_blame__entry *e; size_t last_in_target = 0; bool found = false; *out = 0; for (e=blame->ent; e; e=e->next) { if (e->guilty || !same_suspect(e->suspect, target)) continue; if (last_in_target < e->s_lno + e->num_lines) { found = true; last_in_target = e->s_lno + e->num_lines; } } *out = last_in_target; return found; }
/* * Process one hunk from the patch between the current suspect for blame_entry * e and its parent. Find and split the overlap, and pass blame to the * overlapping part to the parent. */ static void blame_chunk( git_blame *blame, size_t tlno, size_t plno, size_t same, git_blame__origin *target, git_blame__origin *parent) { git_blame__entry *e; for (e = blame->ent; e; e = e->next) { if (e->guilty || !same_suspect(e->suspect, target)) continue; if (same <= e->s_lno) continue; if (tlno < e->s_lno + e->num_lines) { blame_overlap(blame, e, tlno, plno, same, parent); } } }
/* * If two blame entries that are next to each other came from * contiguous lines in the same origin (i.e. <commit, path> pair), * merge them together. */ static void coalesce(git_blame *blame) { git_blame__entry *ent, *next; for (ent=blame->ent; ent && (next = ent->next); ent = next) { if (same_suspect(ent->suspect, next->suspect) && ent->guilty == next->guilty && ent->s_lno + ent->num_lines == next->s_lno) { ent->num_lines += next->num_lines; ent->next = next->next; if (ent->next) ent->next->prev = ent; origin_decref(next->suspect); git__free(next); ent->score = 0; next = ent; /* again */ } } }