{"thread":{"id":"18283","subject":"[PATCH] Removed static variables in blame for catering reuse of blame code","startedAt":"2009-03-12T10:30:03Z","lastAt":"2009-03-14T08:03:08Z","messageCount":2,"participants":["pi song","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"107799","messageId":"49B8E42B.30901@gmail.com","threadId":"18283","inReplyTo":null,"subject":"[PATCH] Removed static variables in blame for catering reuse of blame code","fromName":"pi song","fromEmail":"pi.songs@gmail.com","sentAt":"2009-03-12T10:30:03Z","receivedAt":"2009-03-12T10:30:03Z","isPatch":true,"sender":{"key":"pi.songs@gmail.com","avatar":null},"body":"The patch refactors builtin-blame.c to not rely on static variables \nso that blame code can later be reused in different context.\n\nThis is the first patch toward making grep --blame, based on the latest \"next\" branch.\n\nSigned-off-by: Pi Song <pi.songs@gmail.com>\n---\n builtin-blame.c |  386 ++++++++++++++++++++++++++++++------------------------\n 1 files changed, 214 insertions(+), 172 deletions(-)\n\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex 2aedd17..2e19ddf 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -30,42 +30,29 @@ static const char *blame_opt_usage[] = {\n \tNULL\n };\n \n-static int longest_file;\n-static int longest_author;\n-static int max_orig_digits;\n-static int max_digits;\n-static int max_score_digits;\n-static int show_root;\n-static int reverse;\n-static int blank_boundary;\n-static int incremental;\n-static int xdl_opts = XDF_NEED_MINIMAL;\n-\n-static enum date_mode blame_date_mode = DATE_ISO8601;\n-static size_t blame_date_width;\n-\n-static struct string_list mailmap;\n+/* only use for command line parameter parsing */\n+static unsigned opt_blame_move_score;\n+static unsigned opt_blame_copy_score;\n \n #ifndef DEBUG\n #define DEBUG 0\n #endif\n \n-/* stats */\n-static int num_read_blob;\n-static int num_get_patch;\n-static int num_commits;\n+/* \n+ * for storing stats. it can be used\n+ * across multiple blame operations\n+ */\n+struct blame_stat {\n+        int num_read_blob;\n+        int num_get_patch;\n+        int num_commits;\n+};\n \n #define PICKAXE_BLAME_MOVE\t\t01\n #define PICKAXE_BLAME_COPY\t\t02\n #define PICKAXE_BLAME_COPY_HARDER\t04\n #define PICKAXE_BLAME_COPY_HARDEST\t010\n \n-/*\n- * blame for a blame_entry with score lower than these thresholds\n- * is not passed to the parent using move/copy logic.\n- */\n-static unsigned blame_move_score;\n-static unsigned blame_copy_score;\n #define BLAME_DEFAULT_MOVE_SCORE\t20\n #define BLAME_DEFAULT_COPY_SCORE\t40\n \n@@ -86,56 +73,6 @@ struct origin {\n };\n \n /*\n- * Given an origin, prepare mmfile_t structure to be used by the\n- * diff machinery\n- */\n-static void fill_origin_blob(struct origin *o, mmfile_t *file)\n-{\n-\tif (!o->file.ptr) {\n-\t\tenum object_type type;\n-\t\tnum_read_blob++;\n-\t\tfile->ptr = read_sha1_file(o->blob_sha1, &type,\n-\t\t\t\t\t   (unsigned long *)(&(file->size)));\n-\t\tif (!file->ptr)\n-\t\t\tdie(\"Cannot read blob %s for path %s\",\n-\t\t\t    sha1_to_hex(o->blob_sha1),\n-\t\t\t    o->path);\n-\t\to->file = *file;\n-\t}\n-\telse\n-\t\t*file = o->file;\n-}\n-\n-/*\n- * Origin is refcounted and usually we keep the blob contents to be\n- * reused.\n- */\n-static inline struct origin *origin_incref(struct origin *o)\n-{\n-\tif (o)\n-\t\to->refcnt++;\n-\treturn o;\n-}\n-\n-static void origin_decref(struct origin *o)\n-{\n-\tif (o && --o->refcnt <= 0) {\n-\t\tif (o->previous)\n-\t\t\torigin_decref(o->previous);\n-\t\tfree(o->file.ptr);\n-\t\tfree(o);\n-\t}\n-}\n-\n-static void drop_origin_blob(struct origin *o)\n-{\n-\tif (o->file.ptr) {\n-\t\tfree(o->file.ptr);\n-\t\to->file.ptr = NULL;\n-\t}\n-}\n-\n-/*\n  * Each group of lines is described by a blame_entry; it can be split\n  * as we pass blame to the parents.  They form a linked list in the\n  * scoreboard structure, sorted by the target line number.\n@@ -176,6 +113,47 @@ struct blame_entry {\n };\n \n /*\n+ * stores things that survive across multiple blame operations\n+ * 1) for blame specific global parameters\n+ * 2) for reusable structures (possibly for optimization purpose)\n+ */\n+struct super_scoreboard {\n+\t/* \n+\t * miscellaneous parameters collected during processing\n+\t * for pretty formatting purpose\n+\t */\n+\tint longest_file;\n+\tint longest_author;\n+\tint max_orig_digits;\n+\tint max_digits;\n+\tint max_score_digits;\n+\n+\t/* formatting parameters */\n+\tint show_root;\n+\tint reverse;\n+\tint blank_boundary;\n+\tint incremental;\n+\tint xdl_opts;\n+\n+\t/*\n+\t * blame for a blame_entry with score lower than these thresholds\n+\t * is not passed to the parent using move/copy logic.\n+\t */\n+\tunsigned blame_move_score;\n+\tunsigned blame_copy_score;\n+\n+\t/* date formatting related */\n+\tenum date_mode blame_date_mode;\n+\tsize_t blame_date_width;\n+\n+\t/* for fast mailmap lookup */\n+\tstruct string_list mailmap;\n+\n+\t/* for stat collecting purpose */\n+\tstruct blame_stat *stat;\n+};\n+\n+/*\n  * The current state of the blame assignment.\n  */\n struct scoreboard {\n@@ -198,8 +176,61 @@ struct scoreboard {\n \t/* look-up a line in the final buffer */\n \tint num_lines;\n \tint *lineno;\n+\n+\t/* super scoreboard */\n+\tstruct super_scoreboard *ssb ;\n };\n \n+/*\n+ * Given an origin, prepare mmfile_t structure to be used by the\n+ * diff machinery\n+ */\n+static void fill_origin_blob(struct scoreboard *sb, struct origin *o, mmfile_t *file)\n+{\n+\tif (!o->file.ptr) {\n+\t\tenum object_type type;\n+\t\tsb->ssb->stat->num_read_blob++;\n+\t\tfile->ptr = read_sha1_file(o->blob_sha1, &type,\n+\t\t\t\t\t   (unsigned long *)(&(file->size)));\n+\t\tif (!file->ptr)\n+\t\t\tdie(\"Cannot read blob %s for path %s\",\n+\t\t\t    sha1_to_hex(o->blob_sha1),\n+\t\t\t    o->path);\n+\t\to->file = *file;\n+\t}\n+\telse\n+\t\t*file = o->file;\n+}\n+\n+/*\n+ * Origin is refcounted and usually we keep the blob contents to be\n+ * reused.\n+ */\n+static inline struct origin *origin_incref(struct origin *o)\n+{\n+\tif (o)\n+\t\to->refcnt++;\n+\treturn o;\n+}\n+\n+static void origin_decref(struct origin *o)\n+{\n+\tif (o && --o->refcnt <= 0) {\n+\t\tif (o->previous)\n+\t\t\torigin_decref(o->previous);\n+\t\tfree(o->file.ptr);\n+\t\tfree(o);\n+\t}\n+}\n+\n+static void drop_origin_blob(struct origin *o)\n+{\n+\tif (o->file.ptr) {\n+\t\tfree(o->file.ptr);\n+\t\to->file.ptr = NULL;\n+\t}\n+}\n+\n static inline int same_suspect(struct origin *a, struct origin *b)\n {\n \tif (a == b)\n@@ -731,12 +762,12 @@ static int pass_blame_to_parent(struct scoreboard *sb,\n \tif (last_in_target < 0)\n \t\treturn 1; /* nothing remains for this target */\n \n-\tfill_origin_blob(parent, &file_p);\n-\tfill_origin_blob(target, &file_o);\n-\tnum_get_patch++;\n+\tfill_origin_blob(sb, parent, &file_p);\n+\tfill_origin_blob(sb, target, &file_o);\n+\tsb->ssb->stat->num_get_patch++;\n \n \tmemset(&xpp, 0, sizeof(xpp));\n-\txpp.flags = xdl_opts;\n+\txpp.flags = sb->ssb->xdl_opts;\n \tmemset(&xecfg, 0, sizeof(xecfg));\n \txecfg.ctxlen = 0;\n \txdi_diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, &xpp, &xecfg);\n@@ -887,7 +918,7 @@ static void find_copy_in_blob(struct scoreboard *sb,\n \t * file_p partially may match that image.\n \t */\n \tmemset(&xpp, 0, sizeof(xpp));\n-\txpp.flags = xdl_opts;\n+\txpp.flags = sb->ssb->xdl_opts;\n \tmemset(&xecfg, 0, sizeof(xecfg));\n \txecfg.ctxlen = 1;\n \tmemset(split, 0, sizeof(struct blame_entry [3]));\n@@ -912,7 +943,7 @@ static int find_move_in_parent(struct scoreboard *sb,\n \tif (last_in_target < 0)\n \t\treturn 1; /* nothing remains for this target */\n \n-\tfill_origin_blob(parent, &file_p);\n+\tfill_origin_blob(sb, parent, &file_p);\n \tif (!file_p.ptr)\n \t\treturn 0;\n \n@@ -921,11 +952,11 @@ static int find_move_in_parent(struct scoreboard *sb,\n \t\tmade_progress = 0;\n \t\tfor (e = sb->ent; e; e = e->next) {\n \t\t\tif (e->guilty || !same_suspect(e->suspect, target) ||\n-\t\t\t    ent_score(sb, e) < blame_move_score)\n+\t\t\t    ent_score(sb, e) < sb->ssb->blame_move_score)\n \t\t\t\tcontinue;\n \t\t\tfind_copy_in_blob(sb, e, parent, split, &file_p);\n \t\t\tif (split[1].suspect &&\n-\t\t\t    blame_move_score < ent_score(sb, &split[1])) {\n+\t\t\t    sb->ssb->blame_move_score < ent_score(sb, &split[1])) {\n \t\t\t\tsplit_blame(sb, split, e);\n \t\t\t\tmade_progress = 1;\n \t\t\t}\n@@ -998,7 +1029,7 @@ static int find_copy_in_parent(struct scoreboard *sb,\n \tstruct blame_list *blame_list;\n \tint num_ents;\n \n-\tblame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);\n+\tblame_list = setup_blame_list(sb, target, sb->ssb->blame_copy_score, &num_ents);\n \tif (!blame_list)\n \t\treturn 1; /* nothing remains for this target */\n \n@@ -1053,7 +1084,7 @@ static int find_copy_in_parent(struct scoreboard *sb,\n \n \t\t\tnorigin = get_origin(sb, parent, p->one->path);\n \t\t\thashcpy(norigin->blob_sha1, p->one->sha1);\n-\t\t\tfill_origin_blob(norigin, &file_p);\n+\t\t\tfill_origin_blob(sb, norigin, &file_p);\n \t\t\tif (!file_p.ptr)\n \t\t\t\tcontinue;\n \n@@ -1070,7 +1101,7 @@ static int find_copy_in_parent(struct scoreboard *sb,\n \t\tfor (j = 0; j < num_ents; j++) {\n \t\t\tstruct blame_entry *split = blame_list[j].split;\n \t\t\tif (split[1].suspect &&\n-\t\t\t    blame_copy_score < ent_score(sb, &split[1])) {\n+\t\t\t    sb->ssb->blame_copy_score < ent_score(sb, &split[1])) {\n \t\t\t\tsplit_blame(sb, split, blame_list[j].ent);\n \t\t\t\tmade_progress = 1;\n \t\t\t}\n@@ -1082,7 +1113,7 @@ static int find_copy_in_parent(struct scoreboard *sb,\n \n \t\tif (!made_progress)\n \t\t\tbreak;\n-\t\tblame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);\n+\t\tblame_list = setup_blame_list(sb, target, sb->ssb->blame_copy_score, &num_ents);\n \t\tif (!blame_list) {\n \t\t\tretval = 1;\n \t\t\tbreak;\n@@ -1122,17 +1153,21 @@ static void pass_whole_blame(struct scoreboard *sb,\n  * \"parent\" (and \"porigin\"), but what we mean is to find scapegoat to\n  * exonerate ourselves.\n  */\n-static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)\n+static struct commit_list *first_scapegoat(struct scoreboard *sb, \n+\t\t\t\t\t   struct rev_info *revs, \n+\t\t\t\t\t   struct commit *commit)\n {\n-\tif (!reverse)\n+\tif (!sb->ssb->reverse)\n \t\treturn commit->parents;\n \treturn lookup_decoration(&revs->children, &commit->object);\n }\n \n-static int num_scapegoats(struct rev_info *revs, struct commit *commit)\n+static int num_scapegoats(struct scoreboard *sb,\n+\t\t\t  struct rev_info *revs, \n+\t\t\t  struct commit *commit)\n {\n \tint cnt;\n-\tstruct commit_list *l = first_scapegoat(revs, commit);\n+\tstruct commit_list *l = first_scapegoat(sb, revs, commit);\n \tfor (cnt = 0; l; l = l->next)\n \t\tcnt++;\n \treturn cnt;\n@@ -1149,7 +1184,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \tstruct origin *sg_buf[MAXSG];\n \tstruct origin *porigin, **sg_origin = sg_buf;\n \n-\tnum_sg = num_scapegoats(revs, commit);\n+\tnum_sg = num_scapegoats(sb, revs, commit);\n \tif (!num_sg)\n \t\tgoto finish;\n \telse if (num_sg < ARRAY_SIZE(sg_buf))\n@@ -1166,7 +1201,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t\t\t\t       struct commit *, struct origin *);\n \t\tfind = pass ? find_rename : find_origin;\n \n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\tfor (i = 0, sg = first_scapegoat(sb, revs, commit);\n \t\t     i < num_sg && sg;\n \t\t     sg = sg->next, i++) {\n \t\t\tstruct commit *p = sg->item;\n@@ -1198,8 +1233,8 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t\t}\n \t}\n \n-\tnum_commits++;\n-\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\tsb->ssb->stat->num_commits++;\n+\tfor (i = 0, sg = first_scapegoat(sb, revs, commit);\n \t     i < num_sg && sg;\n \t     sg = sg->next, i++) {\n \t\tstruct origin *porigin = sg_origin[i];\n@@ -1217,7 +1252,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t * Optionally find moves in parents' files.\n \t */\n \tif (opt & PICKAXE_BLAME_MOVE)\n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\tfor (i = 0, sg = first_scapegoat(sb, revs, commit);\n \t\t     i < num_sg && sg;\n \t\t     sg = sg->next, i++) {\n \t\t\tstruct origin *porigin = sg_origin[i];\n@@ -1231,7 +1266,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t * Optionally find copies from parents' files.\n \t */\n \tif (opt & PICKAXE_BLAME_COPY)\n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\tfor (i = 0, sg = first_scapegoat(sb, revs, commit);\n \t\t     i < num_sg && sg;\n \t\t     sg = sg->next, i++) {\n \t\t\tstruct origin *porigin = sg_origin[i];\n@@ -1274,8 +1309,8 @@ struct commit_info\n /*\n  * Parse author/committer line in the commit object buffer\n  */\n-static void get_ac_line(const char *inbuf, const char *what,\n-\t\t\tint person_len, char *person,\n+static void get_ac_line(struct scoreboard *sb, const char *inbuf, \n+\t\t\tconst char *what, int person_len, char *person,\n \t\t\tint mail_len, char *mail,\n \t\t\tunsigned long *time, const char **tz)\n {\n@@ -1323,7 +1358,7 @@ static void get_ac_line(const char *inbuf, const char *what,\n \tmaillen = timepos - tmp;\n \tmemcpy(mail, mailpos, maillen);\n \n-\tif (!mailmap.nr)\n+\tif (!sb->ssb->mailmap.nr)\n \t\treturn;\n \n \t/*\n@@ -1338,7 +1373,7 @@ static void get_ac_line(const char *inbuf, const char *what,\n \t/*\n \t * Now, convert both name and e-mail using mailmap\n \t */\n-\tif(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {\n+\tif(map_user(&sb->ssb->mailmap, mail+1, mail_len-1, person, tmp-person-1)) {\n \t\t/* Add a trailing '>' to email, since map_user returns plain emails\n \t\t   Note: It already has '<', since we replace from mail+1 */\n \t\tmailpos = memchr(mail, '\\0', mail_len);\n@@ -1349,7 +1384,8 @@ static void get_ac_line(const char *inbuf, const char *what,\n \t}\n }\n \n-static void get_commit_info(struct commit *commit,\n+static void get_commit_info(struct scoreboard *sb,\n+\t\t\t    struct commit *commit,\n \t\t\t    struct commit_info *ret,\n \t\t\t    int detailed)\n {\n@@ -1378,7 +1414,7 @@ static void get_commit_info(struct commit *commit,\n \tmessage   = reencoded ? reencoded : commit->buffer;\n \tret->author = author_name;\n \tret->author_mail = author_mail;\n-\tget_ac_line(message, \"\\nauthor \",\n+\tget_ac_line(sb, message, \"\\nauthor \",\n \t\t    sizeof(author_name), author_name,\n \t\t    sizeof(author_mail), author_mail,\n \t\t    &ret->author_time, &ret->author_tz);\n@@ -1390,7 +1426,7 @@ static void get_commit_info(struct commit *commit,\n \n \tret->committer = committer_name;\n \tret->committer_mail = committer_mail;\n-\tget_ac_line(message, \"\\ncommitter \",\n+\tget_ac_line(sb, message, \"\\ncommitter \",\n \t\t    sizeof(committer_name), committer_name,\n \t\t    sizeof(committer_mail), committer_mail,\n \t\t    &ret->committer_time, &ret->committer_tz);\n@@ -1430,7 +1466,8 @@ static void write_filename_info(const char *path)\n  * commit.  Instead of repeating this every line, emit it only once,\n  * the first time each commit appears in the output.\n  */\n-static int emit_one_suspect_detail(struct origin *suspect)\n+static int emit_one_suspect_detail(struct scoreboard *sb, \n+\t\t\t\t   struct origin *suspect)\n {\n \tstruct commit_info ci;\n \n@@ -1438,7 +1475,7 @@ static int emit_one_suspect_detail(struct origin *suspect)\n \t\treturn 0;\n \n \tsuspect->commit->object.flags |= METAINFO_SHOWN;\n-\tget_commit_info(suspect->commit, &ci, 1);\n+\tget_commit_info(sb, suspect->commit, &ci, 1);\n \tprintf(\"author %s\\n\", ci.author);\n \tprintf(\"author-mail %s\\n\", ci.author_mail);\n \tprintf(\"author-time %lu\\n\", ci.author_time);\n@@ -1462,18 +1499,18 @@ static int emit_one_suspect_detail(struct origin *suspect)\n  * The blame_entry is found to be guilty for the range.  Mark it\n  * as such, and show it in incremental output.\n  */\n-static void found_guilty_entry(struct blame_entry *ent)\n+static void found_guilty_entry(struct scoreboard *sb, struct blame_entry *ent)\n {\n \tif (ent->guilty)\n \t\treturn;\n \tent->guilty = 1;\n-\tif (incremental) {\n+\tif (sb->ssb->incremental) {\n \t\tstruct origin *suspect = ent->suspect;\n \n \t\tprintf(\"%s %d %d %d\\n\",\n \t\t       sha1_to_hex(suspect->commit->object.sha1),\n \t\t       ent->s_lno + 1, ent->lno + 1, ent->num_lines);\n-\t\temit_one_suspect_detail(suspect);\n+\t\temit_one_suspect_detail(sb, suspect);\n \t\twrite_filename_info(suspect->path);\n \t\tmaybe_flush_or_die(stdout, \"stdout\");\n \t}\n@@ -1508,7 +1545,7 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \t\tcommit = suspect->commit;\n \t\tif (!commit->object.parsed)\n \t\t\tparse_commit(commit);\n-\t\tif (reverse ||\n+\t\tif (sb->ssb->reverse ||\n \t\t    (!(commit->object.flags & UNINTERESTING) &&\n \t\t     !(revs->max_age != -1 && commit->date < revs->max_age)))\n \t\t\tpass_blame(sb, suspect, opt);\n@@ -1518,13 +1555,13 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \t\t\t\tmark_parents_uninteresting(commit);\n \t\t}\n \t\t/* treat root commit as boundary */\n-\t\tif (!commit->parents && !show_root)\n+\t\tif (!commit->parents && !sb->ssb->show_root)\n \t\t\tcommit->object.flags |= UNINTERESTING;\n \n \t\t/* Take responsibility for the remaining entries */\n \t\tfor (ent = sb->ent; ent; ent = ent->next)\n \t\t\tif (same_suspect(ent->suspect, suspect))\n-\t\t\t\tfound_guilty_entry(ent);\n+\t\t\t\tfound_guilty_entry(sb, ent);\n \t\torigin_decref(suspect);\n \n \t\tif (DEBUG) /* sanity */\n@@ -1532,8 +1569,8 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \t}\n }\n \n-static const char *format_time(unsigned long time, const char *tz_str,\n-\t\t\t       int show_raw_time)\n+static const char *format_time(struct scoreboard *sb, unsigned long time, \n+\t\t\t       const char *tz_str, int show_raw_time)\n {\n \tstatic char time_buf[128];\n \tconst char *time_str;\n@@ -1545,10 +1582,10 @@ static const char *format_time(unsigned long time, const char *tz_str,\n \t}\n \telse {\n \t\ttz = atoi(tz_str);\n-\t\ttime_str = show_date(time, tz, blame_date_mode);\n+\t\ttime_str = show_date(time, tz, sb->ssb->blame_date_mode);\n \t\ttime_len = strlen(time_str);\n \t\tmemcpy(time_buf, time_str, time_len);\n-\t\tmemset(time_buf + time_len, ' ', blame_date_width - time_len);\n+\t\tmemset(time_buf + time_len, ' ', sb->ssb->blame_date_width - time_len);\n \t}\n \treturn time_buf;\n }\n@@ -1576,7 +1613,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)\n \t       ent->s_lno + 1,\n \t       ent->lno + 1,\n \t       ent->num_lines);\n-\tif (emit_one_suspect_detail(suspect) ||\n+\tif (emit_one_suspect_detail(sb, suspect) ||\n \t    (suspect->commit->object.flags & MORE_THAN_ONE_PATH))\n \t\twrite_filename_info(suspect->path);\n \n@@ -1605,7 +1642,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \tchar hex[41];\n \tint show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);\n \n-\tget_commit_info(suspect->commit, &ci, 1);\n+\tget_commit_info(sb, suspect->commit, &ci, 1);\n \tstrcpy(hex, sha1_to_hex(suspect->commit->object.sha1));\n \n \tcp = nth_line(sb, ent->lno);\n@@ -1614,7 +1651,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \t\tint length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;\n \n \t\tif (suspect->commit->object.flags & UNINTERESTING) {\n-\t\t\tif (blank_boundary)\n+\t\t\tif (sb->ssb->blank_boundary)\n \t\t\t\tmemset(hex, ' ', length);\n \t\t\telse if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {\n \t\t\t\tlength--;\n@@ -1625,31 +1662,31 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \t\tprintf(\"%.*s\", length, hex);\n \t\tif (opt & OUTPUT_ANNOTATE_COMPAT)\n \t\t\tprintf(\"\\t(%10s\\t%10s\\t%d)\", ci.author,\n-\t\t\t       format_time(ci.author_time, ci.author_tz,\n-\t\t\t\t\t   show_raw_time),\n+\t\t\t       format_time(sb, ci.author_time, \n+\t\t\t\t\t   ci.author_tz, show_raw_time),\n \t\t\t       ent->lno + 1 + cnt);\n \t\telse {\n \t\t\tif (opt & OUTPUT_SHOW_SCORE)\n \t\t\t\tprintf(\" %*d %02d\",\n-\t\t\t\t       max_score_digits, ent->score,\n+\t\t\t\t       sb->ssb->max_score_digits, ent->score,\n \t\t\t\t       ent->suspect->refcnt);\n \t\t\tif (opt & OUTPUT_SHOW_NAME)\n-\t\t\t\tprintf(\" %-*.*s\", longest_file, longest_file,\n-\t\t\t\t       suspect->path);\n+\t\t\t\tprintf(\" %-*.*s\", sb->ssb->longest_file,\n+\t\t\t\t\t\t  sb->ssb->longest_file, suspect->path);\n \t\t\tif (opt & OUTPUT_SHOW_NUMBER)\n-\t\t\t\tprintf(\" %*d\", max_orig_digits,\n+\t\t\t\tprintf(\" %*d\", sb->ssb->max_orig_digits,\n \t\t\t\t       ent->s_lno + 1 + cnt);\n \n \t\t\tif (!(opt & OUTPUT_NO_AUTHOR)) {\n-\t\t\t\tint pad = longest_author - utf8_strwidth(ci.author);\n+\t\t\t\tint pad = sb->ssb->longest_author - utf8_strwidth(ci.author);\n \t\t\t\tprintf(\" (%s%*s %10s\",\n \t\t\t\t       ci.author, pad, \"\",\n-\t\t\t\t       format_time(ci.author_time,\n+\t\t\t\t       format_time(sb, ci.author_time,\n \t\t\t\t\t\t   ci.author_tz,\n \t\t\t\t\t\t   show_raw_time));\n \t\t\t}\n \t\t\tprintf(\" %*d) \",\n-\t\t\t       max_digits, ent->lno + 1 + cnt);\n+\t\t\t       sb->ssb->max_digits, ent->lno + 1 + cnt);\n \t\t}\n \t\tdo {\n \t\t\tch = *cp++;\n@@ -1773,14 +1810,14 @@ static void find_alignment(struct scoreboard *sb, int *option)\n \t\tif (strcmp(suspect->path, sb->path))\n \t\t\t*option |= OUTPUT_SHOW_NAME;\n \t\tnum = strlen(suspect->path);\n-\t\tif (longest_file < num)\n-\t\t\tlongest_file = num;\n+\t\tif (sb->ssb->longest_file < num)\n+\t\t\tsb->ssb->longest_file = num;\n \t\tif (!(suspect->commit->object.flags & METAINFO_SHOWN)) {\n \t\t\tsuspect->commit->object.flags |= METAINFO_SHOWN;\n-\t\t\tget_commit_info(suspect->commit, &ci, 1);\n+\t\t\tget_commit_info(sb, suspect->commit, &ci, 1);\n \t\t\tnum = utf8_strwidth(ci.author);\n-\t\t\tif (longest_author < num)\n-\t\t\t\tlongest_author = num;\n+\t\t\tif (sb->ssb->longest_author < num)\n+\t\t\t\tsb->ssb->longest_author = num;\n \t\t}\n \t\tnum = e->s_lno + e->num_lines;\n \t\tif (longest_src_lines < num)\n@@ -1791,9 +1828,9 @@ static void find_alignment(struct scoreboard *sb, int *option)\n \t\tif (largest_score < ent_score(sb, e))\n \t\t\tlargest_score = ent_score(sb, e);\n \t}\n-\tmax_orig_digits = lineno_width(longest_src_lines);\n-\tmax_digits = lineno_width(longest_dst_lines);\n-\tmax_score_digits = lineno_width(largest_score);\n+\tsb->ssb->max_orig_digits = lineno_width(longest_src_lines);\n+\tsb->ssb->max_digits = lineno_width(longest_dst_lines);\n+\tsb->ssb->max_score_digits = lineno_width(largest_score);\n }\n \n /*\n@@ -1945,19 +1982,19 @@ static void prepare_blame_range(struct scoreboard *sb,\n }\n \n static int git_blame_config(const char *var, const char *value, void *cb)\n-{\n+{ \n \tif (!strcmp(var, \"blame.showroot\")) {\n-\t\tshow_root = git_config_bool(var, value);\n+\t\t((struct super_scoreboard *)cb)->show_root = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"blame.blankboundary\")) {\n-\t\tblank_boundary = git_config_bool(var, value);\n+\t\t((struct super_scoreboard *)cb)->blank_boundary = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"blame.date\")) {\n \t\tif (!value)\n \t\t\treturn config_error_nonbool(var);\n-\t\tblame_date_mode = parse_date_format(value);\n+\t\t((struct super_scoreboard *)cb)->blame_date_mode = parse_date_format(value);\n \t\treturn 0;\n \t}\n \treturn git_default_config(var, value, cb);\n@@ -2155,7 +2192,7 @@ static int blame_copy_callback(const struct option *option, const char *arg, int\n \t*opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;\n \n \tif (arg)\n-\t\tblame_copy_score = parse_score(arg);\n+\t\topt_blame_copy_score = parse_score(arg);\n \treturn 0;\n }\n \n@@ -2166,7 +2203,7 @@ static int blame_move_callback(const struct option *option, const char *arg, int\n \t*opt |= PICKAXE_BLAME_MOVE;\n \n \tif (arg)\n-\t\tblame_move_score = parse_score(arg);\n+\t\topt_blame_move_score = parse_score(arg);\n \treturn 0;\n }\n \n@@ -2185,9 +2222,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n {\n \tstruct rev_info revs;\n \tconst char *path;\n-\tstruct scoreboard sb;\n \tstruct origin *o;\n \tstruct blame_entry *ent;\n+\tstatic struct blame_stat stat;\n+\tstatic struct super_scoreboard ssb = { .xdl_opts = XDF_NEED_MINIMAL, .blame_date_mode = DATE_ISO8601, .stat = &stat } ;\n+\tstatic struct scoreboard sb ;\n \tlong dashdash_pos, bottom, top, lno;\n \tconst char *final_commit_name = NULL;\n \tenum object_type type;\n@@ -2198,9 +2237,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tstatic const char *revs_file = NULL;\n \tstatic const char *contents_from = NULL;\n \tstatic const struct option options[] = {\n-\t\tOPT_BOOLEAN(0, \"incremental\", &incremental, \"Show blame entries as we find them, incrementally\"),\n-\t\tOPT_BOOLEAN('b', NULL, &blank_boundary, \"Show blank SHA-1 for boundary commits (Default: off)\"),\n-\t\tOPT_BOOLEAN(0, \"root\", &show_root, \"Do not treat root commits as boundaries (Default: off)\"),\n+\t\tOPT_BOOLEAN(0, \"incremental\", &ssb.incremental, \"Show blame entries as we find them, incrementally\"),\n+\t\tOPT_BOOLEAN('b', NULL, &ssb.blank_boundary, \"Show blank SHA-1 for boundary commits (Default: off)\"),\n+\t\tOPT_BOOLEAN(0, \"root\", &ssb.show_root, \"Do not treat root commits as boundaries (Default: off)\"),\n \t\tOPT_BOOLEAN(0, \"show-stats\", &show_stats, \"Show work cost statistics\"),\n \t\tOPT_BIT(0, \"score-debug\", &output_option, \"Show output score for blame entries\", OUTPUT_SHOW_SCORE),\n \t\tOPT_BIT('f', \"show-name\", &output_option, \"Show original filename (Default: auto)\", OUTPUT_SHOW_NAME),\n@@ -2210,7 +2249,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tOPT_BIT('t', NULL, &output_option, \"Show raw timestamp (Default: off)\", OUTPUT_RAW_TIMESTAMP),\n \t\tOPT_BIT('l', NULL, &output_option, \"Show long commit SHA1 (Default: off)\", OUTPUT_LONG_OBJECT_NAME),\n \t\tOPT_BIT('s', NULL, &output_option, \"Suppress author name and timestamp (Default: off)\", OUTPUT_NO_AUTHOR),\n-\t\tOPT_BIT('w', NULL, &xdl_opts, \"Ignore whitespace differences\", XDF_IGNORE_WHITESPACE),\n+\t\tOPT_BIT('w', NULL, &ssb.xdl_opts, \"Ignore whitespace differences\", XDF_IGNORE_WHITESPACE),\n \t\tOPT_STRING('S', NULL, &revs_file, \"file\", \"Use revisions from <file> instead of calling git-rev-list\"),\n \t\tOPT_STRING(0, \"contents\", &contents_from, \"file\", \"Use <file>'s contents as the final image\"),\n \t\t{ OPTION_CALLBACK, 'C', NULL, &opt, \"score\", \"Find line copies within and across files\", PARSE_OPT_OPTARG, blame_copy_callback },\n@@ -2222,9 +2261,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tstruct parse_opt_ctx_t ctx;\n \tint cmd_is_annotate = !strcmp(argv[0], \"annotate\");\n \n-\tgit_config(git_blame_config, NULL);\n+\tgit_config(git_blame_config, &ssb);\n \tinit_revisions(&revs, NULL);\n-\trevs.date_mode = blame_date_mode;\n+\trevs.date_mode = ssb.blame_date_mode;\n \n \tsave_commit_buffer = 0;\n \tdashdash_pos = 0;\n@@ -2243,7 +2282,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \t\tif (!strcmp(ctx.argv[0], \"--reverse\")) {\n \t\t\tctx.argv[0] = \"--children\";\n-\t\t\treverse = 1;\n+\t\t\tssb.reverse = 1;\n \t\t}\n \t\tparse_revision_opt(&revs, &ctx, options, blame_opt_usage);\n \t}\n@@ -2252,42 +2291,45 @@ parse_done:\n \n \tif (cmd_is_annotate) {\n \t\toutput_option |= OUTPUT_ANNOTATE_COMPAT;\n-\t\tblame_date_mode = DATE_ISO8601;\n+\t\tssb.blame_date_mode = DATE_ISO8601;\n \t} else {\n-\t\tblame_date_mode = revs.date_mode;\n+\t\tssb.blame_date_mode = revs.date_mode;\n \t}\n \n+\tssb.blame_move_score = opt_blame_move_score ;\n+\tssb.blame_copy_score = opt_blame_copy_score ;\n+\n \t/* The maximum width used to show the dates */\n-\tswitch (blame_date_mode) {\n+\tswitch (ssb.blame_date_mode) {\n \tcase DATE_RFC2822:\n-\t\tblame_date_width = sizeof(\"Thu, 19 Oct 2006 16:00:04 -0700\");\n+\t\tssb.blame_date_width = sizeof(\"Thu, 19 Oct 2006 16:00:04 -0700\");\n \t\tbreak;\n \tcase DATE_ISO8601:\n-\t\tblame_date_width = sizeof(\"2006-10-19 16:00:04 -0700\");\n+\t\tssb.blame_date_width = sizeof(\"2006-10-19 16:00:04 -0700\");\n \t\tbreak;\n \tcase DATE_RAW:\n-\t\tblame_date_width = sizeof(\"1161298804 -0700\");\n+\t\tssb.blame_date_width = sizeof(\"1161298804 -0700\");\n \t\tbreak;\n \tcase DATE_SHORT:\n-\t\tblame_date_width = sizeof(\"2006-10-19\");\n+\t\tssb.blame_date_width = sizeof(\"2006-10-19\");\n \t\tbreak;\n \tcase DATE_RELATIVE:\n \t\t/* \"normal\" is used as the fallback for \"relative\" */\n \tcase DATE_LOCAL:\n \tcase DATE_NORMAL:\n-\t\tblame_date_width = sizeof(\"Thu Oct 19 16:00:04 2006 -0700\");\n+\t\tssb.blame_date_width = sizeof(\"Thu Oct 19 16:00:04 2006 -0700\");\n \t\tbreak;\n \t}\n-\tblame_date_width -= 1; /* strip the null */\n+\tssb.blame_date_width -= 1; /* strip the null */\n \n \tif (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))\n \t\topt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |\n \t\t\tPICKAXE_BLAME_COPY_HARDER);\n \n-\tif (!blame_move_score)\n-\t\tblame_move_score = BLAME_DEFAULT_MOVE_SCORE;\n-\tif (!blame_copy_score)\n-\t\tblame_copy_score = BLAME_DEFAULT_COPY_SCORE;\n+\tif (!ssb.blame_move_score)\n+\t\tssb.blame_move_score = BLAME_DEFAULT_MOVE_SCORE;\n+\tif (!ssb.blame_copy_score)\n+\t\tssb.blame_copy_score = BLAME_DEFAULT_COPY_SCORE;\n \n \t/*\n \t * We have collected options unknown to us in argv[1..unk]\n@@ -2341,9 +2383,9 @@ parse_done:\n \n \tsetup_revisions(argc, argv, &revs, NULL);\n \tmemset(&sb, 0, sizeof(sb));\n-\n+\tsb.ssb = &ssb ;\n \tsb.revs = &revs;\n-\tif (!reverse)\n+\tif (!sb.ssb->reverse)\n \t\tfinal_commit_name = prepare_final(&sb);\n \telse if (contents_from)\n \t\tdie(\"--contents and --children do not blend well.\");\n@@ -2391,7 +2433,7 @@ parse_done:\n \t\t\t    sha1_to_hex(o->blob_sha1),\n \t\t\t    path);\n \t}\n-\tnum_read_blob++;\n+\tstat.num_read_blob++;\n \tlno = prepare_lines(&sb);\n \n \tbottom = top = 0;\n@@ -2422,14 +2464,14 @@ parse_done:\n \t\tdie(\"reading graft file %s failed: %s\",\n \t\t    revs_file, strerror(errno));\n \n-\tread_mailmap(&mailmap, NULL);\n+\tread_mailmap(&sb.ssb->mailmap, NULL);\n \n-\tif (!incremental)\n+\tif (!sb.ssb->incremental)\n \t\tsetup_pager();\n \n \tassign_blame(&sb, opt);\n \n-\tif (incremental)\n+\tif (sb.ssb->incremental)\n \t\treturn 0;\n \n \tcoalesce(&sb);\n@@ -2446,9 +2488,9 @@ parse_done:\n \t}\n \n \tif (show_stats) {\n-\t\tprintf(\"num read blob: %d\\n\", num_read_blob);\n-\t\tprintf(\"num get patch: %d\\n\", num_get_patch);\n-\t\tprintf(\"num commits: %d\\n\", num_commits);\n+\t\tprintf(\"num read blob: %d\\n\", stat.num_read_blob);\n+\t\tprintf(\"num get patch: %d\\n\", stat.num_get_patch);\n+\t\tprintf(\"num commits: %d\\n\", stat.num_commits);\n \t}\n \treturn 0;\n }\n-- \n1.5.4.3\n"},{"id":"108002","messageId":"7vd4ckcz0j.fsf@gitster.siamese.dyndns.org","threadId":"18283","inReplyTo":"49B8E42B.30901@gmail.com","subject":"Re: [PATCH] Removed static variables in blame for catering reuse of blame code","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-03-14T08:03:08Z","receivedAt":"2009-03-14T08:03:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"pi song <pi.songs@gmail.com> writes:\n\n> diff --git a/builtin-blame.c b/builtin-blame.c\n> index 2aedd17..2e19ddf 100644\n> --- a/builtin-blame.c\n> +++ b/builtin-blame.c\n> @@ -30,42 +30,29 @@ static const char *blame_opt_usage[] = {\n>  \tNULL\n>  };\n>  \n> -static int longest_file;\n> -static int longest_author;\n> ...\n> -static int num_read_blob;\n> -static int num_get_patch;\n> -static int num_commits;\n\nI eyeballed the above removals and the additions to the new structure and\nthey match, which is good.\n\nI'd suggest removing the blame_stat and make its members part of the\nsuper-scoreboard, though.\n\n>  /*\n> - * Given an origin, prepare mmfile_t structure to be used by the\n> - * diff machinery\n> - */\n> -static void fill_origin_blob(struct origin *o, mmfile_t *file)\n> ...\n> -static void drop_origin_blob(struct origin *o)\n> -{\n> -\tif (o->file.ptr) {\n> -\t\tfree(o->file.ptr);\n> -\t\to->file.ptr = NULL;\n> -\t}\n> -}\n\nI do not see why you had to have this huge block of removed lines, and\nanother identcal huge block of added lines later.\n\n> -/*\n>   * Each group of lines is described by a blame_entry; it can be split\n>   * as we pass blame to the parents.  They form a linked list in the\n>   * scoreboard structure, sorted by the target line number.\n> @@ -176,6 +113,47 @@ struct blame_entry {\n>  };\n>  \n>  /*\n> + * stores things that survive across multiple blame operations\n> + * 1) for blame specific global parameters\n> + * 2) for reusable structures (possibly for optimization purpose)\n> + */\n> +struct super_scoreboard {\n\nIf your goal is to make a blame library out of this, I think \"scoreboard\"\nshould be renamed to \"blame_scoreboard\".  And this should be called\nsomething like \"blame_info\".\n\n> +/*\n> + * Given an origin, prepare mmfile_t structure to be used by the\n> + * diff machinery\n> + */\n> +static void fill_origin_blob(struct scoreboard *sb, struct origin *o, mmfile_t *file)\n> +{\n\nWhen the original function did not take \"scoreboard\", please do *not* add\nscoreboard as a new parameter.  Instead, make it directly take the overall\nblame state \"super_scoreboard\" (or even narrower subpart of it when you\ncan).  That way, when somebody later study the code, they can immediately\nsee that such a function does not affect what is in the scoreboard\n(i.e. how the blame is passed around).\n\n> +/*\n> + * Origin is refcounted and usually we keep the blob contents to be\n> + * reused.\n> + */\n> +static inline struct origin *origin_incref(struct origin *o)\n> +...\n> +static void drop_origin_blob(struct origin *o)\n> +{\n> +\tif (o->file.ptr) {\n> +\t\tfree(o->file.ptr);\n> +\t\to->file.ptr = NULL;\n> +\t}\n> +}\n\nThese look like duplicated/moved for no good reason.\n\n> @@ -1122,17 +1153,21 @@ static void pass_whole_blame(struct scoreboard *sb,\n>   * \"parent\" (and \"porigin\"), but what we mean is to find scapegoat to\n>   * exonerate ourselves.\n>   */\n> -static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)\n> +static struct commit_list *first_scapegoat(struct scoreboard *sb, \n\nSame comment as fill_origin_blob() applies here.\n\n> -static int num_scapegoats(struct rev_info *revs, struct commit *commit)\n> +static int num_scapegoats(struct scoreboard *sb,\n\nLikewise.\n\n> @@ -1274,8 +1309,8 @@ struct commit_info\n>  /*\n>   * Parse author/committer line in the commit object buffer\n>   */\n> -static void get_ac_line(const char *inbuf, const char *what,\n> -\t\t\tint person_len, char *person,\n> +static void get_ac_line(struct scoreboard *sb, const char *inbuf, \n\nLikewise.  You only need the mailmap here.\n\n> @@ -1349,7 +1384,8 @@ static void get_ac_line(const char *inbuf, const char *what,\n>  \t}\n>  }\n>  \n> -static void get_commit_info(struct commit *commit,\n> +static void get_commit_info(struct scoreboard *sb,\n\nLikewise.\n\n> @@ -1430,7 +1466,8 @@ static void write_filename_info(const char *path)\n>   * commit.  Instead of repeating this every line, emit it only once,\n>   * the first time each commit appears in the output.\n>   */\n> -static int emit_one_suspect_detail(struct origin *suspect)\n> +static int emit_one_suspect_detail(struct scoreboard *sb, \n> +\t\t\t\t   struct origin *suspect)\n\nLikewise.\n\n> @@ -1462,18 +1499,18 @@ static int emit_one_suspect_detail(struct origin *suspect)\n>   * The blame_entry is found to be guilty for the range.  Mark it\n>   * as such, and show it in incremental output.\n>   */\n> -static void found_guilty_entry(struct blame_entry *ent)\n> +static void found_guilty_entry(struct scoreboard *sb, struct blame_entry *ent)\n\nLikewise.\n\n> @@ -1532,8 +1569,8 @@ static void assign_blame(struct scoreboard *sb, int opt)\n>  \t}\n>  }\n>  \n> -static const char *format_time(unsigned long time, const char *tz_str,\n> -\t\t\t       int show_raw_time)\n> +static const char *format_time(struct scoreboard *sb, unsigned long time, \n\nLikewise.\n\nHere is a fixed-up replacement along the lines I suggested above, except\nthat I did not bother getting rid of \"struct blame_stat\".\n\nI've also fixed up a few style violations I noticed and fixed up the\ncommit log message a bit.\n\n-- >8 --\nFrom: pi song <pi.songs@gmail.com>\nDate: Thu, 12 Mar 2009 21:30:03 +1100\nSubject: [PATCH] blame.c: start libifying the blame infrastructure\n\nThe patch refactors builtin-blame.c to not rely on static variables by\nintroducing a \"blame_info\" structure and moving them into it.  While at\nit, \"scoreboard\" is renamed to \"blame_scoreboard\".\n\nWe can later use a same blame_info that holds the settings with different\nscoreboards to run blame multiple times on different data.\n\nSigned-off-by: Pi Song <pi.songs@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-blame.c |  365 +++++++++++++++++++++++++++++++------------------------\n 1 files changed, 205 insertions(+), 160 deletions(-)\n\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex 2aedd17..efac5d4 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -30,42 +30,29 @@ static const char *blame_opt_usage[] = {\n \tNULL\n };\n \n-static int longest_file;\n-static int longest_author;\n-static int max_orig_digits;\n-static int max_digits;\n-static int max_score_digits;\n-static int show_root;\n-static int reverse;\n-static int blank_boundary;\n-static int incremental;\n-static int xdl_opts = XDF_NEED_MINIMAL;\n-\n-static enum date_mode blame_date_mode = DATE_ISO8601;\n-static size_t blame_date_width;\n-\n-static struct string_list mailmap;\n+/* only use for command line parameter parsing */\n+static unsigned opt_blame_move_score;\n1+static unsigned opt_blame_copy_score;\n \n #ifndef DEBUG\n #define DEBUG 0\n #endif\n \n-/* stats */\n-static int num_read_blob;\n-static int num_get_patch;\n-static int num_commits;\n+/*\n+ * for storing stats. it can be used\n+ * across multiple blame operations\n+ */\n+struct blame_stat {\n+\tint num_read_blob;\n+\tint num_get_patch;\n+\tint num_commits;\n+};\n \n #define PICKAXE_BLAME_MOVE\t\t01\n #define PICKAXE_BLAME_COPY\t\t02\n #define PICKAXE_BLAME_COPY_HARDER\t04\n #define PICKAXE_BLAME_COPY_HARDEST\t010\n \n-/*\n- * blame for a blame_entry with score lower than these thresholds\n- * is not passed to the parent using move/copy logic.\n- */\n-static unsigned blame_move_score;\n-static unsigned blame_copy_score;\n #define BLAME_DEFAULT_MOVE_SCORE\t20\n #define BLAME_DEFAULT_COPY_SCORE\t40\n \n@@ -86,14 +73,55 @@ struct origin {\n };\n \n /*\n+ * stores things that survive across multiple blame operations\n+ * 1) for blame specific global parameters\n+ * 2) for reusable structures (possibly for optimization purpose)\n+ */\n+struct blame_info {\n+\t/*\n+\t * miscellaneous parameters collected during processing\n+\t * for pretty formatting purpose\n+\t */\n+\tint longest_file;\n+\tint longest_author;\n+\tint max_orig_digits;\n+\tint max_digits;\n+\tint max_score_digits;\n+\n+\t/* formatting parameters */\n+\tint show_root;\n+\tint reverse;\n+\tint blank_boundary;\n+\tint incremental;\n+\tint xdl_opts;\n+\n+\t/*\n+\t * blame for a blame_entry with score lower than these thresholds\n+\t * is not passed to the parent using move/copy logic.\n+\t */\n+\tunsigned blame_move_score;\n+\tunsigned blame_copy_score;\n+\n+\t/* date formatting related */\n+\tenum date_mode blame_date_mode;\n+\tsize_t blame_date_width;\n+\n+\t/* for fast mailmap lookup */\n+\tstruct string_list mailmap;\n+\n+\t/* for stat collecting purpose */\n+\tstruct blame_stat *stat;\n+};\n+\n+/*\n  * Given an origin, prepare mmfile_t structure to be used by the\n  * diff machinery\n  */\n-static void fill_origin_blob(struct origin *o, mmfile_t *file)\n+static void fill_origin_blob(struct blame_info *ssb, struct origin *o, mmfile_t *file)\n {\n \tif (!o->file.ptr) {\n \t\tenum object_type type;\n-\t\tnum_read_blob++;\n+\t\tssb->stat->num_read_blob++;\n \t\tfile->ptr = read_sha1_file(o->blob_sha1, &type,\n \t\t\t\t\t   (unsigned long *)(&(file->size)));\n \t\tif (!file->ptr)\n@@ -178,7 +206,7 @@ struct blame_entry {\n /*\n  * The current state of the blame assignment.\n  */\n-struct scoreboard {\n+struct blame_scoreboard {\n \t/* the final commit (i.e. where we started digging from) */\n \tstruct commit *final;\n \tstruct rev_info *revs;\n@@ -198,6 +226,8 @@ struct scoreboard {\n \t/* look-up a line in the final buffer */\n \tint num_lines;\n \tint *lineno;\n+\n+\tstruct blame_info *ssb;\n };\n \n static inline int same_suspect(struct origin *a, struct origin *b)\n@@ -209,14 +239,14 @@ static inline int same_suspect(struct origin *a, struct origin *b)\n \treturn !strcmp(a->path, b->path);\n }\n \n-static void sanity_check_refcnt(struct scoreboard *);\n+static void sanity_check_refcnt(struct blame_scoreboard *);\n \n /*\n  * If two blame entries that are next to each other came from\n  * contiguous lines in the same origin (i.e. <commit, path> pair),\n  * merge them together.\n  */\n-static void coalesce(struct scoreboard *sb)\n+static void coalesce(struct blame_scoreboard *sb)\n {\n \tstruct blame_entry *ent, *next;\n \n@@ -258,7 +288,7 @@ static struct origin *make_origin(struct commit *commit, const char *path)\n /*\n  * Locate an existing origin or create a new one.\n  */\n-static struct origin *get_origin(struct scoreboard *sb,\n+static struct origin *get_origin(struct blame_scoreboard *sb,\n \t\t\t\t struct commit *commit,\n \t\t\t\t const char *path)\n {\n@@ -301,7 +331,7 @@ static int fill_blob_sha1(struct origin *origin)\n  * We have an origin -- check if the same path exists in the\n  * parent and return an origin structure to represent it.\n  */\n-static struct origin *find_origin(struct scoreboard *sb,\n+static struct origin *find_origin(struct blame_scoreboard *sb,\n \t\t\t\t  struct commit *parent,\n \t\t\t\t  struct origin *origin)\n {\n@@ -409,7 +439,7 @@ static struct origin *find_origin(struct scoreboard *sb,\n  * We have an origin -- find the path that corresponds to it in its\n  * parent and return an origin structure to represent it.\n  */\n-static struct origin *find_rename(struct scoreboard *sb,\n+static struct origin *find_rename(struct blame_scoreboard *sb,\n \t\t\t\t  struct commit *parent,\n \t\t\t\t  struct origin *origin)\n {\n@@ -454,7 +484,7 @@ static struct origin *find_rename(struct scoreboard *sb,\n  * Link in a new blame entry to the scoreboard.  Entries that cover the\n  * same line range have been removed from the scoreboard previously.\n  */\n-static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)\n+static void add_blame_entry(struct blame_scoreboard *sb, struct blame_entry *e)\n {\n \tstruct blame_entry *ent, *prev = NULL;\n \n@@ -497,7 +527,7 @@ static void dup_entry(struct blame_entry *dst, struct blame_entry *src)\n \tdst->score = 0;\n }\n \n-static const char *nth_line(struct scoreboard *sb, int lno)\n+static const char *nth_line(struct blame_scoreboard *sb, int lno)\n {\n \treturn sb->final_buf + sb->lineno[lno];\n }\n@@ -564,7 +594,7 @@ static void split_overlap(struct blame_entry *split,\n  * in split.  Adjust the linked list of blames in the scoreboard to\n  * reflect the split.\n  */\n-static void split_blame(struct scoreboard *sb,\n+static void split_blame(struct blame_scoreboard *sb,\n \t\t\tstruct blame_entry *split,\n \t\t\tstruct blame_entry *e)\n {\n@@ -646,7 +676,7 @@ static void decref_split(struct blame_entry *split)\n  * Helper for blame_chunk().  blame_entry e is known to overlap with\n  * the patch hunk; split it and pass blame to the parent.\n  */\n-static void blame_overlap(struct scoreboard *sb, struct blame_entry *e,\n+static void blame_overlap(struct blame_scoreboard *sb, struct blame_entry *e,\n \t\t\t  int tlno, int plno, int same,\n \t\t\t  struct origin *parent)\n {\n@@ -661,7 +691,7 @@ static void blame_overlap(struct scoreboard *sb, struct blame_entry *e,\n /*\n  * Find the line number of the last line the target is suspected for.\n  */\n-static int find_last_in_target(struct scoreboard *sb, struct origin *target)\n+static int find_last_in_target(struct blame_scoreboard *sb, struct origin *target)\n {\n \tstruct blame_entry *e;\n \tint last_in_target = -1;\n@@ -680,7 +710,7 @@ static int find_last_in_target(struct scoreboard *sb, struct origin *target)\n  * blame_entry e and its parent.  Find and split the overlap, and\n  * pass blame to the overlapping part to the parent.\n  */\n-static void blame_chunk(struct scoreboard *sb,\n+static void blame_chunk(struct blame_scoreboard *sb,\n \t\t\tint tlno, int plno, int same,\n \t\t\tstruct origin *target, struct origin *parent)\n {\n@@ -697,7 +727,7 @@ static void blame_chunk(struct scoreboard *sb,\n }\n \n struct blame_chunk_cb_data {\n-\tstruct scoreboard *sb;\n+\tstruct blame_scoreboard *sb;\n \tstruct origin *target;\n \tstruct origin *parent;\n \tlong plno;\n@@ -717,7 +747,7 @@ static void blame_chunk_cb(void *data, long same, long p_next, long t_next)\n  * for the lines it is suspected to its parent.  Run diff to find\n  * which lines came from parent and pass blame for them.\n  */\n-static int pass_blame_to_parent(struct scoreboard *sb,\n+static int pass_blame_to_parent(struct blame_scoreboard *sb,\n \t\t\t\tstruct origin *target,\n \t\t\t\tstruct origin *parent)\n {\n@@ -731,12 +761,12 @@ static int pass_blame_to_parent(struct scoreboard *sb,\n \tif (last_in_target < 0)\n \t\treturn 1; /* nothing remains for this target */\n \n-\tfill_origin_blob(parent, &file_p);\n-\tfill_origin_blob(target, &file_o);\n-\tnum_get_patch++;\n+\tfill_origin_blob(sb->ssb, parent, &file_p);\n+\tfill_origin_blob(sb->ssb, target, &file_o);\n+\tsb->ssb->stat->num_get_patch++;\n \n \tmemset(&xpp, 0, sizeof(xpp));\n-\txpp.flags = xdl_opts;\n+\txpp.flags = sb->ssb->xdl_opts;\n \tmemset(&xecfg, 0, sizeof(xecfg));\n \txecfg.ctxlen = 0;\n \txdi_diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, &xpp, &xecfg);\n@@ -755,7 +785,7 @@ static int pass_blame_to_parent(struct scoreboard *sb,\n  *\n  * Compute how trivial the lines in the blame_entry are.\n  */\n-static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)\n+static unsigned ent_score(struct blame_scoreboard *sb, struct blame_entry *e)\n {\n \tunsigned score;\n \tconst char *cp, *ep;\n@@ -782,7 +812,7 @@ static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)\n  * so far, by comparing this and best_so_far and copying this into\n  * bst_so_far as needed.\n  */\n-static void copy_split_if_better(struct scoreboard *sb,\n+static void copy_split_if_better(struct blame_scoreboard *sb,\n \t\t\t\t struct blame_entry *best_so_far,\n \t\t\t\t struct blame_entry *this)\n {\n@@ -816,7 +846,7 @@ static void copy_split_if_better(struct scoreboard *sb,\n  *\n  * All line numbers are 0-based.\n  */\n-static void handle_split(struct scoreboard *sb,\n+static void handle_split(struct blame_scoreboard *sb,\n \t\t\t struct blame_entry *ent,\n \t\t\t int tlno, int plno, int same,\n \t\t\t struct origin *parent,\n@@ -835,7 +865,7 @@ static void handle_split(struct scoreboard *sb,\n }\n \n struct handle_split_cb_data {\n-\tstruct scoreboard *sb;\n+\tstruct blame_scoreboard *sb;\n \tstruct blame_entry *ent;\n \tstruct origin *parent;\n \tstruct blame_entry *split;\n@@ -856,7 +886,7 @@ static void handle_split_cb(void *data, long same, long p_next, long t_next)\n  * we can pass blames to it.  file_p has the blob contents for\n  * the parent.\n  */\n-static void find_copy_in_blob(struct scoreboard *sb,\n+static void find_copy_in_blob(struct blame_scoreboard *sb,\n \t\t\t      struct blame_entry *ent,\n \t\t\t      struct origin *parent,\n \t\t\t      struct blame_entry *split,\n@@ -887,7 +917,7 @@ static void find_copy_in_blob(struct scoreboard *sb,\n \t * file_p partially may match that image.\n \t */\n \tmemset(&xpp, 0, sizeof(xpp));\n-\txpp.flags = xdl_opts;\n+\txpp.flags = sb->ssb->xdl_opts;\n \tmemset(&xecfg, 0, sizeof(xecfg));\n \txecfg.ctxlen = 1;\n \tmemset(split, 0, sizeof(struct blame_entry [3]));\n@@ -900,7 +930,7 @@ static void find_copy_in_blob(struct scoreboard *sb,\n  * See if lines currently target is suspected for can be attributed to\n  * parent.\n  */\n-static int find_move_in_parent(struct scoreboard *sb,\n+static int find_move_in_parent(struct blame_scoreboard *sb,\n \t\t\t       struct origin *target,\n \t\t\t       struct origin *parent)\n {\n@@ -912,7 +942,7 @@ static int find_move_in_parent(struct scoreboard *sb,\n \tif (last_in_target < 0)\n \t\treturn 1; /* nothing remains for this target */\n \n-\tfill_origin_blob(parent, &file_p);\n+\tfill_origin_blob(sb->ssb, parent, &file_p);\n \tif (!file_p.ptr)\n \t\treturn 0;\n \n@@ -921,11 +951,11 @@ static int find_move_in_parent(struct scoreboard *sb,\n \t\tmade_progress = 0;\n \t\tfor (e = sb->ent; e; e = e->next) {\n \t\t\tif (e->guilty || !same_suspect(e->suspect, target) ||\n-\t\t\t    ent_score(sb, e) < blame_move_score)\n+\t\t\t    ent_score(sb, e) < sb->ssb->blame_move_score)\n \t\t\t\tcontinue;\n \t\t\tfind_copy_in_blob(sb, e, parent, split, &file_p);\n \t\t\tif (split[1].suspect &&\n-\t\t\t    blame_move_score < ent_score(sb, &split[1])) {\n+\t\t\t    sb->ssb->blame_move_score < ent_score(sb, &split[1])) {\n \t\t\t\tsplit_blame(sb, split, e);\n \t\t\t\tmade_progress = 1;\n \t\t\t}\n@@ -944,7 +974,7 @@ struct blame_list {\n  * Count the number of entries the target is suspected for,\n  * and prepare a list of entry and the best split.\n  */\n-static struct blame_list *setup_blame_list(struct scoreboard *sb,\n+static struct blame_list *setup_blame_list(struct blame_scoreboard *sb,\n \t\t\t\t\t   struct origin *target,\n \t\t\t\t\t   int min_score,\n \t\t\t\t\t   int *num_ents_p)\n@@ -973,7 +1003,7 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb,\n /*\n  * Reset the scanned status on all entries.\n  */\n-static void reset_scanned_flag(struct scoreboard *sb)\n+static void reset_scanned_flag(struct blame_scoreboard *sb)\n {\n \tstruct blame_entry *e;\n \tfor (e = sb->ent; e; e = e->next)\n@@ -985,7 +1015,7 @@ static void reset_scanned_flag(struct scoreboard *sb)\n  * across file boundary from the parent commit.  porigin is the path\n  * in the parent we already tried.\n  */\n-static int find_copy_in_parent(struct scoreboard *sb,\n+static int find_copy_in_parent(struct blame_scoreboard *sb,\n \t\t\t       struct origin *target,\n \t\t\t       struct commit *parent,\n \t\t\t       struct origin *porigin,\n@@ -998,7 +1028,7 @@ static int find_copy_in_parent(struct scoreboard *sb,\n \tstruct blame_list *blame_list;\n \tint num_ents;\n \n-\tblame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);\n+\tblame_list = setup_blame_list(sb, target, sb->ssb->blame_copy_score, &num_ents);\n \tif (!blame_list)\n \t\treturn 1; /* nothing remains for this target */\n \n@@ -1053,7 +1083,7 @@ static int find_copy_in_parent(struct scoreboard *sb,\n \n \t\t\tnorigin = get_origin(sb, parent, p->one->path);\n \t\t\thashcpy(norigin->blob_sha1, p->one->sha1);\n-\t\t\tfill_origin_blob(norigin, &file_p);\n+\t\t\tfill_origin_blob(sb->ssb, norigin, &file_p);\n \t\t\tif (!file_p.ptr)\n \t\t\t\tcontinue;\n \n@@ -1070,7 +1100,7 @@ static int find_copy_in_parent(struct scoreboard *sb,\n \t\tfor (j = 0; j < num_ents; j++) {\n \t\t\tstruct blame_entry *split = blame_list[j].split;\n \t\t\tif (split[1].suspect &&\n-\t\t\t    blame_copy_score < ent_score(sb, &split[1])) {\n+\t\t\t    sb->ssb->blame_copy_score < ent_score(sb, &split[1])) {\n \t\t\t\tsplit_blame(sb, split, blame_list[j].ent);\n \t\t\t\tmade_progress = 1;\n \t\t\t}\n@@ -1082,7 +1112,7 @@ static int find_copy_in_parent(struct scoreboard *sb,\n \n \t\tif (!made_progress)\n \t\t\tbreak;\n-\t\tblame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);\n+\t\tblame_list = setup_blame_list(sb, target, sb->ssb->blame_copy_score, &num_ents);\n \t\tif (!blame_list) {\n \t\t\tretval = 1;\n \t\t\tbreak;\n@@ -1098,7 +1128,7 @@ static int find_copy_in_parent(struct scoreboard *sb,\n  * The blobs of origin and porigin exactly match, so everything\n  * origin is suspected for can be blamed on the parent.\n  */\n-static void pass_whole_blame(struct scoreboard *sb,\n+static void pass_whole_blame(struct blame_scoreboard *sb,\n \t\t\t     struct origin *origin, struct origin *porigin)\n {\n \tstruct blame_entry *e;\n@@ -1122,17 +1152,21 @@ static void pass_whole_blame(struct scoreboard *sb,\n  * \"parent\" (and \"porigin\"), but what we mean is to find scapegoat to\n  * exonerate ourselves.\n  */\n-static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)\n+static struct commit_list *first_scapegoat(struct blame_info *ssb,\n+\t\t\t\t\t   struct rev_info *revs,\n+\t\t\t\t\t   struct commit *commit)\n {\n-\tif (!reverse)\n+\tif (!ssb->reverse)\n \t\treturn commit->parents;\n \treturn lookup_decoration(&revs->children, &commit->object);\n }\n \n-static int num_scapegoats(struct rev_info *revs, struct commit *commit)\n+static int num_scapegoats(struct blame_info *ssb,\n+\t\t\t  struct rev_info *revs,\n+\t\t\t  struct commit *commit)\n {\n \tint cnt;\n-\tstruct commit_list *l = first_scapegoat(revs, commit);\n+\tstruct commit_list *l = first_scapegoat(ssb, revs, commit);\n \tfor (cnt = 0; l; l = l->next)\n \t\tcnt++;\n \treturn cnt;\n@@ -1140,7 +1174,7 @@ static int num_scapegoats(struct rev_info *revs, struct commit *commit)\n \n #define MAXSG 16\n \n-static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n+static void pass_blame(struct blame_scoreboard *sb, struct origin *origin, int opt)\n {\n \tstruct rev_info *revs = sb->revs;\n \tint i, pass, num_sg;\n@@ -1149,7 +1183,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \tstruct origin *sg_buf[MAXSG];\n \tstruct origin *porigin, **sg_origin = sg_buf;\n \n-\tnum_sg = num_scapegoats(revs, commit);\n+\tnum_sg = num_scapegoats(sb->ssb, revs, commit);\n \tif (!num_sg)\n \t\tgoto finish;\n \telse if (num_sg < ARRAY_SIZE(sg_buf))\n@@ -1162,11 +1196,11 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t * common cases, then we look for renames in the second pass.\n \t */\n \tfor (pass = 0; pass < 2; pass++) {\n-\t\tstruct origin *(*find)(struct scoreboard *,\n+\t\tstruct origin *(*find)(struct blame_scoreboard *,\n \t\t\t\t       struct commit *, struct origin *);\n \t\tfind = pass ? find_rename : find_origin;\n \n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\tfor (i = 0, sg = first_scapegoat(sb->ssb, revs, commit);\n \t\t     i < num_sg && sg;\n \t\t     sg = sg->next, i++) {\n \t\t\tstruct commit *p = sg->item;\n@@ -1198,8 +1232,8 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t\t}\n \t}\n \n-\tnum_commits++;\n-\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\tsb->ssb->stat->num_commits++;\n+\tfor (i = 0, sg = first_scapegoat(sb->ssb, revs, commit);\n \t     i < num_sg && sg;\n \t     sg = sg->next, i++) {\n \t\tstruct origin *porigin = sg_origin[i];\n@@ -1217,7 +1251,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t * Optionally find moves in parents' files.\n \t */\n \tif (opt & PICKAXE_BLAME_MOVE)\n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\tfor (i = 0, sg = first_scapegoat(sb->ssb, revs, commit);\n \t\t     i < num_sg && sg;\n \t\t     sg = sg->next, i++) {\n \t\t\tstruct origin *porigin = sg_origin[i];\n@@ -1231,7 +1265,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t * Optionally find copies from parents' files.\n \t */\n \tif (opt & PICKAXE_BLAME_COPY)\n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\tfor (i = 0, sg = first_scapegoat(sb->ssb, revs, commit);\n \t\t     i < num_sg && sg;\n \t\t     sg = sg->next, i++) {\n \t\t\tstruct origin *porigin = sg_origin[i];\n@@ -1274,8 +1308,8 @@ struct commit_info\n /*\n  * Parse author/committer line in the commit object buffer\n  */\n-static void get_ac_line(const char *inbuf, const char *what,\n-\t\t\tint person_len, char *person,\n+static void get_ac_line(struct string_list *mailmap, const char *inbuf,\n+\t\t\tconst char *what, int person_len, char *person,\n \t\t\tint mail_len, char *mail,\n \t\t\tunsigned long *time, const char **tz)\n {\n@@ -1323,7 +1357,7 @@ static void get_ac_line(const char *inbuf, const char *what,\n \tmaillen = timepos - tmp;\n \tmemcpy(mail, mailpos, maillen);\n \n-\tif (!mailmap.nr)\n+\tif (!mailmap->nr)\n \t\treturn;\n \n \t/*\n@@ -1338,7 +1372,7 @@ static void get_ac_line(const char *inbuf, const char *what,\n \t/*\n \t * Now, convert both name and e-mail using mailmap\n \t */\n-\tif(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {\n+\tif (map_user(mailmap, mail+1, mail_len-1, person, tmp-person-1)) {\n \t\t/* Add a trailing '>' to email, since map_user returns plain emails\n \t\t   Note: It already has '<', since we replace from mail+1 */\n \t\tmailpos = memchr(mail, '\\0', mail_len);\n@@ -1349,7 +1383,8 @@ static void get_ac_line(const char *inbuf, const char *what,\n \t}\n }\n \n-static void get_commit_info(struct commit *commit,\n+static void get_commit_info(struct string_list *mailmap,\n+\t\t\t    struct commit *commit,\n \t\t\t    struct commit_info *ret,\n \t\t\t    int detailed)\n {\n@@ -1378,7 +1413,7 @@ static void get_commit_info(struct commit *commit,\n \tmessage   = reencoded ? reencoded : commit->buffer;\n \tret->author = author_name;\n \tret->author_mail = author_mail;\n-\tget_ac_line(message, \"\\nauthor \",\n+\tget_ac_line(mailmap, message, \"\\nauthor \",\n \t\t    sizeof(author_name), author_name,\n \t\t    sizeof(author_mail), author_mail,\n \t\t    &ret->author_time, &ret->author_tz);\n@@ -1390,7 +1425,7 @@ static void get_commit_info(struct commit *commit,\n \n \tret->committer = committer_name;\n \tret->committer_mail = committer_mail;\n-\tget_ac_line(message, \"\\ncommitter \",\n+\tget_ac_line(mailmap, message, \"\\ncommitter \",\n \t\t    sizeof(committer_name), committer_name,\n \t\t    sizeof(committer_mail), committer_mail,\n \t\t    &ret->committer_time, &ret->committer_tz);\n@@ -1430,7 +1465,8 @@ static void write_filename_info(const char *path)\n  * commit.  Instead of repeating this every line, emit it only once,\n  * the first time each commit appears in the output.\n  */\n-static int emit_one_suspect_detail(struct origin *suspect)\n+static int emit_one_suspect_detail(struct string_list *mailmap,\n+\t\t\t\t   struct origin *suspect)\n {\n \tstruct commit_info ci;\n \n@@ -1438,7 +1474,7 @@ static int emit_one_suspect_detail(struct origin *suspect)\n \t\treturn 0;\n \n \tsuspect->commit->object.flags |= METAINFO_SHOWN;\n-\tget_commit_info(suspect->commit, &ci, 1);\n+\tget_commit_info(mailmap, suspect->commit, &ci, 1);\n \tprintf(\"author %s\\n\", ci.author);\n \tprintf(\"author-mail %s\\n\", ci.author_mail);\n \tprintf(\"author-time %lu\\n\", ci.author_time);\n@@ -1462,18 +1498,18 @@ static int emit_one_suspect_detail(struct origin *suspect)\n  * The blame_entry is found to be guilty for the range.  Mark it\n  * as such, and show it in incremental output.\n  */\n-static void found_guilty_entry(struct blame_entry *ent)\n+static void found_guilty_entry(struct blame_info *ssb, struct blame_entry *ent)\n {\n \tif (ent->guilty)\n \t\treturn;\n \tent->guilty = 1;\n-\tif (incremental) {\n+\tif (ssb->incremental) {\n \t\tstruct origin *suspect = ent->suspect;\n \n \t\tprintf(\"%s %d %d %d\\n\",\n \t\t       sha1_to_hex(suspect->commit->object.sha1),\n \t\t       ent->s_lno + 1, ent->lno + 1, ent->num_lines);\n-\t\temit_one_suspect_detail(suspect);\n+\t\temit_one_suspect_detail(&ssb->mailmap, suspect);\n \t\twrite_filename_info(suspect->path);\n \t\tmaybe_flush_or_die(stdout, \"stdout\");\n \t}\n@@ -1484,7 +1520,7 @@ static void found_guilty_entry(struct blame_entry *ent)\n  * is still unknown, pick one blame_entry, and allow its current\n  * suspect to pass blames to its parents.\n  */\n-static void assign_blame(struct scoreboard *sb, int opt)\n+static void assign_blame(struct blame_scoreboard *sb, int opt)\n {\n \tstruct rev_info *revs = sb->revs;\n \n@@ -1508,7 +1544,7 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \t\tcommit = suspect->commit;\n \t\tif (!commit->object.parsed)\n \t\t\tparse_commit(commit);\n-\t\tif (reverse ||\n+\t\tif (sb->ssb->reverse ||\n \t\t    (!(commit->object.flags & UNINTERESTING) &&\n \t\t     !(revs->max_age != -1 && commit->date < revs->max_age)))\n \t\t\tpass_blame(sb, suspect, opt);\n@@ -1518,13 +1554,13 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \t\t\t\tmark_parents_uninteresting(commit);\n \t\t}\n \t\t/* treat root commit as boundary */\n-\t\tif (!commit->parents && !show_root)\n+\t\tif (!commit->parents && !sb->ssb->show_root)\n \t\t\tcommit->object.flags |= UNINTERESTING;\n \n \t\t/* Take responsibility for the remaining entries */\n \t\tfor (ent = sb->ent; ent; ent = ent->next)\n \t\t\tif (same_suspect(ent->suspect, suspect))\n-\t\t\t\tfound_guilty_entry(ent);\n+\t\t\t\tfound_guilty_entry(sb->ssb, ent);\n \t\torigin_decref(suspect);\n \n \t\tif (DEBUG) /* sanity */\n@@ -1532,8 +1568,8 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \t}\n }\n \n-static const char *format_time(unsigned long time, const char *tz_str,\n-\t\t\t       int show_raw_time)\n+static const char *format_time(struct blame_info *ssb, unsigned long time,\n+\t\t\t       const char *tz_str, int show_raw_time)\n {\n \tstatic char time_buf[128];\n \tconst char *time_str;\n@@ -1545,10 +1581,10 @@ static const char *format_time(unsigned long time, const char *tz_str,\n \t}\n \telse {\n \t\ttz = atoi(tz_str);\n-\t\ttime_str = show_date(time, tz, blame_date_mode);\n+\t\ttime_str = show_date(time, tz, ssb->blame_date_mode);\n \t\ttime_len = strlen(time_str);\n \t\tmemcpy(time_buf, time_str, time_len);\n-\t\tmemset(time_buf + time_len, ' ', blame_date_width - time_len);\n+\t\tmemset(time_buf + time_len, ' ', ssb->blame_date_width - time_len);\n \t}\n \treturn time_buf;\n }\n@@ -1562,7 +1598,7 @@ static const char *format_time(unsigned long time, const char *tz_str,\n #define OUTPUT_SHOW_SCORE      0100\n #define OUTPUT_NO_AUTHOR       0200\n \n-static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)\n+static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent)\n {\n \tint cnt;\n \tconst char *cp;\n@@ -1576,7 +1612,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)\n \t       ent->s_lno + 1,\n \t       ent->lno + 1,\n \t       ent->num_lines);\n-\tif (emit_one_suspect_detail(suspect) ||\n+\tif (emit_one_suspect_detail(&sb->ssb->mailmap, suspect) ||\n \t    (suspect->commit->object.flags & MORE_THAN_ONE_PATH))\n \t\twrite_filename_info(suspect->path);\n \n@@ -1596,7 +1632,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)\n \t}\n }\n \n-static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n+static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt)\n {\n \tint cnt;\n \tconst char *cp;\n@@ -1605,7 +1641,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \tchar hex[41];\n \tint show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);\n \n-\tget_commit_info(suspect->commit, &ci, 1);\n+\tget_commit_info(&sb->ssb->mailmap, suspect->commit, &ci, 1);\n \tstrcpy(hex, sha1_to_hex(suspect->commit->object.sha1));\n \n \tcp = nth_line(sb, ent->lno);\n@@ -1614,7 +1650,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \t\tint length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;\n \n \t\tif (suspect->commit->object.flags & UNINTERESTING) {\n-\t\t\tif (blank_boundary)\n+\t\t\tif (sb->ssb->blank_boundary)\n \t\t\t\tmemset(hex, ' ', length);\n \t\t\telse if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {\n \t\t\t\tlength--;\n@@ -1625,31 +1661,31 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \t\tprintf(\"%.*s\", length, hex);\n \t\tif (opt & OUTPUT_ANNOTATE_COMPAT)\n \t\t\tprintf(\"\\t(%10s\\t%10s\\t%d)\", ci.author,\n-\t\t\t       format_time(ci.author_time, ci.author_tz,\n-\t\t\t\t\t   show_raw_time),\n+\t\t\t       format_time(sb->ssb, ci.author_time,\n+\t\t\t\t\t   ci.author_tz, show_raw_time),\n \t\t\t       ent->lno + 1 + cnt);\n \t\telse {\n \t\t\tif (opt & OUTPUT_SHOW_SCORE)\n \t\t\t\tprintf(\" %*d %02d\",\n-\t\t\t\t       max_score_digits, ent->score,\n+\t\t\t\t       sb->ssb->max_score_digits, ent->score,\n \t\t\t\t       ent->suspect->refcnt);\n \t\t\tif (opt & OUTPUT_SHOW_NAME)\n-\t\t\t\tprintf(\" %-*.*s\", longest_file, longest_file,\n-\t\t\t\t       suspect->path);\n+\t\t\t\tprintf(\" %-*.*s\", sb->ssb->longest_file,\n+\t\t\t\t\t\t  sb->ssb->longest_file, suspect->path);\n \t\t\tif (opt & OUTPUT_SHOW_NUMBER)\n-\t\t\t\tprintf(\" %*d\", max_orig_digits,\n+\t\t\t\tprintf(\" %*d\", sb->ssb->max_orig_digits,\n \t\t\t\t       ent->s_lno + 1 + cnt);\n \n \t\t\tif (!(opt & OUTPUT_NO_AUTHOR)) {\n-\t\t\t\tint pad = longest_author - utf8_strwidth(ci.author);\n+\t\t\t\tint pad = sb->ssb->longest_author - utf8_strwidth(ci.author);\n \t\t\t\tprintf(\" (%s%*s %10s\",\n \t\t\t\t       ci.author, pad, \"\",\n-\t\t\t\t       format_time(ci.author_time,\n+\t\t\t\t       format_time(sb->ssb, ci.author_time,\n \t\t\t\t\t\t   ci.author_tz,\n \t\t\t\t\t\t   show_raw_time));\n \t\t\t}\n \t\t\tprintf(\" %*d) \",\n-\t\t\t       max_digits, ent->lno + 1 + cnt);\n+\t\t\t       sb->ssb->max_digits, ent->lno + 1 + cnt);\n \t\t}\n \t\tdo {\n \t\t\tch = *cp++;\n@@ -1659,7 +1695,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \t}\n }\n \n-static void output(struct scoreboard *sb, int option)\n+static void output(struct blame_scoreboard *sb, int option)\n {\n \tstruct blame_entry *ent;\n \n@@ -1693,7 +1729,7 @@ static void output(struct scoreboard *sb, int option)\n  * To allow quick access to the contents of nth line in the\n  * final image, prepare an index in the scoreboard.\n  */\n-static int prepare_lines(struct scoreboard *sb)\n+static int prepare_lines(struct blame_scoreboard *sb)\n {\n \tconst char *buf = sb->final_buf;\n \tunsigned long len = sb->final_buf_size;\n@@ -1758,7 +1794,7 @@ static int lineno_width(int lines)\n  * How many columns do we need to show line numbers, authors,\n  * and filenames?\n  */\n-static void find_alignment(struct scoreboard *sb, int *option)\n+static void find_alignment(struct blame_scoreboard *sb, int *option)\n {\n \tint longest_src_lines = 0;\n \tint longest_dst_lines = 0;\n@@ -1773,14 +1809,14 @@ static void find_alignment(struct scoreboard *sb, int *option)\n \t\tif (strcmp(suspect->path, sb->path))\n \t\t\t*option |= OUTPUT_SHOW_NAME;\n \t\tnum = strlen(suspect->path);\n-\t\tif (longest_file < num)\n-\t\t\tlongest_file = num;\n+\t\tif (sb->ssb->longest_file < num)\n+\t\t\tsb->ssb->longest_file = num;\n \t\tif (!(suspect->commit->object.flags & METAINFO_SHOWN)) {\n \t\t\tsuspect->commit->object.flags |= METAINFO_SHOWN;\n-\t\t\tget_commit_info(suspect->commit, &ci, 1);\n+\t\t\tget_commit_info(&sb->ssb->mailmap, suspect->commit, &ci, 1);\n \t\t\tnum = utf8_strwidth(ci.author);\n-\t\t\tif (longest_author < num)\n-\t\t\t\tlongest_author = num;\n+\t\t\tif (sb->ssb->longest_author < num)\n+\t\t\t\tsb->ssb->longest_author = num;\n \t\t}\n \t\tnum = e->s_lno + e->num_lines;\n \t\tif (longest_src_lines < num)\n@@ -1791,16 +1827,16 @@ static void find_alignment(struct scoreboard *sb, int *option)\n \t\tif (largest_score < ent_score(sb, e))\n \t\t\tlargest_score = ent_score(sb, e);\n \t}\n-\tmax_orig_digits = lineno_width(longest_src_lines);\n-\tmax_digits = lineno_width(longest_dst_lines);\n-\tmax_score_digits = lineno_width(largest_score);\n+\tsb->ssb->max_orig_digits = lineno_width(longest_src_lines);\n+\tsb->ssb->max_digits = lineno_width(longest_dst_lines);\n+\tsb->ssb->max_score_digits = lineno_width(largest_score);\n }\n \n /*\n  * For debugging -- origin is refcounted, and this asserts that\n  * we do not underflow.\n  */\n-static void sanity_check_refcnt(struct scoreboard *sb)\n+static void sanity_check_refcnt(struct blame_scoreboard *sb)\n {\n \tint baa = 0;\n \tstruct blame_entry *ent;\n@@ -1851,7 +1887,7 @@ static const char *add_prefix(const char *prefix, const char *path)\n  * Parsing of (comma separated) one item in the -L option\n  */\n static const char *parse_loc(const char *spec,\n-\t\t\t     struct scoreboard *sb, long lno,\n+\t\t\t     struct blame_scoreboard *sb, long lno,\n \t\t\t     long begin, long *ret)\n {\n \tchar *term;\n@@ -1927,7 +1963,7 @@ static const char *parse_loc(const char *spec,\n /*\n  * Parsing of -L option\n  */\n-static void prepare_blame_range(struct scoreboard *sb,\n+static void prepare_blame_range(struct blame_scoreboard *sb,\n \t\t\t\tconst char *bottomtop,\n \t\t\t\tlong lno,\n \t\t\t\tlong *bottom, long *top)\n@@ -1947,17 +1983,17 @@ static void prepare_blame_range(struct scoreboard *sb,\n static int git_blame_config(const char *var, const char *value, void *cb)\n {\n \tif (!strcmp(var, \"blame.showroot\")) {\n-\t\tshow_root = git_config_bool(var, value);\n+\t\t((struct blame_info *)cb)->show_root = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"blame.blankboundary\")) {\n-\t\tblank_boundary = git_config_bool(var, value);\n+\t\t((struct blame_info *)cb)->blank_boundary = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"blame.date\")) {\n \t\tif (!value)\n \t\t\treturn config_error_nonbool(var);\n-\t\tblame_date_mode = parse_date_format(value);\n+\t\t((struct blame_info *)cb)->blame_date_mode = parse_date_format(value);\n \t\treturn 0;\n \t}\n \treturn git_default_config(var, value, cb);\n@@ -2079,7 +2115,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con\n \treturn commit;\n }\n \n-static const char *prepare_final(struct scoreboard *sb)\n+static const char *prepare_final(struct blame_scoreboard *sb)\n {\n \tint i;\n \tconst char *final_commit_name = NULL;\n@@ -2107,7 +2143,7 @@ static const char *prepare_final(struct scoreboard *sb)\n \treturn final_commit_name;\n }\n \n-static const char *prepare_initial(struct scoreboard *sb)\n+static const char *prepare_initial(struct blame_scoreboard *sb)\n {\n \tint i;\n \tconst char *final_commit_name = NULL;\n@@ -2155,7 +2191,7 @@ static int blame_copy_callback(const struct option *option, const char *arg, int\n \t*opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;\n \n \tif (arg)\n-\t\tblame_copy_score = parse_score(arg);\n+\t\topt_blame_copy_score = parse_score(arg);\n \treturn 0;\n }\n \n@@ -2166,7 +2202,7 @@ static int blame_move_callback(const struct option *option, const char *arg, int\n \t*opt |= PICKAXE_BLAME_MOVE;\n \n \tif (arg)\n-\t\tblame_move_score = parse_score(arg);\n+\t\topt_blame_move_score = parse_score(arg);\n \treturn 0;\n }\n \n@@ -2185,9 +2221,15 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n {\n \tstruct rev_info revs;\n \tconst char *path;\n-\tstruct scoreboard sb;\n \tstruct origin *o;\n \tstruct blame_entry *ent;\n+\tstatic struct blame_stat stat;\n+\tstatic struct blame_info ssb = {\n+\t\t.xdl_opts = XDF_NEED_MINIMAL,\n+\t\t.blame_date_mode = DATE_ISO8601,\n+\t\t.stat = &stat\n+\t};\n+\tstatic struct blame_scoreboard sb;\n \tlong dashdash_pos, bottom, top, lno;\n \tconst char *final_commit_name = NULL;\n \tenum object_type type;\n@@ -2198,9 +2240,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tstatic const char *revs_file = NULL;\n \tstatic const char *contents_from = NULL;\n \tstatic const struct option options[] = {\n-\t\tOPT_BOOLEAN(0, \"incremental\", &incremental, \"Show blame entries as we find them, incrementally\"),\n-\t\tOPT_BOOLEAN('b', NULL, &blank_boundary, \"Show blank SHA-1 for boundary commits (Default: off)\"),\n-\t\tOPT_BOOLEAN(0, \"root\", &show_root, \"Do not treat root commits as boundaries (Default: off)\"),\n+\t\tOPT_BOOLEAN(0, \"incremental\", &ssb.incremental, \"Show blame entries as we find them, incrementally\"),\n+\t\tOPT_BOOLEAN('b', NULL, &ssb.blank_boundary, \"Show blank SHA-1 for boundary commits (Default: off)\"),\n+\t\tOPT_BOOLEAN(0, \"root\", &ssb.show_root, \"Do not treat root commits as boundaries (Default: off)\"),\n \t\tOPT_BOOLEAN(0, \"show-stats\", &show_stats, \"Show work cost statistics\"),\n \t\tOPT_BIT(0, \"score-debug\", &output_option, \"Show output score for blame entries\", OUTPUT_SHOW_SCORE),\n \t\tOPT_BIT('f', \"show-name\", &output_option, \"Show original filename (Default: auto)\", OUTPUT_SHOW_NAME),\n@@ -2210,7 +2252,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tOPT_BIT('t', NULL, &output_option, \"Show raw timestamp (Default: off)\", OUTPUT_RAW_TIMESTAMP),\n \t\tOPT_BIT('l', NULL, &output_option, \"Show long commit SHA1 (Default: off)\", OUTPUT_LONG_OBJECT_NAME),\n \t\tOPT_BIT('s', NULL, &output_option, \"Suppress author name and timestamp (Default: off)\", OUTPUT_NO_AUTHOR),\n-\t\tOPT_BIT('w', NULL, &xdl_opts, \"Ignore whitespace differences\", XDF_IGNORE_WHITESPACE),\n+\t\tOPT_BIT('w', NULL, &ssb.xdl_opts, \"Ignore whitespace differences\", XDF_IGNORE_WHITESPACE),\n \t\tOPT_STRING('S', NULL, &revs_file, \"file\", \"Use revisions from <file> instead of calling git-rev-list\"),\n \t\tOPT_STRING(0, \"contents\", &contents_from, \"file\", \"Use <file>'s contents as the final image\"),\n \t\t{ OPTION_CALLBACK, 'C', NULL, &opt, \"score\", \"Find line copies within and across files\", PARSE_OPT_OPTARG, blame_copy_callback },\n@@ -2222,9 +2264,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tstruct parse_opt_ctx_t ctx;\n \tint cmd_is_annotate = !strcmp(argv[0], \"annotate\");\n \n-\tgit_config(git_blame_config, NULL);\n+\tgit_config(git_blame_config, &ssb);\n \tinit_revisions(&revs, NULL);\n-\trevs.date_mode = blame_date_mode;\n+\trevs.date_mode = ssb.blame_date_mode;\n \n \tsave_commit_buffer = 0;\n \tdashdash_pos = 0;\n@@ -2243,7 +2285,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \t\tif (!strcmp(ctx.argv[0], \"--reverse\")) {\n \t\t\tctx.argv[0] = \"--children\";\n-\t\t\treverse = 1;\n+\t\t\tssb.reverse = 1;\n \t\t}\n \t\tparse_revision_opt(&revs, &ctx, options, blame_opt_usage);\n \t}\n@@ -2252,42 +2294,45 @@ parse_done:\n \n \tif (cmd_is_annotate) {\n \t\toutput_option |= OUTPUT_ANNOTATE_COMPAT;\n-\t\tblame_date_mode = DATE_ISO8601;\n+\t\tssb.blame_date_mode = DATE_ISO8601;\n \t} else {\n-\t\tblame_date_mode = revs.date_mode;\n+\t\tssb.blame_date_mode = revs.date_mode;\n \t}\n \n+\tssb.blame_move_score = opt_blame_move_score;\n+\tssb.blame_copy_score = opt_blame_copy_score;\n+\n \t/* The maximum width used to show the dates */\n-\tswitch (blame_date_mode) {\n+\tswitch (ssb.blame_date_mode) {\n \tcase DATE_RFC2822:\n-\t\tblame_date_width = sizeof(\"Thu, 19 Oct 2006 16:00:04 -0700\");\n+\t\tssb.blame_date_width = sizeof(\"Thu, 19 Oct 2006 16:00:04 -0700\");\n \t\tbreak;\n \tcase DATE_ISO8601:\n-\t\tblame_date_width = sizeof(\"2006-10-19 16:00:04 -0700\");\n+\t\tssb.blame_date_width = sizeof(\"2006-10-19 16:00:04 -0700\");\n \t\tbreak;\n \tcase DATE_RAW:\n-\t\tblame_date_width = sizeof(\"1161298804 -0700\");\n+\t\tssb.blame_date_width = sizeof(\"1161298804 -0700\");\n \t\tbreak;\n \tcase DATE_SHORT:\n-\t\tblame_date_width = sizeof(\"2006-10-19\");\n+\t\tssb.blame_date_width = sizeof(\"2006-10-19\");\n \t\tbreak;\n \tcase DATE_RELATIVE:\n \t\t/* \"normal\" is used as the fallback for \"relative\" */\n \tcase DATE_LOCAL:\n \tcase DATE_NORMAL:\n-\t\tblame_date_width = sizeof(\"Thu Oct 19 16:00:04 2006 -0700\");\n+\t\tssb.blame_date_width = sizeof(\"Thu Oct 19 16:00:04 2006 -0700\");\n \t\tbreak;\n \t}\n-\tblame_date_width -= 1; /* strip the null */\n+\tssb.blame_date_width -= 1; /* strip the null */\n \n \tif (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))\n \t\topt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |\n \t\t\tPICKAXE_BLAME_COPY_HARDER);\n \n-\tif (!blame_move_score)\n-\t\tblame_move_score = BLAME_DEFAULT_MOVE_SCORE;\n-\tif (!blame_copy_score)\n-\t\tblame_copy_score = BLAME_DEFAULT_COPY_SCORE;\n+\tif (!ssb.blame_move_score)\n+\t\tssb.blame_move_score = BLAME_DEFAULT_MOVE_SCORE;\n+\tif (!ssb.blame_copy_score)\n+\t\tssb.blame_copy_score = BLAME_DEFAULT_COPY_SCORE;\n \n \t/*\n \t * We have collected options unknown to us in argv[1..unk]\n@@ -2341,9 +2386,9 @@ parse_done:\n \n \tsetup_revisions(argc, argv, &revs, NULL);\n \tmemset(&sb, 0, sizeof(sb));\n-\n+\tsb.ssb = &ssb;\n \tsb.revs = &revs;\n-\tif (!reverse)\n+\tif (!sb.ssb->reverse)\n \t\tfinal_commit_name = prepare_final(&sb);\n \telse if (contents_from)\n \t\tdie(\"--contents and --children do not blend well.\");\n@@ -2391,7 +2436,7 @@ parse_done:\n \t\t\t    sha1_to_hex(o->blob_sha1),\n \t\t\t    path);\n \t}\n-\tnum_read_blob++;\n+\tstat.num_read_blob++;\n \tlno = prepare_lines(&sb);\n \n \tbottom = top = 0;\n@@ -2422,14 +2467,14 @@ parse_done:\n \t\tdie(\"reading graft file %s failed: %s\",\n \t\t    revs_file, strerror(errno));\n \n-\tread_mailmap(&mailmap, NULL);\n+\tread_mailmap(&sb.ssb->mailmap, NULL);\n \n-\tif (!incremental)\n+\tif (!sb.ssb->incremental)\n \t\tsetup_pager();\n \n \tassign_blame(&sb, opt);\n \n-\tif (incremental)\n+\tif (sb.ssb->incremental)\n \t\treturn 0;\n \n \tcoalesce(&sb);\n@@ -2446,9 +2491,9 @@ parse_done:\n \t}\n \n \tif (show_stats) {\n-\t\tprintf(\"num read blob: %d\\n\", num_read_blob);\n-\t\tprintf(\"num get patch: %d\\n\", num_get_patch);\n-\t\tprintf(\"num commits: %d\\n\", num_commits);\n+\t\tprintf(\"num read blob: %d\\n\", stat.num_read_blob);\n+\t\tprintf(\"num get patch: %d\\n\", stat.num_get_patch);\n+\t\tprintf(\"num commits: %d\\n\", stat.num_commits);\n \t}\n \treturn 0;\n }\n-- \n1.6.2\n"}]}