static int max_digits;
static int max_score_digits;
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
#define PICKAXE_BLAME_MOVE 01
#define PICKAXE_BLAME_COPY 02
#define PICKAXE_BLAME_COPY_HARDER 04
#define MORE_THAN_ONE_PATH (1u<<13)
/*
- * One blob in a commit
+ * One blob in a commit that is being suspected
*/
struct origin {
+ int refcnt;
struct commit *commit;
unsigned char blob_sha1[20];
char path[FLEX_ARRAY];
};
+static inline struct origin *origin_incref(struct origin *o)
+{
+ if (o)
+ o->refcnt++;
+ return o;
+}
+
+static void origin_decref(struct origin *o)
+{
+ if (o && --o->refcnt <= 0) {
+ memset(o, 0, sizeof(*o));
+ free(o);
+ }
+}
+
struct blame_entry {
struct blame_entry *prev;
struct blame_entry *next;
return strcmp(a->path, b->path);
}
+static void sanity_check_refcnt(struct scoreboard *);
+
static void coalesce(struct scoreboard *sb)
{
struct blame_entry *ent, *next;
ent->next = next->next;
if (ent->next)
ent->next->prev = ent;
+ origin_decref(next->suspect);
free(next);
ent->score = 0;
next = ent; /* again */
}
}
+
+ if (DEBUG) /* sanity */
+ sanity_check_refcnt(sb);
}
static struct origin *get_origin(struct scoreboard *sb,
for (e = sb->ent; e; e = e->next) {
if (e->suspect->commit == commit &&
!strcmp(e->suspect->path, path))
- return e->suspect;
+ return origin_incref(e->suspect);
}
o = xcalloc(1, sizeof(*o) + strlen(path) + 1);
o->commit = commit;
+ o->refcnt = 1;
strcpy(o->path, path);
return o;
}
{
struct blame_entry *ent, *prev = NULL;
+ origin_incref(e->suspect);
+
for (ent = sb->ent; ent && ent->lno < e->lno; ent = ent->next)
prev = ent;
static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
{
struct blame_entry *p, *n;
+
p = dst->prev;
n = dst->next;
+ origin_incref(src->suspect);
+ origin_decref(dst->suspect);
memcpy(dst, src, sizeof(*src));
dst->prev = p;
dst->next = n;
return sb->final_buf + sb->lineno[lno];
}
-static void split_overlap(struct blame_entry split[3],
+static void split_overlap(struct blame_entry *split,
struct blame_entry *e,
int tlno, int plno, int same,
struct origin *parent)
if (e->s_lno < tlno) {
/* there is a pre-chunk part not blamed on parent */
- split[0].suspect = e->suspect;
+ split[0].suspect = origin_incref(e->suspect);
split[0].lno = e->lno;
split[0].s_lno = e->s_lno;
split[0].num_lines = tlno - e->s_lno;
if (same < e->s_lno + e->num_lines) {
/* there is a post-chunk part not blamed on parent */
- split[2].suspect = e->suspect;
+ split[2].suspect = 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;
if (split[1].num_lines < 1)
return;
- split[1].suspect = parent;
+ split[1].suspect = origin_incref(parent);
}
static void split_blame(struct scoreboard *sb,
- struct blame_entry split[3],
+ struct blame_entry *split,
struct blame_entry *e)
{
struct blame_entry *new_entry;
add_blame_entry(sb, new_entry);
}
- if (1) { /* sanity */
+ if (DEBUG) { /* sanity */
struct blame_entry *ent;
int lno = sb->ent->lno, corrupt = 0;
}
}
+static void decref_split(struct blame_entry *split)
+{
+ int i;
+
+ for (i = 0; i < 3; i++)
+ origin_decref(split[i].suspect);
+}
+
static void blame_overlap(struct scoreboard *sb, struct blame_entry *e,
int tlno, int plno, int same,
struct origin *parent)
struct blame_entry split[3];
split_overlap(split, e, tlno, plno, same, parent);
- if (!split[1].suspect)
- return;
- split_blame(sb, split, e);
+ if (split[1].suspect)
+ split_blame(sb, split, e);
+ decref_split(split);
}
static int find_last_in_target(struct scoreboard *sb, struct origin *target)
}
static void copy_split_if_better(struct scoreboard *sb,
- struct blame_entry best_so_far[3],
- struct blame_entry this[3])
+ struct blame_entry *best_so_far,
+ struct blame_entry *this)
{
+ int i;
+
if (!this[1].suspect)
return;
if (best_so_far[1].suspect) {
if (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1]))
return;
}
+
+ for (i = 0; i < 3; i++)
+ origin_incref(this[i].suspect);
+ decref_split(best_so_far);
memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
}
static void find_copy_in_blob(struct scoreboard *sb,
struct blame_entry *ent,
struct origin *parent,
- struct blame_entry split[3],
+ struct blame_entry *split,
mmfile_t *file_p)
{
const char *cp;
chunk->same + ent->s_lno,
parent);
copy_split_if_better(sb, split, this);
+ decref_split(this);
}
plno = chunk->p_next;
tlno = chunk->t_next;
if (split[1].suspect &&
blame_move_score < ent_score(sb, &split[1]))
split_blame(sb, split, e);
+ decref_split(split);
}
free(blob_p);
return 0;
this);
}
free(blob);
+ origin_decref(norigin);
}
diff_flush(&diff_opts);
if (split[1].suspect &&
blame_copy_score < ent_score(sb, &split[1]))
split_blame(sb, split, blame_list[j].ent);
+ decref_split(split);
}
free(blame_list);
if (!hashcmp(porigin->blob_sha1, origin->blob_sha1)) {
struct blame_entry *e;
for (e = sb->ent; e; e = e->next)
- if (e->suspect == origin)
+ if (e->suspect == origin) {
+ origin_incref(porigin);
+ origin_decref(e->suspect);
e->suspect = porigin;
- return;
+ }
+ origin_decref(porigin);
+ goto finish;
}
parent_origin[i] = porigin;
}
if (!porigin)
continue;
if (pass_blame_to_parent(sb, origin, porigin))
- return;
+ goto finish;
}
/*
if (!porigin)
continue;
if (find_move_in_parent(sb, origin, porigin))
- return;
+ goto finish;
}
/*
struct origin *porigin = parent_origin[i];
if (find_copy_in_parent(sb, origin, parent->item,
porigin, opt))
- return;
+ goto finish;
}
+
+ finish:
+ for (i = 0; i < MAXPARENT; i++)
+ origin_decref(parent_origin[i]);
}
static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
if (!suspect)
return; /* all done */
+ origin_incref(suspect);
commit = suspect->commit;
parse_commit(commit);
if (!(commit->object.flags & UNINTERESTING) &&
for (ent = sb->ent; ent; ent = ent->next)
if (!cmp_suspect(ent->suspect, suspect))
ent->guilty = 1;
-
+ origin_decref(suspect);
coalesce(sb);
}
}
ent->lno + 1 + cnt);
else {
if (opt & OUTPUT_SHOW_SCORE)
- printf(" %*d", max_score_digits, ent->score);
+ printf(" %*d %02d",
+ max_score_digits, ent->score,
+ ent->suspect->refcnt);
if (opt & OUTPUT_SHOW_NAME)
printf(" %-*.*s", longest_file, longest_file,
suspect->path);
max_score_digits = lineno_width(largest_score);
}
+static void sanity_check_refcnt(struct scoreboard *sb)
+{
+ int baa = 0;
+ struct blame_entry *ent;
+
+ for (ent = sb->ent; ent; ent = ent->next) {
+ /* first mark the ones that haven't been checked */
+ if (0 < ent->suspect->refcnt)
+ ent->suspect->refcnt = -ent->suspect->refcnt;
+ else if (!ent->suspect->refcnt)
+ baa = 1;
+ }
+ for (ent = sb->ent; ent; ent = ent->next) {
+ /* then pick each and see if they have the the
+ * correct refcnt
+ */
+ int found;
+ struct blame_entry *e;
+ struct origin *suspect = ent->suspect;
+
+ if (0 < suspect->refcnt)
+ continue;
+ suspect->refcnt = -suspect->refcnt;
+ for (found = 0, e = sb->ent; e; e = e->next) {
+ if (e->suspect != suspect)
+ continue;
+ found++;
+ }
+ if (suspect->refcnt != found)
+ baa = 1;
+ }
+ if (baa) {
+ int opt = 0160;
+ find_alignment(sb, &opt);
+ output(sb, opt);
+ die("Baa!");
+ }
+}
+
static int has_path_in_work_tree(const char *path)
{
struct stat st;