Skip to content

Commit 09002f1

Browse files
whydoubtgitster
authored andcommitted
blame: move scoreboard setup to libgit
Signed-off-by: Jeff Smith <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b543bb1 commit 09002f1

File tree

3 files changed

+281
-284
lines changed

3 files changed

+281
-284
lines changed

blame.c

Lines changed: 275 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "mergesort.h"
55
#include "diff.h"
66
#include "diffcore.h"
7+
#include "tag.h"
78
#include "blame.h"
89

910
void blame_origin_decref(struct blame_origin *o)
@@ -49,7 +50,7 @@ static struct blame_origin *make_origin(struct commit *commit, const char *path)
4950
* Locate an existing origin or create a new one.
5051
* This moves the origin to front position in the commit util list.
5152
*/
52-
struct blame_origin *get_origin(struct commit *commit, const char *path)
53+
static struct blame_origin *get_origin(struct commit *commit, const char *path)
5354
{
5455
struct blame_origin *o, *l;
5556

@@ -142,9 +143,9 @@ static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
142143
* Prepare a dummy commit that represents the work tree (or staged) item.
143144
* Note that annotating work tree item never works in the reverse.
144145
*/
145-
struct commit *fake_working_tree_commit(struct diff_options *opt,
146-
const char *path,
147-
const char *contents_from)
146+
static struct commit *fake_working_tree_commit(struct diff_options *opt,
147+
const char *path,
148+
const char *contents_from)
148149
{
149150
struct commit *commit;
150151
struct blame_origin *origin;
@@ -410,6 +411,13 @@ void blame_sort_final(struct blame_scoreboard *sb)
410411
compare_blame_final);
411412
}
412413

414+
static int compare_commits_by_reverse_commit_date(const void *a,
415+
const void *b,
416+
void *c)
417+
{
418+
return -compare_commits_by_commit_date(a, b, c);
419+
}
420+
413421
/*
414422
* For debugging -- origin is refcounted, and this asserts that
415423
* we do not underflow.
@@ -482,6 +490,32 @@ static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porig
482490
}
483491
}
484492

493+
/*
494+
* Fill the blob_sha1 field of an origin if it hasn't, so that later
495+
* call to fill_origin_blob() can use it to locate the data. blob_sha1
496+
* for an origin is also used to pass the blame for the entire file to
497+
* the parent to detect the case where a child's blob is identical to
498+
* that of its parent's.
499+
*
500+
* This also fills origin->mode for corresponding tree path.
501+
*/
502+
static int fill_blob_sha1_and_mode(struct blame_origin *origin)
503+
{
504+
if (!is_null_oid(&origin->blob_oid))
505+
return 0;
506+
if (get_tree_entry(origin->commit->object.oid.hash,
507+
origin->path,
508+
origin->blob_oid.hash, &origin->mode))
509+
goto error_out;
510+
if (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)
511+
goto error_out;
512+
return 0;
513+
error_out:
514+
oidclr(&origin->blob_oid);
515+
origin->mode = S_IFINVALID;
516+
return -1;
517+
}
518+
485519
/*
486520
* We have an origin -- check if the same path exists in the
487521
* parent and return an origin structure to represent it.
@@ -1574,3 +1608,240 @@ void assign_blame(struct blame_scoreboard *sb, int opt)
15741608
sanity_check_refcnt(sb);
15751609
}
15761610
}
1611+
1612+
static const char *get_next_line(const char *start, const char *end)
1613+
{
1614+
const char *nl = memchr(start, '\n', end - start);
1615+
return nl ? nl + 1 : end;
1616+
}
1617+
1618+
/*
1619+
* To allow quick access to the contents of nth line in the
1620+
* final image, prepare an index in the scoreboard.
1621+
*/
1622+
static int prepare_lines(struct blame_scoreboard *sb)
1623+
{
1624+
const char *buf = sb->final_buf;
1625+
unsigned long len = sb->final_buf_size;
1626+
const char *end = buf + len;
1627+
const char *p;
1628+
int *lineno;
1629+
int num = 0;
1630+
1631+
for (p = buf; p < end; p = get_next_line(p, end))
1632+
num++;
1633+
1634+
ALLOC_ARRAY(sb->lineno, num + 1);
1635+
lineno = sb->lineno;
1636+
1637+
for (p = buf; p < end; p = get_next_line(p, end))
1638+
*lineno++ = p - buf;
1639+
1640+
*lineno = len;
1641+
1642+
sb->num_lines = num;
1643+
return sb->num_lines;
1644+
}
1645+
1646+
static struct commit *find_single_final(struct rev_info *revs,
1647+
const char **name_p)
1648+
{
1649+
int i;
1650+
struct commit *found = NULL;
1651+
const char *name = NULL;
1652+
1653+
for (i = 0; i < revs->pending.nr; i++) {
1654+
struct object *obj = revs->pending.objects[i].item;
1655+
if (obj->flags & UNINTERESTING)
1656+
continue;
1657+
obj = deref_tag(obj, NULL, 0);
1658+
if (obj->type != OBJ_COMMIT)
1659+
die("Non commit %s?", revs->pending.objects[i].name);
1660+
if (found)
1661+
die("More than one commit to dig from %s and %s?",
1662+
revs->pending.objects[i].name, name);
1663+
found = (struct commit *)obj;
1664+
name = revs->pending.objects[i].name;
1665+
}
1666+
if (name_p)
1667+
*name_p = name;
1668+
return found;
1669+
}
1670+
1671+
static struct commit *dwim_reverse_initial(struct rev_info *revs,
1672+
const char **name_p)
1673+
{
1674+
/*
1675+
* DWIM "git blame --reverse ONE -- PATH" as
1676+
* "git blame --reverse ONE..HEAD -- PATH" but only do so
1677+
* when it makes sense.
1678+
*/
1679+
struct object *obj;
1680+
struct commit *head_commit;
1681+
unsigned char head_sha1[20];
1682+
1683+
if (revs->pending.nr != 1)
1684+
return NULL;
1685+
1686+
/* Is that sole rev a committish? */
1687+
obj = revs->pending.objects[0].item;
1688+
obj = deref_tag(obj, NULL, 0);
1689+
if (obj->type != OBJ_COMMIT)
1690+
return NULL;
1691+
1692+
/* Do we have HEAD? */
1693+
if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
1694+
return NULL;
1695+
head_commit = lookup_commit_reference_gently(head_sha1, 1);
1696+
if (!head_commit)
1697+
return NULL;
1698+
1699+
/* Turn "ONE" into "ONE..HEAD" then */
1700+
obj->flags |= UNINTERESTING;
1701+
add_pending_object(revs, &head_commit->object, "HEAD");
1702+
1703+
if (name_p)
1704+
*name_p = revs->pending.objects[0].name;
1705+
return (struct commit *)obj;
1706+
}
1707+
1708+
static struct commit *find_single_initial(struct rev_info *revs,
1709+
const char **name_p)
1710+
{
1711+
int i;
1712+
struct commit *found = NULL;
1713+
const char *name = NULL;
1714+
1715+
/*
1716+
* There must be one and only one negative commit, and it must be
1717+
* the boundary.
1718+
*/
1719+
for (i = 0; i < revs->pending.nr; i++) {
1720+
struct object *obj = revs->pending.objects[i].item;
1721+
if (!(obj->flags & UNINTERESTING))
1722+
continue;
1723+
obj = deref_tag(obj, NULL, 0);
1724+
if (obj->type != OBJ_COMMIT)
1725+
die("Non commit %s?", revs->pending.objects[i].name);
1726+
if (found)
1727+
die("More than one commit to dig up from, %s and %s?",
1728+
revs->pending.objects[i].name, name);
1729+
found = (struct commit *) obj;
1730+
name = revs->pending.objects[i].name;
1731+
}
1732+
1733+
if (!name)
1734+
found = dwim_reverse_initial(revs, &name);
1735+
if (!name)
1736+
die("No commit to dig up from?");
1737+
1738+
if (name_p)
1739+
*name_p = name;
1740+
return found;
1741+
}
1742+
1743+
void init_scoreboard(struct blame_scoreboard *sb)
1744+
{
1745+
memset(sb, 0, sizeof(struct blame_scoreboard));
1746+
sb->move_score = BLAME_DEFAULT_MOVE_SCORE;
1747+
sb->copy_score = BLAME_DEFAULT_COPY_SCORE;
1748+
}
1749+
1750+
void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig)
1751+
{
1752+
const char *final_commit_name = NULL;
1753+
struct blame_origin *o;
1754+
struct commit *final_commit = NULL;
1755+
enum object_type type;
1756+
1757+
if (sb->reverse && sb->contents_from)
1758+
die(_("--contents and --reverse do not blend well."));
1759+
1760+
if (!sb->reverse) {
1761+
sb->final = find_single_final(sb->revs, &final_commit_name);
1762+
sb->commits.compare = compare_commits_by_commit_date;
1763+
} else {
1764+
sb->final = find_single_initial(sb->revs, &final_commit_name);
1765+
sb->commits.compare = compare_commits_by_reverse_commit_date;
1766+
}
1767+
1768+
if (sb->final && sb->contents_from)
1769+
die(_("cannot use --contents with final commit object name"));
1770+
1771+
if (sb->reverse && sb->revs->first_parent_only)
1772+
sb->revs->children.name = NULL;
1773+
1774+
if (!sb->final) {
1775+
/*
1776+
* "--not A B -- path" without anything positive;
1777+
* do not default to HEAD, but use the working tree
1778+
* or "--contents".
1779+
*/
1780+
setup_work_tree();
1781+
sb->final = fake_working_tree_commit(&sb->revs->diffopt,
1782+
path, sb->contents_from);
1783+
add_pending_object(sb->revs, &(sb->final->object), ":");
1784+
}
1785+
1786+
if (sb->reverse && sb->revs->first_parent_only) {
1787+
final_commit = find_single_final(sb->revs, NULL);
1788+
if (!final_commit)
1789+
die(_("--reverse and --first-parent together require specified latest commit"));
1790+
}
1791+
1792+
/*
1793+
* If we have bottom, this will mark the ancestors of the
1794+
* bottom commits we would reach while traversing as
1795+
* uninteresting.
1796+
*/
1797+
if (prepare_revision_walk(sb->revs))
1798+
die(_("revision walk setup failed"));
1799+
1800+
if (sb->reverse && sb->revs->first_parent_only) {
1801+
struct commit *c = final_commit;
1802+
1803+
sb->revs->children.name = "children";
1804+
while (c->parents &&
1805+
oidcmp(&c->object.oid, &sb->final->object.oid)) {
1806+
struct commit_list *l = xcalloc(1, sizeof(*l));
1807+
1808+
l->item = c;
1809+
if (add_decoration(&sb->revs->children,
1810+
&c->parents->item->object, l))
1811+
die("BUG: not unique item in first-parent chain");
1812+
c = c->parents->item;
1813+
}
1814+
1815+
if (oidcmp(&c->object.oid, &sb->final->object.oid))
1816+
die(_("--reverse --first-parent together require range along first-parent chain"));
1817+
}
1818+
1819+
if (is_null_oid(&sb->final->object.oid)) {
1820+
o = sb->final->util;
1821+
sb->final_buf = xmemdupz(o->file.ptr, o->file.size);
1822+
sb->final_buf_size = o->file.size;
1823+
}
1824+
else {
1825+
o = get_origin(sb->final, path);
1826+
if (fill_blob_sha1_and_mode(o))
1827+
die(_("no such path %s in %s"), path, final_commit_name);
1828+
1829+
if (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) &&
1830+
textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
1831+
&sb->final_buf_size))
1832+
;
1833+
else
1834+
sb->final_buf = read_sha1_file(o->blob_oid.hash, &type,
1835+
&sb->final_buf_size);
1836+
1837+
if (!sb->final_buf)
1838+
die(_("cannot read blob %s for path %s"),
1839+
oid_to_hex(&o->blob_oid),
1840+
path);
1841+
}
1842+
sb->num_read_blob++;
1843+
prepare_lines(sb);
1844+
1845+
if (orig)
1846+
*orig = o;
1847+
}

