From 2f3f8b218abae6fc0574d0e22d3614fc0f5e983d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 2 Nov 2006 00:02:11 -0800 Subject: [PATCH 1/7] git-pickaxe: rename detection optimization The idea is that we are interested in renaming into only one path, so we do not care about renames that happen elsewhere. Signed-off-by: Junio C Hamano --- builtin-pickaxe.c | 1 + diff.h | 1 + diffcore-rename.c | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin-pickaxe.c b/builtin-pickaxe.c index f6e861a26d..97b3732419 100644 --- a/builtin-pickaxe.c +++ b/builtin-pickaxe.c @@ -289,6 +289,7 @@ static struct origin *find_rename(struct scoreboard *sb, diff_opts.recursive = 1; diff_opts.detect_rename = DIFF_DETECT_RENAME; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; + diff_opts.single_follow = origin->path; paths[0] = NULL; diff_tree_setup_paths(paths, &diff_opts); if (diff_setup_done(&diff_opts) < 0) diff --git a/diff.h b/diff.h index ce3058e437..6fda92a7b5 100644 --- a/diff.h +++ b/diff.h @@ -46,6 +46,7 @@ struct diff_options { const char *filter; const char *orderfile; const char *pickaxe; + const char *single_follow; unsigned recursive:1, tree_in_recursive:1, binary:1, diff --git a/diffcore-rename.c b/diffcore-rename.c index ef239012b6..57a74b6bb8 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -256,11 +256,15 @@ void diffcore_rename(struct diff_options *options) for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; - if (!DIFF_FILE_VALID(p->one)) + if (!DIFF_FILE_VALID(p->one)) { if (!DIFF_FILE_VALID(p->two)) continue; /* unmerged */ + else if (options->single_follow && + strcmp(options->single_follow, p->two->path)) + continue; /* not interested */ else locate_rename_dst(p->two, 1); + } else if (!DIFF_FILE_VALID(p->two)) { /* If the source is a broken "delete", and * they did not really want to get broken, From 0421d9f812acc819dcf9e9c6707618aca7e80bf4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 4 Nov 2006 12:20:09 -0800 Subject: [PATCH 2/7] git-pickaxe: simplify Octopus merges further If more than one parents in an Octopus merge have the same origin, ignore later ones because it would not make any difference in the outcome. Signed-off-by: Junio C Hamano --- builtin-pickaxe.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/builtin-pickaxe.c b/builtin-pickaxe.c index 97b3732419..082ff45fe8 100644 --- a/builtin-pickaxe.c +++ b/builtin-pickaxe.c @@ -915,6 +915,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) i < MAXPARENT && parent; parent = parent->next, i++) { struct commit *p = parent->item; + int j, same; if (parent_origin[i]) continue; @@ -934,7 +935,16 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) origin_decref(porigin); goto finish; } - parent_origin[i] = porigin; + for (j = same = 0; j < i; j++) + if (!hashcmp(parent_origin[j]->blob_sha1, + porigin->blob_sha1)) { + same = 1; + break; + } + if (!same) + parent_origin[i] = porigin; + else + origin_decref(porigin); } } From 650e2f6752ad29b0cba394535d3b098cf4bb102b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 4 Nov 2006 12:37:02 -0800 Subject: [PATCH 3/7] git-pickaxe: re-scan the blob after making progress with -M Otherwise we would miss copied lines that are contained in the parts before or after the part that we find after splitting the blame_entry (i.e. split[0] and split[2]). Signed-off-by: Junio C Hamano --- builtin-pickaxe.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/builtin-pickaxe.c b/builtin-pickaxe.c index 082ff45fe8..29839cd48b 100644 --- a/builtin-pickaxe.c +++ b/builtin-pickaxe.c @@ -766,7 +766,7 @@ static int find_move_in_parent(struct scoreboard *sb, struct origin *target, struct origin *parent) { - int last_in_target; + int last_in_target, made_progress; struct blame_entry *e, split[3]; mmfile_t file_p; char type[10]; @@ -784,14 +784,20 @@ static int find_move_in_parent(struct scoreboard *sb, return 0; } - for (e = sb->ent; e; e = e->next) { - if (e->guilty || cmp_suspect(e->suspect, target)) - continue; - find_copy_in_blob(sb, e, parent, split, &file_p); - if (split[1].suspect && - blame_move_score < ent_score(sb, &split[1])) - split_blame(sb, split, e); - decref_split(split); + made_progress = 1; + while (made_progress) { + made_progress = 0; + for (e = sb->ent; e; e = e->next) { + if (e->guilty || cmp_suspect(e->suspect, target)) + continue; + find_copy_in_blob(sb, e, parent, split, &file_p); + if (split[1].suspect && + blame_move_score < ent_score(sb, &split[1])) { + split_blame(sb, split, e); + made_progress = 1; + } + decref_split(split); + } } free(blob_p); return 0; From 334947843cbd50895f1dc58d8bd2f217e1f1ef77 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 4 Nov 2006 16:39:03 -0800 Subject: [PATCH 4/7] git-pickaxe: re-scan the blob after making progress with -C The reason to do this is the same as in the previous change for line copy detection within the same file (-M). Also this fixes -C and -C -C (aka find-copies-harder) logic; in this application we are not interested in the similarity matching diffcore-rename makes, because we are only interested in scanning files that were modified, or in the case of -C -C, scanning all files in the parent and we want to do that ourselves. Signed-off-by: Junio C Hamano --- builtin-pickaxe.c | 163 +++++++++++++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 61 deletions(-) diff --git a/builtin-pickaxe.c b/builtin-pickaxe.c index 29839cd48b..b25e039f05 100644 --- a/builtin-pickaxe.c +++ b/builtin-pickaxe.c @@ -803,6 +803,36 @@ static int find_move_in_parent(struct scoreboard *sb, return 0; } + +struct blame_list { + struct blame_entry *ent; + struct blame_entry split[3]; +}; + +static struct blame_list *setup_blame_list(struct scoreboard *sb, + struct origin *target, + int *num_ents_p) +{ + struct blame_entry *e; + int num_ents, i; + struct blame_list *blame_list = NULL; + + /* Count the number of entries the target is suspected for, + * and prepare a list of entry and the best split. + */ + for (e = sb->ent, num_ents = 0; e; e = e->next) + if (!e->guilty && !cmp_suspect(e->suspect, target)) + num_ents++; + if (num_ents) { + blame_list = xcalloc(num_ents, sizeof(struct blame_list)); + for (e = sb->ent, i = 0; e; e = e->next) + if (!e->guilty && !cmp_suspect(e->suspect, target)) + blame_list[i++].ent = e; + } + *num_ents_p = num_ents; + return blame_list; +} + static int find_copy_in_parent(struct scoreboard *sb, struct origin *target, struct commit *parent, @@ -811,91 +841,101 @@ static int find_copy_in_parent(struct scoreboard *sb, { struct diff_options diff_opts; const char *paths[1]; - struct blame_entry *e; int i, j; - struct blame_list { - struct blame_entry *ent; - struct blame_entry split[3]; - } *blame_list; + int retval; + struct blame_list *blame_list; int num_ents; - /* Count the number of entries the target is suspected for, - * and prepare a list of entry and the best split. - */ - for (e = sb->ent, num_ents = 0; e; e = e->next) - if (!e->guilty && !cmp_suspect(e->suspect, target)) - num_ents++; - if (!num_ents) + blame_list = setup_blame_list(sb, target, &num_ents); + if (!blame_list) return 1; /* nothing remains for this target */ - blame_list = xcalloc(num_ents, sizeof(struct blame_list)); - for (e = sb->ent, i = 0; e; e = e->next) - if (!e->guilty && !cmp_suspect(e->suspect, target)) - blame_list[i++].ent = e; - diff_setup(&diff_opts); diff_opts.recursive = 1; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - /* Try "find copies harder" on new path */ - if ((opt & PICKAXE_BLAME_COPY_HARDER) && - (!porigin || strcmp(target->path, porigin->path))) { - diff_opts.detect_rename = DIFF_DETECT_COPY; - diff_opts.find_copies_harder = 1; - } paths[0] = NULL; diff_tree_setup_paths(paths, &diff_opts); if (diff_setup_done(&diff_opts) < 0) die("diff-setup"); + + /* Try "find copies harder" on new path if requested; + * we do not want to use diffcore_rename() actually to + * match things up; find_copies_harder is set only to + * force diff_tree_sha1() to feed all filepairs to diff_queue, + * and this code needs to be after diff_setup_done(), which + * usually makes find-copies-harder imply copy detection. + */ + if ((opt & PICKAXE_BLAME_COPY_HARDER) && + (!porigin || strcmp(target->path, porigin->path))) + diff_opts.find_copies_harder = 1; + diff_tree_sha1(parent->tree->object.sha1, target->commit->tree->object.sha1, "", &diff_opts); - diffcore_std(&diff_opts); - for (i = 0; i < diff_queued_diff.nr; i++) { - struct diff_filepair *p = diff_queued_diff.queue[i]; - struct origin *norigin; - mmfile_t file_p; - char type[10]; - char *blob; - struct blame_entry this[3]; + if (!diff_opts.find_copies_harder) + diffcore_std(&diff_opts); - if (!DIFF_FILE_VALID(p->one)) - continue; /* does not exist in parent */ - if (porigin && !strcmp(p->one->path, porigin->path)) - /* find_move already dealt with this path */ - continue; + retval = 0; + while (1) { + int made_progress = 0; - norigin = get_origin(sb, parent, p->one->path); - hashcpy(norigin->blob_sha1, p->one->sha1); - blob = read_sha1_file(norigin->blob_sha1, type, - (unsigned long *) &file_p.size); - file_p.ptr = blob; - if (!file_p.ptr) - continue; + for (i = 0; i < diff_queued_diff.nr; i++) { + struct diff_filepair *p = diff_queued_diff.queue[i]; + struct origin *norigin; + mmfile_t file_p; + char type[10]; + char *blob; + struct blame_entry this[3]; + + if (!DIFF_FILE_VALID(p->one)) + continue; /* does not exist in parent */ + if (porigin && !strcmp(p->one->path, porigin->path)) + /* find_move already dealt with this path */ + continue; + + norigin = get_origin(sb, parent, p->one->path); + hashcpy(norigin->blob_sha1, p->one->sha1); + blob = read_sha1_file(norigin->blob_sha1, type, + (unsigned long *) &file_p.size); + file_p.ptr = blob; + if (!file_p.ptr) + continue; + + for (j = 0; j < num_ents; j++) { + find_copy_in_blob(sb, blame_list[j].ent, + norigin, this, &file_p); + copy_split_if_better(sb, blame_list[j].split, + this); + decref_split(this); + } + free(blob); + origin_decref(norigin); + } for (j = 0; j < num_ents; j++) { - find_copy_in_blob(sb, blame_list[j].ent, norigin, - this, &file_p); - copy_split_if_better(sb, blame_list[j].split, - this); - decref_split(this); + struct blame_entry *split = blame_list[j].split; + if (split[1].suspect && + blame_copy_score < ent_score(sb, &split[1])) { + split_blame(sb, split, blame_list[j].ent); + made_progress = 1; + } + decref_split(split); + } + free(blame_list); + + if (!made_progress) + break; + blame_list = setup_blame_list(sb, target, &num_ents); + if (!blame_list) { + retval = 1; + break; } - free(blob); - origin_decref(norigin); } diff_flush(&diff_opts); - for (j = 0; j < num_ents; j++) { - struct blame_entry *split = blame_list[j].split; - 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); - - return 0; + return retval; } #define MAXPARENT 16 @@ -942,7 +982,8 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) goto finish; } for (j = same = 0; j < i; j++) - if (!hashcmp(parent_origin[j]->blob_sha1, + if (parent_origin[j] && + !hashcmp(parent_origin[j]->blob_sha1, porigin->blob_sha1)) { same = 1; break; From 854b97f6eb3bcbeb76b2705bfbffead1558bde77 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 4 Nov 2006 19:18:50 -0800 Subject: [PATCH 5/7] git-pickaxe: fix origin refcounting When we introduced the cached origin per commit, we gave up proper garbage collecting because it meant that commits hold onto their cached copy. There is no need to do so. Signed-off-by: Junio C Hamano --- builtin-pickaxe.c | 61 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/builtin-pickaxe.c b/builtin-pickaxe.c index b25e039f05..332e6a2e3c 100644 --- a/builtin-pickaxe.c +++ b/builtin-pickaxe.c @@ -168,23 +168,28 @@ static void coalesce(struct scoreboard *sb) sanity_check_refcnt(sb); } +static struct origin *make_origin(struct commit *commit, const char *path) +{ + struct origin *o; + o = xcalloc(1, sizeof(*o) + strlen(path) + 1); + o->commit = commit; + o->refcnt = 1; + strcpy(o->path, path); + return o; +} + static struct origin *get_origin(struct scoreboard *sb, struct commit *commit, const char *path) { struct blame_entry *e; - struct origin *o; for (e = sb->ent; e; e = e->next) { if (e->suspect->commit == commit && !strcmp(e->suspect->path, path)) 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; + return make_origin(commit, path); } static int fill_blob_sha1(struct origin *origin) @@ -216,9 +221,19 @@ static struct origin *find_origin(struct scoreboard *sb, const char *paths[2]; if (parent->util) { + /* This is a freestanding copy of origin and not + * refcounted. + */ struct origin *cached = parent->util; - if (!strcmp(cached->path, origin->path)) - return origin_incref(cached); + if (!strcmp(cached->path, origin->path)) { + porigin = get_origin(sb, parent, cached->path); + if (porigin->refcnt == 1) + hashcpy(porigin->blob_sha1, cached->blob_sha1); + return porigin; + } + /* otherwise it was not very useful; free it */ + free(parent->util); + parent->util = NULL; } /* See if the origin->path is different between parent @@ -268,10 +283,10 @@ static struct origin *find_origin(struct scoreboard *sb, } diff_flush(&diff_opts); if (porigin) { - origin_incref(porigin); - if (parent->util) - origin_decref(parent->util); - parent->util = porigin; + struct origin *cached; + cached = make_origin(porigin->commit, porigin->path); + hashcpy(cached->blob_sha1, porigin->blob_sha1); + parent->util = cached; } return porigin; } @@ -1434,8 +1449,13 @@ static void sanity_check_refcnt(struct scoreboard *sb) for (ent = sb->ent; ent; ent = ent->next) { /* Nobody should have zero or negative refcnt */ - if (ent->suspect->refcnt <= 0) + if (ent->suspect->refcnt <= 0) { + fprintf(stderr, "%s in %s has negative refcnt %d\n", + ent->suspect->path, + sha1_to_hex(ent->suspect->commit->object.sha1), + ent->suspect->refcnt); baa = 1; + } } for (ent = sb->ent; ent; ent = ent->next) { /* Mark the ones that haven't been checked */ @@ -1444,9 +1464,7 @@ static void sanity_check_refcnt(struct scoreboard *sb) } for (ent = sb->ent; ent; ent = ent->next) { /* then pick each and see if they have the the correct - * refcnt; note that ->util caching means origin's refcnt - * may well be greater than the number of blame entries - * that use it. + * refcnt. */ int found; struct blame_entry *e; @@ -1460,14 +1478,19 @@ static void sanity_check_refcnt(struct scoreboard *sb) continue; found++; } - if (suspect->refcnt < found) - baa = 1; + if (suspect->refcnt != found) { + fprintf(stderr, "%s in %s has refcnt %d, not %d\n", + ent->suspect->path, + sha1_to_hex(ent->suspect->commit->object.sha1), + ent->suspect->refcnt, found); + baa = 2; + } } if (baa) { int opt = 0160; find_alignment(sb, &opt); output(sb, opt); - die("Baa!"); + die("Baa %d!", baa); } } From 2bc45477a53b9b7a5202a816b96c14428f683c38 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 5 Nov 2006 11:47:53 -0800 Subject: [PATCH 6/7] git-blame: add internal statistics to count read blobs. --- blame.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blame.c b/blame.c index 8cfd5d94c7..d0e506c02b 100644 --- a/blame.c +++ b/blame.c @@ -59,6 +59,7 @@ static void get_blob(struct commit *commit); static int num_get_patch; static int num_commits; static int patch_time; +static int num_read_blob; struct blame_diff_state { struct xdiff_emit_state xm; @@ -204,6 +205,7 @@ static void get_blob(struct commit *commit) return; info->buf = read_sha1_file(info->sha1, type, &info->size); + num_read_blob++; assert(!strcmp(type, blob_type)); } @@ -910,6 +912,7 @@ int main(int argc, const char **argv) } if (DEBUG) { + printf("num read blob: %d\n", num_read_blob); printf("num get patch: %d\n", num_get_patch); printf("num commits: %d\n", num_commits); printf("patch time: %f\n", patch_time / 1000000.0); From c2e525d97f81bc178567cdf4dd7056ce6224eb58 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 5 Nov 2006 11:51:41 -0800 Subject: [PATCH 7/7] git-pickaxe: optimize by avoiding repeated read_sha1_file(). It turns out that pickaxe reads the same blob repeatedly while blame can reuse the blob already read for the parent when handling a child commit when it's parent's turn to pass its blame to the grandparent. Have a cache in the origin structure to keep the blob there, which will be garbage collected when the origin loses the last reference to it. Signed-off-by: Junio C Hamano --- builtin-pickaxe.c | 100 ++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/builtin-pickaxe.c b/builtin-pickaxe.c index 332e6a2e3c..f12b2d4544 100644 --- a/builtin-pickaxe.c +++ b/builtin-pickaxe.c @@ -40,6 +40,11 @@ static int max_score_digits; #define DEBUG 0 #endif +/* stats */ +static int num_read_blob; +static int num_get_patch; +static int num_commits; + #define PICKAXE_BLAME_MOVE 01 #define PICKAXE_BLAME_COPY 02 #define PICKAXE_BLAME_COPY_HARDER 04 @@ -63,10 +68,25 @@ static unsigned blame_copy_score; struct origin { int refcnt; struct commit *commit; + mmfile_t file; unsigned char blob_sha1[20]; char path[FLEX_ARRAY]; }; +static char *fill_origin_blob(struct origin *o, mmfile_t *file) +{ + if (!o->file.ptr) { + char type[10]; + num_read_blob++; + file->ptr = read_sha1_file(o->blob_sha1, type, + (unsigned long *)(&(file->size))); + o->file = *file; + } + else + *file = o->file; + return file->ptr; +} + static inline struct origin *origin_incref(struct origin *o) { if (o) @@ -77,6 +97,8 @@ static inline struct origin *origin_incref(struct origin *o) static void origin_decref(struct origin *o) { if (o && --o->refcnt <= 0) { + if (o->file.ptr) + free(o->file.ptr); memset(o, 0, sizeof(*o)); free(o); } @@ -431,25 +453,14 @@ static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o, static struct patch *get_patch(struct origin *parent, struct origin *origin) { mmfile_t file_p, file_o; - char type[10]; - char *blob_p, *blob_o; struct patch *patch; - blob_p = read_sha1_file(parent->blob_sha1, type, - (unsigned long *) &file_p.size); - blob_o = read_sha1_file(origin->blob_sha1, type, - (unsigned long *) &file_o.size); - file_p.ptr = blob_p; - file_o.ptr = blob_o; - if (!file_p.ptr || !file_o.ptr) { - free(blob_p); - free(blob_o); + fill_origin_blob(parent, &file_p); + fill_origin_blob(origin, &file_o); + if (!file_p.ptr || !file_o.ptr) return NULL; - } - patch = compare_buffer(&file_p, &file_o, 0); - free(blob_p); - free(blob_o); + num_get_patch++; return patch; } @@ -784,20 +795,14 @@ static int find_move_in_parent(struct scoreboard *sb, int last_in_target, made_progress; struct blame_entry *e, split[3]; mmfile_t file_p; - char type[10]; - char *blob_p; last_in_target = find_last_in_target(sb, target); if (last_in_target < 0) return 1; /* nothing remains for this target */ - blob_p = read_sha1_file(parent->blob_sha1, type, - (unsigned long *) &file_p.size); - file_p.ptr = blob_p; - if (!file_p.ptr) { - free(blob_p); + fill_origin_blob(parent, &file_p); + if (!file_p.ptr) return 0; - } made_progress = 1; while (made_progress) { @@ -814,7 +819,6 @@ static int find_move_in_parent(struct scoreboard *sb, decref_split(split); } } - free(blob_p); return 0; } @@ -900,8 +904,6 @@ static int find_copy_in_parent(struct scoreboard *sb, struct diff_filepair *p = diff_queued_diff.queue[i]; struct origin *norigin; mmfile_t file_p; - char type[10]; - char *blob; struct blame_entry this[3]; if (!DIFF_FILE_VALID(p->one)) @@ -912,9 +914,7 @@ static int find_copy_in_parent(struct scoreboard *sb, norigin = get_origin(sb, parent, p->one->path); hashcpy(norigin->blob_sha1, p->one->sha1); - blob = read_sha1_file(norigin->blob_sha1, type, - (unsigned long *) &file_p.size); - file_p.ptr = blob; + fill_origin_blob(norigin, &file_p); if (!file_p.ptr) continue; @@ -925,7 +925,6 @@ static int find_copy_in_parent(struct scoreboard *sb, this); decref_split(this); } - free(blob); origin_decref(norigin); } @@ -953,6 +952,28 @@ static int find_copy_in_parent(struct scoreboard *sb, return retval; } +/* The blobs of origin and porigin exactly match, so everything + * origin is suspected for can be blamed on the parent. + */ +static void pass_whole_blame(struct scoreboard *sb, + struct origin *origin, struct origin *porigin) +{ + struct blame_entry *e; + + if (!porigin->file.ptr && origin->file.ptr) { + /* Steal its file */ + porigin->file = origin->file; + origin->file.ptr = NULL; + } + for (e = sb->ent; e; e = e->next) { + if (cmp_suspect(e->suspect, origin)) + continue; + origin_incref(porigin); + origin_decref(e->suspect); + e->suspect = porigin; + } +} + #define MAXPARENT 16 static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) @@ -986,13 +1007,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) if (!porigin) continue; if (!hashcmp(porigin->blob_sha1, origin->blob_sha1)) { - struct blame_entry *e; - for (e = sb->ent; e; e = e->next) - if (e->suspect == origin) { - origin_incref(porigin); - origin_decref(e->suspect); - e->suspect = porigin; - } + pass_whole_blame(sb, origin, porigin); origin_decref(porigin); goto finish; } @@ -1010,6 +1025,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) } } + num_commits++; for (i = 0, parent = commit->parents; i < MAXPARENT && parent; parent = parent->next, i++) { @@ -1068,7 +1084,8 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt) origin_incref(suspect); commit = suspect->commit; - parse_commit(commit); + if (!commit->object.parsed) + parse_commit(commit); if (!(commit->object.flags & UNINTERESTING) && !(revs->max_age != -1 && commit->date < revs->max_age)) pass_blame(sb, suspect, opt); @@ -1735,6 +1752,7 @@ int cmd_pickaxe(int argc, const char **argv, const char *prefix) die("no such path %s in %s", path, final_commit_name); sb.final_buf = read_sha1_file(o->blob_sha1, type, &sb.final_buf_size); + num_read_blob++; lno = prepare_lines(&sb); if (bottom < 1) @@ -1772,5 +1790,11 @@ int cmd_pickaxe(int argc, const char **argv, const char *prefix) free(ent); ent = e; } + + if (DEBUG) { + printf("num read blob: %d\n", num_read_blob); + printf("num get patch: %d\n", num_get_patch); + printf("num commits: %d\n", num_commits); + } return 0; }