Skip to content

Commit

Permalink
Merge branch 'jc/blame' (early part) into HEAD
Browse files Browse the repository at this point in the history
* 'jc/blame' (early part):
  git-blame --reverse
  builtin-blame.c: allow more than 16 parents
  builtin-blame.c: move prepare_final() into a separate function.
  rev-list --children
  revision traversal: --children option

Conflicts:

	Documentation/rev-list-options.txt
	revision.c
  • Loading branch information
Junio C Hamano committed Jul 8, 2008
2 parents a32a4ea + 85af792 commit 8bb6588
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 61 deletions.
4 changes: 4 additions & 0 deletions Documentation/rev-list-options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ endif::git-rev-list[]

Print the parents of the commit.

--children::

Print the children of the commit.

ifdef::git-rev-list[]
--timestamp::
Print the raw commit timestamp.
Expand Down
206 changes: 147 additions & 59 deletions builtin-blame.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ static int max_orig_digits;
static int max_digits;
static int max_score_digits;
static int show_root;
static int reverse;
static int blank_boundary;
static int incremental;
static int cmd_is_annotate;
Expand Down Expand Up @@ -91,7 +92,7 @@ struct origin {
* Given an origin, prepare mmfile_t structure to be used by the
* diff machinery
*/
static char *fill_origin_blob(struct origin *o, mmfile_t *file)
static void fill_origin_blob(struct origin *o, mmfile_t *file)
{
if (!o->file.ptr) {
enum object_type type;
Expand All @@ -106,7 +107,6 @@ static char *fill_origin_blob(struct origin *o, mmfile_t *file)
}
else
*file = o->file;
return file->ptr;
}

/*
Expand Down Expand Up @@ -178,7 +178,7 @@ struct blame_entry {
struct scoreboard {
/* the final commit (i.e. where we started digging from) */
struct commit *final;

struct rev_info *revs;
const char *path;

/*
Expand Down Expand Up @@ -1192,32 +1192,62 @@ static void pass_whole_blame(struct scoreboard *sb,
}
}

#define MAXPARENT 16
/*
* We pass blame from the current commit to its parents. We keep saying
* "parent" (and "porigin"), but what we mean is to find scapegoat to
* exonerate ourselves.
*/
static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
{
if (!reverse)
return commit->parents;
return lookup_decoration(&revs->children, &commit->object);
}

static int num_scapegoats(struct rev_info *revs, struct commit *commit)
{
int cnt;
struct commit_list *l = first_scapegoat(revs, commit);
for (cnt = 0; l; l = l->next)
cnt++;
return cnt;
}

#define MAXSG 16

static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
{
int i, pass;
struct rev_info *revs = sb->revs;
int i, pass, num_sg;
struct commit *commit = origin->commit;
struct commit_list *parent;
struct origin *parent_origin[MAXPARENT], *porigin;

memset(parent_origin, 0, sizeof(parent_origin));
struct commit_list *sg;
struct origin *sg_buf[MAXSG];
struct origin *porigin, **sg_origin = sg_buf;

num_sg = num_scapegoats(revs, commit);
if (!num_sg)
goto finish;
else if (num_sg < ARRAY_SIZE(sg_buf))
memset(sg_buf, 0, sizeof(sg_buf));
else
sg_origin = xcalloc(num_sg, sizeof(*sg_origin));

/* The first pass looks for unrenamed path to optimize for
/*
* The first pass looks for unrenamed path to optimize for
* common cases, then we look for renames in the second pass.
*/
for (pass = 0; pass < 2; pass++) {
struct origin *(*find)(struct scoreboard *,
struct commit *, struct origin *);
find = pass ? find_rename : find_origin;

for (i = 0, parent = commit->parents;
i < MAXPARENT && parent;
parent = parent->next, i++) {
struct commit *p = parent->item;
for (i = 0, sg = first_scapegoat(revs, commit);
i < num_sg && sg;
sg = sg->next, i++) {
struct commit *p = sg->item;
int j, same;

if (parent_origin[i])
if (sg_origin[i])
continue;
if (parse_commit(p))
continue;
Expand All @@ -1230,24 +1260,24 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
goto finish;
}
for (j = same = 0; j < i; j++)
if (parent_origin[j] &&
!hashcmp(parent_origin[j]->blob_sha1,
if (sg_origin[j] &&
!hashcmp(sg_origin[j]->blob_sha1,
porigin->blob_sha1)) {
same = 1;
break;
}
if (!same)
parent_origin[i] = porigin;
sg_origin[i] = porigin;
else
origin_decref(porigin);
}
}

num_commits++;
for (i = 0, parent = commit->parents;
i < MAXPARENT && parent;
parent = parent->next, i++) {
struct origin *porigin = parent_origin[i];
for (i = 0, sg = first_scapegoat(revs, commit);
i < num_sg && sg;
sg = sg->next, i++) {
struct origin *porigin = sg_origin[i];
if (!porigin)
continue;
if (pass_blame_to_parent(sb, origin, porigin))
Expand All @@ -1258,10 +1288,10 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
* Optionally find moves in parents' files.
*/
if (opt & PICKAXE_BLAME_MOVE)
for (i = 0, parent = commit->parents;
i < MAXPARENT && parent;
parent = parent->next, i++) {
struct origin *porigin = parent_origin[i];
for (i = 0, sg = first_scapegoat(revs, commit);
i < num_sg && sg;
sg = sg->next, i++) {
struct origin *porigin = sg_origin[i];
if (!porigin)
continue;
if (find_move_in_parent(sb, origin, porigin))
Expand All @@ -1272,23 +1302,25 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
* Optionally find copies from parents' files.
*/
if (opt & PICKAXE_BLAME_COPY)
for (i = 0, parent = commit->parents;
i < MAXPARENT && parent;
parent = parent->next, i++) {
struct origin *porigin = parent_origin[i];
if (find_copy_in_parent(sb, origin, parent->item,
for (i = 0, sg = first_scapegoat(revs, commit);
i < num_sg && sg;
sg = sg->next, i++) {
struct origin *porigin = sg_origin[i];
if (find_copy_in_parent(sb, origin, sg->item,
porigin, opt))
goto finish;
}

finish:
for (i = 0; i < MAXPARENT; i++) {
if (parent_origin[i]) {
drop_origin_blob(parent_origin[i]);
origin_decref(parent_origin[i]);
for (i = 0; i < num_sg; i++) {
if (sg_origin[i]) {
drop_origin_blob(sg_origin[i]);
origin_decref(sg_origin[i]);
}
}
drop_origin_blob(origin);
if (sg_buf != sg_origin)
free(sg_origin);
}

/*
Expand Down Expand Up @@ -1487,8 +1519,10 @@ static void found_guilty_entry(struct blame_entry *ent)
* is still unknown, pick one blame_entry, and allow its current
* suspect to pass blames to its parents.
*/
static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
static void assign_blame(struct scoreboard *sb, int opt)
{
struct rev_info *revs = sb->revs;

while (1) {
struct blame_entry *ent;
struct commit *commit;
Expand All @@ -1509,8 +1543,9 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
commit = suspect->commit;
if (!commit->object.parsed)
parse_commit(commit);
if (!(commit->object.flags & UNINTERESTING) &&
!(revs->max_age != -1 && commit->date < revs->max_age))
if (reverse ||
(!(commit->object.flags & UNINTERESTING) &&
!(revs->max_age != -1 && commit->date < revs->max_age)))
pass_blame(sb, suspect, opt);
else {
commit->object.flags |= UNINTERESTING;
Expand Down Expand Up @@ -2006,6 +2041,10 @@ static int git_blame_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}

/*
* Prepare a dummy commit that represents the work tree (or staged) item.
* Note that annotating work tree item never works in the reverse.
*/
static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
{
struct commit *commit;
Expand Down Expand Up @@ -2122,6 +2161,64 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
return commit;
}

static const char *prepare_final(struct scoreboard *sb)
{
int i;
const char *final_commit_name = NULL;
struct rev_info *revs = sb->revs;

/*
* There must be one and only one positive commit in the
* revs->pending array.
*/
for (i = 0; i < revs->pending.nr; i++) {
struct object *obj = revs->pending.objects[i].item;
if (obj->flags & UNINTERESTING)
continue;
while (obj->type == OBJ_TAG)
obj = deref_tag(obj, NULL, 0);
if (obj->type != OBJ_COMMIT)
die("Non commit %s?", revs->pending.objects[i].name);
if (sb->final)
die("More than one commit to dig from %s and %s?",
revs->pending.objects[i].name,
final_commit_name);
sb->final = (struct commit *) obj;
final_commit_name = revs->pending.objects[i].name;
}
return final_commit_name;
}

static const char *prepare_initial(struct scoreboard *sb)
{
int i;
const char *final_commit_name = NULL;
struct rev_info *revs = sb->revs;

/*
* There must be one and only one negative commit, and it must be
* the boundary.
*/
for (i = 0; i < revs->pending.nr; i++) {
struct object *obj = revs->pending.objects[i].item;
if (!(obj->flags & UNINTERESTING))
continue;
while (obj->type == OBJ_TAG)
obj = deref_tag(obj, NULL, 0);
if (obj->type != OBJ_COMMIT)
die("Non commit %s?", revs->pending.objects[i].name);
if (sb->final)
die("More than one commit to dig down to %s and %s?",
revs->pending.objects[i].name,
final_commit_name);
sb->final = (struct commit *) obj;
final_commit_name = revs->pending.objects[i].name;
}
if (!final_commit_name)
die("No commit to dig down to?");
return final_commit_name;
}

int cmd_blame(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
Expand Down Expand Up @@ -2154,6 +2251,10 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
blank_boundary = 1;
else if (!strcmp("--root", arg))
show_root = 1;
else if (!strcmp("--reverse", arg)) {
argv[unk++] = "--children";
reverse = 1;
}
else if (!strcmp(arg, "--show-stats"))
show_stats = 1;
else if (!strcmp("-c", arg))
Expand Down Expand Up @@ -2327,26 +2428,13 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
setup_revisions(unk, argv, &revs, NULL);
memset(&sb, 0, sizeof(sb));

/*
* There must be one and only one positive commit in the
* revs->pending array.
*/
for (i = 0; i < revs.pending.nr; i++) {
struct object *obj = revs.pending.objects[i].item;
if (obj->flags & UNINTERESTING)
continue;
while (obj->type == OBJ_TAG)
obj = deref_tag(obj, NULL, 0);
if (obj->type != OBJ_COMMIT)
die("Non commit %s?",
revs.pending.objects[i].name);
if (sb.final)
die("More than one commit to dig from %s and %s?",
revs.pending.objects[i].name,
final_commit_name);
sb.final = (struct commit *) obj;
final_commit_name = revs.pending.objects[i].name;
}
sb.revs = &revs;
if (!reverse)
final_commit_name = prepare_final(&sb);
else if (contents_from)
die("--contents and --children do not blend well.");
else
final_commit_name = prepare_initial(&sb);

if (!sb.final) {
/*
Expand Down Expand Up @@ -2425,7 +2513,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
if (!incremental)
setup_pager();

assign_blame(&sb, &revs, opt);
assign_blame(&sb, opt);

if (incremental)
return 0;
Expand Down
10 changes: 10 additions & 0 deletions builtin-rev-list.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ static const char rev_list_usage[] =
" --reverse\n"
" formatting output:\n"
" --parents\n"
" --children\n"
" --objects | --objects-edge\n"
" --unpacked\n"
" --header | --pretty\n"
Expand Down Expand Up @@ -90,6 +91,15 @@ static void show_commit(struct commit *commit)
parents = parents->next;
}
}
if (revs.children.name) {
struct commit_list *children;

children = lookup_decoration(&revs.children, &commit->object);
while (children) {
printf(" %s", sha1_to_hex(children->item->object.sha1));
children = children->next;
}
}
show_decorations(commit);
if (revs.commit_format == CMIT_FMT_ONELINE)
putchar(' ');
Expand Down
Loading

0 comments on commit 8bb6588

Please sign in to comment.