{"thread":{"id":"18323","subject":"[PATCH2/2] Libify blame","startedAt":"2009-03-16T13:30:14Z","lastAt":"2009-03-17T00:48:18Z","messageCount":5,"participants":["pi song","Sverre Rabbelier","Miklos Vajna","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"108081","messageId":"49BE5466.5050202@gmail.com","threadId":"18323","inReplyTo":null,"subject":"[PATCH2/2] Libify blame","fromName":"pi song","fromEmail":"pi.songs@gmail.com","sentAt":"2009-03-16T13:30:14Z","receivedAt":"2009-03-16T13:30:14Z","isPatch":false,"sender":{"key":"pi.songs@gmail.com","avatar":null},"body":"This looks like a very HUGE patch but it does merely splitting the current builtin-blame.c into smaller blame.c without changing any logic.\nThis is still WIP. The next patch will organize functions more appropriately.\n\n\nSigned-off-by: Pi Song <pi.songs@gmail.com>\n---\n Makefile        |    1 +\n blame.c         | 1744 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n blame.h         |   21 +\n builtin-blame.c | 1703 -----------------------------------------------------\n 4 files changed, 1766 insertions(+), 1703 deletions(-)\n create mode 100644 blame.c\n\ndiff --git a/Makefile b/Makefile\nindex cc0fb5a..ac438a5 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -423,6 +423,7 @@ LIB_OBJS += archive-tar.o\n LIB_OBJS += archive-zip.o\n LIB_OBJS += attr.o\n LIB_OBJS += base85.o\n+LIB_OBJS += blame.o\n LIB_OBJS += blob.o\n LIB_OBJS += branch.o\n LIB_OBJS += bundle.o\ndiff --git a/blame.c b/blame.c\nnew file mode 100644\nindex 0000000..f408581\n--- /dev/null\n+++ b/blame.c\n@@ -0,0 +1,1744 @@\n+/*\n+ * Blame\n+ *\n+ * Copyright (c) 2006, Junio C Hamano\n+ */\n+\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"blob.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n+#include \"tree-walk.h\"\n+#include \"diff.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n+#include \"quote.h\"\n+#include \"xdiff-interface.h\"\n+#include \"cache-tree.h\"\n+#include \"string-list.h\"\n+#include \"mailmap.h\"\n+#include \"parse-options.h\"\n+#include \"utf8.h\"\n+#include \"blame.h\"\n+\n+static char blame_usage[] = \"git blame [options] [rev-opts] [rev] [--] file\";\n+\n+static const char *blame_opt_usage[] = {\n+\tblame_usage,\n+\t\"\",\n+\t\"[rev-opts] are documented in git-rev-list(1)\",\n+\tNULL\n+};\n+\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+/*\n+ * Given an origin, prepare mmfile_t structure to be used by the\n+ * diff machinery\n+ */\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\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+\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+\t\treturn 1;\n+\tif (a->commit != b->commit)\n+\t\treturn 0;\n+\treturn !strcmp(a->path, b->path);\n+}\n+\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+void coalesce(struct blame_scoreboard *sb)\n+{\n+\tstruct blame_entry *ent, *next;\n+\n+\tfor (ent = sb->ent; ent && (next = ent->next); ent = next) {\n+\t\tif (same_suspect(ent->suspect, next->suspect) &&\n+\t\t    ent->guilty == next->guilty &&\n+\t\t    ent->s_lno + ent->num_lines == next->s_lno) {\n+\t\t\tent->num_lines += next->num_lines;\n+\t\t\tent->next = next->next;\n+\t\t\tif (ent->next)\n+\t\t\t\tent->next->prev = ent;\n+\t\t\torigin_decref(next->suspect);\n+\t\t\tfree(next);\n+\t\t\tent->score = 0;\n+\t\t\tnext = ent; /* again */\n+\t\t}\n+\t}\n+\n+\tif (DEBUG) /* sanity */\n+\t\tsanity_check_refcnt(sb);\n+}\n+\n+/*\n+ * Given a commit and a path in it, create a new origin structure.\n+ * The callers that add blame to the scoreboard should use\n+ * get_origin() to obtain shared, refcounted copy instead of calling\n+ * this function directly.\n+ */\n+struct origin *make_origin(struct commit *commit, const char *path)\n+{\n+\tstruct origin *o;\n+\to = xcalloc(1, sizeof(*o) + strlen(path) + 1);\n+\to->commit = commit;\n+\to->refcnt = 1;\n+\tstrcpy(o->path, path);\n+\treturn o;\n+}\n+\n+/*\n+ * Locate an existing origin or create a new one.\n+ */\n+struct origin *get_origin(struct blame_scoreboard *sb,\n+\t \t\t  struct commit *commit,\n+\t\t\t  const char *path)\n+{\n+\tstruct blame_entry *e;\n+\n+\tfor (e = sb->ent; e; e = e->next) {\n+\t\tif (e->suspect->commit == commit &&\n+\t\t    !strcmp(e->suspect->path, path))\n+\t\t\treturn origin_incref(e->suspect);\n+\t}\n+\treturn make_origin(commit, path);\n+}\n+\n+/*\n+ * Fill the blob_sha1 field of an origin if it hasn't, so that later\n+ * call to fill_origin_blob() can use it to locate the data.  blob_sha1\n+ * for an origin is also used to pass the blame for the entire file to\n+ * the parent to detect the case where a child's blob is identical to\n+ * that of its parent's.\n+ */\n+int fill_blob_sha1(struct origin *origin)\n+{\n+\tunsigned mode;\n+\n+\tif (!is_null_sha1(origin->blob_sha1))\n+\t\treturn 0;\n+\tif (get_tree_entry(origin->commit->object.sha1,\n+\t\t\t   origin->path,\n+\t\t\t   origin->blob_sha1, &mode))\n+\t\tgoto error_out;\n+\tif (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)\n+\t\tgoto error_out;\n+\treturn 0;\n+ error_out:\n+\thashclr(origin->blob_sha1);\n+\treturn -1;\n+}\n+\n+/*\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 blame_scoreboard *sb,\n+\t\t\t\t  struct commit *parent,\n+\t\t\t\t  struct origin *origin)\n+{\n+\tstruct origin *porigin = NULL;\n+\tstruct diff_options diff_opts;\n+\tconst char *paths[2];\n+\n+\tif (parent->util) {\n+\t\t/*\n+\t\t * Each commit object can cache one origin in that\n+\t\t * commit.  This is a freestanding copy of origin and\n+\t\t * not refcounted.\n+\t\t */\n+\t\tstruct origin *cached = parent->util;\n+\t\tif (!strcmp(cached->path, origin->path)) {\n+\t\t\t/*\n+\t\t\t * The same path between origin and its parent\n+\t\t\t * without renaming -- the most common case.\n+\t\t\t */\n+\t\t\tporigin = get_origin(sb, parent, cached->path);\n+\n+\t\t\t/*\n+\t\t\t * If the origin was newly created (i.e. get_origin\n+\t\t\t * would call make_origin if none is found in the\n+\t\t\t * scoreboard), it does not know the blob_sha1,\n+\t\t\t * so copy it.  Otherwise porigin was in the\n+\t\t\t * scoreboard and already knows blob_sha1.\n+\t\t\t */\n+\t\t\tif (porigin->refcnt == 1)\n+\t\t\t\thashcpy(porigin->blob_sha1, cached->blob_sha1);\n+\t\t\treturn porigin;\n+\t\t}\n+\t\t/* otherwise it was not very useful; free it */\n+\t\tfree(parent->util);\n+\t\tparent->util = NULL;\n+\t}\n+\n+\t/* See if the origin->path is different between parent\n+\t * and origin first.  Most of the time they are the\n+\t * same and diff-tree is fairly efficient about this.\n+\t */\n+\tdiff_setup(&diff_opts);\n+\tDIFF_OPT_SET(&diff_opts, RECURSIVE);\n+\tdiff_opts.detect_rename = 0;\n+\tdiff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;\n+\tpaths[0] = origin->path;\n+\tpaths[1] = NULL;\n+\n+\tdiff_tree_setup_paths(paths, &diff_opts);\n+\tif (diff_setup_done(&diff_opts) < 0)\n+\t\tdie(\"diff-setup\");\n+\n+\tif (is_null_sha1(origin->commit->object.sha1))\n+\t\tdo_diff_cache(parent->tree->object.sha1, &diff_opts);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.sha1,\n+\t\t\t       origin->commit->tree->object.sha1,\n+\t\t\t       \"\", &diff_opts);\n+\tdiffcore_std(&diff_opts);\n+\n+\t/* It is either one entry that says \"modified\", or \"created\",\n+\t * or nothing.\n+\t */\n+\tif (!diff_queued_diff.nr) {\n+\t\t/* The path is the same as parent */\n+\t\tporigin = get_origin(sb, parent, origin->path);\n+\t\thashcpy(porigin->blob_sha1, origin->blob_sha1);\n+\t}\n+\telse if (diff_queued_diff.nr != 1)\n+\t\tdie(\"internal error in blame::find_origin\");\n+\telse {\n+\t\tstruct diff_filepair *p = diff_queued_diff.queue[0];\n+\t\tswitch (p->status) {\n+\t\tdefault:\n+\t\t\tdie(\"internal error in blame::find_origin (%c)\",\n+\t\t\t    p->status);\n+\t\tcase 'M':\n+\t\t\tporigin = get_origin(sb, parent, origin->path);\n+\t\t\thashcpy(porigin->blob_sha1, p->one->sha1);\n+\t\t\tbreak;\n+\t\tcase 'A':\n+\t\tcase 'T':\n+\t\t\t/* Did not exist in parent, or type changed */\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tdiff_flush(&diff_opts);\n+\tdiff_tree_release_paths(&diff_opts);\n+\tif (porigin) {\n+\t\t/*\n+\t\t * Create a freestanding copy that is not part of\n+\t\t * the refcounted origin found in the scoreboard, and\n+\t\t * cache it in the commit.\n+\t\t */\n+\t\tstruct origin *cached;\n+\n+\t\tcached = make_origin(porigin->commit, porigin->path);\n+\t\thashcpy(cached->blob_sha1, porigin->blob_sha1);\n+\t\tparent->util = cached;\n+\t}\n+\treturn porigin;\n+}\n+\n+/*\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 blame_scoreboard *sb,\n+\t\t\t\t  struct commit *parent,\n+\t\t\t\t  struct origin *origin)\n+{\n+\tstruct origin *porigin = NULL;\n+\tstruct diff_options diff_opts;\n+\tint i;\n+\tconst char *paths[2];\n+\n+\tdiff_setup(&diff_opts);\n+\tDIFF_OPT_SET(&diff_opts, RECURSIVE);\n+\tdiff_opts.detect_rename = DIFF_DETECT_RENAME;\n+\tdiff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;\n+\tdiff_opts.single_follow = origin->path;\n+\tpaths[0] = NULL;\n+\tdiff_tree_setup_paths(paths, &diff_opts);\n+\tif (diff_setup_done(&diff_opts) < 0)\n+\t\tdie(\"diff-setup\");\n+\n+\tif (is_null_sha1(origin->commit->object.sha1))\n+\t\tdo_diff_cache(parent->tree->object.sha1, &diff_opts);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.sha1,\n+\t\t\t       origin->commit->tree->object.sha1,\n+\t\t\t       \"\", &diff_opts);\n+\tdiffcore_std(&diff_opts);\n+\n+\tfor (i = 0; i < diff_queued_diff.nr; i++) {\n+\t\tstruct diff_filepair *p = diff_queued_diff.queue[i];\n+\t\tif ((p->status == 'R' || p->status == 'C') &&\n+\t\t    !strcmp(p->two->path, origin->path)) {\n+\t\t\tporigin = get_origin(sb, parent, p->one->path);\n+\t\t\thashcpy(porigin->blob_sha1, p->one->sha1);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tdiff_flush(&diff_opts);\n+\tdiff_tree_release_paths(&diff_opts);\n+\treturn porigin;\n+}\n+\n+/*\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 blame_scoreboard *sb, struct blame_entry *e)\n+{\n+\tstruct blame_entry *ent, *prev = NULL;\n+\n+\torigin_incref(e->suspect);\n+\n+\tfor (ent = sb->ent; ent && ent->lno < e->lno; ent = ent->next)\n+\t\tprev = ent;\n+\n+\t/* prev, if not NULL, is the last one that is below e */\n+\te->prev = prev;\n+\tif (prev) {\n+\t\te->next = prev->next;\n+\t\tprev->next = e;\n+\t}\n+\telse {\n+\t\te->next = sb->ent;\n+\t\tsb->ent = e;\n+\t}\n+\tif (e->next)\n+\t\te->next->prev = e;\n+}\n+\n+/*\n+ * src typically is on-stack; we want to copy the information in it to\n+ * a malloced blame_entry that is already on the linked list of the\n+ * scoreboard.  The origin of dst loses a refcnt while the origin of src\n+ * gains one.\n+ */\n+static void dup_entry(struct blame_entry *dst, struct blame_entry *src)\n+{\n+\tstruct blame_entry *p, *n;\n+\n+\tp = dst->prev;\n+\tn = dst->next;\n+\torigin_incref(src->suspect);\n+\torigin_decref(dst->suspect);\n+\tmemcpy(dst, src, sizeof(*src));\n+\tdst->prev = p;\n+\tdst->next = n;\n+\tdst->score = 0;\n+}\n+\n+const char *nth_line(struct blame_scoreboard *sb, int lno)\n+{\n+\treturn sb->final_buf + sb->lineno[lno];\n+}\n+\n+/*\n+ * It is known that lines between tlno to same came from parent, and e\n+ * has an overlap with that range.  it also is known that parent's\n+ * line plno corresponds to e's line tlno.\n+ *\n+ *                <---- e ----->\n+ *                   <------>\n+ *                   <------------>\n+ *             <------------>\n+ *             <------------------>\n+ *\n+ * Split e into potentially three parts; before this chunk, the chunk\n+ * to be blamed for the parent, and after that portion.\n+ */\n+static void split_overlap(struct blame_entry *split,\n+\t\t\t  struct blame_entry *e,\n+\t\t\t  int tlno, int plno, int same,\n+\t\t\t  struct origin *parent)\n+{\n+\tint chunk_end_lno;\n+\tmemset(split, 0, sizeof(struct blame_entry [3]));\n+\n+\tif (e->s_lno < tlno) {\n+\t\t/* there is a pre-chunk part not blamed on parent */\n+\t\tsplit[0].suspect = origin_incref(e->suspect);\n+\t\tsplit[0].lno = e->lno;\n+\t\tsplit[0].s_lno = e->s_lno;\n+\t\tsplit[0].num_lines = tlno - e->s_lno;\n+\t\tsplit[1].lno = e->lno + tlno - e->s_lno;\n+\t\tsplit[1].s_lno = plno;\n+\t}\n+\telse {\n+\t\tsplit[1].lno = e->lno;\n+\t\tsplit[1].s_lno = plno + (e->s_lno - tlno);\n+\t}\n+\n+\tif (same < e->s_lno + e->num_lines) {\n+\t\t/* there is a post-chunk part not blamed on parent */\n+\t\tsplit[2].suspect = origin_incref(e->suspect);\n+\t\tsplit[2].lno = e->lno + (same - e->s_lno);\n+\t\tsplit[2].s_lno = e->s_lno + (same - e->s_lno);\n+\t\tsplit[2].num_lines = e->s_lno + e->num_lines - same;\n+\t\tchunk_end_lno = split[2].lno;\n+\t}\n+\telse\n+\t\tchunk_end_lno = e->lno + e->num_lines;\n+\tsplit[1].num_lines = chunk_end_lno - split[1].lno;\n+\n+\t/*\n+\t * if it turns out there is nothing to blame the parent for,\n+\t * forget about the splitting.  !split[1].suspect signals this.\n+\t */\n+\tif (split[1].num_lines < 1)\n+\t\treturn;\n+\tsplit[1].suspect = origin_incref(parent);\n+}\n+\n+/*\n+ * split_overlap() divided an existing blame e into up to three parts\n+ * in split.  Adjust the linked list of blames in the scoreboard to\n+ * reflect the split.\n+ */\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+\tstruct blame_entry *new_entry;\n+\n+\tif (split[0].suspect && split[2].suspect) {\n+\t\t/* The first part (reuse storage for the existing entry e) */\n+\t\tdup_entry(e, &split[0]);\n+\n+\t\t/* The last part -- me */\n+\t\tnew_entry = xmalloc(sizeof(*new_entry));\n+\t\tmemcpy(new_entry, &(split[2]), sizeof(struct blame_entry));\n+\t\tadd_blame_entry(sb, new_entry);\n+\n+\t\t/* ... and the middle part -- parent */\n+\t\tnew_entry = xmalloc(sizeof(*new_entry));\n+\t\tmemcpy(new_entry, &(split[1]), sizeof(struct blame_entry));\n+\t\tadd_blame_entry(sb, new_entry);\n+\t}\n+\telse if (!split[0].suspect && !split[2].suspect)\n+\t\t/*\n+\t\t * The parent covers the entire area; reuse storage for\n+\t\t * e and replace it with the parent.\n+\t\t */\n+\t\tdup_entry(e, &split[1]);\n+\telse if (split[0].suspect) {\n+\t\t/* me and then parent */\n+\t\tdup_entry(e, &split[0]);\n+\n+\t\tnew_entry = xmalloc(sizeof(*new_entry));\n+\t\tmemcpy(new_entry, &(split[1]), sizeof(struct blame_entry));\n+\t\tadd_blame_entry(sb, new_entry);\n+\t}\n+\telse {\n+\t\t/* parent and then me */\n+\t\tdup_entry(e, &split[1]);\n+\n+\t\tnew_entry = xmalloc(sizeof(*new_entry));\n+\t\tmemcpy(new_entry, &(split[2]), sizeof(struct blame_entry));\n+\t\tadd_blame_entry(sb, new_entry);\n+\t}\n+\n+\tif (DEBUG) { /* sanity */\n+\t\tstruct blame_entry *ent;\n+\t\tint lno = sb->ent->lno, corrupt = 0;\n+\n+\t\tfor (ent = sb->ent; ent; ent = ent->next) {\n+\t\t\tif (lno != ent->lno)\n+\t\t\t\tcorrupt = 1;\n+\t\t\tif (ent->s_lno < 0)\n+\t\t\t\tcorrupt = 1;\n+\t\t\tlno += ent->num_lines;\n+\t\t}\n+\t\tif (corrupt) {\n+\t\t\tlno = sb->ent->lno;\n+\t\t\tfor (ent = sb->ent; ent; ent = ent->next) {\n+\t\t\t\tprintf(\"L %8d l %8d n %8d\\n\",\n+\t\t\t\t       lno, ent->lno, ent->num_lines);\n+\t\t\t\tlno = ent->lno + ent->num_lines;\n+\t\t\t}\n+\t\t\tdie(\"oops\");\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * After splitting the blame, the origins used by the\n+ * on-stack blame_entry should lose one refcnt each.\n+ */\n+static void decref_split(struct blame_entry *split)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < 3; i++)\n+\t\torigin_decref(split[i].suspect);\n+}\n+\n+/*\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 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+\tstruct blame_entry split[3];\n+\n+\tsplit_overlap(split, e, tlno, plno, same, parent);\n+\tif (split[1].suspect)\n+\t\tsplit_blame(sb, split, e);\n+\tdecref_split(split);\n+}\n+\n+/*\n+ * Find the line number of the last line the target is suspected for.\n+ */\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+\n+\tfor (e = sb->ent; e; e = e->next) {\n+\t\tif (e->guilty || !same_suspect(e->suspect, target))\n+\t\t\tcontinue;\n+\t\tif (last_in_target < e->s_lno + e->num_lines)\n+\t\t\tlast_in_target = e->s_lno + e->num_lines;\n+\t}\n+\treturn last_in_target;\n+}\n+\n+/*\n+ * Process one hunk from the patch between the current suspect for\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 blame_scoreboard *sb,\n+\t\t\tint tlno, int plno, int same,\n+\t\t\tstruct origin *target, struct origin *parent)\n+{\n+\tstruct blame_entry *e;\n+\n+\tfor (e = sb->ent; e; e = e->next) {\n+\t\tif (e->guilty || !same_suspect(e->suspect, target))\n+\t\t\tcontinue;\n+\t\tif (same <= e->s_lno)\n+\t\t\tcontinue;\n+\t\tif (tlno < e->s_lno + e->num_lines)\n+\t\t\tblame_overlap(sb, e, tlno, plno, same, parent);\n+\t}\n+}\n+\n+struct blame_chunk_cb_data {\n+\tstruct blame_scoreboard *sb;\n+\tstruct origin *target;\n+\tstruct origin *parent;\n+\tlong plno;\n+\tlong tlno;\n+};\n+\n+static void blame_chunk_cb(void *data, long same, long p_next, long t_next)\n+{\n+\tstruct blame_chunk_cb_data *d = data;\n+\tblame_chunk(d->sb, d->tlno, d->plno, same, d->target, d->parent);\n+\td->plno = p_next;\n+\td->tlno = t_next;\n+}\n+\n+/*\n+ * We are looking at the origin 'target' and aiming to pass blame\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 blame_scoreboard *sb,\n+\t\t\t\tstruct origin *target,\n+\t\t\t\tstruct origin *parent)\n+{\n+\tint last_in_target;\n+\tmmfile_t file_p, file_o;\n+\tstruct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };\n+\txpparam_t xpp;\n+\txdemitconf_t xecfg;\n+\n+\tlast_in_target = find_last_in_target(sb, target);\n+\tif (last_in_target < 0)\n+\t\treturn 1; /* nothing remains for this target */\n+\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 = 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+\t/* The rest (i.e. anything after tlno) are the same as the parent */\n+\tblame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * The lines in blame_entry after splitting blames many times can become\n+ * very small and trivial, and at some point it becomes pointless to\n+ * blame the parents.  E.g. \"\\t\\t}\\n\\t}\\n\\n\" appears everywhere in any\n+ * ordinary C program, and it is not worth to say it was copied from\n+ * totally unrelated file in the parent.\n+ *\n+ * Compute how trivial the lines in the blame_entry are.\n+ */\n+static unsigned ent_score(struct blame_scoreboard *sb, struct blame_entry *e)\n+{\n+\tunsigned score;\n+\tconst char *cp, *ep;\n+\n+\tif (e->score)\n+\t\treturn e->score;\n+\n+\tscore = 1;\n+\tcp = nth_line(sb, e->lno);\n+\tep = nth_line(sb, e->lno + e->num_lines);\n+\twhile (cp < ep) {\n+\t\tunsigned ch = *((unsigned char *)cp);\n+\t\tif (isalnum(ch))\n+\t\t\tscore++;\n+\t\tcp++;\n+\t}\n+\te->score = score;\n+\treturn score;\n+}\n+\n+/*\n+ * best_so_far[] and this[] are both a split of an existing blame_entry\n+ * that passes blame to the parent.  Maintain best_so_far the best split\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 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+\tint i;\n+\n+\tif (!this[1].suspect)\n+\t\treturn;\n+\tif (best_so_far[1].suspect) {\n+\t\tif (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1]))\n+\t\t\treturn;\n+\t}\n+\n+\tfor (i = 0; i < 3; i++)\n+\t\torigin_incref(this[i].suspect);\n+\tdecref_split(best_so_far);\n+\tmemcpy(best_so_far, this, sizeof(struct blame_entry [3]));\n+}\n+\n+/*\n+ * We are looking at a part of the final image represented by\n+ * ent (tlno and same are offset by ent->s_lno).\n+ * tlno is where we are looking at in the final image.\n+ * up to (but not including) same match preimage.\n+ * plno is where we are looking at in the preimage.\n+ *\n+ * <-------------- final image ---------------------->\n+ *       <------ent------>\n+ *         ^tlno ^same\n+ *    <---------preimage----->\n+ *         ^plno\n+ *\n+ * All line numbers are 0-based.\n+ */\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+\t\t\t struct blame_entry *split)\n+{\n+\tif (ent->num_lines <= tlno)\n+\t\treturn;\n+\tif (tlno < same) {\n+\t\tstruct blame_entry this[3];\n+\t\ttlno += ent->s_lno;\n+\t\tsame += ent->s_lno;\n+\t\tsplit_overlap(this, ent, tlno, plno, same, parent);\n+\t\tcopy_split_if_better(sb, split, this);\n+\t\tdecref_split(this);\n+\t}\n+}\n+\n+struct handle_split_cb_data {\n+\tstruct blame_scoreboard *sb;\n+\tstruct blame_entry *ent;\n+\tstruct origin *parent;\n+\tstruct blame_entry *split;\n+\tlong plno;\n+\tlong tlno;\n+};\n+\n+static void handle_split_cb(void *data, long same, long p_next, long t_next)\n+{\n+\tstruct handle_split_cb_data *d = data;\n+\thandle_split(d->sb, d->ent, d->tlno, d->plno, same, d->parent, d->split);\n+\td->plno = p_next;\n+\td->tlno = t_next;\n+}\n+\n+/*\n+ * Find the lines from parent that are the same as ent so that\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 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+\t\t\t      mmfile_t *file_p)\n+{\n+\tconst char *cp;\n+\tint cnt;\n+\tmmfile_t file_o;\n+\tstruct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };\n+\txpparam_t xpp;\n+\txdemitconf_t xecfg;\n+\n+\t/*\n+\t * Prepare mmfile that contains only the lines in ent.\n+\t */\n+\tcp = nth_line(sb, ent->lno);\n+\tfile_o.ptr = (char*) cp;\n+\tcnt = ent->num_lines;\n+\n+\twhile (cnt && cp < sb->final_buf + sb->final_buf_size) {\n+\t\tif (*cp++ == '\\n')\n+\t\t\tcnt--;\n+\t}\n+\tfile_o.size = cp - file_o.ptr;\n+\n+\t/*\n+\t * file_o is a part of final image we are annotating.\n+\t * file_p partially may match that image.\n+\t */\n+\tmemset(&xpp, 0, sizeof(xpp));\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+\txdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg);\n+\t/* remainder, if any, all match the preimage */\n+\thandle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);\n+}\n+\n+/*\n+ * See if lines currently target is suspected for can be attributed to\n+ * parent.\n+ */\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+\tint last_in_target, made_progress;\n+\tstruct blame_entry *e, split[3];\n+\tmmfile_t file_p;\n+\n+\tlast_in_target = find_last_in_target(sb, target);\n+\tif (last_in_target < 0)\n+\t\treturn 1; /* nothing remains for this target */\n+\n+\tfill_origin_blob(sb->ssb, parent, &file_p);\n+\tif (!file_p.ptr)\n+\t\treturn 0;\n+\n+\tmade_progress = 1;\n+\twhile (made_progress) {\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) < 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    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+\t\t\tdecref_split(split);\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+struct blame_list {\n+\tstruct blame_entry *ent;\n+\tstruct blame_entry split[3];\n+};\n+\n+/*\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 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+{\n+\tstruct blame_entry *e;\n+\tint num_ents, i;\n+\tstruct blame_list *blame_list = NULL;\n+\n+\tfor (e = sb->ent, num_ents = 0; e; e = e->next)\n+\t\tif (!e->scanned && !e->guilty &&\n+\t\t    same_suspect(e->suspect, target) &&\n+\t\t    min_score < ent_score(sb, e))\n+\t\t\tnum_ents++;\n+\tif (num_ents) {\n+\t\tblame_list = xcalloc(num_ents, sizeof(struct blame_list));\n+\t\tfor (e = sb->ent, i = 0; e; e = e->next)\n+\t\t\tif (!e->scanned && !e->guilty &&\n+\t\t\t    same_suspect(e->suspect, target) &&\n+\t\t\t    min_score < ent_score(sb, e))\n+\t\t\t\tblame_list[i++].ent = e;\n+\t}\n+\t*num_ents_p = num_ents;\n+\treturn blame_list;\n+}\n+\n+/*\n+ * Reset the scanned status on all entries.\n+ */\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+\t\te->scanned = 0;\n+}\n+\n+/*\n+ * For lines target is suspected for, see if we can find code movement\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 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+\t\t\t       int opt)\n+{\n+\tstruct diff_options diff_opts;\n+\tconst char *paths[1];\n+\tint i, j;\n+\tint retval;\n+\tstruct blame_list *blame_list;\n+\tint num_ents;\n+\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+\tdiff_setup(&diff_opts);\n+\tDIFF_OPT_SET(&diff_opts, RECURSIVE);\n+\tdiff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;\n+\n+\tpaths[0] = NULL;\n+\tdiff_tree_setup_paths(paths, &diff_opts);\n+\tif (diff_setup_done(&diff_opts) < 0)\n+\t\tdie(\"diff-setup\");\n+\n+\t/* Try \"find copies harder\" on new path if requested;\n+\t * we do not want to use diffcore_rename() actually to\n+\t * match things up; find_copies_harder is set only to\n+\t * force diff_tree_sha1() to feed all filepairs to diff_queue,\n+\t * and this code needs to be after diff_setup_done(), which\n+\t * usually makes find-copies-harder imply copy detection.\n+\t */\n+\tif ((opt & PICKAXE_BLAME_COPY_HARDEST)\n+\t    || ((opt & PICKAXE_BLAME_COPY_HARDER)\n+\t\t&& (!porigin || strcmp(target->path, porigin->path))))\n+\t\tDIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);\n+\n+\tif (is_null_sha1(target->commit->object.sha1))\n+\t\tdo_diff_cache(parent->tree->object.sha1, &diff_opts);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.sha1,\n+\t\t\t       target->commit->tree->object.sha1,\n+\t\t\t       \"\", &diff_opts);\n+\n+\tif (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))\n+\t\tdiffcore_std(&diff_opts);\n+\n+\tretval = 0;\n+\twhile (1) {\n+\t\tint made_progress = 0;\n+\n+\t\tfor (i = 0; i < diff_queued_diff.nr; i++) {\n+\t\t\tstruct diff_filepair *p = diff_queued_diff.queue[i];\n+\t\t\tstruct origin *norigin;\n+\t\t\tmmfile_t file_p;\n+\t\t\tstruct blame_entry this[3];\n+\n+\t\t\tif (!DIFF_FILE_VALID(p->one))\n+\t\t\t\tcontinue; /* does not exist in parent */\n+\t\t\tif (S_ISGITLINK(p->one->mode))\n+\t\t\t\tcontinue; /* ignore git links */\n+\t\t\tif (porigin && !strcmp(p->one->path, porigin->path))\n+\t\t\t\t/* find_move already dealt with this path */\n+\t\t\t\tcontinue;\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(sb->ssb, norigin, &file_p);\n+\t\t\tif (!file_p.ptr)\n+\t\t\t\tcontinue;\n+\n+\t\t\tfor (j = 0; j < num_ents; j++) {\n+\t\t\t\tfind_copy_in_blob(sb, blame_list[j].ent,\n+\t\t\t\t\t\t  norigin, this, &file_p);\n+\t\t\t\tcopy_split_if_better(sb, blame_list[j].split,\n+\t\t\t\t\t\t     this);\n+\t\t\t\tdecref_split(this);\n+\t\t\t}\n+\t\t\torigin_decref(norigin);\n+\t\t}\n+\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    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+\t\t\telse\n+\t\t\t\tblame_list[j].ent->scanned = 1;\n+\t\t\tdecref_split(split);\n+\t\t}\n+\t\tfree(blame_list);\n+\n+\t\tif (!made_progress)\n+\t\t\tbreak;\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+\t\t}\n+\t}\n+\treset_scanned_flag(sb);\n+\tdiff_flush(&diff_opts);\n+\tdiff_tree_release_paths(&diff_opts);\n+\treturn retval;\n+}\n+\n+/*\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 blame_scoreboard *sb,\n+\t\t\t     struct origin *origin, struct origin *porigin)\n+{\n+\tstruct blame_entry *e;\n+\n+\tif (!porigin->file.ptr && origin->file.ptr) {\n+\t\t/* Steal its file */\n+\t\tporigin->file = origin->file;\n+\t\torigin->file.ptr = NULL;\n+\t}\n+\tfor (e = sb->ent; e; e = e->next) {\n+\t\tif (!same_suspect(e->suspect, origin))\n+\t\t\tcontinue;\n+\t\torigin_incref(porigin);\n+\t\torigin_decref(e->suspect);\n+\t\te->suspect = porigin;\n+\t}\n+}\n+\n+/*\n+ * We pass blame from the current commit to its parents.  We keep saying\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 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 (!ssb->reverse)\n+\t\treturn commit->parents;\n+\treturn lookup_decoration(&revs->children, &commit->object);\n+}\n+\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(ssb, revs, commit);\n+\tfor (cnt = 0; l; l = l->next)\n+\t\tcnt++;\n+\treturn cnt;\n+}\n+\n+#define MAXSG 16\n+\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+\tstruct commit *commit = origin->commit;\n+\tstruct commit_list *sg;\n+\tstruct origin *sg_buf[MAXSG];\n+\tstruct origin *porigin, **sg_origin = sg_buf;\n+\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+\t\tmemset(sg_buf, 0, sizeof(sg_buf));\n+\telse\n+\t\tsg_origin = xcalloc(num_sg, sizeof(*sg_origin));\n+\n+\t/*\n+\t * The first pass looks for unrenamed path to optimize for\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 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(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+\t\t\tint j, same;\n+\n+\t\t\tif (sg_origin[i])\n+\t\t\t\tcontinue;\n+\t\t\tif (parse_commit(p))\n+\t\t\t\tcontinue;\n+\t\t\tporigin = find(sb, p, origin);\n+\t\t\tif (!porigin)\n+\t\t\t\tcontinue;\n+\t\t\tif (!hashcmp(porigin->blob_sha1, origin->blob_sha1)) {\n+\t\t\t\tpass_whole_blame(sb, origin, porigin);\n+\t\t\t\torigin_decref(porigin);\n+\t\t\t\tgoto finish;\n+\t\t\t}\n+\t\t\tfor (j = same = 0; j < i; j++)\n+\t\t\t\tif (sg_origin[j] &&\n+\t\t\t\t    !hashcmp(sg_origin[j]->blob_sha1,\n+\t\t\t\t\t     porigin->blob_sha1)) {\n+\t\t\t\t\tsame = 1;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\tif (!same)\n+\t\t\t\tsg_origin[i] = porigin;\n+\t\t\telse\n+\t\t\t\torigin_decref(porigin);\n+\t\t}\n+\t}\n+\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+\t\tif (!porigin)\n+\t\t\tcontinue;\n+\t\tif (!origin->previous) {\n+\t\t\torigin_incref(porigin);\n+\t\t\torigin->previous = porigin;\n+\t\t}\n+\t\tif (pass_blame_to_parent(sb, origin, porigin))\n+\t\t\tgoto finish;\n+\t}\n+\n+\t/*\n+\t * Optionally find moves in parents' files.\n+\t */\n+\tif (opt & PICKAXE_BLAME_MOVE)\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+\t\t\tif (!porigin)\n+\t\t\t\tcontinue;\n+\t\t\tif (find_move_in_parent(sb, origin, porigin))\n+\t\t\t\tgoto finish;\n+\t\t}\n+\n+\t/*\n+\t * Optionally find copies from parents' files.\n+\t */\n+\tif (opt & PICKAXE_BLAME_COPY)\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+\t\t\tif (find_copy_in_parent(sb, origin, sg->item,\n+\t\t\t\t\t\tporigin, opt))\n+\t\t\t\tgoto finish;\n+\t\t}\n+\n+ finish:\n+\tfor (i = 0; i < num_sg; i++) {\n+\t\tif (sg_origin[i]) {\n+\t\t\tdrop_origin_blob(sg_origin[i]);\n+\t\t\torigin_decref(sg_origin[i]);\n+\t\t}\n+\t}\n+\tdrop_origin_blob(origin);\n+\tif (sg_buf != sg_origin)\n+\t\tfree(sg_origin);\n+}\n+\n+/*\n+ * Information on commits, used for output.\n+ */\n+struct commit_info\n+{\n+\tconst char *author;\n+\tconst char *author_mail;\n+\tunsigned long author_time;\n+\tconst char *author_tz;\n+\n+\t/* filled only when asked for details */\n+\tconst char *committer;\n+\tconst char *committer_mail;\n+\tunsigned long committer_time;\n+\tconst char *committer_tz;\n+\n+\tconst char *summary;\n+};\n+\n+/*\n+ * Parse author/committer line in the commit object buffer\n+ */\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+\tint len, tzlen, maillen;\n+\tchar *tmp, *endp, *timepos, *mailpos;\n+\n+\ttmp = strstr(inbuf, what);\n+\tif (!tmp)\n+\t\tgoto error_out;\n+\ttmp += strlen(what);\n+\tendp = strchr(tmp, '\\n');\n+\tif (!endp)\n+\t\tlen = strlen(tmp);\n+\telse\n+\t\tlen = endp - tmp;\n+\tif (person_len <= len) {\n+\terror_out:\n+\t\t/* Ugh */\n+\t\t*tz = \"(unknown)\";\n+\t\tstrcpy(mail, *tz);\n+\t\t*time = 0;\n+\t\treturn;\n+\t}\n+\tmemcpy(person, tmp, len);\n+\n+\ttmp = person;\n+\ttmp += len;\n+\t*tmp = 0;\n+\twhile (*tmp != ' ')\n+\t\ttmp--;\n+\t*tz = tmp+1;\n+\ttzlen = (person+len)-(tmp+1);\n+\n+\t*tmp = 0;\n+\twhile (*tmp != ' ')\n+\t\ttmp--;\n+\t*time = strtoul(tmp, NULL, 10);\n+\ttimepos = tmp;\n+\n+\t*tmp = 0;\n+\twhile (*tmp != ' ')\n+\t\ttmp--;\n+\tmailpos = tmp + 1;\n+\t*tmp = 0;\n+\tmaillen = timepos - tmp;\n+\tmemcpy(mail, mailpos, maillen);\n+\n+\tif (!mailmap->nr)\n+\t\treturn;\n+\n+\t/*\n+\t * mailmap expansion may make the name longer.\n+\t * make room by pushing stuff down.\n+\t */\n+\ttmp = person + person_len - (tzlen + 1);\n+\tmemmove(tmp, *tz, tzlen);\n+\ttmp[tzlen] = 0;\n+\t*tz = tmp;\n+\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+\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+\t\tif (mailpos && mailpos-mail < mail_len - 1) {\n+\t\t\t*mailpos = '>';\n+\t\t\t*(mailpos+1) = '\\0';\n+\t\t}\n+\t}\n+}\n+\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+\tint len;\n+\tchar *tmp, *endp, *reencoded, *message;\n+\tstatic char author_name[1024];\n+\tstatic char author_mail[1024];\n+\tstatic char committer_name[1024];\n+\tstatic char committer_mail[1024];\n+\tstatic char summary_buf[1024];\n+\n+\t/*\n+\t * We've operated without save_commit_buffer, so\n+\t * we now need to populate them for output.\n+\t */\n+\tif (!commit->buffer) {\n+\t\tenum object_type type;\n+\t\tunsigned long size;\n+\t\tcommit->buffer =\n+\t\t\tread_sha1_file(commit->object.sha1, &type, &size);\n+\t\tif (!commit->buffer)\n+\t\t\tdie(\"Cannot read commit %s\",\n+\t\t\t    sha1_to_hex(commit->object.sha1));\n+\t}\n+\treencoded = reencode_commit_message(commit, NULL);\n+\tmessage   = reencoded ? reencoded : commit->buffer;\n+\tret->author = author_name;\n+\tret->author_mail = author_mail;\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+\n+\tif (!detailed) {\n+\t\tfree(reencoded);\n+\t\treturn;\n+\t}\n+\n+\tret->committer = committer_name;\n+\tret->committer_mail = committer_mail;\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+\n+\tret->summary = summary_buf;\n+\ttmp = strstr(message, \"\\n\\n\");\n+\tif (!tmp) {\n+\terror_out:\n+\t\tsprintf(summary_buf, \"(%s)\", sha1_to_hex(commit->object.sha1));\n+\t\tfree(reencoded);\n+\t\treturn;\n+\t}\n+\ttmp += 2;\n+\tendp = strchr(tmp, '\\n');\n+\tif (!endp)\n+\t\tendp = tmp + strlen(tmp);\n+\tlen = endp - tmp;\n+\tif (len >= sizeof(summary_buf) || len == 0)\n+\t\tgoto error_out;\n+\tmemcpy(summary_buf, tmp, len);\n+\tsummary_buf[len] = 0;\n+\tfree(reencoded);\n+}\n+\n+/*\n+ * To allow LF and other nonportable characters in pathnames,\n+ * they are c-style quoted as needed.\n+ */\n+static void write_filename_info(const char *path)\n+{\n+\tprintf(\"filename \");\n+\twrite_name_quoted(path, stdout, '\\n');\n+}\n+\n+/*\n+ * Porcelain/Incremental format wants to show a lot of details per\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 string_list *mailmap,\n+\t\t\t\t   struct origin *suspect)\n+{\n+\tstruct commit_info ci;\n+\n+\tif (suspect->commit->object.flags & METAINFO_SHOWN)\n+\t\treturn 0;\n+\n+\tsuspect->commit->object.flags |= METAINFO_SHOWN;\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+\tprintf(\"author-tz %s\\n\", ci.author_tz);\n+\tprintf(\"committer %s\\n\", ci.committer);\n+\tprintf(\"committer-mail %s\\n\", ci.committer_mail);\n+\tprintf(\"committer-time %lu\\n\", ci.committer_time);\n+\tprintf(\"committer-tz %s\\n\", ci.committer_tz);\n+\tprintf(\"summary %s\\n\", ci.summary);\n+\tif (suspect->commit->object.flags & UNINTERESTING)\n+\t\tprintf(\"boundary\\n\");\n+\tif (suspect->previous) {\n+\t\tstruct origin *prev = suspect->previous;\n+\t\tprintf(\"previous %s \", sha1_to_hex(prev->commit->object.sha1));\n+\t\twrite_name_quoted(prev->path, stdout, '\\n');\n+\t}\n+\treturn 1;\n+}\n+\n+/*\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_info *ssb, struct blame_entry *ent)\n+{\n+\tif (ent->guilty)\n+\t\treturn;\n+\tent->guilty = 1;\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(&ssb->mailmap, suspect);\n+\t\twrite_filename_info(suspect->path);\n+\t\tmaybe_flush_or_die(stdout, \"stdout\");\n+\t}\n+}\n+\n+/*\n+ * The main loop -- while the scoreboard has lines whose true origin\n+ * is still unknown, pick one blame_entry, and allow its current\n+ * suspect to pass blames to its parents.\n+ */\n+void assign_blame(struct blame_scoreboard *sb, int opt)\n+{\n+\tstruct rev_info *revs = sb->revs;\n+\n+\twhile (1) {\n+\t\tstruct blame_entry *ent;\n+\t\tstruct commit *commit;\n+\t\tstruct origin *suspect = NULL;\n+\n+\t\t/* find one suspect to break down */\n+\t\tfor (ent = sb->ent; !suspect && ent; ent = ent->next)\n+\t\t\tif (!ent->guilty)\n+\t\t\t\tsuspect = ent->suspect;\n+\t\tif (!suspect)\n+\t\t\treturn; /* all done */\n+\n+\t\t/*\n+\t\t * We will use this suspect later in the loop,\n+\t\t * so hold onto it in the meantime.\n+\t\t */\n+\t\torigin_incref(suspect);\n+\t\tcommit = suspect->commit;\n+\t\tif (!commit->object.parsed)\n+\t\t\tparse_commit(commit);\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+\t\telse {\n+\t\t\tcommit->object.flags |= UNINTERESTING;\n+\t\t\tif (commit->object.parsed)\n+\t\t\t\tmark_parents_uninteresting(commit);\n+\t\t}\n+\t\t/* treat root commit as boundary */\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(sb->ssb, ent);\n+\t\torigin_decref(suspect);\n+\n+\t\tif (DEBUG) /* sanity */\n+\t\t\tsanity_check_refcnt(sb);\n+\t}\n+}\n+\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+\tint time_len;\n+\tint tz;\n+\n+\tif (show_raw_time) {\n+\t\tsprintf(time_buf, \"%lu %s\", time, tz_str);\n+\t}\n+\telse {\n+\t\ttz = atoi(tz_str);\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, ' ', ssb->blame_date_width - time_len);\n+\t}\n+\treturn time_buf;\n+}\n+\n+#define OUTPUT_ANNOTATE_COMPAT\t001\n+#define OUTPUT_LONG_OBJECT_NAME\t002\n+#define OUTPUT_RAW_TIMESTAMP\t004\n+#define OUTPUT_PORCELAIN\t010\n+#define OUTPUT_SHOW_NAME\t020\n+#define OUTPUT_SHOW_NUMBER\t040\n+#define OUTPUT_SHOW_SCORE      0100\n+#define OUTPUT_NO_AUTHOR       0200\n+\n+static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent)\n+{\n+\tint cnt;\n+\tconst char *cp;\n+\tstruct origin *suspect = ent->suspect;\n+\tchar hex[41];\n+\n+\tstrcpy(hex, sha1_to_hex(suspect->commit->object.sha1));\n+\tprintf(\"%s%c%d %d %d\\n\",\n+\t       hex,\n+\t       ent->guilty ? ' ' : '*', // purely for debugging\n+\t       ent->s_lno + 1,\n+\t       ent->lno + 1,\n+\t       ent->num_lines);\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+\tcp = nth_line(sb, ent->lno);\n+\tfor (cnt = 0; cnt < ent->num_lines; cnt++) {\n+\t\tchar ch;\n+\t\tif (cnt)\n+\t\t\tprintf(\"%s %d %d\\n\", hex,\n+\t\t\t       ent->s_lno + 1 + cnt,\n+\t\t\t       ent->lno + 1 + cnt);\n+\t\tputchar('\\t');\n+\t\tdo {\n+\t\t\tch = *cp++;\n+\t\t\tputchar(ch);\n+\t\t} while (ch != '\\n' &&\n+\t\t\t cp < sb->final_buf + sb->final_buf_size);\n+\t}\n+}\n+\n+static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt)\n+{\n+\tint cnt;\n+\tconst char *cp;\n+\tstruct origin *suspect = ent->suspect;\n+\tstruct commit_info ci;\n+\tchar hex[41];\n+\tint show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);\n+\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+\tfor (cnt = 0; cnt < ent->num_lines; cnt++) {\n+\t\tchar ch;\n+\t\tint length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;\n+\n+\t\tif (suspect->commit->object.flags & UNINTERESTING) {\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+\t\t\t\tputchar('^');\n+\t\t\t}\n+\t\t}\n+\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(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       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\", 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\", 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 = 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(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       sb->ssb->max_digits, ent->lno + 1 + cnt);\n+\t\t}\n+\t\tdo {\n+\t\t\tch = *cp++;\n+\t\t\tputchar(ch);\n+\t\t} while (ch != '\\n' &&\n+\t\t\t cp < sb->final_buf + sb->final_buf_size);\n+\t}\n+}\n+\n+void output(struct blame_scoreboard *sb, int option)\n+{\n+\tstruct blame_entry *ent;\n+\n+\tif (option & OUTPUT_PORCELAIN) {\n+\t\tfor (ent = sb->ent; ent; ent = ent->next) {\n+\t\t\tstruct blame_entry *oth;\n+\t\t\tstruct origin *suspect = ent->suspect;\n+\t\t\tstruct commit *commit = suspect->commit;\n+\t\t\tif (commit->object.flags & MORE_THAN_ONE_PATH)\n+\t\t\t\tcontinue;\n+\t\t\tfor (oth = ent->next; oth; oth = oth->next) {\n+\t\t\t\tif ((oth->suspect->commit != commit) ||\n+\t\t\t\t    !strcmp(oth->suspect->path, suspect->path))\n+\t\t\t\t\tcontinue;\n+\t\t\t\tcommit->object.flags |= MORE_THAN_ONE_PATH;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tfor (ent = sb->ent; ent; ent = ent->next) {\n+\t\tif (option & OUTPUT_PORCELAIN)\n+\t\t\temit_porcelain(sb, ent);\n+\t\telse {\n+\t\t\temit_other(sb, ent, option);\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * To allow quick access to the contents of nth line in the\n+ * final image, prepare an index in the scoreboard.\n+ */\n+int prepare_lines(struct blame_scoreboard *sb)\n+{\n+\tconst char *buf = sb->final_buf;\n+\tunsigned long len = sb->final_buf_size;\n+\tint num = 0, incomplete = 0, bol = 1;\n+\n+\tif (len && buf[len-1] != '\\n')\n+\t\tincomplete++; /* incomplete line at the end */\n+\twhile (len--) {\n+\t\tif (bol) {\n+\t\t\tsb->lineno = xrealloc(sb->lineno,\n+\t\t\t\t\t      sizeof(int* ) * (num + 1));\n+\t\t\tsb->lineno[num] = buf - sb->final_buf;\n+\t\t\tbol = 0;\n+\t\t}\n+\t\tif (*buf++ == '\\n') {\n+\t\t\tnum++;\n+\t\t\tbol = 1;\n+\t\t}\n+\t}\n+\tsb->lineno = xrealloc(sb->lineno,\n+\t\t\t      sizeof(int* ) * (num + incomplete + 1));\n+\tsb->lineno[num + incomplete] = buf - sb->final_buf;\n+\tsb->num_lines = num + incomplete;\n+\treturn sb->num_lines;\n+}\n+\n+/*\n+ * Add phony grafts for use with -S; this is primarily to\n+ * support git's cvsserver that wants to give a linear history\n+ * to its clients.\n+ */\n+int read_ancestry(const char *graft_file)\n+{\n+\tFILE *fp = fopen(graft_file, \"r\");\n+\tchar buf[1024];\n+\tif (!fp)\n+\t\treturn -1;\n+\twhile (fgets(buf, sizeof(buf), fp)) {\n+\t\t/* The format is just \"Commit Parent1 Parent2 ...\\n\" */\n+\t\tint len = strlen(buf);\n+\t\tstruct commit_graft *graft = read_graft_line(buf, len);\n+\t\tif (graft)\n+\t\t\tregister_commit_graft(graft, 0);\n+\t}\n+\tfclose(fp);\n+\treturn 0;\n+}\n+\n+/*\n+ * How many columns do we need to show line numbers in decimal?\n+ */\n+static int lineno_width(int lines)\n+{\n+\tint i, width;\n+\n+\tfor (width = 1, i = 10; i <= lines + 1; width++)\n+\t\ti *= 10;\n+\treturn width;\n+}\n+\n+/*\n+ * How many columns do we need to show line numbers, authors,\n+ * and filenames?\n+ */\n+void find_alignment(struct blame_scoreboard *sb, int *option)\n+{\n+\tint longest_src_lines = 0;\n+\tint longest_dst_lines = 0;\n+\tunsigned largest_score = 0;\n+\tstruct blame_entry *e;\n+\n+\tfor (e = sb->ent; e; e = e->next) {\n+\t\tstruct origin *suspect = e->suspect;\n+\t\tstruct commit_info ci;\n+\t\tint num;\n+\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 (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(&sb->ssb->mailmap, suspect->commit, &ci, 1);\n+\t\t\tnum = utf8_strwidth(ci.author);\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+\t\t\tlongest_src_lines = num;\n+\t\tnum = e->lno + e->num_lines;\n+\t\tif (longest_dst_lines < num)\n+\t\t\tlongest_dst_lines = num;\n+\t\tif (largest_score < ent_score(sb, e))\n+\t\t\tlargest_score = ent_score(sb, e);\n+\t}\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 blame_scoreboard *sb)\n+{\n+\tint baa = 0;\n+\tstruct blame_entry *ent;\n+\n+\tfor (ent = sb->ent; ent; ent = ent->next) {\n+\t\t/* Nobody should have zero or negative refcnt */\n+\t\tif (ent->suspect->refcnt <= 0) {\n+\t\t\tfprintf(stderr, \"%s in %s has negative refcnt %d\\n\",\n+\t\t\t\tent->suspect->path,\n+\t\t\t\tsha1_to_hex(ent->suspect->commit->object.sha1),\n+\t\t\t\tent->suspect->refcnt);\n+\t\t\tbaa = 1;\n+\t\t}\n+\t}\n+\tif (baa) {\n+\t\tint opt = 0160;\n+\t\tfind_alignment(sb, &opt);\n+\t\toutput(sb, opt);\n+\t\tdie(\"Baa %d!\", baa);\n+\t}\n+}\n+\n+/*\n+ * Used for the command line parsing; check if the path exists\n+ * in the working tree.\n+ */\n+int has_string_in_work_tree(const char *path)\n+{\n+\tstruct stat st;\n+\treturn !lstat(path, &st);\n+}\n+\n+unsigned parse_score(const char *arg)\n+{\n+\tchar *end;\n+\tunsigned long score = strtoul(arg, &end, 10);\n+\tif (*end)\n+\t\treturn 0;\n+\treturn score;\n+}\n+\n+const char *add_prefix(const char *prefix, const char *path)\n+{\n+\treturn prefix_path(prefix, prefix ? strlen(prefix) : 0, path);\n+}\n+\ndiff --git a/blame.h b/blame.h\nindex 72d1e2a..37318b5 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -161,6 +161,27 @@ struct blame_scoreboard {\n \tstruct blame_info *ssb;\n };\n \n+/* these will be reorganized again */\n+extern const char *nth_line(struct blame_scoreboard *sb, int lno) ;\n+extern struct origin *make_origin(struct commit *commit, const char *path) ;\n+extern const char *add_prefix(const char *prefix, const char *path) ;\n+extern 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+extern unsigned parse_score(const char *arg) ;\n+\n+extern int has_string_in_work_tree(const char *path) ;\n+extern int fill_blob_sha1(struct origin *origin) ;\n+extern int prepare_lines(struct blame_scoreboard *sb) ;\n+\n+extern int read_ancestry(const char *graft_file) ;\n+\n+extern void output(struct blame_scoreboard *sb, int option) ;\n+\n+extern void find_alignment(struct blame_scoreboard *sb, int *option) ;\n+\n extern void assign_blame(struct blame_scoreboard *sb, int opt) ;\n+extern void coalesce(struct blame_scoreboard *sb) ;\n \n #endif\ndiff --git a/builtin-blame.c b/builtin-blame.c\nindex d4f812b..59e6461 100644\n--- a/builtin-blame.c\n+++ b/builtin-blame.c\n@@ -40,1709 +40,6 @@ static unsigned opt_blame_copy_score;\n #endif\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 blame_info *ssb, struct origin *o, mmfile_t *file)\n-{\n-\tif (!o->file.ptr) {\n-\t\tenum object_type type;\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-\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-\t\treturn 1;\n-\tif (a->commit != b->commit)\n-\t\treturn 0;\n-\treturn !strcmp(a->path, b->path);\n-}\n-\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 blame_scoreboard *sb)\n-{\n-\tstruct blame_entry *ent, *next;\n-\n-\tfor (ent = sb->ent; ent && (next = ent->next); ent = next) {\n-\t\tif (same_suspect(ent->suspect, next->suspect) &&\n-\t\t    ent->guilty == next->guilty &&\n-\t\t    ent->s_lno + ent->num_lines == next->s_lno) {\n-\t\t\tent->num_lines += next->num_lines;\n-\t\t\tent->next = next->next;\n-\t\t\tif (ent->next)\n-\t\t\t\tent->next->prev = ent;\n-\t\t\torigin_decref(next->suspect);\n-\t\t\tfree(next);\n-\t\t\tent->score = 0;\n-\t\t\tnext = ent; /* again */\n-\t\t}\n-\t}\n-\n-\tif (DEBUG) /* sanity */\n-\t\tsanity_check_refcnt(sb);\n-}\n-\n-/*\n- * Given a commit and a path in it, create a new origin structure.\n- * The callers that add blame to the scoreboard should use\n- * get_origin() to obtain shared, refcounted copy instead of calling\n- * this function directly.\n- */\n-static struct origin *make_origin(struct commit *commit, const char *path)\n-{\n-\tstruct origin *o;\n-\to = xcalloc(1, sizeof(*o) + strlen(path) + 1);\n-\to->commit = commit;\n-\to->refcnt = 1;\n-\tstrcpy(o->path, path);\n-\treturn o;\n-}\n-\n-/*\n- * Locate an existing origin or create a new one.\n- */\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-\tstruct blame_entry *e;\n-\n-\tfor (e = sb->ent; e; e = e->next) {\n-\t\tif (e->suspect->commit == commit &&\n-\t\t    !strcmp(e->suspect->path, path))\n-\t\t\treturn origin_incref(e->suspect);\n-\t}\n-\treturn make_origin(commit, path);\n-}\n-\n-/*\n- * Fill the blob_sha1 field of an origin if it hasn't, so that later\n- * call to fill_origin_blob() can use it to locate the data.  blob_sha1\n- * for an origin is also used to pass the blame for the entire file to\n- * the parent to detect the case where a child's blob is identical to\n- * that of its parent's.\n- */\n-static int fill_blob_sha1(struct origin *origin)\n-{\n-\tunsigned mode;\n-\n-\tif (!is_null_sha1(origin->blob_sha1))\n-\t\treturn 0;\n-\tif (get_tree_entry(origin->commit->object.sha1,\n-\t\t\t   origin->path,\n-\t\t\t   origin->blob_sha1, &mode))\n-\t\tgoto error_out;\n-\tif (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)\n-\t\tgoto error_out;\n-\treturn 0;\n- error_out:\n-\thashclr(origin->blob_sha1);\n-\treturn -1;\n-}\n-\n-/*\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 blame_scoreboard *sb,\n-\t\t\t\t  struct commit *parent,\n-\t\t\t\t  struct origin *origin)\n-{\n-\tstruct origin *porigin = NULL;\n-\tstruct diff_options diff_opts;\n-\tconst char *paths[2];\n-\n-\tif (parent->util) {\n-\t\t/*\n-\t\t * Each commit object can cache one origin in that\n-\t\t * commit.  This is a freestanding copy of origin and\n-\t\t * not refcounted.\n-\t\t */\n-\t\tstruct origin *cached = parent->util;\n-\t\tif (!strcmp(cached->path, origin->path)) {\n-\t\t\t/*\n-\t\t\t * The same path between origin and its parent\n-\t\t\t * without renaming -- the most common case.\n-\t\t\t */\n-\t\t\tporigin = get_origin(sb, parent, cached->path);\n-\n-\t\t\t/*\n-\t\t\t * If the origin was newly created (i.e. get_origin\n-\t\t\t * would call make_origin if none is found in the\n-\t\t\t * scoreboard), it does not know the blob_sha1,\n-\t\t\t * so copy it.  Otherwise porigin was in the\n-\t\t\t * scoreboard and already knows blob_sha1.\n-\t\t\t */\n-\t\t\tif (porigin->refcnt == 1)\n-\t\t\t\thashcpy(porigin->blob_sha1, cached->blob_sha1);\n-\t\t\treturn porigin;\n-\t\t}\n-\t\t/* otherwise it was not very useful; free it */\n-\t\tfree(parent->util);\n-\t\tparent->util = NULL;\n-\t}\n-\n-\t/* See if the origin->path is different between parent\n-\t * and origin first.  Most of the time they are the\n-\t * same and diff-tree is fairly efficient about this.\n-\t */\n-\tdiff_setup(&diff_opts);\n-\tDIFF_OPT_SET(&diff_opts, RECURSIVE);\n-\tdiff_opts.detect_rename = 0;\n-\tdiff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;\n-\tpaths[0] = origin->path;\n-\tpaths[1] = NULL;\n-\n-\tdiff_tree_setup_paths(paths, &diff_opts);\n-\tif (diff_setup_done(&diff_opts) < 0)\n-\t\tdie(\"diff-setup\");\n-\n-\tif (is_null_sha1(origin->commit->object.sha1))\n-\t\tdo_diff_cache(parent->tree->object.sha1, &diff_opts);\n-\telse\n-\t\tdiff_tree_sha1(parent->tree->object.sha1,\n-\t\t\t       origin->commit->tree->object.sha1,\n-\t\t\t       \"\", &diff_opts);\n-\tdiffcore_std(&diff_opts);\n-\n-\t/* It is either one entry that says \"modified\", or \"created\",\n-\t * or nothing.\n-\t */\n-\tif (!diff_queued_diff.nr) {\n-\t\t/* The path is the same as parent */\n-\t\tporigin = get_origin(sb, parent, origin->path);\n-\t\thashcpy(porigin->blob_sha1, origin->blob_sha1);\n-\t}\n-\telse if (diff_queued_diff.nr != 1)\n-\t\tdie(\"internal error in blame::find_origin\");\n-\telse {\n-\t\tstruct diff_filepair *p = diff_queued_diff.queue[0];\n-\t\tswitch (p->status) {\n-\t\tdefault:\n-\t\t\tdie(\"internal error in blame::find_origin (%c)\",\n-\t\t\t    p->status);\n-\t\tcase 'M':\n-\t\t\tporigin = get_origin(sb, parent, origin->path);\n-\t\t\thashcpy(porigin->blob_sha1, p->one->sha1);\n-\t\t\tbreak;\n-\t\tcase 'A':\n-\t\tcase 'T':\n-\t\t\t/* Did not exist in parent, or type changed */\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\tdiff_flush(&diff_opts);\n-\tdiff_tree_release_paths(&diff_opts);\n-\tif (porigin) {\n-\t\t/*\n-\t\t * Create a freestanding copy that is not part of\n-\t\t * the refcounted origin found in the scoreboard, and\n-\t\t * cache it in the commit.\n-\t\t */\n-\t\tstruct origin *cached;\n-\n-\t\tcached = make_origin(porigin->commit, porigin->path);\n-\t\thashcpy(cached->blob_sha1, porigin->blob_sha1);\n-\t\tparent->util = cached;\n-\t}\n-\treturn porigin;\n-}\n-\n-/*\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 blame_scoreboard *sb,\n-\t\t\t\t  struct commit *parent,\n-\t\t\t\t  struct origin *origin)\n-{\n-\tstruct origin *porigin = NULL;\n-\tstruct diff_options diff_opts;\n-\tint i;\n-\tconst char *paths[2];\n-\n-\tdiff_setup(&diff_opts);\n-\tDIFF_OPT_SET(&diff_opts, RECURSIVE);\n-\tdiff_opts.detect_rename = DIFF_DETECT_RENAME;\n-\tdiff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;\n-\tdiff_opts.single_follow = origin->path;\n-\tpaths[0] = NULL;\n-\tdiff_tree_setup_paths(paths, &diff_opts);\n-\tif (diff_setup_done(&diff_opts) < 0)\n-\t\tdie(\"diff-setup\");\n-\n-\tif (is_null_sha1(origin->commit->object.sha1))\n-\t\tdo_diff_cache(parent->tree->object.sha1, &diff_opts);\n-\telse\n-\t\tdiff_tree_sha1(parent->tree->object.sha1,\n-\t\t\t       origin->commit->tree->object.sha1,\n-\t\t\t       \"\", &diff_opts);\n-\tdiffcore_std(&diff_opts);\n-\n-\tfor (i = 0; i < diff_queued_diff.nr; i++) {\n-\t\tstruct diff_filepair *p = diff_queued_diff.queue[i];\n-\t\tif ((p->status == 'R' || p->status == 'C') &&\n-\t\t    !strcmp(p->two->path, origin->path)) {\n-\t\t\tporigin = get_origin(sb, parent, p->one->path);\n-\t\t\thashcpy(porigin->blob_sha1, p->one->sha1);\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\tdiff_flush(&diff_opts);\n-\tdiff_tree_release_paths(&diff_opts);\n-\treturn porigin;\n-}\n-\n-/*\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 blame_scoreboard *sb, struct blame_entry *e)\n-{\n-\tstruct blame_entry *ent, *prev = NULL;\n-\n-\torigin_incref(e->suspect);\n-\n-\tfor (ent = sb->ent; ent && ent->lno < e->lno; ent = ent->next)\n-\t\tprev = ent;\n-\n-\t/* prev, if not NULL, is the last one that is below e */\n-\te->prev = prev;\n-\tif (prev) {\n-\t\te->next = prev->next;\n-\t\tprev->next = e;\n-\t}\n-\telse {\n-\t\te->next = sb->ent;\n-\t\tsb->ent = e;\n-\t}\n-\tif (e->next)\n-\t\te->next->prev = e;\n-}\n-\n-/*\n- * src typically is on-stack; we want to copy the information in it to\n- * a malloced blame_entry that is already on the linked list of the\n- * scoreboard.  The origin of dst loses a refcnt while the origin of src\n- * gains one.\n- */\n-static void dup_entry(struct blame_entry *dst, struct blame_entry *src)\n-{\n-\tstruct blame_entry *p, *n;\n-\n-\tp = dst->prev;\n-\tn = dst->next;\n-\torigin_incref(src->suspect);\n-\torigin_decref(dst->suspect);\n-\tmemcpy(dst, src, sizeof(*src));\n-\tdst->prev = p;\n-\tdst->next = n;\n-\tdst->score = 0;\n-}\n-\n-static const char *nth_line(struct blame_scoreboard *sb, int lno)\n-{\n-\treturn sb->final_buf + sb->lineno[lno];\n-}\n-\n-/*\n- * It is known that lines between tlno to same came from parent, and e\n- * has an overlap with that range.  it also is known that parent's\n- * line plno corresponds to e's line tlno.\n- *\n- *                <---- e ----->\n- *                   <------>\n- *                   <------------>\n- *             <------------>\n- *             <------------------>\n- *\n- * Split e into potentially three parts; before this chunk, the chunk\n- * to be blamed for the parent, and after that portion.\n- */\n-static void split_overlap(struct blame_entry *split,\n-\t\t\t  struct blame_entry *e,\n-\t\t\t  int tlno, int plno, int same,\n-\t\t\t  struct origin *parent)\n-{\n-\tint chunk_end_lno;\n-\tmemset(split, 0, sizeof(struct blame_entry [3]));\n-\n-\tif (e->s_lno < tlno) {\n-\t\t/* there is a pre-chunk part not blamed on parent */\n-\t\tsplit[0].suspect = origin_incref(e->suspect);\n-\t\tsplit[0].lno = e->lno;\n-\t\tsplit[0].s_lno = e->s_lno;\n-\t\tsplit[0].num_lines = tlno - e->s_lno;\n-\t\tsplit[1].lno = e->lno + tlno - e->s_lno;\n-\t\tsplit[1].s_lno = plno;\n-\t}\n-\telse {\n-\t\tsplit[1].lno = e->lno;\n-\t\tsplit[1].s_lno = plno + (e->s_lno - tlno);\n-\t}\n-\n-\tif (same < e->s_lno + e->num_lines) {\n-\t\t/* there is a post-chunk part not blamed on parent */\n-\t\tsplit[2].suspect = origin_incref(e->suspect);\n-\t\tsplit[2].lno = e->lno + (same - e->s_lno);\n-\t\tsplit[2].s_lno = e->s_lno + (same - e->s_lno);\n-\t\tsplit[2].num_lines = e->s_lno + e->num_lines - same;\n-\t\tchunk_end_lno = split[2].lno;\n-\t}\n-\telse\n-\t\tchunk_end_lno = e->lno + e->num_lines;\n-\tsplit[1].num_lines = chunk_end_lno - split[1].lno;\n-\n-\t/*\n-\t * if it turns out there is nothing to blame the parent for,\n-\t * forget about the splitting.  !split[1].suspect signals this.\n-\t */\n-\tif (split[1].num_lines < 1)\n-\t\treturn;\n-\tsplit[1].suspect = origin_incref(parent);\n-}\n-\n-/*\n- * split_overlap() divided an existing blame e into up to three parts\n- * in split.  Adjust the linked list of blames in the scoreboard to\n- * reflect the split.\n- */\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-\tstruct blame_entry *new_entry;\n-\n-\tif (split[0].suspect && split[2].suspect) {\n-\t\t/* The first part (reuse storage for the existing entry e) */\n-\t\tdup_entry(e, &split[0]);\n-\n-\t\t/* The last part -- me */\n-\t\tnew_entry = xmalloc(sizeof(*new_entry));\n-\t\tmemcpy(new_entry, &(split[2]), sizeof(struct blame_entry));\n-\t\tadd_blame_entry(sb, new_entry);\n-\n-\t\t/* ... and the middle part -- parent */\n-\t\tnew_entry = xmalloc(sizeof(*new_entry));\n-\t\tmemcpy(new_entry, &(split[1]), sizeof(struct blame_entry));\n-\t\tadd_blame_entry(sb, new_entry);\n-\t}\n-\telse if (!split[0].suspect && !split[2].suspect)\n-\t\t/*\n-\t\t * The parent covers the entire area; reuse storage for\n-\t\t * e and replace it with the parent.\n-\t\t */\n-\t\tdup_entry(e, &split[1]);\n-\telse if (split[0].suspect) {\n-\t\t/* me and then parent */\n-\t\tdup_entry(e, &split[0]);\n-\n-\t\tnew_entry = xmalloc(sizeof(*new_entry));\n-\t\tmemcpy(new_entry, &(split[1]), sizeof(struct blame_entry));\n-\t\tadd_blame_entry(sb, new_entry);\n-\t}\n-\telse {\n-\t\t/* parent and then me */\n-\t\tdup_entry(e, &split[1]);\n-\n-\t\tnew_entry = xmalloc(sizeof(*new_entry));\n-\t\tmemcpy(new_entry, &(split[2]), sizeof(struct blame_entry));\n-\t\tadd_blame_entry(sb, new_entry);\n-\t}\n-\n-\tif (DEBUG) { /* sanity */\n-\t\tstruct blame_entry *ent;\n-\t\tint lno = sb->ent->lno, corrupt = 0;\n-\n-\t\tfor (ent = sb->ent; ent; ent = ent->next) {\n-\t\t\tif (lno != ent->lno)\n-\t\t\t\tcorrupt = 1;\n-\t\t\tif (ent->s_lno < 0)\n-\t\t\t\tcorrupt = 1;\n-\t\t\tlno += ent->num_lines;\n-\t\t}\n-\t\tif (corrupt) {\n-\t\t\tlno = sb->ent->lno;\n-\t\t\tfor (ent = sb->ent; ent; ent = ent->next) {\n-\t\t\t\tprintf(\"L %8d l %8d n %8d\\n\",\n-\t\t\t\t       lno, ent->lno, ent->num_lines);\n-\t\t\t\tlno = ent->lno + ent->num_lines;\n-\t\t\t}\n-\t\t\tdie(\"oops\");\n-\t\t}\n-\t}\n-}\n-\n-/*\n- * After splitting the blame, the origins used by the\n- * on-stack blame_entry should lose one refcnt each.\n- */\n-static void decref_split(struct blame_entry *split)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i < 3; i++)\n-\t\torigin_decref(split[i].suspect);\n-}\n-\n-/*\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 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-\tstruct blame_entry split[3];\n-\n-\tsplit_overlap(split, e, tlno, plno, same, parent);\n-\tif (split[1].suspect)\n-\t\tsplit_blame(sb, split, e);\n-\tdecref_split(split);\n-}\n-\n-/*\n- * Find the line number of the last line the target is suspected for.\n- */\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-\n-\tfor (e = sb->ent; e; e = e->next) {\n-\t\tif (e->guilty || !same_suspect(e->suspect, target))\n-\t\t\tcontinue;\n-\t\tif (last_in_target < e->s_lno + e->num_lines)\n-\t\t\tlast_in_target = e->s_lno + e->num_lines;\n-\t}\n-\treturn last_in_target;\n-}\n-\n-/*\n- * Process one hunk from the patch between the current suspect for\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 blame_scoreboard *sb,\n-\t\t\tint tlno, int plno, int same,\n-\t\t\tstruct origin *target, struct origin *parent)\n-{\n-\tstruct blame_entry *e;\n-\n-\tfor (e = sb->ent; e; e = e->next) {\n-\t\tif (e->guilty || !same_suspect(e->suspect, target))\n-\t\t\tcontinue;\n-\t\tif (same <= e->s_lno)\n-\t\t\tcontinue;\n-\t\tif (tlno < e->s_lno + e->num_lines)\n-\t\t\tblame_overlap(sb, e, tlno, plno, same, parent);\n-\t}\n-}\n-\n-struct blame_chunk_cb_data {\n-\tstruct blame_scoreboard *sb;\n-\tstruct origin *target;\n-\tstruct origin *parent;\n-\tlong plno;\n-\tlong tlno;\n-};\n-\n-static void blame_chunk_cb(void *data, long same, long p_next, long t_next)\n-{\n-\tstruct blame_chunk_cb_data *d = data;\n-\tblame_chunk(d->sb, d->tlno, d->plno, same, d->target, d->parent);\n-\td->plno = p_next;\n-\td->tlno = t_next;\n-}\n-\n-/*\n- * We are looking at the origin 'target' and aiming to pass blame\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 blame_scoreboard *sb,\n-\t\t\t\tstruct origin *target,\n-\t\t\t\tstruct origin *parent)\n-{\n-\tint last_in_target;\n-\tmmfile_t file_p, file_o;\n-\tstruct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };\n-\txpparam_t xpp;\n-\txdemitconf_t xecfg;\n-\n-\tlast_in_target = find_last_in_target(sb, target);\n-\tif (last_in_target < 0)\n-\t\treturn 1; /* nothing remains for this target */\n-\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 = 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-\t/* The rest (i.e. anything after tlno) are the same as the parent */\n-\tblame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * The lines in blame_entry after splitting blames many times can become\n- * very small and trivial, and at some point it becomes pointless to\n- * blame the parents.  E.g. \"\\t\\t}\\n\\t}\\n\\n\" appears everywhere in any\n- * ordinary C program, and it is not worth to say it was copied from\n- * totally unrelated file in the parent.\n- *\n- * Compute how trivial the lines in the blame_entry are.\n- */\n-static unsigned ent_score(struct blame_scoreboard *sb, struct blame_entry *e)\n-{\n-\tunsigned score;\n-\tconst char *cp, *ep;\n-\n-\tif (e->score)\n-\t\treturn e->score;\n-\n-\tscore = 1;\n-\tcp = nth_line(sb, e->lno);\n-\tep = nth_line(sb, e->lno + e->num_lines);\n-\twhile (cp < ep) {\n-\t\tunsigned ch = *((unsigned char *)cp);\n-\t\tif (isalnum(ch))\n-\t\t\tscore++;\n-\t\tcp++;\n-\t}\n-\te->score = score;\n-\treturn score;\n-}\n-\n-/*\n- * best_so_far[] and this[] are both a split of an existing blame_entry\n- * that passes blame to the parent.  Maintain best_so_far the best split\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 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-\tint i;\n-\n-\tif (!this[1].suspect)\n-\t\treturn;\n-\tif (best_so_far[1].suspect) {\n-\t\tif (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1]))\n-\t\t\treturn;\n-\t}\n-\n-\tfor (i = 0; i < 3; i++)\n-\t\torigin_incref(this[i].suspect);\n-\tdecref_split(best_so_far);\n-\tmemcpy(best_so_far, this, sizeof(struct blame_entry [3]));\n-}\n-\n-/*\n- * We are looking at a part of the final image represented by\n- * ent (tlno and same are offset by ent->s_lno).\n- * tlno is where we are looking at in the final image.\n- * up to (but not including) same match preimage.\n- * plno is where we are looking at in the preimage.\n- *\n- * <-------------- final image ---------------------->\n- *       <------ent------>\n- *         ^tlno ^same\n- *    <---------preimage----->\n- *         ^plno\n- *\n- * All line numbers are 0-based.\n- */\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-\t\t\t struct blame_entry *split)\n-{\n-\tif (ent->num_lines <= tlno)\n-\t\treturn;\n-\tif (tlno < same) {\n-\t\tstruct blame_entry this[3];\n-\t\ttlno += ent->s_lno;\n-\t\tsame += ent->s_lno;\n-\t\tsplit_overlap(this, ent, tlno, plno, same, parent);\n-\t\tcopy_split_if_better(sb, split, this);\n-\t\tdecref_split(this);\n-\t}\n-}\n-\n-struct handle_split_cb_data {\n-\tstruct blame_scoreboard *sb;\n-\tstruct blame_entry *ent;\n-\tstruct origin *parent;\n-\tstruct blame_entry *split;\n-\tlong plno;\n-\tlong tlno;\n-};\n-\n-static void handle_split_cb(void *data, long same, long p_next, long t_next)\n-{\n-\tstruct handle_split_cb_data *d = data;\n-\thandle_split(d->sb, d->ent, d->tlno, d->plno, same, d->parent, d->split);\n-\td->plno = p_next;\n-\td->tlno = t_next;\n-}\n-\n-/*\n- * Find the lines from parent that are the same as ent so that\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 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-\t\t\t      mmfile_t *file_p)\n-{\n-\tconst char *cp;\n-\tint cnt;\n-\tmmfile_t file_o;\n-\tstruct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };\n-\txpparam_t xpp;\n-\txdemitconf_t xecfg;\n-\n-\t/*\n-\t * Prepare mmfile that contains only the lines in ent.\n-\t */\n-\tcp = nth_line(sb, ent->lno);\n-\tfile_o.ptr = (char*) cp;\n-\tcnt = ent->num_lines;\n-\n-\twhile (cnt && cp < sb->final_buf + sb->final_buf_size) {\n-\t\tif (*cp++ == '\\n')\n-\t\t\tcnt--;\n-\t}\n-\tfile_o.size = cp - file_o.ptr;\n-\n-\t/*\n-\t * file_o is a part of final image we are annotating.\n-\t * file_p partially may match that image.\n-\t */\n-\tmemset(&xpp, 0, sizeof(xpp));\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-\txdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg);\n-\t/* remainder, if any, all match the preimage */\n-\thandle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);\n-}\n-\n-/*\n- * See if lines currently target is suspected for can be attributed to\n- * parent.\n- */\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-\tint last_in_target, made_progress;\n-\tstruct blame_entry *e, split[3];\n-\tmmfile_t file_p;\n-\n-\tlast_in_target = find_last_in_target(sb, target);\n-\tif (last_in_target < 0)\n-\t\treturn 1; /* nothing remains for this target */\n-\n-\tfill_origin_blob(sb->ssb, parent, &file_p);\n-\tif (!file_p.ptr)\n-\t\treturn 0;\n-\n-\tmade_progress = 1;\n-\twhile (made_progress) {\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) < 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    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-\t\t\tdecref_split(split);\n-\t\t}\n-\t}\n-\treturn 0;\n-}\n-\n-struct blame_list {\n-\tstruct blame_entry *ent;\n-\tstruct blame_entry split[3];\n-};\n-\n-/*\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 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-{\n-\tstruct blame_entry *e;\n-\tint num_ents, i;\n-\tstruct blame_list *blame_list = NULL;\n-\n-\tfor (e = sb->ent, num_ents = 0; e; e = e->next)\n-\t\tif (!e->scanned && !e->guilty &&\n-\t\t    same_suspect(e->suspect, target) &&\n-\t\t    min_score < ent_score(sb, e))\n-\t\t\tnum_ents++;\n-\tif (num_ents) {\n-\t\tblame_list = xcalloc(num_ents, sizeof(struct blame_list));\n-\t\tfor (e = sb->ent, i = 0; e; e = e->next)\n-\t\t\tif (!e->scanned && !e->guilty &&\n-\t\t\t    same_suspect(e->suspect, target) &&\n-\t\t\t    min_score < ent_score(sb, e))\n-\t\t\t\tblame_list[i++].ent = e;\n-\t}\n-\t*num_ents_p = num_ents;\n-\treturn blame_list;\n-}\n-\n-/*\n- * Reset the scanned status on all entries.\n- */\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-\t\te->scanned = 0;\n-}\n-\n-/*\n- * For lines target is suspected for, see if we can find code movement\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 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-\t\t\t       int opt)\n-{\n-\tstruct diff_options diff_opts;\n-\tconst char *paths[1];\n-\tint i, j;\n-\tint retval;\n-\tstruct blame_list *blame_list;\n-\tint num_ents;\n-\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-\tdiff_setup(&diff_opts);\n-\tDIFF_OPT_SET(&diff_opts, RECURSIVE);\n-\tdiff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;\n-\n-\tpaths[0] = NULL;\n-\tdiff_tree_setup_paths(paths, &diff_opts);\n-\tif (diff_setup_done(&diff_opts) < 0)\n-\t\tdie(\"diff-setup\");\n-\n-\t/* Try \"find copies harder\" on new path if requested;\n-\t * we do not want to use diffcore_rename() actually to\n-\t * match things up; find_copies_harder is set only to\n-\t * force diff_tree_sha1() to feed all filepairs to diff_queue,\n-\t * and this code needs to be after diff_setup_done(), which\n-\t * usually makes find-copies-harder imply copy detection.\n-\t */\n-\tif ((opt & PICKAXE_BLAME_COPY_HARDEST)\n-\t    || ((opt & PICKAXE_BLAME_COPY_HARDER)\n-\t\t&& (!porigin || strcmp(target->path, porigin->path))))\n-\t\tDIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);\n-\n-\tif (is_null_sha1(target->commit->object.sha1))\n-\t\tdo_diff_cache(parent->tree->object.sha1, &diff_opts);\n-\telse\n-\t\tdiff_tree_sha1(parent->tree->object.sha1,\n-\t\t\t       target->commit->tree->object.sha1,\n-\t\t\t       \"\", &diff_opts);\n-\n-\tif (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))\n-\t\tdiffcore_std(&diff_opts);\n-\n-\tretval = 0;\n-\twhile (1) {\n-\t\tint made_progress = 0;\n-\n-\t\tfor (i = 0; i < diff_queued_diff.nr; i++) {\n-\t\t\tstruct diff_filepair *p = diff_queued_diff.queue[i];\n-\t\t\tstruct origin *norigin;\n-\t\t\tmmfile_t file_p;\n-\t\t\tstruct blame_entry this[3];\n-\n-\t\t\tif (!DIFF_FILE_VALID(p->one))\n-\t\t\t\tcontinue; /* does not exist in parent */\n-\t\t\tif (S_ISGITLINK(p->one->mode))\n-\t\t\t\tcontinue; /* ignore git links */\n-\t\t\tif (porigin && !strcmp(p->one->path, porigin->path))\n-\t\t\t\t/* find_move already dealt with this path */\n-\t\t\t\tcontinue;\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(sb->ssb, norigin, &file_p);\n-\t\t\tif (!file_p.ptr)\n-\t\t\t\tcontinue;\n-\n-\t\t\tfor (j = 0; j < num_ents; j++) {\n-\t\t\t\tfind_copy_in_blob(sb, blame_list[j].ent,\n-\t\t\t\t\t\t  norigin, this, &file_p);\n-\t\t\t\tcopy_split_if_better(sb, blame_list[j].split,\n-\t\t\t\t\t\t     this);\n-\t\t\t\tdecref_split(this);\n-\t\t\t}\n-\t\t\torigin_decref(norigin);\n-\t\t}\n-\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    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-\t\t\telse\n-\t\t\t\tblame_list[j].ent->scanned = 1;\n-\t\t\tdecref_split(split);\n-\t\t}\n-\t\tfree(blame_list);\n-\n-\t\tif (!made_progress)\n-\t\t\tbreak;\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-\t\t}\n-\t}\n-\treset_scanned_flag(sb);\n-\tdiff_flush(&diff_opts);\n-\tdiff_tree_release_paths(&diff_opts);\n-\treturn retval;\n-}\n-\n-/*\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 blame_scoreboard *sb,\n-\t\t\t     struct origin *origin, struct origin *porigin)\n-{\n-\tstruct blame_entry *e;\n-\n-\tif (!porigin->file.ptr && origin->file.ptr) {\n-\t\t/* Steal its file */\n-\t\tporigin->file = origin->file;\n-\t\torigin->file.ptr = NULL;\n-\t}\n-\tfor (e = sb->ent; e; e = e->next) {\n-\t\tif (!same_suspect(e->suspect, origin))\n-\t\t\tcontinue;\n-\t\torigin_incref(porigin);\n-\t\torigin_decref(e->suspect);\n-\t\te->suspect = porigin;\n-\t}\n-}\n-\n-/*\n- * We pass blame from the current commit to its parents.  We keep saying\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 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 (!ssb->reverse)\n-\t\treturn commit->parents;\n-\treturn lookup_decoration(&revs->children, &commit->object);\n-}\n-\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(ssb, revs, commit);\n-\tfor (cnt = 0; l; l = l->next)\n-\t\tcnt++;\n-\treturn cnt;\n-}\n-\n-#define MAXSG 16\n-\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-\tstruct commit *commit = origin->commit;\n-\tstruct commit_list *sg;\n-\tstruct origin *sg_buf[MAXSG];\n-\tstruct origin *porigin, **sg_origin = sg_buf;\n-\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-\t\tmemset(sg_buf, 0, sizeof(sg_buf));\n-\telse\n-\t\tsg_origin = xcalloc(num_sg, sizeof(*sg_origin));\n-\n-\t/*\n-\t * The first pass looks for unrenamed path to optimize for\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 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(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-\t\t\tint j, same;\n-\n-\t\t\tif (sg_origin[i])\n-\t\t\t\tcontinue;\n-\t\t\tif (parse_commit(p))\n-\t\t\t\tcontinue;\n-\t\t\tporigin = find(sb, p, origin);\n-\t\t\tif (!porigin)\n-\t\t\t\tcontinue;\n-\t\t\tif (!hashcmp(porigin->blob_sha1, origin->blob_sha1)) {\n-\t\t\t\tpass_whole_blame(sb, origin, porigin);\n-\t\t\t\torigin_decref(porigin);\n-\t\t\t\tgoto finish;\n-\t\t\t}\n-\t\t\tfor (j = same = 0; j < i; j++)\n-\t\t\t\tif (sg_origin[j] &&\n-\t\t\t\t    !hashcmp(sg_origin[j]->blob_sha1,\n-\t\t\t\t\t     porigin->blob_sha1)) {\n-\t\t\t\t\tsame = 1;\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\tif (!same)\n-\t\t\t\tsg_origin[i] = porigin;\n-\t\t\telse\n-\t\t\t\torigin_decref(porigin);\n-\t\t}\n-\t}\n-\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-\t\tif (!porigin)\n-\t\t\tcontinue;\n-\t\tif (!origin->previous) {\n-\t\t\torigin_incref(porigin);\n-\t\t\torigin->previous = porigin;\n-\t\t}\n-\t\tif (pass_blame_to_parent(sb, origin, porigin))\n-\t\t\tgoto finish;\n-\t}\n-\n-\t/*\n-\t * Optionally find moves in parents' files.\n-\t */\n-\tif (opt & PICKAXE_BLAME_MOVE)\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-\t\t\tif (!porigin)\n-\t\t\t\tcontinue;\n-\t\t\tif (find_move_in_parent(sb, origin, porigin))\n-\t\t\t\tgoto finish;\n-\t\t}\n-\n-\t/*\n-\t * Optionally find copies from parents' files.\n-\t */\n-\tif (opt & PICKAXE_BLAME_COPY)\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-\t\t\tif (find_copy_in_parent(sb, origin, sg->item,\n-\t\t\t\t\t\tporigin, opt))\n-\t\t\t\tgoto finish;\n-\t\t}\n-\n- finish:\n-\tfor (i = 0; i < num_sg; i++) {\n-\t\tif (sg_origin[i]) {\n-\t\t\tdrop_origin_blob(sg_origin[i]);\n-\t\t\torigin_decref(sg_origin[i]);\n-\t\t}\n-\t}\n-\tdrop_origin_blob(origin);\n-\tif (sg_buf != sg_origin)\n-\t\tfree(sg_origin);\n-}\n-\n-/*\n- * Information on commits, used for output.\n- */\n-struct commit_info\n-{\n-\tconst char *author;\n-\tconst char *author_mail;\n-\tunsigned long author_time;\n-\tconst char *author_tz;\n-\n-\t/* filled only when asked for details */\n-\tconst char *committer;\n-\tconst char *committer_mail;\n-\tunsigned long committer_time;\n-\tconst char *committer_tz;\n-\n-\tconst char *summary;\n-};\n-\n-/*\n- * Parse author/committer line in the commit object buffer\n- */\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-\tint len, tzlen, maillen;\n-\tchar *tmp, *endp, *timepos, *mailpos;\n-\n-\ttmp = strstr(inbuf, what);\n-\tif (!tmp)\n-\t\tgoto error_out;\n-\ttmp += strlen(what);\n-\tendp = strchr(tmp, '\\n');\n-\tif (!endp)\n-\t\tlen = strlen(tmp);\n-\telse\n-\t\tlen = endp - tmp;\n-\tif (person_len <= len) {\n-\terror_out:\n-\t\t/* Ugh */\n-\t\t*tz = \"(unknown)\";\n-\t\tstrcpy(mail, *tz);\n-\t\t*time = 0;\n-\t\treturn;\n-\t}\n-\tmemcpy(person, tmp, len);\n-\n-\ttmp = person;\n-\ttmp += len;\n-\t*tmp = 0;\n-\twhile (*tmp != ' ')\n-\t\ttmp--;\n-\t*tz = tmp+1;\n-\ttzlen = (person+len)-(tmp+1);\n-\n-\t*tmp = 0;\n-\twhile (*tmp != ' ')\n-\t\ttmp--;\n-\t*time = strtoul(tmp, NULL, 10);\n-\ttimepos = tmp;\n-\n-\t*tmp = 0;\n-\twhile (*tmp != ' ')\n-\t\ttmp--;\n-\tmailpos = tmp + 1;\n-\t*tmp = 0;\n-\tmaillen = timepos - tmp;\n-\tmemcpy(mail, mailpos, maillen);\n-\n-\tif (!mailmap->nr)\n-\t\treturn;\n-\n-\t/*\n-\t * mailmap expansion may make the name longer.\n-\t * make room by pushing stuff down.\n-\t */\n-\ttmp = person + person_len - (tzlen + 1);\n-\tmemmove(tmp, *tz, tzlen);\n-\ttmp[tzlen] = 0;\n-\t*tz = tmp;\n-\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-\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-\t\tif (mailpos && mailpos-mail < mail_len - 1) {\n-\t\t\t*mailpos = '>';\n-\t\t\t*(mailpos+1) = '\\0';\n-\t\t}\n-\t}\n-}\n-\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-\tint len;\n-\tchar *tmp, *endp, *reencoded, *message;\n-\tstatic char author_name[1024];\n-\tstatic char author_mail[1024];\n-\tstatic char committer_name[1024];\n-\tstatic char committer_mail[1024];\n-\tstatic char summary_buf[1024];\n-\n-\t/*\n-\t * We've operated without save_commit_buffer, so\n-\t * we now need to populate them for output.\n-\t */\n-\tif (!commit->buffer) {\n-\t\tenum object_type type;\n-\t\tunsigned long size;\n-\t\tcommit->buffer =\n-\t\t\tread_sha1_file(commit->object.sha1, &type, &size);\n-\t\tif (!commit->buffer)\n-\t\t\tdie(\"Cannot read commit %s\",\n-\t\t\t    sha1_to_hex(commit->object.sha1));\n-\t}\n-\treencoded = reencode_commit_message(commit, NULL);\n-\tmessage   = reencoded ? reencoded : commit->buffer;\n-\tret->author = author_name;\n-\tret->author_mail = author_mail;\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-\n-\tif (!detailed) {\n-\t\tfree(reencoded);\n-\t\treturn;\n-\t}\n-\n-\tret->committer = committer_name;\n-\tret->committer_mail = committer_mail;\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-\n-\tret->summary = summary_buf;\n-\ttmp = strstr(message, \"\\n\\n\");\n-\tif (!tmp) {\n-\terror_out:\n-\t\tsprintf(summary_buf, \"(%s)\", sha1_to_hex(commit->object.sha1));\n-\t\tfree(reencoded);\n-\t\treturn;\n-\t}\n-\ttmp += 2;\n-\tendp = strchr(tmp, '\\n');\n-\tif (!endp)\n-\t\tendp = tmp + strlen(tmp);\n-\tlen = endp - tmp;\n-\tif (len >= sizeof(summary_buf) || len == 0)\n-\t\tgoto error_out;\n-\tmemcpy(summary_buf, tmp, len);\n-\tsummary_buf[len] = 0;\n-\tfree(reencoded);\n-}\n-\n-/*\n- * To allow LF and other nonportable characters in pathnames,\n- * they are c-style quoted as needed.\n- */\n-static void write_filename_info(const char *path)\n-{\n-\tprintf(\"filename \");\n-\twrite_name_quoted(path, stdout, '\\n');\n-}\n-\n-/*\n- * Porcelain/Incremental format wants to show a lot of details per\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 string_list *mailmap,\n-\t\t\t\t   struct origin *suspect)\n-{\n-\tstruct commit_info ci;\n-\n-\tif (suspect->commit->object.flags & METAINFO_SHOWN)\n-\t\treturn 0;\n-\n-\tsuspect->commit->object.flags |= METAINFO_SHOWN;\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-\tprintf(\"author-tz %s\\n\", ci.author_tz);\n-\tprintf(\"committer %s\\n\", ci.committer);\n-\tprintf(\"committer-mail %s\\n\", ci.committer_mail);\n-\tprintf(\"committer-time %lu\\n\", ci.committer_time);\n-\tprintf(\"committer-tz %s\\n\", ci.committer_tz);\n-\tprintf(\"summary %s\\n\", ci.summary);\n-\tif (suspect->commit->object.flags & UNINTERESTING)\n-\t\tprintf(\"boundary\\n\");\n-\tif (suspect->previous) {\n-\t\tstruct origin *prev = suspect->previous;\n-\t\tprintf(\"previous %s \", sha1_to_hex(prev->commit->object.sha1));\n-\t\twrite_name_quoted(prev->path, stdout, '\\n');\n-\t}\n-\treturn 1;\n-}\n-\n-/*\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_info *ssb, struct blame_entry *ent)\n-{\n-\tif (ent->guilty)\n-\t\treturn;\n-\tent->guilty = 1;\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(&ssb->mailmap, suspect);\n-\t\twrite_filename_info(suspect->path);\n-\t\tmaybe_flush_or_die(stdout, \"stdout\");\n-\t}\n-}\n-\n-/*\n- * The main loop -- while the scoreboard has lines whose true origin\n- * is still unknown, pick one blame_entry, and allow its current\n- * suspect to pass blames to its parents.\n- */\n-void assign_blame(struct blame_scoreboard *sb, int opt)\n-{\n-\tstruct rev_info *revs = sb->revs;\n-\n-\twhile (1) {\n-\t\tstruct blame_entry *ent;\n-\t\tstruct commit *commit;\n-\t\tstruct origin *suspect = NULL;\n-\n-\t\t/* find one suspect to break down */\n-\t\tfor (ent = sb->ent; !suspect && ent; ent = ent->next)\n-\t\t\tif (!ent->guilty)\n-\t\t\t\tsuspect = ent->suspect;\n-\t\tif (!suspect)\n-\t\t\treturn; /* all done */\n-\n-\t\t/*\n-\t\t * We will use this suspect later in the loop,\n-\t\t * so hold onto it in the meantime.\n-\t\t */\n-\t\torigin_incref(suspect);\n-\t\tcommit = suspect->commit;\n-\t\tif (!commit->object.parsed)\n-\t\t\tparse_commit(commit);\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-\t\telse {\n-\t\t\tcommit->object.flags |= UNINTERESTING;\n-\t\t\tif (commit->object.parsed)\n-\t\t\t\tmark_parents_uninteresting(commit);\n-\t\t}\n-\t\t/* treat root commit as boundary */\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(sb->ssb, ent);\n-\t\torigin_decref(suspect);\n-\n-\t\tif (DEBUG) /* sanity */\n-\t\t\tsanity_check_refcnt(sb);\n-\t}\n-}\n-\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-\tint time_len;\n-\tint tz;\n-\n-\tif (show_raw_time) {\n-\t\tsprintf(time_buf, \"%lu %s\", time, tz_str);\n-\t}\n-\telse {\n-\t\ttz = atoi(tz_str);\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, ' ', ssb->blame_date_width - time_len);\n-\t}\n-\treturn time_buf;\n-}\n-\n-#define OUTPUT_ANNOTATE_COMPAT\t001\n-#define OUTPUT_LONG_OBJECT_NAME\t002\n-#define OUTPUT_RAW_TIMESTAMP\t004\n-#define OUTPUT_PORCELAIN\t010\n-#define OUTPUT_SHOW_NAME\t020\n-#define OUTPUT_SHOW_NUMBER\t040\n-#define OUTPUT_SHOW_SCORE      0100\n-#define OUTPUT_NO_AUTHOR       0200\n-\n-static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent)\n-{\n-\tint cnt;\n-\tconst char *cp;\n-\tstruct origin *suspect = ent->suspect;\n-\tchar hex[41];\n-\n-\tstrcpy(hex, sha1_to_hex(suspect->commit->object.sha1));\n-\tprintf(\"%s%c%d %d %d\\n\",\n-\t       hex,\n-\t       ent->guilty ? ' ' : '*', // purely for debugging\n-\t       ent->s_lno + 1,\n-\t       ent->lno + 1,\n-\t       ent->num_lines);\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-\tcp = nth_line(sb, ent->lno);\n-\tfor (cnt = 0; cnt < ent->num_lines; cnt++) {\n-\t\tchar ch;\n-\t\tif (cnt)\n-\t\t\tprintf(\"%s %d %d\\n\", hex,\n-\t\t\t       ent->s_lno + 1 + cnt,\n-\t\t\t       ent->lno + 1 + cnt);\n-\t\tputchar('\\t');\n-\t\tdo {\n-\t\t\tch = *cp++;\n-\t\t\tputchar(ch);\n-\t\t} while (ch != '\\n' &&\n-\t\t\t cp < sb->final_buf + sb->final_buf_size);\n-\t}\n-}\n-\n-static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt)\n-{\n-\tint cnt;\n-\tconst char *cp;\n-\tstruct origin *suspect = ent->suspect;\n-\tstruct commit_info ci;\n-\tchar hex[41];\n-\tint show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);\n-\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-\tfor (cnt = 0; cnt < ent->num_lines; cnt++) {\n-\t\tchar ch;\n-\t\tint length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;\n-\n-\t\tif (suspect->commit->object.flags & UNINTERESTING) {\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-\t\t\t\tputchar('^');\n-\t\t\t}\n-\t\t}\n-\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(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       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\", 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\", 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 = 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(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       sb->ssb->max_digits, ent->lno + 1 + cnt);\n-\t\t}\n-\t\tdo {\n-\t\t\tch = *cp++;\n-\t\t\tputchar(ch);\n-\t\t} while (ch != '\\n' &&\n-\t\t\t cp < sb->final_buf + sb->final_buf_size);\n-\t}\n-}\n-\n-static void output(struct blame_scoreboard *sb, int option)\n-{\n-\tstruct blame_entry *ent;\n-\n-\tif (option & OUTPUT_PORCELAIN) {\n-\t\tfor (ent = sb->ent; ent; ent = ent->next) {\n-\t\t\tstruct blame_entry *oth;\n-\t\t\tstruct origin *suspect = ent->suspect;\n-\t\t\tstruct commit *commit = suspect->commit;\n-\t\t\tif (commit->object.flags & MORE_THAN_ONE_PATH)\n-\t\t\t\tcontinue;\n-\t\t\tfor (oth = ent->next; oth; oth = oth->next) {\n-\t\t\t\tif ((oth->suspect->commit != commit) ||\n-\t\t\t\t    !strcmp(oth->suspect->path, suspect->path))\n-\t\t\t\t\tcontinue;\n-\t\t\t\tcommit->object.flags |= MORE_THAN_ONE_PATH;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\tfor (ent = sb->ent; ent; ent = ent->next) {\n-\t\tif (option & OUTPUT_PORCELAIN)\n-\t\t\temit_porcelain(sb, ent);\n-\t\telse {\n-\t\t\temit_other(sb, ent, option);\n-\t\t}\n-\t}\n-}\n-\n-/*\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 blame_scoreboard *sb)\n-{\n-\tconst char *buf = sb->final_buf;\n-\tunsigned long len = sb->final_buf_size;\n-\tint num = 0, incomplete = 0, bol = 1;\n-\n-\tif (len && buf[len-1] != '\\n')\n-\t\tincomplete++; /* incomplete line at the end */\n-\twhile (len--) {\n-\t\tif (bol) {\n-\t\t\tsb->lineno = xrealloc(sb->lineno,\n-\t\t\t\t\t      sizeof(int* ) * (num + 1));\n-\t\t\tsb->lineno[num] = buf - sb->final_buf;\n-\t\t\tbol = 0;\n-\t\t}\n-\t\tif (*buf++ == '\\n') {\n-\t\t\tnum++;\n-\t\t\tbol = 1;\n-\t\t}\n-\t}\n-\tsb->lineno = xrealloc(sb->lineno,\n-\t\t\t      sizeof(int* ) * (num + incomplete + 1));\n-\tsb->lineno[num + incomplete] = buf - sb->final_buf;\n-\tsb->num_lines = num + incomplete;\n-\treturn sb->num_lines;\n-}\n-\n-/*\n- * Add phony grafts for use with -S; this is primarily to\n- * support git's cvsserver that wants to give a linear history\n- * to its clients.\n- */\n-static int read_ancestry(const char *graft_file)\n-{\n-\tFILE *fp = fopen(graft_file, \"r\");\n-\tchar buf[1024];\n-\tif (!fp)\n-\t\treturn -1;\n-\twhile (fgets(buf, sizeof(buf), fp)) {\n-\t\t/* The format is just \"Commit Parent1 Parent2 ...\\n\" */\n-\t\tint len = strlen(buf);\n-\t\tstruct commit_graft *graft = read_graft_line(buf, len);\n-\t\tif (graft)\n-\t\t\tregister_commit_graft(graft, 0);\n-\t}\n-\tfclose(fp);\n-\treturn 0;\n-}\n-\n-/*\n- * How many columns do we need to show line numbers in decimal?\n- */\n-static int lineno_width(int lines)\n-{\n-\tint i, width;\n-\n-\tfor (width = 1, i = 10; i <= lines + 1; width++)\n-\t\ti *= 10;\n-\treturn width;\n-}\n-\n-/*\n- * How many columns do we need to show line numbers, authors,\n- * and filenames?\n- */\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-\tunsigned largest_score = 0;\n-\tstruct blame_entry *e;\n-\n-\tfor (e = sb->ent; e; e = e->next) {\n-\t\tstruct origin *suspect = e->suspect;\n-\t\tstruct commit_info ci;\n-\t\tint num;\n-\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 (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(&sb->ssb->mailmap, suspect->commit, &ci, 1);\n-\t\t\tnum = utf8_strwidth(ci.author);\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-\t\t\tlongest_src_lines = num;\n-\t\tnum = e->lno + e->num_lines;\n-\t\tif (longest_dst_lines < num)\n-\t\t\tlongest_dst_lines = num;\n-\t\tif (largest_score < ent_score(sb, e))\n-\t\t\tlargest_score = ent_score(sb, e);\n-\t}\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 blame_scoreboard *sb)\n-{\n-\tint baa = 0;\n-\tstruct blame_entry *ent;\n-\n-\tfor (ent = sb->ent; ent; ent = ent->next) {\n-\t\t/* Nobody should have zero or negative refcnt */\n-\t\tif (ent->suspect->refcnt <= 0) {\n-\t\t\tfprintf(stderr, \"%s in %s has negative refcnt %d\\n\",\n-\t\t\t\tent->suspect->path,\n-\t\t\t\tsha1_to_hex(ent->suspect->commit->object.sha1),\n-\t\t\t\tent->suspect->refcnt);\n-\t\t\tbaa = 1;\n-\t\t}\n-\t}\n-\tif (baa) {\n-\t\tint opt = 0160;\n-\t\tfind_alignment(sb, &opt);\n-\t\toutput(sb, opt);\n-\t\tdie(\"Baa %d!\", baa);\n-\t}\n-}\n-\n-/*\n- * Used for the command line parsing; check if the path exists\n- * in the working tree.\n- */\n-static int has_string_in_work_tree(const char *path)\n-{\n-\tstruct stat st;\n-\treturn !lstat(path, &st);\n-}\n-\n-static unsigned parse_score(const char *arg)\n-{\n-\tchar *end;\n-\tunsigned long score = strtoul(arg, &end, 10);\n-\tif (*end)\n-\t\treturn 0;\n-\treturn score;\n-}\n-\n-static const char *add_prefix(const char *prefix, const char *path)\n-{\n-\treturn prefix_path(prefix, prefix ? strlen(prefix) : 0, path);\n-}\n-\n-/*\n  * Parsing of (comma separated) one item in the -L option\n  */\n static const char *parse_loc(const char *spec,\n-- \n1.5.4.3\n"},{"id":"108082","messageId":"fabb9a1e0903160649o6b576976jeb884e18713c154e@mail.gmail.com","threadId":"18323","inReplyTo":"49BE5466.5050202@gmail.com","subject":"Re: [PATCH2/2] Libify blame","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2009-03-16T13:49:31Z","receivedAt":"2009-03-16T13:49:31Z","isPatch":false,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Mon, Mar 16, 2009 at 14:30, pi song <pi.songs@gmail.com> wrote:\n> This looks like a very HUGE patch but it does merely splitting the current builtin-blame.c into smaller blame.c without changing any logic.\n> This is still WIP. The next patch will organize functions more appropriately.\n\nIt would be nice if you could paste the output of \"git diff -M\" after\nthe triple-dash to show that it is indeed only a small change.\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"108116","messageId":"20090316200418.GM3817@genesis.frugalware.org","threadId":"18323","inReplyTo":"fabb9a1e0903160649o6b576976jeb884e18713c154e@mail.gmail.com","subject":"Re: [PATCH2/2] Libify blame","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2009-03-16T20:04:18Z","receivedAt":"2009-03-16T20:04:18Z","isPatch":false,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Mon, Mar 16, 2009 at 02:49:31PM +0100, Sverre Rabbelier <srabbelier@gmail.com> wrote:\n> It would be nice if you could paste the output of \"git diff -M\" after\n> the triple-dash to show that it is indeed only a small change.\n\nOr just use git format-patch -M?\n"},{"id":"108159","messageId":"1b29507a0903161611w735d3ed2l958eeec9295c7432@mail.gmail.com","threadId":"18323","inReplyTo":"20090316200418.GM3817@genesis.frugalware.org","subject":"Re: [PATCH2/2] Libify blame","fromName":"pi song","fromEmail":"pi.songs@gmail.com","sentAt":"2009-03-16T23:11:05Z","receivedAt":"2009-03-16T23:11:05Z","isPatch":false,"sender":{"key":"pi.songs@gmail.com","avatar":null},"body":"This was generated from git format-patch -M. After applying the patch,\nyou can look in git gui to confirm that I didn't change any logic.\n\nPi Song\n\nOn Tue, Mar 17, 2009 at 7:04 AM, Miklos Vajna <vmiklos@frugalware.org> wrote:\n> On Mon, Mar 16, 2009 at 02:49:31PM +0100, Sverre Rabbelier <srabbelier@gmail.com> wrote:\n>> It would be nice if you could paste the output of \"git diff -M\" after\n>> the triple-dash to show that it is indeed only a small change.\n>\n> Or just use git format-patch -M?\n>\n"},{"id":"108165","messageId":"7vmyblt1nx.fsf@gitster.siamese.dyndns.org","threadId":"18323","inReplyTo":"20090316200418.GM3817@genesis.frugalware.org","subject":"Re: [PATCH2/2] Libify blame","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-03-17T00:48:18Z","receivedAt":"2009-03-17T00:48:18Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@frugalware.org> writes:\n\n> On Mon, Mar 16, 2009 at 02:49:31PM +0100, Sverre Rabbelier <srabbelier@gmail.com> wrote:\n>> It would be nice if you could paste the output of \"git diff -M\" after\n>> the triple-dash to show that it is indeed only a small change.\n>\n> Or just use git format-patch -M?\n\nNo file disappears, so -M alone won't cut it.\n\nI think -B -M may do something interesting, but even then, I suspect you\nwill see a lot of deletions (e.g. you start from two copies of the\noriginal builtin-blame.c and remove different parts to arrive at the new\nbuiltin-blame.c and blame.c) that amounts roughly about the same size as\nthe original builtin-blame.c itself.\n"}]}