Commit b6f60a4d by Ben Straub

Clean up ported code

parent 77db6ff5
...@@ -260,6 +260,8 @@ static int load_blob(git_blame *blame) ...@@ -260,6 +260,8 @@ static int load_blob(git_blame *blame)
{ {
int error; int error;
if (blame->final_blob) return 0;
error = git_commit_lookup(&blame->final, blame->repository, &blame->options.newest_commit); error = git_commit_lookup(&blame->final, blame->repository, &blame->options.newest_commit);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
...@@ -276,20 +278,13 @@ static int blame_internal(git_blame *blame) ...@@ -276,20 +278,13 @@ static int blame_internal(git_blame *blame)
{ {
int error; int error;
git_blame__entry *ent = NULL; git_blame__entry *ent = NULL;
git_blob *blob = NULL;
git_blame__origin *o; git_blame__origin *o;
if ((error = git_commit_lookup(&blame->final, blame->repository, if ((error = load_blob(blame)) < 0 ||
&blame->options.newest_commit)) (error = git_blame__get_origin(&o, blame, blame->final, blame->path)) < 0)
< 0 ||
(error = git_object_lookup_bypath((git_object**)&blob, (git_object*)blame->final,
blame->path, GIT_OBJ_BLOB))
< 0)
goto cleanup;
blame->final_buf = git_blob_rawcontent(blob);
blame->final_buf_size = git_blob_rawsize(blob);
if ((error = get_origin(&o, blame, blame->final, blame->path)) < 0)
goto cleanup; goto cleanup;
blame->final_buf = git_blob_rawcontent(blame->final_blob);
blame->final_buf_size = git_blob_rawsize(blame->final_blob);
ent = git__calloc(1, sizeof(git_blame__entry)); ent = git__calloc(1, sizeof(git_blame__entry));
ent->num_lines = index_blob_lines(blame); ent->num_lines = index_blob_lines(blame);
...@@ -303,8 +298,7 @@ static int blame_internal(git_blame *blame) ...@@ -303,8 +298,7 @@ static int blame_internal(git_blame *blame)
blame->ent = ent; blame->ent = ent;
blame->path = blame->path; blame->path = blame->path;
assign_blame(blame, blame->options.flags); git_blame__like_git(blame, blame->options.flags);
coalesce(blame);
cleanup: cleanup:
for (ent = blame->ent; ent; ) { for (ent = blame->ent; ent; ) {
...@@ -312,12 +306,10 @@ cleanup: ...@@ -312,12 +306,10 @@ cleanup:
git_vector_insert(&blame->hunks, hunk_from_entry(ent)); git_vector_insert(&blame->hunks, hunk_from_entry(ent));
origin_decref(ent->suspect); git_blame__free_entry(ent);
git__free(ent);
ent = e; ent = e;
} }
git_blob_free(blob);
return error; return error;
} }
......
...@@ -7,30 +7,32 @@ ...@@ -7,30 +7,32 @@
#include "blame_git.h" #include "blame_git.h"
#include "commit.h" #include "commit.h"
#include "xdiff/xinclude.h" #include "blob.h"
/* /*
* Locate an existing origin or create a new one. * Origin is refcounted and usually we keep the blob contents to be
* reused.
*/ */
int get_origin(git_blame__origin **out, git_blame *blame, git_commit *commit, const char *path) static git_blame__origin *origin_incref(git_blame__origin *o)
{ {
git_blame__entry *e; if (o)
o->refcnt++;
return o;
}
for (e = blame->ent; e; e = e->next) { static void origin_decref(git_blame__origin *o)
if (e->suspect->commit == commit && !strcmp(e->suspect->path, path)) { {
*out = origin_incref(e->suspect); if (o && --o->refcnt <= 0) {
} if (o->previous)
origin_decref(o->previous);
git_blob_free(o->blob);
git_commit_free(o->commit);
git__free(o);
} }
return make_origin(out, commit, path);
} }
/* /* Given a commit and a path in it, create a new origin structure. */
* Given a commit and a path in it, create a new origin structure. static int make_origin(git_blame__origin **out, git_commit *commit, const char *path)
* The callers that add blame to the scoreboard should use
* get_origin() to obtain shared, refcounted copy instead of calling
* this function directly.
*/
int make_origin(git_blame__origin **out, git_commit *commit, const char *path)
{ {
int error = 0; int error = 0;
git_blame__origin *o; git_blame__origin *o;
...@@ -50,13 +52,30 @@ int make_origin(git_blame__origin **out, git_commit *commit, const char *path) ...@@ -50,13 +52,30 @@ int make_origin(git_blame__origin **out, git_commit *commit, const char *path)
return error; return error;
} }
struct blame_chunk_cb_data { /* Locate an existing origin or create a new one. */
int git_blame__get_origin(
git_blame__origin **out,
git_blame *blame,
git_commit *commit,
const char *path)
{
git_blame__entry *e;
for (e = blame->ent; e; e = e->next) {
if (e->suspect->commit == commit && !strcmp(e->suspect->path, path)) {
*out = origin_incref(e->suspect);
}
}
return make_origin(out, commit, path);
}
typedef struct blame_chunk_cb_data {
git_blame *blame; git_blame *blame;
git_blame__origin *target; git_blame__origin *target;
git_blame__origin *parent; git_blame__origin *parent;
long tlno; long tlno;
long plno; long plno;
}; }blame_chunk_cb_data;
static bool same_suspect(git_blame__origin *a, git_blame__origin *b) static bool same_suspect(git_blame__origin *a, git_blame__origin *b)
{ {
...@@ -88,10 +107,10 @@ static int find_last_in_target(git_blame *blame, git_blame__origin *target) ...@@ -88,10 +107,10 @@ static int find_last_in_target(git_blame *blame, git_blame__origin *target)
* line plno corresponds to e's line tlno. * line plno corresponds to e's line tlno.
* *
* <---- e -----> * <---- e ----->
* <------> * <------> (entirely within)
* <------------> * <------------> (overlaps after)
* <------------> * <------------> (overlaps before)
* <------------------> * <------------------> (overlaps both)
* *
* Split e into potentially three parts; before this chunk, the chunk * Split e into potentially three parts; before this chunk, the chunk
* to be blamed for the parent, and after that portion. * to be blamed for the parent, and after that portion.
...@@ -237,7 +256,13 @@ static void decref_split(git_blame__entry *split) ...@@ -237,7 +256,13 @@ static void decref_split(git_blame__entry *split)
* Helper for blame_chunk(). blame_entry e is known to overlap with the patch * Helper for blame_chunk(). blame_entry e is known to overlap with the patch
* hunk; split it and pass blame to the parent. * hunk; split it and pass blame to the parent.
*/ */
static void blame_overlap(git_blame *blame, git_blame__entry *e, int tlno, int plno, int same, git_blame__origin *parent) static void blame_overlap(
git_blame *blame,
git_blame__entry *e,
int tlno,
int plno,
int same,
git_blame__origin *parent)
{ {
git_blame__entry split[3] = {{0}}; git_blame__entry split[3] = {{0}};
...@@ -252,7 +277,13 @@ static void blame_overlap(git_blame *blame, git_blame__entry *e, int tlno, int p ...@@ -252,7 +277,13 @@ static void blame_overlap(git_blame *blame, git_blame__entry *e, int tlno, int p
* e and its parent. Find and split the overlap, and pass blame to the * e and its parent. Find and split the overlap, and pass blame to the
* overlapping part to the parent. * overlapping part to the parent.
*/ */
static void blame_chunk(git_blame *blame, int tlno, int plno, int same, git_blame__origin *target, git_blame__origin *parent) static void blame_chunk(
git_blame *blame,
int tlno,
int plno,
int same,
git_blame__origin *target,
git_blame__origin *parent)
{ {
git_blame__entry *e; git_blame__entry *e;
...@@ -267,21 +298,20 @@ static void blame_chunk(git_blame *blame, int tlno, int plno, int same, git_blam ...@@ -267,21 +298,20 @@ static void blame_chunk(git_blame *blame, int tlno, int plno, int same, git_blam
} }
} }
static void blame_chunk_cb(long start_a, long count_a, long start_b, long count_b, void *data) static int my_emit(
{ xdfenv_t *xe,
struct blame_chunk_cb_data *d = data; xdchange_t *xscr,
blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent); xdemitcb_t *ecb,
d->plno = start_a + count_a; xdemitconf_t const *xecfg)
d->tlno = start_b + count_b;
}
static int my_emit_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg)
{ {
xdchange_t *xch = xscr; xdchange_t *xch = xscr;
GIT_UNUSED(xe); GIT_UNUSED(xe);
GIT_UNUSED(xecfg); GIT_UNUSED(xecfg);
while (xch) { while (xch) {
blame_chunk_cb(xch->i1, xch->chg1, xch->i2, xch->chg2, ecb->priv); blame_chunk_cb_data *d = ecb->priv;
blame_chunk(d->blame, d->tlno, d->plno, xch->i2, d->target, d->parent);
d->plno = xch->i1 + xch->chg1;
d->tlno = xch->i2 + xch->chg2;
xch = xch->next; xch = xch->next;
} }
return 0; return 0;
...@@ -311,26 +341,17 @@ static void trim_common_tail(mmfile_t *a, mmfile_t *b, long ctx) ...@@ -311,26 +341,17 @@ static void trim_common_tail(mmfile_t *a, mmfile_t *b, long ctx)
b->size -= trimmed - recovered; b->size -= trimmed - recovered;
} }
static int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *xecb) static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data)
{
mmfile_t a = *mf1;
mmfile_t b = *mf2;
trim_common_tail(&a, &b, xecfg->ctxlen);
return xdl_diff(&a, &b, xpp, xecfg, xecb);
}
static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, void *cb_data)
{ {
xpparam_t xpp = {0}; xpparam_t xpp = {0};
xdemitconf_t xecfg = {0}; xdemitconf_t xecfg = {0};
xdemitcb_t ecb = {0}; xdemitcb_t ecb = {0};
xecfg.emit_func = (void(*)(void))my_emit_func; xecfg.emit_func = (void(*)(void))my_emit;
ecb.priv = cb_data; ecb.priv = cb_data;
return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
trim_common_tail(&file_a, &file_b, 0);
return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb);
} }
static void fill_origin_blob(git_blame__origin *o, mmfile_t *file) static void fill_origin_blob(git_blame__origin *o, mmfile_t *file)
...@@ -342,13 +363,14 @@ static void fill_origin_blob(git_blame__origin *o, mmfile_t *file) ...@@ -342,13 +363,14 @@ static void fill_origin_blob(git_blame__origin *o, mmfile_t *file)
} }
} }
static int pass_blame_to_parent(git_blame *blame, static int pass_blame_to_parent(
git_blame__origin *target, git_blame *blame,
git_blame__origin *parent) git_blame__origin *target,
git_blame__origin *parent)
{ {
int last_in_target; int last_in_target;
mmfile_t file_p, file_o; mmfile_t file_p, file_o;
struct blame_chunk_cb_data d = { blame, target, parent, 0, 0 }; blame_chunk_cb_data d = { blame, target, parent, 0, 0 };
last_in_target = find_last_in_target(blame, target); last_in_target = find_last_in_target(blame, target);
if (last_in_target < 0) if (last_in_target < 0)
...@@ -357,7 +379,7 @@ static int pass_blame_to_parent(git_blame *blame, ...@@ -357,7 +379,7 @@ static int pass_blame_to_parent(git_blame *blame,
fill_origin_blob(parent, &file_p); fill_origin_blob(parent, &file_p);
fill_origin_blob(target, &file_o); fill_origin_blob(target, &file_o);
diff_hunks(&file_p, &file_o, &d); diff_hunks(file_p, file_o, &d);
/* The reset (i.e. anything after tlno) are the same as the parent */ /* The reset (i.e. anything after tlno) are the same as the parent */
blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent); blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent);
...@@ -370,7 +392,10 @@ static int paths_on_dup(void **old, void *new) ...@@ -370,7 +392,10 @@ static int paths_on_dup(void **old, void *new)
git__free(new); git__free(new);
return -1; return -1;
} }
static git_blame__origin* find_origin(git_blame *blame, git_commit *parent,
static git_blame__origin* find_origin(
git_blame *blame,
git_commit *parent,
git_blame__origin *origin) git_blame__origin *origin)
{ {
git_blame__origin *porigin = NULL; git_blame__origin *porigin = NULL;
...@@ -395,7 +420,7 @@ static git_blame__origin* find_origin(git_blame *blame, git_commit *parent, ...@@ -395,7 +420,7 @@ static git_blame__origin* find_origin(git_blame *blame, git_commit *parent,
if (!git_diff_num_deltas(difflist)) { if (!git_diff_num_deltas(difflist)) {
/* No changes; copy data */ /* No changes; copy data */
get_origin(&porigin, blame, parent, origin->path); git_blame__get_origin(&porigin, blame, parent, origin->path);
} else { } else {
git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
int i; int i;
...@@ -418,7 +443,8 @@ static git_blame__origin* find_origin(git_blame *blame, git_commit *parent, ...@@ -418,7 +443,8 @@ static git_blame__origin* find_origin(git_blame *blame, git_commit *parent,
if (git_vector_bsearch(NULL, &blame->paths, delta->new_file.path) != 0) if (git_vector_bsearch(NULL, &blame->paths, delta->new_file.path) != 0)
continue; continue;
git_vector_insert_sorted(&blame->paths, (void*)git__strdup(delta->old_file.path), paths_on_dup); git_vector_insert_sorted(&blame->paths, (void*)git__strdup(delta->old_file.path),
paths_on_dup);
make_origin(&porigin, parent, delta->old_file.path); make_origin(&porigin, parent, delta->old_file.path);
} }
} }
...@@ -440,8 +466,8 @@ static void pass_whole_blame(git_blame *blame, ...@@ -440,8 +466,8 @@ static void pass_whole_blame(git_blame *blame,
git_blame__entry *e; git_blame__entry *e;
if (!porigin->blob) if (!porigin->blob)
git_object_lookup((git_object**)&porigin->blob, blame->repository, git_blob_id(origin->blob), git_object_lookup((git_object**)&porigin->blob, blame->repository,
GIT_OBJ_BLOB); git_blob_id(origin->blob), GIT_OBJ_BLOB);
for (e=blame->ent; e; e=e->next) { for (e=blame->ent; e; e=e->next) {
if (!same_suspect(e->suspect, origin)) if (!same_suspect(e->suspect, origin))
continue; continue;
...@@ -454,25 +480,26 @@ static void pass_whole_blame(git_blame *blame, ...@@ -454,25 +480,26 @@ static void pass_whole_blame(git_blame *blame,
static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
{ {
git_commit *commit = origin->commit; git_commit *commit = origin->commit;
int i, num_sg; int i, num_parents;
git_blame__origin *sg_buf[16]; git_blame__origin *sg_buf[16];
git_blame__origin *porigin, **sg_origin = sg_buf; git_blame__origin *porigin, **sg_origin = sg_buf;
GIT_UNUSED(opt); GIT_UNUSED(opt);
num_sg = git_commit_parentcount(commit); num_parents = git_commit_parentcount(commit);
if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit)) if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit))
num_sg = 0; /* Stop at oldest specified commit */
if (!num_sg) { num_parents = 0;
if (!num_parents) {
git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit)); git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit));
goto finish; goto finish;
} }
else if (num_sg < (int)ARRAY_SIZE(sg_buf)) else if (num_parents < (int)ARRAY_SIZE(sg_buf))
memset(sg_buf, 0, sizeof(sg_buf)); memset(sg_buf, 0, sizeof(sg_buf));
else else
sg_origin = git__calloc(num_sg, sizeof(*sg_origin)); sg_origin = git__calloc(num_parents, sizeof(*sg_origin));
for (i=0; i<num_sg; i++) { for (i=0; i<num_parents; i++) {
git_commit *p; git_commit *p;
int j, same; int j, same;
...@@ -502,7 +529,8 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt ...@@ -502,7 +529,8 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt
origin_decref(porigin); origin_decref(porigin);
} }
for (i=0; i<num_sg; i++) { /* Standard blame */
for (i=0; i<num_parents; i++) {
git_blame__origin *porigin = sg_origin[i]; git_blame__origin *porigin = sg_origin[i];
if (!porigin) if (!porigin)
continue; continue;
...@@ -519,7 +547,7 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt ...@@ -519,7 +547,7 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt
/* TODO: optionally find copies in parents' files */ /* TODO: optionally find copies in parents' files */
finish: finish:
for (i=0; i<num_sg; i++) for (i=0; i<num_parents; i++)
if (sg_origin[i]) if (sg_origin[i])
origin_decref(sg_origin[i]); origin_decref(sg_origin[i]);
if (sg_origin != sg_buf) if (sg_origin != sg_buf)
...@@ -528,28 +556,32 @@ finish: ...@@ -528,28 +556,32 @@ finish:
} }
/* /*
* Origin is refcounted and usually we keep the blob contents to be * If two blame entries that are next to each other came from
* reused. * contiguous lines in the same origin (i.e. <commit, path> pair),
* merge them together.
*/ */
git_blame__origin *origin_incref(git_blame__origin *o) static void coalesce(git_blame *blame)
{ {
if (o) git_blame__entry *ent, *next;
o->refcnt++;
return o;
}
void origin_decref(git_blame__origin *o) for (ent=blame->ent; ent && (next = ent->next); ent = next) {
{ if (same_suspect(ent->suspect, next->suspect) &&
if (o && --o->refcnt <= 0) { ent->guilty == next->guilty &&
if (o->previous) ent->s_lno + ent->num_lines == next->s_lno)
origin_decref(o->previous); {
git_blob_free(o->blob); ent->num_lines += next->num_lines;
git_commit_free(o->commit); ent->next = next->next;
git__free(o); if (ent->next)
ent->next->prev = ent;
origin_decref(next->suspect);
git__free(next);
ent->score = 0;
next = ent; /* again */
}
} }
} }
void assign_blame(git_blame *blame, uint32_t opt) void git_blame__like_git(git_blame *blame, uint32_t opt)
{ {
while (true) { while (true) {
git_blame__entry *ent; git_blame__entry *ent;
...@@ -577,26 +609,13 @@ void assign_blame(git_blame *blame, uint32_t opt) ...@@ -577,26 +609,13 @@ void assign_blame(git_blame *blame, uint32_t opt)
} }
origin_decref(suspect); origin_decref(suspect);
} }
coalesce(blame);
} }
void coalesce(git_blame *blame) void git_blame__free_entry(git_blame__entry *ent)
{ {
git_blame__entry *ent, *next; if (!ent) return;
origin_decref(ent->suspect);
for (ent=blame->ent; ent && (next = ent->next); ent = next) { git__free(ent);
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 */
}
}
} }
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_blame_git__ #ifndef INCLUDE_blame_git__
#define INCLUDE_blame_git__ #define INCLUDE_blame_git__
#include "git2.h"
#include "blame.h" #include "blame.h"
#include "xdiff/xinclude.h" #include "xdiff/xinclude.h"
int get_origin(git_blame__origin **out, git_blame *sb, git_commit *commit, const char *path); int git_blame__get_origin(
int make_origin(git_blame__origin **out, git_commit *commit, const char *path); git_blame__origin **out,
git_blame__origin *origin_incref(git_blame__origin *o); git_blame *sb,
void origin_decref(git_blame__origin *o); git_commit *commit,
void assign_blame(git_blame *sb, uint32_t flags); const char *path);
void coalesce(git_blame *sb); void git_blame__free_entry(git_blame__entry *ent);
void git_blame__like_git(git_blame *sb, uint32_t flags);
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment