989
return;
990
o->num_lines = find_line_starts(&line_starts, o->file.ptr,
991
o->file.size);
992
- /* TODO: Will fill in fingerprints in a future commit */
992
+ o->fingerprints = xcalloc(sizeof(struct fingerprint), o->num_lines);
993
+ get_line_fingerprints(o->fingerprints, o->file.ptr, line_starts,
994
+ 0, o->num_lines);
995
free(line_starts);
996
}
997
998
static void drop_origin_fingerprints(struct blame_origin *o)
999
{
1000
+ if (o->fingerprints) {
1001
+ free_line_fingerprints(o->fingerprints, o->num_lines);
1002
+ o->num_lines = 0;
1003
+ FREE_AND_NULL(o->fingerprints);
1004
+ }
1005
}
1006
1007
/*
1579
first->s_lno + 1 == second->s_lno;
1580
}
1581
1582
+static int scan_parent_range(struct fingerprint *p_fps,
1583
+ struct fingerprint *t_fps, int t_idx,
1584
+ int from, int nr_lines)
1585
+{
1586
+ int sim, p_idx;
1587
+ #define FINGERPRINT_FILE_THRESHOLD 10
1588
+ int best_sim_val = FINGERPRINT_FILE_THRESHOLD;
1589
+ int best_sim_idx = -1;
1590
+
1591
+ for (p_idx = from; p_idx < from + nr_lines; p_idx++) {
1592
+ sim = fingerprint_similarity(&t_fps[t_idx], &p_fps[p_idx]);
1593
+ if (sim < best_sim_val)
1594
+ continue;
1595
+ /* Break ties with the closest-to-target line number */
1596
+ if (sim == best_sim_val && best_sim_idx != -1 &&
1597
+ abs(best_sim_idx - t_idx) < abs(p_idx - t_idx))
1598
+ continue;
1599
+ best_sim_val = sim;
1600
+ best_sim_idx = p_idx;
1601
+ }
1602
+ return best_sim_idx;
1603
+}
1604
+
1605
/*
1576
- * This cheap heuristic assigns lines in the chunk to their relative location in
1577
- * the parent's chunk. Any additional lines are left with the target.
1606
+ * The first pass checks the blame entry (from the target) against the parent's
1607
+ * diff chunk. If that fails for a line, the second pass tries to match that
1608
+ * line to any part of parent file. That catches cases where a change was
1609
+ * broken into two chunks by 'context.'
1610
*/
1611
static void guess_line_blames(struct blame_origin *parent,
1612
struct blame_origin *target,
1615
{
1616
int i, best_idx, target_idx;
1617
int parent_slno = tlno + offset;
1618
+ int *fuzzy_matches;
1619
1620
+ fuzzy_matches = fuzzy_find_matching_lines(parent, target,
1621
+ tlno, parent_slno, same,
1622
+ parent_len);
1623
for (i = 0; i < same - tlno; i++) {
1624
target_idx = tlno + i;
1589
- best_idx = target_idx + offset;
1590
- if (best_idx < parent_slno + parent_len) {
1625
+ if (fuzzy_matches && fuzzy_matches[i] >= 0) {
1626
+ best_idx = fuzzy_matches[i];
1627
+ } else {
1628
+ best_idx = scan_parent_range(parent->fingerprints,
1629
+ target->fingerprints,
1630
+ target_idx, 0,
1631
+ parent->num_lines);
1632
+ }
1633
+ if (best_idx >= 0) {
1634
line_blames[i].is_parent = 1;
1635
line_blames[i].s_lno = best_idx;
1636
} else {
1638
line_blames[i].s_lno = target_idx;
1639
}
1640
}
1641
+ free(fuzzy_matches);
1642
}
1643
1644
/*
2415
if (!porigin)
2416
continue;
2417
pass_blame_to_parent(sb, origin, porigin, 1);
2418
+ /*
2419
+ * Preemptively drop porigin so we can refresh the
2420
+ * fingerprints if we use the parent again, which can
2421
+ * occur if you ignore back-to-back commits.
2422
+ */
2423
+ drop_origin_blob(porigin);
2424
if (!origin->suspects)
2425
goto finish;
2426
}