blame.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#define PICKAXE_BLAME_COPY_HARDER 04
1414
#define PICKAXE_BLAME_COPY_HARDEST 010
1515

16+
#define BLAME_DEFAULT_MOVE_SCORE 20
17+
#define BLAME_DEFAULT_COPY_SCORE 40
18+
1619
/*
1720
* One blob in a commit that is being suspected
1821
*/
@@ -158,14 +161,13 @@ static inline struct blame_origin *blame_origin_incref(struct blame_origin *o)
158161
}
159162
extern void blame_origin_decref(struct blame_origin *o);
160163

161-
extern struct blame_origin *get_origin(struct commit *commit, const char *path);
162-
163-
extern struct commit *fake_working_tree_commit(struct diff_options *opt, const char *path, const char *contents_from);
164-
165164
extern void blame_coalesce(struct blame_scoreboard *sb);
166165
extern void blame_sort_final(struct blame_scoreboard *sb);
167166
extern unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e);
168167
extern void assign_blame(struct blame_scoreboard *sb, int opt);
169168
extern const char *blame_nth_line(struct blame_scoreboard *sb, long lno);
170169

170+
extern void init_scoreboard(struct blame_scoreboard *sb);
171+
extern void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig);
172+
171173
#endif /* BLAME_H */

0 commit comments

Comments
 (0)