{"thread":{"id":"45873","subject":"[RFC PATCH 00/10] Add blame to libgit","startedAt":"2017-05-05T05:27:40Z","lastAt":"2017-05-16T03:21:45Z","messageCount":52,"participants":["Jeff Smith","Ævar Arnfjörð Bjarmason","Jeffrey Smith","Junio C Hamano","Stefan Beller","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":10},"messages":[{"id":"318819","messageId":"20170505052729.7576-1-whydoubt@gmail.com","threadId":"45873","inReplyTo":null,"subject":"[RFC PATCH 00/10] Add blame to libgit","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:19Z","receivedAt":"2017-05-05T05:27:40Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"In adding blame functionality to cgit, I do not wish to duplicate huge\nportions of builtin/blame.c there.  I would rather shift its core\nfunctionality into libgit.a -- which cgit already uses -- and in cgit I\ncan focus on the web presentation aspect.\n\nI was able to split three separate cohesive pieces from blame.c into\nlibgit.  The functionality left in blame.c mostly relates to terminal\npresentation.\n\nJeff Smith (10):\n  Remove unneeded dependency on blob.h from blame\n  Move textconv_object to be with other textconv methods\n  Add some missing definitions to header files\n  Remove unused parameter from get_origin()\n  Split blame origin into its own file\n  Move fake_working_tree_commit() to lib\n  Break out scoreboard a little better\n  Split blame scoreboard into its own file\n  Break out scoreboard init and setup\n  Move scoreboard init and setup to lib\n\n Makefile           |    3 +\n builtin.h          |    2 -\n builtin/blame.c    | 2045 ++--------------------------------------------------\n builtin/cat-file.c |    1 +\n commit-fake.c      |  200 +++++\n commit-fake.h      |    9 +\n diff.c             |   23 +\n diff.h             |    7 +\n object.h           |    2 +\n origin.c           |   62 ++\n origin.h           |  101 +++\n pathspec.h         |    4 +\n refs.h             |    3 +\n scoreboard.c       | 1569 ++++++++++++++++++++++++++++++++++++++++\n scoreboard.h       |   77 ++\n tree-walk.h        |    2 +\n 16 files changed, 2108 insertions(+), 2002 deletions(-)\n create mode 100644 commit-fake.c\n create mode 100644 commit-fake.h\n create mode 100644 origin.c\n create mode 100644 origin.h\n create mode 100644 scoreboard.c\n create mode 100644 scoreboard.h\n\n-- \n2.9.3\n\n"},{"id":"318820","messageId":"20170505052729.7576-2-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH 01/10] Remove unneeded dependency on blob.h from blame","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:20Z","receivedAt":"2017-05-05T05:27:41Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin/blame.c | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 07506a3..42c56eb 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -8,7 +8,6 @@\n #include \"cache.h\"\n #include \"refs.h\"\n #include \"builtin.h\"\n-#include \"blob.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"tree-walk.h\"\n-- \n2.9.3\n\n"},{"id":"318821","messageId":"20170505052729.7576-4-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH 03/10] Add some missing definitions to header files","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:22Z","receivedAt":"2017-05-05T05:27:47Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n object.h    | 2 ++\n pathspec.h  | 4 ++++\n refs.h      | 3 +++\n tree-walk.h | 2 ++\n 4 files changed, 11 insertions(+)\n\ndiff --git a/object.h b/object.h\nindex f52957d..9737582 100644\n--- a/object.h\n+++ b/object.h\n@@ -1,6 +1,8 @@\n #ifndef OBJECT_H\n #define OBJECT_H\n \n+#include \"cache.h\"\n+\n struct object_list {\n \tstruct object *item;\n \tstruct object_list *next;\ndiff --git a/pathspec.h b/pathspec.h\nindex 55e9769..ea18e24 100644\n--- a/pathspec.h\n+++ b/pathspec.h\n@@ -1,6 +1,10 @@\n #ifndef PATHSPEC_H\n #define PATHSPEC_H\n \n+#include <stddef.h>\n+#include <string.h>\n+#include <strings.h>\n+\n /* Pathspec magic */\n #define PATHSPEC_FROMTOP\t(1<<0)\n #define PATHSPEC_MAXDEPTH\t(1<<1)\ndiff --git a/refs.h b/refs.h\nindex 07cf4cd..e9d19fd 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -1,6 +1,9 @@\n #ifndef REFS_H\n #define REFS_H\n \n+#include <stdio.h>\n+#include <string.h>\n+\n struct object_id;\n struct ref_store;\n struct strbuf;\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 68bb78b..8b0fccd 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -1,6 +1,8 @@\n #ifndef TREE_WALK_H\n #define TREE_WALK_H\n \n+struct strbuf;\n+\n struct name_entry {\n \tconst struct object_id *oid;\n \tconst char *path;\n-- \n2.9.3\n\n"},{"id":"318822","messageId":"20170505052729.7576-6-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH 05/10] Split blame origin into its own file","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:24Z","receivedAt":"2017-05-05T05:27:49Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n Makefile        |   1 +\n builtin/blame.c | 151 +-------------------------------------------------------\n origin.c        |  62 +++++++++++++++++++++++\n origin.h        | 101 +++++++++++++++++++++++++++++++++++++\n 4 files changed, 165 insertions(+), 150 deletions(-)\n create mode 100644 origin.c\n create mode 100644 origin.h\n\ndiff --git a/Makefile b/Makefile\nindex e35542e..8cbb56c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -791,6 +791,7 @@ LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n LIB_OBJS += object.o\n LIB_OBJS += oidset.o\n+LIB_OBJS += origin.o\n LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-bitmap-write.o\n LIB_OBJS += pack-check.o\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex cc46f56..7ee84c1 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -28,6 +28,7 @@\n #include \"line-log.h\"\n #include \"dir.h\"\n #include \"progress.h\"\n+#include \"origin.h\"\n \n static char blame_usage[] = N_(\"git blame [<options>] [<rev-opts>] [<rev>] [--] <file>\");\n \n@@ -84,50 +85,6 @@ static unsigned blame_copy_score;\n #define METAINFO_SHOWN\t\t(1u<<12)\n #define MORE_THAN_ONE_PATH\t(1u<<13)\n \n-/*\n- * One blob in a commit that is being suspected\n- */\n-struct origin {\n-\tint refcnt;\n-\t/* Record preceding blame record for this blob */\n-\tstruct origin *previous;\n-\t/* origins are put in a list linked via `next' hanging off the\n-\t * corresponding commit's util field in order to make finding\n-\t * them fast.  The presence in this chain does not count\n-\t * towards the origin's reference count.  It is tempting to\n-\t * let it count as long as the commit is pending examination,\n-\t * but even under circumstances where the commit will be\n-\t * present multiple times in the priority queue of unexamined\n-\t * commits, processing the first instance will not leave any\n-\t * work requiring the origin data for the second instance.  An\n-\t * interspersed commit changing that would have to be\n-\t * preexisting with a different ancestry and with the same\n-\t * commit date in order to wedge itself between two instances\n-\t * of the same commit in the priority queue _and_ produce\n-\t * blame entries relevant for it.  While we don't want to let\n-\t * us get tripped up by this case, it certainly does not seem\n-\t * worth optimizing for.\n-\t */\n-\tstruct origin *next;\n-\tstruct commit *commit;\n-\t/* `suspects' contains blame entries that may be attributed to\n-\t * this origin's commit or to parent commits.  When a commit\n-\t * is being processed, all suspects will be moved, either by\n-\t * assigning them to an origin in a different commit, or by\n-\t * shipping them to the scoreboard's ent list because they\n-\t * cannot be attributed to a different commit.\n-\t */\n-\tstruct blame_entry *suspects;\n-\tmmfile_t file;\n-\tstruct object_id blob_oid;\n-\tunsigned mode;\n-\t/* guilty gets set when shipping any suspects to the final\n-\t * blame list instead of other commits\n-\t */\n-\tchar guilty;\n-\tchar path[FLEX_ARRAY];\n-};\n-\n struct progress_info {\n \tstruct progress *progress;\n \tint blamed_lines;\n@@ -176,39 +133,6 @@ static void fill_origin_blob(struct diff_options *opt,\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\tstruct origin *p, *l = NULL;\n-\t\tif (o->previous)\n-\t\t\torigin_decref(o->previous);\n-\t\tfree(o->file.ptr);\n-\t\t/* Should be present exactly once in commit chain */\n-\t\tfor (p = o->commit->util; p; l = p, p = p->next) {\n-\t\t\tif (p == o) {\n-\t\t\t\tif (l)\n-\t\t\t\t\tl->next = p->next;\n-\t\t\t\telse\n-\t\t\t\t\to->commit->util = p->next;\n-\t\t\t\tfree(o);\n-\t\t\t\treturn;\n-\t\t\t}\n-\t\t}\n-\t\tdie(\"internal error in blame::origin_decref\");\n-\t}\n-}\n-\n static void drop_origin_blob(struct origin *o)\n {\n \tif (o->file.ptr) {\n@@ -218,40 +142,6 @@ static void drop_origin_blob(struct origin *o)\n }\n \n /*\n- * Each group of lines is described by a blame_entry; it can be split\n- * as we pass blame to the parents.  They are arranged in linked lists\n- * kept as `suspects' of some unprocessed origin, or entered (when the\n- * blame origin has been finalized) into the scoreboard structure.\n- * While the scoreboard structure is only sorted at the end of\n- * processing (according to final image line number), the lists\n- * attached to an origin are sorted by the target line number.\n- */\n-struct blame_entry {\n-\tstruct blame_entry *next;\n-\n-\t/* the first line of this group in the final image;\n-\t * internally all line numbers are 0 based.\n-\t */\n-\tint lno;\n-\n-\t/* how many lines this group has */\n-\tint num_lines;\n-\n-\t/* the commit that introduced this group into the final image */\n-\tstruct origin *suspect;\n-\n-\t/* the line number of the first line of this group in the\n-\t * suspect's file; internally all line numbers are 0 based.\n-\t */\n-\tint s_lno;\n-\n-\t/* how significant this entry is -- cached to avoid\n-\t * scanning the lines over and over.\n-\t */\n-\tunsigned score;\n-};\n-\n-/*\n  * Any merge of blames happens on lists of blames that arrived via\n  * different parents in a single suspect.  In this case, we want to\n  * sort according to the suspect line numbers as opposed to the final\n@@ -429,45 +319,6 @@ static void queue_blames(struct scoreboard *sb, struct origin *porigin,\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-\tFLEX_ALLOC_STR(o, path, path);\n-\to->commit = commit;\n-\to->refcnt = 1;\n-\to->next = commit->util;\n-\tcommit->util = o;\n-\treturn o;\n-}\n-\n-/*\n- * Locate an existing origin or create a new one.\n- * This moves the origin to front position in the commit util list.\n- */\n-static struct origin *get_origin(struct commit *commit, const char *path)\n-{\n-\tstruct origin *o, *l;\n-\n-\tfor (o = commit->util, l = NULL; o; l = o, o = o->next) {\n-\t\tif (!strcmp(o->path, path)) {\n-\t\t\t/* bump to front */\n-\t\t\tif (l) {\n-\t\t\t\tl->next = o->next;\n-\t\t\t\to->next = commit->util;\n-\t\t\t\tcommit->util = o;\n-\t\t\t}\n-\t\t\treturn origin_incref(o);\n-\t\t}\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\ndiff --git a/origin.c b/origin.c\nnew file mode 100644\nindex 0000000..2ec25c7\n--- /dev/null\n+++ b/origin.c\n@@ -0,0 +1,62 @@\n+#include \"origin.h\"\n+\n+void origin_decref(struct origin *o)\n+{\n+\tif (o && --o->refcnt <= 0) {\n+\t\tstruct origin *p, *l = NULL;\n+\t\tif (o->previous)\n+\t\t\torigin_decref(o->previous);\n+\t\tfree(o->file.ptr);\n+\t\t/* Should be present exactly once in commit chain */\n+\t\tfor (p = o->commit->util; p; l = p, p = p->next) {\n+\t\t\tif (p == o) {\n+\t\t\t\tif (l)\n+\t\t\t\t\tl->next = p->next;\n+\t\t\t\telse\n+\t\t\t\t\to->commit->util = p->next;\n+\t\t\t\tfree(o);\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t}\n+\t\tdie(\"internal error in blame::origin_decref\");\n+\t}\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+\tFLEX_ALLOC_STR(o, path, path);\n+\to->commit = commit;\n+\to->refcnt = 1;\n+\to->next = commit->util;\n+\tcommit->util = o;\n+\treturn o;\n+}\n+\n+/*\n+ * Locate an existing origin or create a new one.\n+ * This moves the origin to front position in the commit util list.\n+ */\n+struct origin *get_origin(struct commit *commit, const char *path)\n+{\n+\tstruct origin *o, *l;\n+\n+\tfor (o = commit->util, l = NULL; o; l = o, o = o->next) {\n+\t\tif (!strcmp(o->path, path)) {\n+\t\t\t/* bump to front */\n+\t\t\tif (l) {\n+\t\t\t\tl->next = o->next;\n+\t\t\t\to->next = commit->util;\n+\t\t\t\tcommit->util = o;\n+\t\t\t}\n+\t\t\treturn origin_incref(o);\n+\t\t}\n+\t}\n+\treturn make_origin(commit, path);\n+}\ndiff --git a/origin.h b/origin.h\nnew file mode 100644\nindex 0000000..a189b78\n--- /dev/null\n+++ b/origin.h\n@@ -0,0 +1,101 @@\n+#ifndef ORIGIN_H\n+#define ORIGIN_H\n+\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"xdiff-interface.h\"\n+\n+/*\n+ * One blob in a commit that is being suspected\n+ */\n+struct origin {\n+\tint refcnt;\n+\t/* Record preceding blame record for this blob */\n+\tstruct origin *previous;\n+\t/* origins are put in a list linked via `next' hanging off the\n+\t * corresponding commit's util field in order to make finding\n+\t * them fast.  The presence in this chain does not count\n+\t * towards the origin's reference count.  It is tempting to\n+\t * let it count as long as the commit is pending examination,\n+\t * but even under circumstances where the commit will be\n+\t * present multiple times in the priority queue of unexamined\n+\t * commits, processing the first instance will not leave any\n+\t * work requiring the origin data for the second instance.  An\n+\t * interspersed commit changing that would have to be\n+\t * preexisting with a different ancestry and with the same\n+\t * commit date in order to wedge itself between two instances\n+\t * of the same commit in the priority queue _and_ produce\n+\t * blame entries relevant for it.  While we don't want to let\n+\t * us get tripped up by this case, it certainly does not seem\n+\t * worth optimizing for.\n+\t */\n+\tstruct origin *next;\n+\tstruct commit *commit;\n+\t/* `suspects' contains blame entries that may be attributed to\n+\t * this origin's commit or to parent commits.  When a commit\n+\t * is being processed, all suspects will be moved, either by\n+\t * assigning them to an origin in a different commit, or by\n+\t * shipping them to the scoreboard's ent list because they\n+\t * cannot be attributed to a different commit.\n+\t */\n+\tstruct blame_entry *suspects;\n+\tmmfile_t file;\n+\tstruct object_id blob_oid;\n+\tunsigned mode;\n+\t/* guilty gets set when shipping any suspects to the final\n+\t * blame list instead of other commits\n+\t */\n+\tchar guilty;\n+\tchar path[FLEX_ARRAY];\n+};\n+\n+/*\n+ * Each group of lines is described by a blame_entry; it can be split\n+ * as we pass blame to the parents.  They are arranged in linked lists\n+ * kept as `suspects' of some unprocessed origin, or entered (when the\n+ * blame origin has been finalized) into the scoreboard structure.\n+ * While the scoreboard structure is only sorted at the end of\n+ * processing (according to final image line number), the lists\n+ * attached to an origin are sorted by the target line number.\n+ */\n+struct blame_entry {\n+\tstruct blame_entry *next;\n+\n+\t/* the first line of this group in the final image;\n+\t * internally all line numbers are 0 based.\n+\t */\n+\tint lno;\n+\n+\t/* how many lines this group has */\n+\tint num_lines;\n+\n+\t/* the commit that introduced this group into the final image */\n+\tstruct origin *suspect;\n+\n+\t/* the line number of the first line of this group in the\n+\t * suspect's file; internally all line numbers are 0 based.\n+\t */\n+\tint s_lno;\n+\n+\t/* how significant this entry is -- cached to avoid\n+\t * scanning the lines over and over.\n+\t */\n+\tunsigned score;\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+void origin_decref(struct origin *o);\n+\n+struct origin *make_origin(struct commit *commit, const char *path);\n+struct origin *get_origin(struct commit *commit, const char *path);\n+\n+#endif /* ORIGIN_H */\n-- \n2.9.3\n\n"},{"id":"318823","messageId":"20170505052729.7576-8-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH 07/10] Break out scoreboard a little better","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:26Z","receivedAt":"2017-05-05T05:28:01Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin/blame.c | 149 +++++++++++++++++++++++++++++++++++---------------------\n 1 file changed, 93 insertions(+), 56 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex c873cc2..dc7600c 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -62,11 +62,6 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP;\n #define DEBUG 0\n #endif\n \n-/* stats */\n-static int num_read_blob;\n-static int num_get_patch;\n-static int num_commits;\n-\n #define PICKAXE_BLAME_MOVE\t\t01\n #define PICKAXE_BLAME_COPY\t\t02\n #define PICKAXE_BLAME_COPY_HARDER\t04\n@@ -91,7 +86,7 @@ struct progress_info {\n };\n \n static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n-\t\t      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)\n+\t\t      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts)\n {\n \txpparam_t xpp = {0};\n \txdemitconf_t xecfg = {0};\n@@ -108,13 +103,13 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n  * diff machinery\n  */\n static void fill_origin_blob(struct diff_options *opt,\n-\t\t\t     struct origin *o, mmfile_t *file)\n+\t\t\t     struct origin *o, mmfile_t *file, int *num_read_blob)\n {\n \tif (!o->file.ptr) {\n \t\tenum object_type type;\n \t\tunsigned long file_size;\n \n-\t\tnum_read_blob++;\n+\t\t(*num_read_blob)++;\n \t\tif (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&\n \t\t    textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))\n \t\t\t;\n@@ -265,9 +260,36 @@ struct scoreboard {\n \t/* look-up a line in the final buffer */\n \tint num_lines;\n \tint *lineno;\n+\n+\t/*\n+\t * blame for a blame_entry with score lower than these thresholds\n+\t * is not passed to the parent using move/copy logic.\n+\t */\n+\tunsigned blame_move_score;\n+\tunsigned blame_copy_score;\n+\n+\t/* stats */\n+\tint num_read_blob;\n+\tint num_get_patch;\n+\tint num_commits;\n+\n+\t/* options */\n+\tint show_root;\n+\tint reverse;\n+\tint xdl_opts;\n+\tint no_whole_file_rename;\n+\n+\t/* callbacks */\n+\tvoid(*sanity_check)(struct scoreboard *);\n+\tvoid(*found_guilty_entry)(struct blame_entry *, void *);\n+\n+\tvoid *found_guilty_entry_data;\n };\n \n-static void sanity_check_refcnt(struct scoreboard *);\n+static void blame_sort_final(struct scoreboard *sb)\n+{\n+\tsb->ent = blame_sort(sb->ent, compare_blame_final);\n+}\n \n /*\n  * If two blame entries that are next to each other came from\n@@ -290,8 +312,8 @@ static void coalesce(struct scoreboard *sb)\n \t\t}\n \t}\n \n-\tif (DEBUG) /* sanity */\n-\t\tsanity_check_refcnt(sb);\n+\tif (sb->sanity_check) /* sanity */\n+\t\tsb->sanity_check(sb);\n }\n \n /*\n@@ -787,11 +809,11 @@ static void pass_blame_to_parent(struct scoreboard *sb,\n \td.offset = 0;\n \td.dstq = &newdest; d.srcq = &target->suspects;\n \n-\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p);\n-\tfill_origin_blob(&sb->revs->diffopt, target, &file_o);\n-\tnum_get_patch++;\n+\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n+\tfill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob);\n+\tsb->num_get_patch++;\n \n-\tif (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d))\n+\tif (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts))\n \t\tdie(\"unable to generate diff (%s -> %s)\",\n \t\t    oid_to_hex(&parent->commit->object.oid),\n \t\t    oid_to_hex(&target->commit->object.oid));\n@@ -940,7 +962,7 @@ static void find_copy_in_blob(struct scoreboard *sb,\n \t * file_p partially may match that image.\n \t */\n \tmemset(split, 0, sizeof(struct blame_entry [3]));\n-\tif (diff_hunks(file_p, &file_o, handle_split_cb, &d))\n+\tif (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts))\n \t\tdie(\"unable to generate diff (%s)\",\n \t\t    oid_to_hex(&parent->commit->object.oid));\n \t/* remainder, if any, all match the preimage */\n@@ -993,7 +1015,7 @@ static void find_move_in_parent(struct scoreboard *sb,\n \tif (!unblamed)\n \t\treturn; /* nothing remains for this target */\n \n-\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p);\n+\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n \tif (!file_p.ptr)\n \t\treturn;\n \n@@ -1009,7 +1031,7 @@ static void find_move_in_parent(struct scoreboard *sb,\n \t\t\tnext = e->next;\n \t\t\tfind_copy_in_blob(sb, e, parent, split, &file_p);\n \t\t\tif (split[1].suspect &&\n-\t\t\t    blame_move_score < ent_score(sb, &split[1])) {\n+\t\t\t    sb->blame_move_score < ent_score(sb, &split[1])) {\n \t\t\t\tsplit_blame(blamed, &unblamedtail, split, e);\n \t\t\t} else {\n \t\t\t\te->next = leftover;\n@@ -1018,7 +1040,7 @@ static void find_move_in_parent(struct scoreboard *sb,\n \t\t\tdecref_split(split);\n \t\t}\n \t\t*unblamedtail = NULL;\n-\t\ttoosmall = filter_small(sb, toosmall, &unblamed, blame_move_score);\n+\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->blame_move_score);\n \t} while (unblamed);\n \ttarget->suspects = reverse_blame(leftover, NULL);\n }\n@@ -1122,7 +1144,7 @@ static void find_copy_in_parent(struct scoreboard *sb,\n \t\t\tnorigin = get_origin(parent, p->one->path);\n \t\t\toidcpy(&norigin->blob_oid, &p->one->oid);\n \t\t\tnorigin->mode = p->one->mode;\n-\t\t\tfill_origin_blob(&sb->revs->diffopt, norigin, &file_p);\n+\t\t\tfill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob);\n \t\t\tif (!file_p.ptr)\n \t\t\t\tcontinue;\n \n@@ -1139,7 +1161,7 @@ static void find_copy_in_parent(struct scoreboard *sb,\n \t\tfor (j = 0; j < num_ents; j++) {\n \t\t\tstruct blame_entry *split = blame_list[j].split;\n \t\t\tif (split[1].suspect &&\n-\t\t\t    blame_copy_score < ent_score(sb, &split[1])) {\n+\t\t\t    sb->blame_copy_score < ent_score(sb, &split[1])) {\n \t\t\t\tsplit_blame(blamed, &unblamedtail, split,\n \t\t\t\t\t    blame_list[j].ent);\n \t\t\t} else {\n@@ -1150,7 +1172,7 @@ static void find_copy_in_parent(struct scoreboard *sb,\n \t\t}\n \t\tfree(blame_list);\n \t\t*unblamedtail = NULL;\n-\t\ttoosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score);\n+\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->blame_copy_score);\n \t} while (unblamed);\n \ttarget->suspects = reverse_blame(leftover, NULL);\n \tdiff_flush(&diff_opts);\n@@ -1186,7 +1208,8 @@ static void pass_whole_blame(struct scoreboard *sb,\n  * \"parent\" (and \"porigin\"), but what we mean is to find scapegoat to\n  * exonerate ourselves.\n  */\n-static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)\n+static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit,\n+\t\t\t\t\tint reverse)\n {\n \tif (!reverse) {\n \t\tif (revs->first_parent_only &&\n@@ -1200,9 +1223,9 @@ static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit\n \treturn lookup_decoration(&revs->children, &commit->object);\n }\n \n-static int num_scapegoats(struct rev_info *revs, struct commit *commit)\n+static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse)\n {\n-\tstruct commit_list *l = first_scapegoat(revs, commit);\n+\tstruct commit_list *l = first_scapegoat(revs, commit, reverse);\n \treturn commit_list_count(l);\n }\n \n@@ -1240,7 +1263,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \tstruct blame_entry *toosmall = NULL;\n \tstruct blame_entry *blames, **blametail = &blames;\n \n-\tnum_sg = num_scapegoats(revs, commit);\n+\tnum_sg = num_scapegoats(revs, commit, sb->reverse);\n \tif (!num_sg)\n \t\tgoto finish;\n \telse if (num_sg < ARRAY_SIZE(sg_buf))\n@@ -1252,12 +1275,12 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\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 - no_whole_file_rename; pass++) {\n+\tfor (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) {\n \t\tstruct origin *(*find)(struct scoreboard *,\n \t\t\t\t       struct commit *, struct origin *);\n \t\tfind = pass ? find_rename : find_origin;\n \n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n \t\t     i < num_sg && sg;\n \t\t     sg = sg->next, i++) {\n \t\t\tstruct commit *p = sg->item;\n@@ -1288,8 +1311,8 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t\t}\n \t}\n \n-\tnum_commits++;\n-\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\tsb->num_commits++;\n+\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n \t     i < num_sg && sg;\n \t     sg = sg->next, i++) {\n \t\tstruct origin *porigin = sg_origin[i];\n@@ -1308,9 +1331,9 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t * Optionally find moves in parents' files.\n \t */\n \tif (opt & PICKAXE_BLAME_MOVE) {\n-\t\tfilter_small(sb, &toosmall, &origin->suspects, blame_move_score);\n+\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->blame_move_score);\n \t\tif (origin->suspects) {\n-\t\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n \t\t\t     i < num_sg && sg;\n \t\t\t     sg = sg->next, i++) {\n \t\t\t\tstruct origin *porigin = sg_origin[i];\n@@ -1327,17 +1350,17 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t * Optionally find copies from parents' files.\n \t */\n \tif (opt & PICKAXE_BLAME_COPY) {\n-\t\tif (blame_copy_score > blame_move_score)\n-\t\t\tfilter_small(sb, &toosmall, &origin->suspects, blame_copy_score);\n-\t\telse if (blame_copy_score < blame_move_score) {\n+\t\tif (sb->blame_copy_score > sb->blame_move_score)\n+\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->blame_copy_score);\n+\t\telse if (sb->blame_copy_score < sb->blame_move_score) {\n \t\t\torigin->suspects = blame_merge(origin->suspects, toosmall);\n \t\t\ttoosmall = NULL;\n-\t\t\tfilter_small(sb, &toosmall, &origin->suspects, blame_copy_score);\n+\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->blame_copy_score);\n \t\t}\n \t\tif (!origin->suspects)\n \t\t\tgoto finish;\n \n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n \t\t     i < num_sg && sg;\n \t\t     sg = sg->next, i++) {\n \t\t\tstruct origin *porigin = sg_origin[i];\n@@ -1566,9 +1589,10 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)\n  * The blame_entry is found to be guilty for the range.\n  * Show it in incremental output.\n  */\n-static void found_guilty_entry(struct blame_entry *ent,\n-\t\t\t   struct progress_info *pi)\n+static void found_guilty_entry(struct blame_entry *ent, void *data)\n {\n+\tstruct progress_info *pi = (struct progress_info *)data;\n+\n \tif (incremental) {\n \t\tstruct origin *suspect = ent->suspect;\n \n@@ -1591,11 +1615,6 @@ static void assign_blame(struct scoreboard *sb, int opt)\n {\n \tstruct rev_info *revs = sb->revs;\n \tstruct commit *commit = prio_queue_get(&sb->commits);\n-\tstruct progress_info pi = { NULL, 0 };\n-\n-\tif (show_progress)\n-\t\tpi.progress = start_progress_delay(_(\"Blaming lines\"),\n-\t\t\t\t\t\t   sb->num_lines, 50, 1);\n \n \twhile (commit) {\n \t\tstruct blame_entry *ent;\n@@ -1618,7 +1637,7 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \t\t */\n \t\torigin_incref(suspect);\n \t\tparse_commit(commit);\n-\t\tif (reverse ||\n+\t\tif (sb->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@@ -1628,7 +1647,7 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \t\t\t\tmark_parents_uninteresting(commit);\n \t\t}\n \t\t/* treat root commit as boundary */\n-\t\tif (!commit->parents && !show_root)\n+\t\tif (!commit->parents && !sb->show_root)\n \t\t\tcommit->object.flags |= UNINTERESTING;\n \n \t\t/* Take responsibility for the remaining entries */\n@@ -1637,7 +1656,8 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \t\t\tsuspect->guilty = 1;\n \t\t\tfor (;;) {\n \t\t\t\tstruct blame_entry *next = ent->next;\n-\t\t\t\tfound_guilty_entry(ent, &pi);\n+\t\t\t\tif (sb->found_guilty_entry)\n+\t\t\t\t\tsb->found_guilty_entry(ent, sb->found_guilty_entry_data);\n \t\t\t\tif (next) {\n \t\t\t\t\tent = next;\n \t\t\t\t\tcontinue;\n@@ -1650,11 +1670,9 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \t\t}\n \t\torigin_decref(suspect);\n \n-\t\tif (DEBUG) /* sanity */\n-\t\t\tsanity_check_refcnt(sb);\n+\t\tif (sb->sanity_check) /* sanity */\n+\t\t\tsb->sanity_check(sb);\n \t}\n-\n-\tstop_progress(&pi.progress);\n }\n \n static const char *format_time(unsigned long time, const char *tz_str,\n@@ -2187,6 +2205,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tchar *final_commit_name = NULL;\n \tenum object_type type;\n \tstruct commit *final_commit = NULL;\n+\tstruct progress_info pi = { NULL, 0 };\n \n \tstruct string_list range_list = STRING_LIST_INIT_NODUP;\n \tint output_option = 0, opt = 0;\n@@ -2476,7 +2495,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\t\t    oid_to_hex(&o->blob_oid),\n \t\t\t    path);\n \t}\n-\tnum_read_blob++;\n+\tsb.num_read_blob++;\n \tlno = prepare_lines(&sb);\n \n \tif (lno && !range_list.nr)\n@@ -2529,10 +2548,28 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tsb.ent = NULL;\n \tsb.path = path;\n \n+\tsb.blame_move_score = blame_move_score;\n+\tsb.blame_copy_score = blame_copy_score;\n+\n+\tsb.sanity_check = DEBUG ? &sanity_check_refcnt : NULL;\n+\n+\tsb.show_root = show_root;\n+\tsb.reverse = reverse;\n+\tsb.xdl_opts = xdl_opts;\n+\tsb.no_whole_file_rename = no_whole_file_rename;\n+\n \tread_mailmap(&mailmap, NULL);\n \n+\tsb.found_guilty_entry = &found_guilty_entry;\n+\tsb.found_guilty_entry_data = &pi;\n+\tif (show_progress)\n+\t\tpi.progress = start_progress_delay(_(\"Blaming lines\"),\n+\t\t\t\t\t\t   sb.num_lines, 50, 1);\n+\n \tassign_blame(&sb, opt);\n \n+\tstop_progress(&pi.progress);\n+\n \tif (!incremental)\n \t\tsetup_pager();\n \n@@ -2541,7 +2578,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (incremental)\n \t\treturn 0;\n \n-\tsb.ent = blame_sort(sb.ent, compare_blame_final);\n+\tblame_sort_final(&sb);\n \n \tcoalesce(&sb);\n \n@@ -2557,9 +2594,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (show_stats) {\n-\t\tprintf(\"num read blob: %d\\n\", num_read_blob);\n-\t\tprintf(\"num get patch: %d\\n\", num_get_patch);\n-\t\tprintf(\"num commits: %d\\n\", num_commits);\n+\t\tprintf(\"num read blob: %d\\n\", sb.num_read_blob);\n+\t\tprintf(\"num get patch: %d\\n\", sb.num_get_patch);\n+\t\tprintf(\"num commits: %d\\n\", sb.num_commits);\n \t}\n \treturn 0;\n }\n-- \n2.9.3\n\n"},{"id":"318824","messageId":"20170505052729.7576-10-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH 09/10] Break out scoreboard init and setup","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:28Z","receivedAt":"2017-05-05T05:28:05Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin/blame.c | 264 +++++++++++++++++++++++++++++---------------------------\n scoreboard.h    |   1 +\n 2 files changed, 140 insertions(+), 125 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 0af99e3..3bde5a6 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -731,14 +731,8 @@ static struct commit *find_single_final(struct rev_info *revs,\n \treturn found;\n }\n \n-static char *prepare_final(struct scoreboard *sb)\n-{\n-\tconst char *name;\n-\tsb->final = find_single_final(sb->revs, &name);\n-\treturn xstrdup_or_null(name);\n-}\n-\n-static const char *dwim_reverse_initial(struct scoreboard *sb)\n+static struct commit *dwim_reverse_initial(struct rev_info *revs,\n+\t\t\t\t\t   const char **name_p)\n {\n \t/*\n \t * DWIM \"git blame --reverse ONE -- PATH\" as\n@@ -749,11 +743,11 @@ static const char *dwim_reverse_initial(struct scoreboard *sb)\n \tstruct commit *head_commit;\n \tunsigned char head_sha1[20];\n \n-\tif (sb->revs->pending.nr != 1)\n+\tif (revs->pending.nr != 1)\n \t\treturn NULL;\n \n \t/* Is that sole rev a committish? */\n-\tobj = sb->revs->pending.objects[0].item;\n+\tobj = revs->pending.objects[0].item;\n \tobj = deref_tag(obj, NULL, 0);\n \tif (obj->type != OBJ_COMMIT)\n \t\treturn NULL;\n@@ -767,17 +761,19 @@ static const char *dwim_reverse_initial(struct scoreboard *sb)\n \n \t/* Turn \"ONE\" into \"ONE..HEAD\" then */\n \tobj->flags |= UNINTERESTING;\n-\tadd_pending_object(sb->revs, &head_commit->object, \"HEAD\");\n+\tadd_pending_object(revs, &head_commit->object, \"HEAD\");\n \n-\tsb->final = (struct commit *)obj;\n-\treturn sb->revs->pending.objects[0].name;\n+\tif (name_p)\n+\t\t*name_p = revs->pending.objects[0].name;\n+\treturn (struct commit *)obj;\n }\n \n-static char *prepare_initial(struct scoreboard *sb)\n+static struct commit *find_single_initial(struct rev_info *revs,\n+\t\t\t\t\t  const char **name_p)\n {\n \tint i;\n-\tconst char *final_commit_name = NULL;\n-\tstruct rev_info *revs = sb->revs;\n+\tstruct commit *found = NULL;\n+\tconst char *name = NULL;\n \n \t/*\n \t * There must be one and only one negative commit, and it must be\n@@ -790,19 +786,127 @@ static char *prepare_initial(struct scoreboard *sb)\n \t\tobj = deref_tag(obj, NULL, 0);\n \t\tif (obj->type != OBJ_COMMIT)\n \t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n-\t\tif (sb->final)\n+\t\tif (found)\n \t\t\tdie(\"More than one commit to dig up from, %s and %s?\",\n-\t\t\t    revs->pending.objects[i].name,\n-\t\t\t    final_commit_name);\n-\t\tsb->final = (struct commit *) obj;\n-\t\tfinal_commit_name = revs->pending.objects[i].name;\n+\t\t\t    revs->pending.objects[i].name, name);\n+\t\tfound = (struct commit *) obj;\n+\t\tname = revs->pending.objects[i].name;\n \t}\n \n-\tif (!final_commit_name)\n-\t\tfinal_commit_name = dwim_reverse_initial(sb);\n-\tif (!final_commit_name)\n+\tif (!name)\n+\t\tfound = dwim_reverse_initial(revs, &name);\n+\tif (!name)\n \t\tdie(\"No commit to dig up from?\");\n-\treturn xstrdup(final_commit_name);\n+\n+\tif (name_p)\n+\t\t*name_p = name;\n+\treturn found;\n+}\n+\n+void init_scoreboard(struct scoreboard *sb)\n+{\n+\tmemset(sb, 0, sizeof(struct scoreboard));\n+\tsb->blame_move_score = BLAME_DEFAULT_MOVE_SCORE;\n+\tsb->blame_copy_score = BLAME_DEFAULT_COPY_SCORE;\n+}\n+\n+void setup_scoreboard(struct scoreboard *sb, const char *path, struct origin **orig)\n+{\n+\tconst char *final_commit_name = NULL;\n+\tstruct origin *o;\n+\tstruct commit *final_commit = NULL;\n+\tenum object_type type;\n+\n+\tif (sb->reverse && sb->contents_from)\n+\t\tdie(_(\"--contents and --reverse do not blend well.\"));\n+\n+\tif (!sb->reverse) {\n+\t\tsb->final = find_single_final(sb->revs, &final_commit_name);\n+\t\tsb->commits.compare = compare_commits_by_commit_date;\n+\t} else {\n+\t\tsb->final = find_single_initial(sb->revs, &final_commit_name);\n+\t\tsb->commits.compare = compare_commits_by_reverse_commit_date;\n+\t}\n+\n+\tif (sb->final && sb->contents_from)\n+\t\tdie(_(\"cannot use --contents with final commit object name\"));\n+\n+\tif (sb->reverse && sb->revs->first_parent_only)\n+\t\tsb->revs->children.name = NULL;\n+\n+\tif (!sb->final) {\n+\t\t/*\n+\t\t * \"--not A B -- path\" without anything positive;\n+\t\t * do not default to HEAD, but use the working tree\n+\t\t * or \"--contents\".\n+\t\t */\n+\t\tsetup_work_tree();\n+\t\tsb->final = fake_working_tree_commit(&sb->revs->diffopt,\n+\t\t\t\t\t\t     path, sb->contents_from);\n+\t\tadd_pending_object(sb->revs, &(sb->final->object), \":\");\n+\t}\n+\n+\tif (sb->reverse && sb->revs->first_parent_only) {\n+\t\tfinal_commit = find_single_final(sb->revs, NULL);\n+\t\tif (!final_commit)\n+\t\t\tdie(_(\"--reverse and --first-parent together require specified latest commit\"));\n+\t}\n+\n+\t/*\n+\t * If we have bottom, this will mark the ancestors of the\n+\t * bottom commits we would reach while traversing as\n+\t * uninteresting.\n+\t */\n+\tif (prepare_revision_walk(sb->revs))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tif (sb->reverse && sb->revs->first_parent_only) {\n+\t\tstruct commit *c = final_commit;\n+\n+\t\tsb->revs->children.name = \"children\";\n+\t\twhile (c->parents &&\n+\t\t       oidcmp(&c->object.oid, &sb->final->object.oid)) {\n+\t\t\tstruct commit_list *l = xcalloc(1, sizeof(*l));\n+\n+\t\t\tl->item = c;\n+\t\t\tif (add_decoration(&sb->revs->children,\n+\t\t\t\t\t   &c->parents->item->object, l))\n+\t\t\t\tdie(\"BUG: not unique item in first-parent chain\");\n+\t\t\tc = c->parents->item;\n+\t\t}\n+\n+\t\tif (oidcmp(&c->object.oid, &sb->final->object.oid))\n+\t\t\tdie(_(\"--reverse --first-parent together require range along first-parent chain\"));\n+\t}\n+\n+\tif (is_null_oid(&sb->final->object.oid)) {\n+\t\to = sb->final->util;\n+\t\tsb->final_buf = xmemdupz(o->file.ptr, o->file.size);\n+\t\tsb->final_buf_size = o->file.size;\n+\t}\n+\telse {\n+\t\to = get_origin(sb->final, path);\n+\t\tif (fill_blob_sha1_and_mode(o))\n+\t\t\tdie(_(\"no such path %s in %s\"), path, final_commit_name);\n+\n+\t\tif (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) &&\n+\t\t    textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,\n+\t\t\t\t    &sb->final_buf_size))\n+\t\t\t;\n+\t\telse\n+\t\t\tsb->final_buf = read_sha1_file(o->blob_oid.hash, &type,\n+\t\t\t\t\t\t       &sb->final_buf_size);\n+\n+\t\tif (!sb->final_buf)\n+\t\t\tdie(_(\"cannot read blob %s for path %s\"),\n+\t\t\t    oid_to_hex(&o->blob_oid),\n+\t\t\t    path);\n+\t}\n+\tsb->num_read_blob++;\n+\tprepare_lines(sb);\n+\n+\tif (orig)\n+\t\t*orig = o;\n }\n \n static int blame_copy_callback(const struct option *option, const char *arg, int unset)\n@@ -846,9 +950,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tstruct origin *o;\n \tstruct blame_entry *ent = NULL;\n \tlong dashdash_pos, lno;\n-\tchar *final_commit_name = NULL;\n-\tenum object_type type;\n-\tstruct commit *final_commit = NULL;\n \tstruct progress_info pi = { NULL, 0 };\n \n \tstruct string_list range_list = STRING_LIST_INIT_NODUP;\n@@ -996,11 +1097,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\topt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |\n \t\t\tPICKAXE_BLAME_COPY_HARDER);\n \n-\tif (!blame_move_score)\n-\t\tblame_move_score = BLAME_DEFAULT_MOVE_SCORE;\n-\tif (!blame_copy_score)\n-\t\tblame_copy_score = BLAME_DEFAULT_COPY_SCORE;\n-\n \t/*\n \t * We have collected options unknown to us in argv[1..unk]\n \t * which are to be passed to revision machinery if we are\n@@ -1053,94 +1149,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \trevs.disable_stdin = 1;\n \tsetup_revisions(argc, argv, &revs, NULL);\n-\tmemset(&sb, 0, sizeof(sb));\n \n+\tinit_scoreboard(&sb);\n \tsb.revs = &revs;\n-\tif (!reverse) {\n-\t\tfinal_commit_name = prepare_final(&sb);\n-\t\tsb.commits.compare = compare_commits_by_commit_date;\n-\t}\n-\telse if (contents_from)\n-\t\tdie(_(\"--contents and --reverse do not blend well.\"));\n-\telse {\n-\t\tfinal_commit_name = prepare_initial(&sb);\n-\t\tsb.commits.compare = compare_commits_by_reverse_commit_date;\n-\t\tif (revs.first_parent_only)\n-\t\t\trevs.children.name = NULL;\n-\t}\n-\n-\tif (!sb.final) {\n-\t\t/*\n-\t\t * \"--not A B -- path\" without anything positive;\n-\t\t * do not default to HEAD, but use the working tree\n-\t\t * or \"--contents\".\n-\t\t */\n-\t\tsetup_work_tree();\n-\t\tsb.final = fake_working_tree_commit(&sb.revs->diffopt,\n-\t\t\t\t\t\t    path, contents_from);\n-\t\tadd_pending_object(&revs, &(sb.final->object), \":\");\n-\t}\n-\telse if (contents_from)\n-\t\tdie(_(\"cannot use --contents with final commit object name\"));\n-\n-\tif (reverse && revs.first_parent_only) {\n-\t\tfinal_commit = find_single_final(sb.revs, NULL);\n-\t\tif (!final_commit)\n-\t\t\tdie(_(\"--reverse and --first-parent together require specified latest commit\"));\n-\t}\n-\n-\t/*\n-\t * If we have bottom, this will mark the ancestors of the\n-\t * bottom commits we would reach while traversing as\n-\t * uninteresting.\n-\t */\n-\tif (prepare_revision_walk(&revs))\n-\t\tdie(_(\"revision walk setup failed\"));\n-\n-\tif (reverse && revs.first_parent_only) {\n-\t\tstruct commit *c = final_commit;\n-\n-\t\tsb.revs->children.name = \"children\";\n-\t\twhile (c->parents &&\n-\t\t       oidcmp(&c->object.oid, &sb.final->object.oid)) {\n-\t\t\tstruct commit_list *l = xcalloc(1, sizeof(*l));\n-\n-\t\t\tl->item = c;\n-\t\t\tif (add_decoration(&sb.revs->children,\n-\t\t\t\t\t   &c->parents->item->object, l))\n-\t\t\t\tdie(\"BUG: not unique item in first-parent chain\");\n-\t\t\tc = c->parents->item;\n-\t\t}\n-\n-\t\tif (oidcmp(&c->object.oid, &sb.final->object.oid))\n-\t\t\tdie(_(\"--reverse --first-parent together require range along first-parent chain\"));\n-\t}\n-\n-\tif (is_null_oid(&sb.final->object.oid)) {\n-\t\to = sb.final->util;\n-\t\tsb.final_buf = xmemdupz(o->file.ptr, o->file.size);\n-\t\tsb.final_buf_size = o->file.size;\n-\t}\n-\telse {\n-\t\to = get_origin(sb.final, path);\n-\t\tif (fill_blob_sha1_and_mode(o))\n-\t\t\tdie(_(\"no such path %s in %s\"), path, final_commit_name);\n+\tsb.reverse = reverse;\n+\tsb.contents_from = contents_from;\n+\tsetup_scoreboard(&sb, path, &o);\n \n-\t\tif (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&\n-\t\t    textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb.final_buf,\n-\t\t\t\t    &sb.final_buf_size))\n-\t\t\t;\n-\t\telse\n-\t\t\tsb.final_buf = read_sha1_file(o->blob_oid.hash, &type,\n-\t\t\t\t\t\t      &sb.final_buf_size);\n-\n-\t\tif (!sb.final_buf)\n-\t\t\tdie(_(\"cannot read blob %s for path %s\"),\n-\t\t\t    oid_to_hex(&o->blob_oid),\n-\t\t\t    path);\n-\t}\n-\tsb.num_read_blob++;\n-\tlno = prepare_lines(&sb);\n+\tlno = sb.num_lines;\n \n \tif (lno && !range_list.nr)\n \t\tstring_list_append(&range_list, \"1\");\n@@ -1192,13 +1208,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tsb.ent = NULL;\n \tsb.path = path;\n \n-\tsb.blame_move_score = blame_move_score;\n-\tsb.blame_copy_score = blame_copy_score;\n+\tif (blame_move_score)\n+\t\tsb.blame_move_score = blame_move_score;\n+\tif (blame_copy_score)\n+\t\tsb.blame_copy_score = blame_copy_score;\n \n \tsb.sanity_check = DEBUG ? &sanity_check_refcnt : NULL;\n \n \tsb.show_root = show_root;\n-\tsb.reverse = reverse;\n \tsb.xdl_opts = xdl_opts;\n \tsb.no_whole_file_rename = no_whole_file_rename;\n \n@@ -1216,10 +1233,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \tif (!incremental)\n \t\tsetup_pager();\n-\n-\tfree(final_commit_name);\n-\n-\tif (incremental)\n+\telse\n \t\treturn 0;\n \n \tblame_sort_final(&sb);\ndiff --git a/scoreboard.h b/scoreboard.h\nindex 7e9e313..42d9aac 100644\n--- a/scoreboard.h\n+++ b/scoreboard.h\n@@ -24,6 +24,7 @@ struct scoreboard {\n \tstruct prio_queue commits;\n \tstruct rev_info *revs;\n \tconst char *path;\n+\tconst char *contents_from;\n \n \t/*\n \t * The contents in the final image.\n-- \n2.9.3\n\n"},{"id":"318825","messageId":"20170505052729.7576-9-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH 08/10] Split blame scoreboard into its own file","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:27Z","receivedAt":"2017-05-05T05:28:09Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n Makefile        |    1 +\n builtin/blame.c | 1358 +------------------------------------------------------\n scoreboard.c    | 1296 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n scoreboard.h    |   74 +++\n 4 files changed, 1372 insertions(+), 1357 deletions(-)\n create mode 100644 scoreboard.c\n create mode 100644 scoreboard.h\n\ndiff --git a/Makefile b/Makefile\nindex b197a37..6e6726a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -827,6 +827,7 @@ LIB_OBJS += rerere.o\n LIB_OBJS += resolve-undo.o\n LIB_OBJS += revision.o\n LIB_OBJS += run-command.o\n+LIB_OBJS += scoreboard.o\n LIB_OBJS += send-pack.o\n LIB_OBJS += sequencer.o\n LIB_OBJS += server-info.o\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex dc7600c..0af99e3 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -12,13 +12,10 @@\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 \"string-list.h\"\n #include \"mailmap.h\"\n-#include \"mergesort.h\"\n #include \"parse-options.h\"\n #include \"prio-queue.h\"\n #include \"utf8.h\"\n@@ -29,6 +26,7 @@\n #include \"progress.h\"\n #include \"origin.h\"\n #include \"commit-fake.h\"\n+#include \"scoreboard.h\"\n \n static char blame_usage[] = N_(\"git blame [<options>] [<rev-opts>] [<rev>] [--] <file>\");\n \n@@ -62,19 +60,8 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP;\n #define DEBUG 0\n #endif\n \n-#define PICKAXE_BLAME_MOVE\t\t01\n-#define PICKAXE_BLAME_COPY\t\t02\n-#define PICKAXE_BLAME_COPY_HARDER\t04\n-#define PICKAXE_BLAME_COPY_HARDEST\t010\n-\n-/*\n- * blame for a blame_entry with score lower than these thresholds\n- * is not passed to the parent using move/copy logic.\n- */\n static unsigned blame_move_score;\n static unsigned blame_copy_score;\n-#define BLAME_DEFAULT_MOVE_SCORE\t20\n-#define BLAME_DEFAULT_COPY_SCORE\t40\n \n /* Remember to update object flag allocation in object.h */\n #define METAINFO_SHOWN\t\t(1u<<12)\n@@ -85,149 +72,6 @@ struct progress_info {\n \tint blamed_lines;\n };\n \n-static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n-\t\t      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts)\n-{\n-\txpparam_t xpp = {0};\n-\txdemitconf_t xecfg = {0};\n-\txdemitcb_t ecb = {NULL};\n-\n-\txpp.flags = xdl_opts;\n-\txecfg.hunk_func = hunk_func;\n-\tecb.priv = cb_data;\n-\treturn xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);\n-}\n-\n-/*\n- * Given an origin, prepare mmfile_t structure to be used by the\n- * diff machinery\n- */\n-static void fill_origin_blob(struct diff_options *opt,\n-\t\t\t     struct origin *o, mmfile_t *file, int *num_read_blob)\n-{\n-\tif (!o->file.ptr) {\n-\t\tenum object_type type;\n-\t\tunsigned long file_size;\n-\n-\t\t(*num_read_blob)++;\n-\t\tif (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&\n-\t\t    textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))\n-\t\t\t;\n-\t\telse\n-\t\t\tfile->ptr = read_sha1_file(o->blob_oid.hash, &type,\n-\t\t\t\t\t\t   &file_size);\n-\t\tfile->size = file_size;\n-\n-\t\tif (!file->ptr)\n-\t\t\tdie(\"Cannot read blob %s for path %s\",\n-\t\t\t    oid_to_hex(&o->blob_oid),\n-\t\t\t    o->path);\n-\t\to->file = *file;\n-\t}\n-\telse\n-\t\t*file = o->file;\n-}\n-\n-static void drop_origin_blob(struct origin *o)\n-{\n-\tif (o->file.ptr) {\n-\t\tfree(o->file.ptr);\n-\t\to->file.ptr = NULL;\n-\t}\n-}\n-\n-/*\n- * Any merge of blames happens on lists of blames that arrived via\n- * different parents in a single suspect.  In this case, we want to\n- * sort according to the suspect line numbers as opposed to the final\n- * image line numbers.  The function body is somewhat longish because\n- * it avoids unnecessary writes.\n- */\n-\n-static struct blame_entry *blame_merge(struct blame_entry *list1,\n-\t\t\t\t       struct blame_entry *list2)\n-{\n-\tstruct blame_entry *p1 = list1, *p2 = list2,\n-\t\t**tail = &list1;\n-\n-\tif (!p1)\n-\t\treturn p2;\n-\tif (!p2)\n-\t\treturn p1;\n-\n-\tif (p1->s_lno <= p2->s_lno) {\n-\t\tdo {\n-\t\t\ttail = &p1->next;\n-\t\t\tif ((p1 = *tail) == NULL) {\n-\t\t\t\t*tail = p2;\n-\t\t\t\treturn list1;\n-\t\t\t}\n-\t\t} while (p1->s_lno <= p2->s_lno);\n-\t}\n-\tfor (;;) {\n-\t\t*tail = p2;\n-\t\tdo {\n-\t\t\ttail = &p2->next;\n-\t\t\tif ((p2 = *tail) == NULL)  {\n-\t\t\t\t*tail = p1;\n-\t\t\t\treturn list1;\n-\t\t\t}\n-\t\t} while (p1->s_lno > p2->s_lno);\n-\t\t*tail = p1;\n-\t\tdo {\n-\t\t\ttail = &p1->next;\n-\t\t\tif ((p1 = *tail) == NULL) {\n-\t\t\t\t*tail = p2;\n-\t\t\t\treturn list1;\n-\t\t\t}\n-\t\t} while (p1->s_lno <= p2->s_lno);\n-\t}\n-}\n-\n-static void *get_next_blame(const void *p)\n-{\n-\treturn ((struct blame_entry *)p)->next;\n-}\n-\n-static void set_next_blame(void *p1, void *p2)\n-{\n-\t((struct blame_entry *)p1)->next = p2;\n-}\n-\n-/*\n- * Final image line numbers are all different, so we don't need a\n- * three-way comparison here.\n- */\n-\n-static int compare_blame_final(const void *p1, const void *p2)\n-{\n-\treturn ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno\n-\t\t? 1 : -1;\n-}\n-\n-static int compare_blame_suspect(const void *p1, const void *p2)\n-{\n-\tconst struct blame_entry *s1 = p1, *s2 = p2;\n-\t/*\n-\t * to allow for collating suspects, we sort according to the\n-\t * respective pointer value as the primary sorting criterion.\n-\t * The actual relation is pretty unimportant as long as it\n-\t * establishes a total order.  Comparing as integers gives us\n-\t * that.\n-\t */\n-\tif (s1->suspect != s2->suspect)\n-\t\treturn (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;\n-\tif (s1->s_lno == s2->s_lno)\n-\t\treturn 0;\n-\treturn s1->s_lno > s2->s_lno ? 1 : -1;\n-}\n-\n-static struct blame_entry *blame_sort(struct blame_entry *head,\n-\t\t\t\t      int (*compare_fn)(const void *, const void *))\n-{\n-\treturn llist_mergesort (head, get_next_blame, set_next_blame, compare_fn);\n-}\n-\n static int compare_commits_by_reverse_commit_date(const void *a,\n \t\t\t\t\t\t  const void *b,\n \t\t\t\t\t\t  void *c)\n@@ -236,111 +80,6 @@ static int compare_commits_by_reverse_commit_date(const void *a,\n }\n \n /*\n- * The current state of the blame assignment.\n- */\n-struct scoreboard {\n-\t/* the final commit (i.e. where we started digging from) */\n-\tstruct commit *final;\n-\t/* Priority queue for commits with unassigned blame records */\n-\tstruct prio_queue commits;\n-\tstruct rev_info *revs;\n-\tconst char *path;\n-\n-\t/*\n-\t * The contents in the final image.\n-\t * Used by many functions to obtain contents of the nth line,\n-\t * indexed with scoreboard.lineno[blame_entry.lno].\n-\t */\n-\tconst char *final_buf;\n-\tunsigned long final_buf_size;\n-\n-\t/* linked list of blames */\n-\tstruct blame_entry *ent;\n-\n-\t/* look-up a line in the final buffer */\n-\tint num_lines;\n-\tint *lineno;\n-\n-\t/*\n-\t * blame for a blame_entry with score lower than these thresholds\n-\t * is not passed to the parent using move/copy logic.\n-\t */\n-\tunsigned blame_move_score;\n-\tunsigned blame_copy_score;\n-\n-\t/* stats */\n-\tint num_read_blob;\n-\tint num_get_patch;\n-\tint num_commits;\n-\n-\t/* options */\n-\tint show_root;\n-\tint reverse;\n-\tint xdl_opts;\n-\tint no_whole_file_rename;\n-\n-\t/* callbacks */\n-\tvoid(*sanity_check)(struct scoreboard *);\n-\tvoid(*found_guilty_entry)(struct blame_entry *, void *);\n-\n-\tvoid *found_guilty_entry_data;\n-};\n-\n-static void blame_sort_final(struct scoreboard *sb)\n-{\n-\tsb->ent = blame_sort(sb->ent, compare_blame_final);\n-}\n-\n-/*\n- * If two blame entries that are next to each other came from\n- * contiguous lines in the same origin (i.e. <commit, path> pair),\n- * merge them together.\n- */\n-static void coalesce(struct scoreboard *sb)\n-{\n-\tstruct blame_entry *ent, *next;\n-\n-\tfor (ent = sb->ent; ent && (next = ent->next); ent = next) {\n-\t\tif (ent->suspect == next->suspect &&\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\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 (sb->sanity_check) /* sanity */\n-\t\tsb->sanity_check(sb);\n-}\n-\n-/*\n- * Merge the given sorted list of blames into a preexisting origin.\n- * If there were no previous blames to that commit, it is entered into\n- * the commit priority queue of the score board.\n- */\n-\n-static void queue_blames(struct scoreboard *sb, struct origin *porigin,\n-\t\t\t struct blame_entry *sorted)\n-{\n-\tif (porigin->suspects)\n-\t\tporigin->suspects = blame_merge(porigin->suspects, sorted);\n-\telse {\n-\t\tstruct origin *o;\n-\t\tfor (o = porigin->commit->util; o; o = o->next) {\n-\t\t\tif (o->suspects) {\n-\t\t\t\tporigin->suspects = sorted;\n-\t\t\t\treturn;\n-\t\t\t}\n-\t\t}\n-\t\tporigin->suspects = sorted;\n-\t\tprio_queue_put(&sb->commits, porigin->commit);\n-\t}\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@@ -366,1039 +105,12 @@ static int fill_blob_sha1_and_mode(struct origin *origin)\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 scoreboard *sb,\n-\t\t\t\t  struct commit *parent,\n-\t\t\t\t  struct origin *origin)\n-{\n-\tstruct origin *porigin;\n-\tstruct diff_options diff_opts;\n-\tconst char *paths[2];\n-\n-\t/* First check any existing origins */\n-\tfor (porigin = parent->util; porigin; porigin = porigin->next)\n-\t\tif (!strcmp(porigin->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\treturn origin_incref (porigin);\n-\t\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-\tparse_pathspec(&diff_opts.pathspec,\n-\t\t       PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,\n-\t\t       PATHSPEC_LITERAL_PATH, \"\", paths);\n-\tdiff_setup_done(&diff_opts);\n-\n-\tif (is_null_oid(&origin->commit->object.oid))\n-\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n-\telse\n-\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n-\t\t\t       origin->commit->tree->object.oid.hash,\n-\t\t\t       \"\", &diff_opts);\n-\tdiffcore_std(&diff_opts);\n-\n-\tif (!diff_queued_diff.nr) {\n-\t\t/* The path is the same as parent */\n-\t\tporigin = get_origin(parent, origin->path);\n-\t\toidcpy(&porigin->blob_oid, &origin->blob_oid);\n-\t\tporigin->mode = origin->mode;\n-\t} else {\n-\t\t/*\n-\t\t * Since origin->path is a pathspec, if the parent\n-\t\t * commit had it as a directory, we will see a whole\n-\t\t * bunch of deletion of files in the directory that we\n-\t\t * do not care about.\n-\t\t */\n-\t\tint i;\n-\t\tstruct diff_filepair *p = NULL;\n-\t\tfor (i = 0; i < diff_queued_diff.nr; i++) {\n-\t\t\tconst char *name;\n-\t\t\tp = diff_queued_diff.queue[i];\n-\t\t\tname = p->one->path ? p->one->path : p->two->path;\n-\t\t\tif (!strcmp(name, origin->path))\n-\t\t\t\tbreak;\n-\t\t}\n-\t\tif (!p)\n-\t\t\tdie(\"internal error in blame::find_origin\");\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(parent, origin->path);\n-\t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n-\t\t\tporigin->mode = p->one->mode;\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-\tclear_pathspec(&diff_opts.pathspec);\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 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-\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-\tdiff_setup_done(&diff_opts);\n-\n-\tif (is_null_oid(&origin->commit->object.oid))\n-\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n-\telse\n-\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n-\t\t\t       origin->commit->tree->object.oid.hash,\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(parent, p->one->path);\n-\t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n-\t\t\tporigin->mode = p->one->mode;\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\tdiff_flush(&diff_opts);\n-\tclear_pathspec(&diff_opts.pathspec);\n-\treturn porigin;\n-}\n-\n-/*\n- * Append a new blame entry to a given output queue.\n- */\n-static void add_blame_entry(struct blame_entry ***queue,\n-\t\t\t    const struct blame_entry *src)\n-{\n-\tstruct blame_entry *e = xmalloc(sizeof(*e));\n-\tmemcpy(e, src, sizeof(*e));\n-\torigin_incref(e->suspect);\n-\n-\te->next = **queue;\n-\t**queue = e;\n-\t*queue = &e->next;\n-}\n-\n-/*\n- * src typically is on-stack; we want to copy the information in it to\n- * a malloced blame_entry that gets added to the given queue.  The\n- * origin of dst loses a refcnt.\n- */\n-static void dup_entry(struct blame_entry ***queue,\n-\t\t      struct blame_entry *dst, struct blame_entry *src)\n-{\n-\torigin_incref(src->suspect);\n-\torigin_decref(dst->suspect);\n-\tmemcpy(dst, src, sizeof(*src));\n-\tdst->next = **queue;\n-\t**queue = dst;\n-\t*queue = &dst->next;\n-}\n-\n-static const char *nth_line(struct scoreboard *sb, long lno)\n-{\n-\treturn sb->final_buf + sb->lineno[lno];\n-}\n-\n static const char *nth_line_cb(void *data, long lno)\n {\n \treturn nth_line((struct scoreboard *)data, 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.  Any assigned blame is moved to queue to\n- * reflect the split.\n- */\n-static void split_blame(struct blame_entry ***blamed,\n-\t\t\tstruct blame_entry ***unblamed,\n-\t\t\tstruct blame_entry *split,\n-\t\t\tstruct blame_entry *e)\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(unblamed, e, &split[0]);\n-\n-\t\t/* The last part -- me */\n-\t\tadd_blame_entry(unblamed, &split[2]);\n-\n-\t\t/* ... and the middle part -- parent */\n-\t\tadd_blame_entry(blamed, &split[1]);\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(blamed, e, &split[1]);\n-\telse if (split[0].suspect) {\n-\t\t/* me and then parent */\n-\t\tdup_entry(unblamed, e, &split[0]);\n-\t\tadd_blame_entry(blamed, &split[1]);\n-\t}\n-\telse {\n-\t\t/* parent and then me */\n-\t\tdup_entry(blamed, e, &split[1]);\n-\t\tadd_blame_entry(unblamed, &split[2]);\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- * reverse_blame reverses the list given in head, appending tail.\n- * That allows us to build lists in reverse order, then reverse them\n- * afterwards.  This can be faster than building the list in proper\n- * order right away.  The reason is that building in proper order\n- * requires writing a link in the _previous_ element, while building\n- * in reverse order just requires placing the list head into the\n- * _current_ element.\n- */\n-\n-static struct blame_entry *reverse_blame(struct blame_entry *head,\n-\t\t\t\t\t struct blame_entry *tail)\n-{\n-\twhile (head) {\n-\t\tstruct blame_entry *next = head->next;\n-\t\thead->next = tail;\n-\t\ttail = head;\n-\t\thead = next;\n-\t}\n-\treturn tail;\n-}\n-\n-/*\n- * Process one hunk from the patch between the current suspect for\n- * blame_entry e and its parent.  This first blames any unfinished\n- * entries before the chunk (which is where target and parent start\n- * differing) on the parent, and then splits blame entries at the\n- * start and at the end of the difference region.  Since use of -M and\n- * -C options may lead to overlapping/duplicate source line number\n- * ranges, all we can rely on from sorting/merging is the order of the\n- * first suspect line number.\n- */\n-static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,\n-\t\t\tint tlno, int offset, int same,\n-\t\t\tstruct origin *parent)\n-{\n-\tstruct blame_entry *e = **srcq;\n-\tstruct blame_entry *samep = NULL, *diffp = NULL;\n-\n-\twhile (e && e->s_lno < tlno) {\n-\t\tstruct blame_entry *next = e->next;\n-\t\t/*\n-\t\t * current record starts before differing portion.  If\n-\t\t * it reaches into it, we need to split it up and\n-\t\t * examine the second part separately.\n-\t\t */\n-\t\tif (e->s_lno + e->num_lines > tlno) {\n-\t\t\t/* Move second half to a new record */\n-\t\t\tint len = tlno - e->s_lno;\n-\t\t\tstruct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));\n-\t\t\tn->suspect = e->suspect;\n-\t\t\tn->lno = e->lno + len;\n-\t\t\tn->s_lno = e->s_lno + len;\n-\t\t\tn->num_lines = e->num_lines - len;\n-\t\t\te->num_lines = len;\n-\t\t\te->score = 0;\n-\t\t\t/* Push new record to diffp */\n-\t\t\tn->next = diffp;\n-\t\t\tdiffp = n;\n-\t\t} else\n-\t\t\torigin_decref(e->suspect);\n-\t\t/* Pass blame for everything before the differing\n-\t\t * chunk to the parent */\n-\t\te->suspect = origin_incref(parent);\n-\t\te->s_lno += offset;\n-\t\te->next = samep;\n-\t\tsamep = e;\n-\t\te = next;\n-\t}\n-\t/*\n-\t * As we don't know how much of a common stretch after this\n-\t * diff will occur, the currently blamed parts are all that we\n-\t * can assign to the parent for now.\n-\t */\n-\n-\tif (samep) {\n-\t\t**dstq = reverse_blame(samep, **dstq);\n-\t\t*dstq = &samep->next;\n-\t}\n-\t/*\n-\t * Prepend the split off portions: everything after e starts\n-\t * after the blameable portion.\n-\t */\n-\te = reverse_blame(diffp, e);\n-\n-\t/*\n-\t * Now retain records on the target while parts are different\n-\t * from the parent.\n-\t */\n-\tsamep = NULL;\n-\tdiffp = NULL;\n-\twhile (e && e->s_lno < same) {\n-\t\tstruct blame_entry *next = e->next;\n-\n-\t\t/*\n-\t\t * If current record extends into sameness, need to split.\n-\t\t */\n-\t\tif (e->s_lno + e->num_lines > same) {\n-\t\t\t/*\n-\t\t\t * Move second half to a new record to be\n-\t\t\t * processed by later chunks\n-\t\t\t */\n-\t\t\tint len = same - e->s_lno;\n-\t\t\tstruct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));\n-\t\t\tn->suspect = origin_incref(e->suspect);\n-\t\t\tn->lno = e->lno + len;\n-\t\t\tn->s_lno = e->s_lno + len;\n-\t\t\tn->num_lines = e->num_lines - len;\n-\t\t\te->num_lines = len;\n-\t\t\te->score = 0;\n-\t\t\t/* Push new record to samep */\n-\t\t\tn->next = samep;\n-\t\t\tsamep = n;\n-\t\t}\n-\t\te->next = diffp;\n-\t\tdiffp = e;\n-\t\te = next;\n-\t}\n-\t**srcq = reverse_blame(diffp, reverse_blame(samep, e));\n-\t/* Move across elements that are in the unblamable portion */\n-\tif (diffp)\n-\t\t*srcq = &diffp->next;\n-}\n-\n-struct blame_chunk_cb_data {\n-\tstruct origin *parent;\n-\tlong offset;\n-\tstruct blame_entry **dstq;\n-\tstruct blame_entry **srcq;\n-};\n-\n-/* diff chunks are from parent to target */\n-static int blame_chunk_cb(long start_a, long count_a,\n-\t\t\t  long start_b, long count_b, void *data)\n-{\n-\tstruct blame_chunk_cb_data *d = data;\n-\tif (start_a - start_b != d->offset)\n-\t\tdie(\"internal error in blame::blame_chunk_cb\");\n-\tblame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,\n-\t\t    start_b + count_b, d->parent);\n-\td->offset = start_a + count_a - (start_b + count_b);\n-\treturn 0;\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 void pass_blame_to_parent(struct scoreboard *sb,\n-\t\t\t\t struct origin *target,\n-\t\t\t\t struct origin *parent)\n-{\n-\tmmfile_t file_p, file_o;\n-\tstruct blame_chunk_cb_data d;\n-\tstruct blame_entry *newdest = NULL;\n-\n-\tif (!target->suspects)\n-\t\treturn; /* nothing remains for this target */\n-\n-\td.parent = parent;\n-\td.offset = 0;\n-\td.dstq = &newdest; d.srcq = &target->suspects;\n-\n-\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n-\tfill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob);\n-\tsb->num_get_patch++;\n-\n-\tif (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts))\n-\t\tdie(\"unable to generate diff (%s -> %s)\",\n-\t\t    oid_to_hex(&parent->commit->object.oid),\n-\t\t    oid_to_hex(&target->commit->object.oid));\n-\t/* The rest are the same as the parent */\n-\tblame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);\n-\t*d.dstq = NULL;\n-\tqueue_blames(sb, parent, newdest);\n-\n-\treturn;\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 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 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 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 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 int handle_split_cb(long start_a, long count_a,\n-\t\t\t   long start_b, long count_b, void *data)\n-{\n-\tstruct handle_split_cb_data *d = data;\n-\thandle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,\n-\t\t     d->split);\n-\td->plno = start_a + count_a;\n-\td->tlno = start_b + count_b;\n-\treturn 0;\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 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-\tmmfile_t file_o;\n-\tstruct handle_split_cb_data d;\n-\n-\tmemset(&d, 0, sizeof(d));\n-\td.sb = sb; d.ent = ent; d.parent = parent; d.split = split;\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-\tfile_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;\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(split, 0, sizeof(struct blame_entry [3]));\n-\tif (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts))\n-\t\tdie(\"unable to generate diff (%s)\",\n-\t\t    oid_to_hex(&parent->commit->object.oid));\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-/* Move all blame entries from list *source that have a score smaller\n- * than score_min to the front of list *small.\n- * Returns a pointer to the link pointing to the old head of the small list.\n- */\n-\n-static struct blame_entry **filter_small(struct scoreboard *sb,\n-\t\t\t\t\t struct blame_entry **small,\n-\t\t\t\t\t struct blame_entry **source,\n-\t\t\t\t\t unsigned score_min)\n-{\n-\tstruct blame_entry *p = *source;\n-\tstruct blame_entry *oldsmall = *small;\n-\twhile (p) {\n-\t\tif (ent_score(sb, p) <= score_min) {\n-\t\t\t*small = p;\n-\t\t\tsmall = &p->next;\n-\t\t\tp = *small;\n-\t\t} else {\n-\t\t\t*source = p;\n-\t\t\tsource = &p->next;\n-\t\t\tp = *source;\n-\t\t}\n-\t}\n-\t*small = oldsmall;\n-\t*source = NULL;\n-\treturn small;\n-}\n-\n-/*\n- * See if lines currently target is suspected for can be attributed to\n- * parent.\n- */\n-static void find_move_in_parent(struct scoreboard *sb,\n-\t\t\t\tstruct blame_entry ***blamed,\n-\t\t\t\tstruct blame_entry **toosmall,\n-\t\t\t\tstruct origin *target,\n-\t\t\t\tstruct origin *parent)\n-{\n-\tstruct blame_entry *e, split[3];\n-\tstruct blame_entry *unblamed = target->suspects;\n-\tstruct blame_entry *leftover = NULL;\n-\tmmfile_t file_p;\n-\n-\tif (!unblamed)\n-\t\treturn; /* nothing remains for this target */\n-\n-\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n-\tif (!file_p.ptr)\n-\t\treturn;\n-\n-\t/* At each iteration, unblamed has a NULL-terminated list of\n-\t * entries that have not yet been tested for blame.  leftover\n-\t * contains the reversed list of entries that have been tested\n-\t * without being assignable to the parent.\n-\t */\n-\tdo {\n-\t\tstruct blame_entry **unblamedtail = &unblamed;\n-\t\tstruct blame_entry *next;\n-\t\tfor (e = unblamed; e; e = next) {\n-\t\t\tnext = e->next;\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->blame_move_score < ent_score(sb, &split[1])) {\n-\t\t\t\tsplit_blame(blamed, &unblamedtail, split, e);\n-\t\t\t} else {\n-\t\t\t\te->next = leftover;\n-\t\t\t\tleftover = e;\n-\t\t\t}\n-\t\t\tdecref_split(split);\n-\t\t}\n-\t\t*unblamedtail = NULL;\n-\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->blame_move_score);\n-\t} while (unblamed);\n-\ttarget->suspects = reverse_blame(leftover, NULL);\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_entry *unblamed,\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 = unblamed, num_ents = 0; e; e = e->next)\n-\t\tnum_ents++;\n-\tif (num_ents) {\n-\t\tblame_list = xcalloc(num_ents, sizeof(struct blame_list));\n-\t\tfor (e = unblamed, i = 0; e; e = e->next)\n-\t\t\tblame_list[i++].ent = e;\n-\t}\n-\t*num_ents_p = num_ents;\n-\treturn blame_list;\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 void find_copy_in_parent(struct scoreboard *sb,\n-\t\t\t\tstruct blame_entry ***blamed,\n-\t\t\t\tstruct blame_entry **toosmall,\n-\t\t\t\tstruct origin *target,\n-\t\t\t\tstruct commit *parent,\n-\t\t\t\tstruct origin *porigin,\n-\t\t\t\tint opt)\n-{\n-\tstruct diff_options diff_opts;\n-\tint i, j;\n-\tstruct blame_list *blame_list;\n-\tint num_ents;\n-\tstruct blame_entry *unblamed = target->suspects;\n-\tstruct blame_entry *leftover = NULL;\n-\n-\tif (!unblamed)\n-\t\treturn; /* 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-\tdiff_setup_done(&diff_opts);\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_oid(&target->commit->object.oid))\n-\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n-\telse\n-\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n-\t\t\t       target->commit->tree->object.oid.hash,\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-\tdo {\n-\t\tstruct blame_entry **unblamedtail = &unblamed;\n-\t\tblame_list = setup_blame_list(unblamed, &num_ents);\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(parent, p->one->path);\n-\t\t\toidcpy(&norigin->blob_oid, &p->one->oid);\n-\t\t\tnorigin->mode = p->one->mode;\n-\t\t\tfill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob);\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->blame_copy_score < ent_score(sb, &split[1])) {\n-\t\t\t\tsplit_blame(blamed, &unblamedtail, split,\n-\t\t\t\t\t    blame_list[j].ent);\n-\t\t\t} else {\n-\t\t\t\tblame_list[j].ent->next = leftover;\n-\t\t\t\tleftover = blame_list[j].ent;\n-\t\t\t}\n-\t\t\tdecref_split(split);\n-\t\t}\n-\t\tfree(blame_list);\n-\t\t*unblamedtail = NULL;\n-\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->blame_copy_score);\n-\t} while (unblamed);\n-\ttarget->suspects = reverse_blame(leftover, NULL);\n-\tdiff_flush(&diff_opts);\n-\tclear_pathspec(&diff_opts.pathspec);\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 scoreboard *sb,\n-\t\t\t     struct origin *origin, struct origin *porigin)\n-{\n-\tstruct blame_entry *e, *suspects;\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-\tsuspects = origin->suspects;\n-\torigin->suspects = NULL;\n-\tfor (e = suspects; e; e = e->next) {\n-\t\torigin_incref(porigin);\n-\t\torigin_decref(e->suspect);\n-\t\te->suspect = porigin;\n-\t}\n-\tqueue_blames(sb, porigin, suspects);\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 rev_info *revs, struct commit *commit,\n-\t\t\t\t\tint reverse)\n-{\n-\tif (!reverse) {\n-\t\tif (revs->first_parent_only &&\n-\t\t    commit->parents &&\n-\t\t    commit->parents->next) {\n-\t\t\tfree_commit_list(commit->parents->next);\n-\t\t\tcommit->parents->next = NULL;\n-\t\t}\n-\t\treturn commit->parents;\n-\t}\n-\treturn lookup_decoration(&revs->children, &commit->object);\n-}\n-\n-static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse)\n-{\n-\tstruct commit_list *l = first_scapegoat(revs, commit, reverse);\n-\treturn commit_list_count(l);\n-}\n-\n-/* Distribute collected unsorted blames to the respected sorted lists\n- * in the various origins.\n- */\n-static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)\n-{\n-\tblamed = blame_sort(blamed, compare_blame_suspect);\n-\twhile (blamed)\n-\t{\n-\t\tstruct origin *porigin = blamed->suspect;\n-\t\tstruct blame_entry *suspects = NULL;\n-\t\tdo {\n-\t\t\tstruct blame_entry *next = blamed->next;\n-\t\t\tblamed->next = suspects;\n-\t\t\tsuspects = blamed;\n-\t\t\tblamed = next;\n-\t\t} while (blamed && blamed->suspect == porigin);\n-\t\tsuspects = reverse_blame(suspects, NULL);\n-\t\tqueue_blames(sb, porigin, suspects);\n-\t}\n-}\n-\n-#define MAXSG 16\n-\n-static void pass_blame(struct 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-\tstruct blame_entry *toosmall = NULL;\n-\tstruct blame_entry *blames, **blametail = &blames;\n-\n-\tnum_sg = num_scapegoats(revs, commit, sb->reverse);\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 - sb->no_whole_file_rename; pass++) {\n-\t\tstruct origin *(*find)(struct scoreboard *,\n-\t\t\t\t       struct commit *, struct origin *);\n-\t\tfind = pass ? find_rename : find_origin;\n-\n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\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 (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {\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    !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) {\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->num_commits++;\n-\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\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\tpass_blame_to_parent(sb, origin, porigin);\n-\t\tif (!origin->suspects)\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\tfilter_small(sb, &toosmall, &origin->suspects, sb->blame_move_score);\n-\t\tif (origin->suspects) {\n-\t\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n-\t\t\t     i < num_sg && sg;\n-\t\t\t     sg = sg->next, i++) {\n-\t\t\t\tstruct origin *porigin = sg_origin[i];\n-\t\t\t\tif (!porigin)\n-\t\t\t\t\tcontinue;\n-\t\t\t\tfind_move_in_parent(sb, &blametail, &toosmall, origin, porigin);\n-\t\t\t\tif (!origin->suspects)\n-\t\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\t/*\n-\t * Optionally find copies from parents' files.\n-\t */\n-\tif (opt & PICKAXE_BLAME_COPY) {\n-\t\tif (sb->blame_copy_score > sb->blame_move_score)\n-\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->blame_copy_score);\n-\t\telse if (sb->blame_copy_score < sb->blame_move_score) {\n-\t\t\torigin->suspects = blame_merge(origin->suspects, toosmall);\n-\t\t\ttoosmall = NULL;\n-\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->blame_copy_score);\n-\t\t}\n-\t\tif (!origin->suspects)\n-\t\t\tgoto finish;\n-\n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\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\tfind_copy_in_parent(sb, &blametail, &toosmall,\n-\t\t\t\t\t    origin, sg->item, porigin, opt);\n-\t\t\tif (!origin->suspects)\n-\t\t\t\tgoto finish;\n-\t\t}\n-\t}\n-\n-finish:\n-\t*blametail = NULL;\n-\tdistribute_blame(sb, blames);\n-\t/*\n-\t * prepend toosmall to origin->suspects\n-\t *\n-\t * There is no point in sorting: this ends up on a big\n-\t * unsorted list in the caller anyway.\n-\t */\n-\tif (toosmall) {\n-\t\tstruct blame_entry **tail = &toosmall;\n-\t\twhile (*tail)\n-\t\t\ttail = &(*tail)->next;\n-\t\t*tail = origin->suspects;\n-\t\torigin->suspects = toosmall;\n-\t}\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@@ -1607,74 +319,6 @@ static void found_guilty_entry(struct blame_entry *ent, void *data)\n \tdisplay_progress(pi->progress, pi->blamed_lines);\n }\n \n-/*\n- * The main loop -- while we have blobs with lines whose true origin\n- * is still unknown, pick one blob, and allow its lines to pass blames\n- * to its parents. */\n-static void assign_blame(struct scoreboard *sb, int opt)\n-{\n-\tstruct rev_info *revs = sb->revs;\n-\tstruct commit *commit = prio_queue_get(&sb->commits);\n-\n-\twhile (commit) {\n-\t\tstruct blame_entry *ent;\n-\t\tstruct origin *suspect = commit->util;\n-\n-\t\t/* find one suspect to break down */\n-\t\twhile (suspect && !suspect->suspects)\n-\t\t\tsuspect = suspect->next;\n-\n-\t\tif (!suspect) {\n-\t\t\tcommit = prio_queue_get(&sb->commits);\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tassert(commit == suspect->commit);\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\tparse_commit(commit);\n-\t\tif (sb->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->show_root)\n-\t\t\tcommit->object.flags |= UNINTERESTING;\n-\n-\t\t/* Take responsibility for the remaining entries */\n-\t\tent = suspect->suspects;\n-\t\tif (ent) {\n-\t\t\tsuspect->guilty = 1;\n-\t\t\tfor (;;) {\n-\t\t\t\tstruct blame_entry *next = ent->next;\n-\t\t\t\tif (sb->found_guilty_entry)\n-\t\t\t\t\tsb->found_guilty_entry(ent, sb->found_guilty_entry_data);\n-\t\t\t\tif (next) {\n-\t\t\t\t\tent = next;\n-\t\t\t\t\tcontinue;\n-\t\t\t\t}\n-\t\t\t\tent->next = sb->ent;\n-\t\t\t\tsb->ent = suspect->suspects;\n-\t\t\t\tsuspect->suspects = NULL;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t\torigin_decref(suspect);\n-\n-\t\tif (sb->sanity_check) /* sanity */\n-\t\t\tsb->sanity_check(sb);\n-\t}\n-}\n-\n static const char *format_time(unsigned long time, const char *tz_str,\n \t\t\t       int show_raw_time)\n {\ndiff --git a/scoreboard.c b/scoreboard.c\nnew file mode 100644\nindex 0000000..0463688\n--- /dev/null\n+++ b/scoreboard.c\n@@ -0,0 +1,1296 @@\n+#include \"mergesort.h\"\n+#include \"diff.h\"\n+#include \"diffcore.h\"\n+#include \"scoreboard.h\"\n+\n+static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n+\t\t      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts)\n+{\n+\txpparam_t xpp = {0};\n+\txdemitconf_t xecfg = {0};\n+\txdemitcb_t ecb = {NULL};\n+\n+\txpp.flags = xdl_opts;\n+\txecfg.hunk_func = hunk_func;\n+\tecb.priv = cb_data;\n+\treturn xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);\n+}\n+\n+/*\n+ * Given an origin, prepare mmfile_t structure to be used by the\n+ * diff machinery\n+ */\n+static void fill_origin_blob(struct diff_options *opt,\n+\t\t\t     struct origin *o, mmfile_t *file, int *num_read_blob)\n+{\n+\tif (!o->file.ptr) {\n+\t\tenum object_type type;\n+\t\tunsigned long file_size;\n+\n+\t\t(*num_read_blob)++;\n+\t\tif (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&\n+\t\t    textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))\n+\t\t\t;\n+\t\telse\n+\t\t\tfile->ptr = read_sha1_file(o->blob_oid.hash, &type,\n+\t\t\t\t\t\t   &file_size);\n+\t\tfile->size = file_size;\n+\n+\t\tif (!file->ptr)\n+\t\t\tdie(\"Cannot read blob %s for path %s\",\n+\t\t\t    oid_to_hex(&o->blob_oid),\n+\t\t\t    o->path);\n+\t\to->file = *file;\n+\t}\n+\telse\n+\t\t*file = o->file;\n+}\n+\n+static void drop_origin_blob(struct origin *o)\n+{\n+\tif (o->file.ptr) {\n+\t\tfree(o->file.ptr);\n+\t\to->file.ptr = NULL;\n+\t}\n+}\n+\n+/*\n+ * Any merge of blames happens on lists of blames that arrived via\n+ * different parents in a single suspect.  In this case, we want to\n+ * sort according to the suspect line numbers as opposed to the final\n+ * image line numbers.  The function body is somewhat longish because\n+ * it avoids unnecessary writes.\n+ */\n+\n+static struct blame_entry *blame_merge(struct blame_entry *list1,\n+\t\t\t\t       struct blame_entry *list2)\n+{\n+\tstruct blame_entry *p1 = list1, *p2 = list2,\n+\t\t**tail = &list1;\n+\n+\tif (!p1)\n+\t\treturn p2;\n+\tif (!p2)\n+\t\treturn p1;\n+\n+\tif (p1->s_lno <= p2->s_lno) {\n+\t\tdo {\n+\t\t\ttail = &p1->next;\n+\t\t\tif ((p1 = *tail) == NULL) {\n+\t\t\t\t*tail = p2;\n+\t\t\t\treturn list1;\n+\t\t\t}\n+\t\t} while (p1->s_lno <= p2->s_lno);\n+\t}\n+\tfor (;;) {\n+\t\t*tail = p2;\n+\t\tdo {\n+\t\t\ttail = &p2->next;\n+\t\t\tif ((p2 = *tail) == NULL)  {\n+\t\t\t\t*tail = p1;\n+\t\t\t\treturn list1;\n+\t\t\t}\n+\t\t} while (p1->s_lno > p2->s_lno);\n+\t\t*tail = p1;\n+\t\tdo {\n+\t\t\ttail = &p1->next;\n+\t\t\tif ((p1 = *tail) == NULL) {\n+\t\t\t\t*tail = p2;\n+\t\t\t\treturn list1;\n+\t\t\t}\n+\t\t} while (p1->s_lno <= p2->s_lno);\n+\t}\n+}\n+\n+static void *get_next_blame(const void *p)\n+{\n+\treturn ((struct blame_entry *)p)->next;\n+}\n+\n+static void set_next_blame(void *p1, void *p2)\n+{\n+\t((struct blame_entry *)p1)->next = p2;\n+}\n+\n+/*\n+ * Final image line numbers are all different, so we don't need a\n+ * three-way comparison here.\n+ */\n+\n+static int compare_blame_final(const void *p1, const void *p2)\n+{\n+\treturn ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno\n+\t\t? 1 : -1;\n+}\n+\n+static int compare_blame_suspect(const void *p1, const void *p2)\n+{\n+\tconst struct blame_entry *s1 = p1, *s2 = p2;\n+\t/*\n+\t * to allow for collating suspects, we sort according to the\n+\t * respective pointer value as the primary sorting criterion.\n+\t * The actual relation is pretty unimportant as long as it\n+\t * establishes a total order.  Comparing as integers gives us\n+\t * that.\n+\t */\n+\tif (s1->suspect != s2->suspect)\n+\t\treturn (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;\n+\tif (s1->s_lno == s2->s_lno)\n+\t\treturn 0;\n+\treturn s1->s_lno > s2->s_lno ? 1 : -1;\n+}\n+\n+static struct blame_entry *blame_sort(struct blame_entry *head,\n+\t\t\t\t      int (*compare_fn)(const void *, const void *))\n+{\n+\treturn llist_mergesort (head, get_next_blame, set_next_blame, compare_fn);\n+}\n+\n+void blame_sort_final(struct scoreboard *sb)\n+{\n+\tsb->ent = blame_sort(sb->ent, compare_blame_final);\n+}\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 scoreboard *sb)\n+{\n+\tstruct blame_entry *ent, *next;\n+\n+\tfor (ent = sb->ent; ent && (next = ent->next); ent = next) {\n+\t\tif (ent->suspect == next->suspect &&\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\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 (sb->sanity_check) /* sanity */\n+\t\tsb->sanity_check(sb);\n+}\n+\n+/*\n+ * Merge the given sorted list of blames into a preexisting origin.\n+ * If there were no previous blames to that commit, it is entered into\n+ * the commit priority queue of the score board.\n+ */\n+\n+static void queue_blames(struct scoreboard *sb, struct origin *porigin,\n+\t\t\t struct blame_entry *sorted)\n+{\n+\tif (porigin->suspects)\n+\t\tporigin->suspects = blame_merge(porigin->suspects, sorted);\n+\telse {\n+\t\tstruct origin *o;\n+\t\tfor (o = porigin->commit->util; o; o = o->next) {\n+\t\t\tif (o->suspects) {\n+\t\t\t\tporigin->suspects = sorted;\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t}\n+\t\tporigin->suspects = sorted;\n+\t\tprio_queue_put(&sb->commits, porigin->commit);\n+\t}\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 scoreboard *sb,\n+\t\t\t\t  struct commit *parent,\n+\t\t\t\t  struct origin *origin)\n+{\n+\tstruct origin *porigin;\n+\tstruct diff_options diff_opts;\n+\tconst char *paths[2];\n+\n+\t/* First check any existing origins */\n+\tfor (porigin = parent->util; porigin; porigin = porigin->next)\n+\t\tif (!strcmp(porigin->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\treturn origin_incref (porigin);\n+\t\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+\tparse_pathspec(&diff_opts.pathspec,\n+\t\t       PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,\n+\t\t       PATHSPEC_LITERAL_PATH, \"\", paths);\n+\tdiff_setup_done(&diff_opts);\n+\n+\tif (is_null_oid(&origin->commit->object.oid))\n+\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n+\t\t\t       origin->commit->tree->object.oid.hash,\n+\t\t\t       \"\", &diff_opts);\n+\tdiffcore_std(&diff_opts);\n+\n+\tif (!diff_queued_diff.nr) {\n+\t\t/* The path is the same as parent */\n+\t\tporigin = get_origin(parent, origin->path);\n+\t\toidcpy(&porigin->blob_oid, &origin->blob_oid);\n+\t\tporigin->mode = origin->mode;\n+\t} else {\n+\t\t/*\n+\t\t * Since origin->path is a pathspec, if the parent\n+\t\t * commit had it as a directory, we will see a whole\n+\t\t * bunch of deletion of files in the directory that we\n+\t\t * do not care about.\n+\t\t */\n+\t\tint i;\n+\t\tstruct diff_filepair *p = NULL;\n+\t\tfor (i = 0; i < diff_queued_diff.nr; i++) {\n+\t\t\tconst char *name;\n+\t\t\tp = diff_queued_diff.queue[i];\n+\t\t\tname = p->one->path ? p->one->path : p->two->path;\n+\t\t\tif (!strcmp(name, origin->path))\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (!p)\n+\t\t\tdie(\"internal error in blame::find_origin\");\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(parent, origin->path);\n+\t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n+\t\t\tporigin->mode = p->one->mode;\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+\tclear_pathspec(&diff_opts.pathspec);\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 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+\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+\tdiff_setup_done(&diff_opts);\n+\n+\tif (is_null_oid(&origin->commit->object.oid))\n+\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n+\t\t\t       origin->commit->tree->object.oid.hash,\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(parent, p->one->path);\n+\t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n+\t\t\tporigin->mode = p->one->mode;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tdiff_flush(&diff_opts);\n+\tclear_pathspec(&diff_opts.pathspec);\n+\treturn porigin;\n+}\n+\n+/*\n+ * Append a new blame entry to a given output queue.\n+ */\n+static void add_blame_entry(struct blame_entry ***queue,\n+\t\t\t    const struct blame_entry *src)\n+{\n+\tstruct blame_entry *e = xmalloc(sizeof(*e));\n+\tmemcpy(e, src, sizeof(*e));\n+\torigin_incref(e->suspect);\n+\n+\te->next = **queue;\n+\t**queue = e;\n+\t*queue = &e->next;\n+}\n+\n+/*\n+ * src typically is on-stack; we want to copy the information in it to\n+ * a malloced blame_entry that gets added to the given queue.  The\n+ * origin of dst loses a refcnt.\n+ */\n+static void dup_entry(struct blame_entry ***queue,\n+\t\t      struct blame_entry *dst, struct blame_entry *src)\n+{\n+\torigin_incref(src->suspect);\n+\torigin_decref(dst->suspect);\n+\tmemcpy(dst, src, sizeof(*src));\n+\tdst->next = **queue;\n+\t**queue = dst;\n+\t*queue = &dst->next;\n+}\n+\n+const char *nth_line(struct scoreboard *sb, long 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.  Any assigned blame is moved to queue to\n+ * reflect the split.\n+ */\n+static void split_blame(struct blame_entry ***blamed,\n+\t\t\tstruct blame_entry ***unblamed,\n+\t\t\tstruct blame_entry *split,\n+\t\t\tstruct blame_entry *e)\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(unblamed, e, &split[0]);\n+\n+\t\t/* The last part -- me */\n+\t\tadd_blame_entry(unblamed, &split[2]);\n+\n+\t\t/* ... and the middle part -- parent */\n+\t\tadd_blame_entry(blamed, &split[1]);\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(blamed, e, &split[1]);\n+\telse if (split[0].suspect) {\n+\t\t/* me and then parent */\n+\t\tdup_entry(unblamed, e, &split[0]);\n+\t\tadd_blame_entry(blamed, &split[1]);\n+\t}\n+\telse {\n+\t\t/* parent and then me */\n+\t\tdup_entry(blamed, e, &split[1]);\n+\t\tadd_blame_entry(unblamed, &split[2]);\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+ * reverse_blame reverses the list given in head, appending tail.\n+ * That allows us to build lists in reverse order, then reverse them\n+ * afterwards.  This can be faster than building the list in proper\n+ * order right away.  The reason is that building in proper order\n+ * requires writing a link in the _previous_ element, while building\n+ * in reverse order just requires placing the list head into the\n+ * _current_ element.\n+ */\n+\n+static struct blame_entry *reverse_blame(struct blame_entry *head,\n+\t\t\t\t\t struct blame_entry *tail)\n+{\n+\twhile (head) {\n+\t\tstruct blame_entry *next = head->next;\n+\t\thead->next = tail;\n+\t\ttail = head;\n+\t\thead = next;\n+\t}\n+\treturn tail;\n+}\n+\n+/*\n+ * Process one hunk from the patch between the current suspect for\n+ * blame_entry e and its parent.  This first blames any unfinished\n+ * entries before the chunk (which is where target and parent start\n+ * differing) on the parent, and then splits blame entries at the\n+ * start and at the end of the difference region.  Since use of -M and\n+ * -C options may lead to overlapping/duplicate source line number\n+ * ranges, all we can rely on from sorting/merging is the order of the\n+ * first suspect line number.\n+ */\n+static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,\n+\t\t\tint tlno, int offset, int same,\n+\t\t\tstruct origin *parent)\n+{\n+\tstruct blame_entry *e = **srcq;\n+\tstruct blame_entry *samep = NULL, *diffp = NULL;\n+\n+\twhile (e && e->s_lno < tlno) {\n+\t\tstruct blame_entry *next = e->next;\n+\t\t/*\n+\t\t * current record starts before differing portion.  If\n+\t\t * it reaches into it, we need to split it up and\n+\t\t * examine the second part separately.\n+\t\t */\n+\t\tif (e->s_lno + e->num_lines > tlno) {\n+\t\t\t/* Move second half to a new record */\n+\t\t\tint len = tlno - e->s_lno;\n+\t\t\tstruct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));\n+\t\t\tn->suspect = e->suspect;\n+\t\t\tn->lno = e->lno + len;\n+\t\t\tn->s_lno = e->s_lno + len;\n+\t\t\tn->num_lines = e->num_lines - len;\n+\t\t\te->num_lines = len;\n+\t\t\te->score = 0;\n+\t\t\t/* Push new record to diffp */\n+\t\t\tn->next = diffp;\n+\t\t\tdiffp = n;\n+\t\t} else\n+\t\t\torigin_decref(e->suspect);\n+\t\t/* Pass blame for everything before the differing\n+\t\t * chunk to the parent */\n+\t\te->suspect = origin_incref(parent);\n+\t\te->s_lno += offset;\n+\t\te->next = samep;\n+\t\tsamep = e;\n+\t\te = next;\n+\t}\n+\t/*\n+\t * As we don't know how much of a common stretch after this\n+\t * diff will occur, the currently blamed parts are all that we\n+\t * can assign to the parent for now.\n+\t */\n+\n+\tif (samep) {\n+\t\t**dstq = reverse_blame(samep, **dstq);\n+\t\t*dstq = &samep->next;\n+\t}\n+\t/*\n+\t * Prepend the split off portions: everything after e starts\n+\t * after the blameable portion.\n+\t */\n+\te = reverse_blame(diffp, e);\n+\n+\t/*\n+\t * Now retain records on the target while parts are different\n+\t * from the parent.\n+\t */\n+\tsamep = NULL;\n+\tdiffp = NULL;\n+\twhile (e && e->s_lno < same) {\n+\t\tstruct blame_entry *next = e->next;\n+\n+\t\t/*\n+\t\t * If current record extends into sameness, need to split.\n+\t\t */\n+\t\tif (e->s_lno + e->num_lines > same) {\n+\t\t\t/*\n+\t\t\t * Move second half to a new record to be\n+\t\t\t * processed by later chunks\n+\t\t\t */\n+\t\t\tint len = same - e->s_lno;\n+\t\t\tstruct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));\n+\t\t\tn->suspect = origin_incref(e->suspect);\n+\t\t\tn->lno = e->lno + len;\n+\t\t\tn->s_lno = e->s_lno + len;\n+\t\t\tn->num_lines = e->num_lines - len;\n+\t\t\te->num_lines = len;\n+\t\t\te->score = 0;\n+\t\t\t/* Push new record to samep */\n+\t\t\tn->next = samep;\n+\t\t\tsamep = n;\n+\t\t}\n+\t\te->next = diffp;\n+\t\tdiffp = e;\n+\t\te = next;\n+\t}\n+\t**srcq = reverse_blame(diffp, reverse_blame(samep, e));\n+\t/* Move across elements that are in the unblamable portion */\n+\tif (diffp)\n+\t\t*srcq = &diffp->next;\n+}\n+\n+struct blame_chunk_cb_data {\n+\tstruct origin *parent;\n+\tlong offset;\n+\tstruct blame_entry **dstq;\n+\tstruct blame_entry **srcq;\n+};\n+\n+/* diff chunks are from parent to target */\n+static int blame_chunk_cb(long start_a, long count_a,\n+\t\t\t  long start_b, long count_b, void *data)\n+{\n+\tstruct blame_chunk_cb_data *d = data;\n+\tif (start_a - start_b != d->offset)\n+\t\tdie(\"internal error in blame::blame_chunk_cb\");\n+\tblame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,\n+\t\t    start_b + count_b, d->parent);\n+\td->offset = start_a + count_a - (start_b + count_b);\n+\treturn 0;\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 void pass_blame_to_parent(struct scoreboard *sb,\n+\t\t\t\t struct origin *target,\n+\t\t\t\t struct origin *parent)\n+{\n+\tmmfile_t file_p, file_o;\n+\tstruct blame_chunk_cb_data d;\n+\tstruct blame_entry *newdest = NULL;\n+\n+\tif (!target->suspects)\n+\t\treturn; /* nothing remains for this target */\n+\n+\td.parent = parent;\n+\td.offset = 0;\n+\td.dstq = &newdest; d.srcq = &target->suspects;\n+\n+\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n+\tfill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob);\n+\tsb->num_get_patch++;\n+\n+\tif (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts))\n+\t\tdie(\"unable to generate diff (%s -> %s)\",\n+\t\t    oid_to_hex(&parent->commit->object.oid),\n+\t\t    oid_to_hex(&target->commit->object.oid));\n+\t/* The rest are the same as the parent */\n+\tblame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);\n+\t*d.dstq = NULL;\n+\tqueue_blames(sb, parent, newdest);\n+\n+\treturn;\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+unsigned ent_score(struct 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 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 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 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 int handle_split_cb(long start_a, long count_a,\n+\t\t\t   long start_b, long count_b, void *data)\n+{\n+\tstruct handle_split_cb_data *d = data;\n+\thandle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,\n+\t\t     d->split);\n+\td->plno = start_a + count_a;\n+\td->tlno = start_b + count_b;\n+\treturn 0;\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 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+\tmmfile_t file_o;\n+\tstruct handle_split_cb_data d;\n+\n+\tmemset(&d, 0, sizeof(d));\n+\td.sb = sb; d.ent = ent; d.parent = parent; d.split = split;\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+\tfile_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;\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(split, 0, sizeof(struct blame_entry [3]));\n+\tif (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts))\n+\t\tdie(\"unable to generate diff (%s)\",\n+\t\t    oid_to_hex(&parent->commit->object.oid));\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+/* Move all blame entries from list *source that have a score smaller\n+ * than score_min to the front of list *small.\n+ * Returns a pointer to the link pointing to the old head of the small list.\n+ */\n+\n+static struct blame_entry **filter_small(struct scoreboard *sb,\n+\t\t\t\t\t struct blame_entry **small,\n+\t\t\t\t\t struct blame_entry **source,\n+\t\t\t\t\t unsigned score_min)\n+{\n+\tstruct blame_entry *p = *source;\n+\tstruct blame_entry *oldsmall = *small;\n+\twhile (p) {\n+\t\tif (ent_score(sb, p) <= score_min) {\n+\t\t\t*small = p;\n+\t\t\tsmall = &p->next;\n+\t\t\tp = *small;\n+\t\t} else {\n+\t\t\t*source = p;\n+\t\t\tsource = &p->next;\n+\t\t\tp = *source;\n+\t\t}\n+\t}\n+\t*small = oldsmall;\n+\t*source = NULL;\n+\treturn small;\n+}\n+\n+/*\n+ * See if lines currently target is suspected for can be attributed to\n+ * parent.\n+ */\n+static void find_move_in_parent(struct scoreboard *sb,\n+\t\t\t\tstruct blame_entry ***blamed,\n+\t\t\t\tstruct blame_entry **toosmall,\n+\t\t\t\tstruct origin *target,\n+\t\t\t\tstruct origin *parent)\n+{\n+\tstruct blame_entry *e, split[3];\n+\tstruct blame_entry *unblamed = target->suspects;\n+\tstruct blame_entry *leftover = NULL;\n+\tmmfile_t file_p;\n+\n+\tif (!unblamed)\n+\t\treturn; /* nothing remains for this target */\n+\n+\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n+\tif (!file_p.ptr)\n+\t\treturn;\n+\n+\t/* At each iteration, unblamed has a NULL-terminated list of\n+\t * entries that have not yet been tested for blame.  leftover\n+\t * contains the reversed list of entries that have been tested\n+\t * without being assignable to the parent.\n+\t */\n+\tdo {\n+\t\tstruct blame_entry **unblamedtail = &unblamed;\n+\t\tstruct blame_entry *next;\n+\t\tfor (e = unblamed; e; e = next) {\n+\t\t\tnext = e->next;\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->blame_move_score < ent_score(sb, &split[1])) {\n+\t\t\t\tsplit_blame(blamed, &unblamedtail, split, e);\n+\t\t\t} else {\n+\t\t\t\te->next = leftover;\n+\t\t\t\tleftover = e;\n+\t\t\t}\n+\t\t\tdecref_split(split);\n+\t\t}\n+\t\t*unblamedtail = NULL;\n+\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->blame_move_score);\n+\t} while (unblamed);\n+\ttarget->suspects = reverse_blame(leftover, NULL);\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_entry *unblamed,\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 = unblamed, num_ents = 0; e; e = e->next)\n+\t\tnum_ents++;\n+\tif (num_ents) {\n+\t\tblame_list = xcalloc(num_ents, sizeof(struct blame_list));\n+\t\tfor (e = unblamed, i = 0; e; e = e->next)\n+\t\t\tblame_list[i++].ent = e;\n+\t}\n+\t*num_ents_p = num_ents;\n+\treturn blame_list;\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 void find_copy_in_parent(struct scoreboard *sb,\n+\t\t\t\tstruct blame_entry ***blamed,\n+\t\t\t\tstruct blame_entry **toosmall,\n+\t\t\t\tstruct origin *target,\n+\t\t\t\tstruct commit *parent,\n+\t\t\t\tstruct origin *porigin,\n+\t\t\t\tint opt)\n+{\n+\tstruct diff_options diff_opts;\n+\tint i, j;\n+\tstruct blame_list *blame_list;\n+\tint num_ents;\n+\tstruct blame_entry *unblamed = target->suspects;\n+\tstruct blame_entry *leftover = NULL;\n+\n+\tif (!unblamed)\n+\t\treturn; /* 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+\tdiff_setup_done(&diff_opts);\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_oid(&target->commit->object.oid))\n+\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n+\t\t\t       target->commit->tree->object.oid.hash,\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+\tdo {\n+\t\tstruct blame_entry **unblamedtail = &unblamed;\n+\t\tblame_list = setup_blame_list(unblamed, &num_ents);\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(parent, p->one->path);\n+\t\t\toidcpy(&norigin->blob_oid, &p->one->oid);\n+\t\t\tnorigin->mode = p->one->mode;\n+\t\t\tfill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob);\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->blame_copy_score < ent_score(sb, &split[1])) {\n+\t\t\t\tsplit_blame(blamed, &unblamedtail, split,\n+\t\t\t\t\t    blame_list[j].ent);\n+\t\t\t} else {\n+\t\t\t\tblame_list[j].ent->next = leftover;\n+\t\t\t\tleftover = blame_list[j].ent;\n+\t\t\t}\n+\t\t\tdecref_split(split);\n+\t\t}\n+\t\tfree(blame_list);\n+\t\t*unblamedtail = NULL;\n+\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->blame_copy_score);\n+\t} while (unblamed);\n+\ttarget->suspects = reverse_blame(leftover, NULL);\n+\tdiff_flush(&diff_opts);\n+\tclear_pathspec(&diff_opts.pathspec);\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 scoreboard *sb,\n+\t\t\t     struct origin *origin, struct origin *porigin)\n+{\n+\tstruct blame_entry *e, *suspects;\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+\tsuspects = origin->suspects;\n+\torigin->suspects = NULL;\n+\tfor (e = suspects; e; e = e->next) {\n+\t\torigin_incref(porigin);\n+\t\torigin_decref(e->suspect);\n+\t\te->suspect = porigin;\n+\t}\n+\tqueue_blames(sb, porigin, suspects);\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 rev_info *revs, struct commit *commit,\n+\t\t\t\t\tint reverse)\n+{\n+\tif (!reverse) {\n+\t\tif (revs->first_parent_only &&\n+\t\t    commit->parents &&\n+\t\t    commit->parents->next) {\n+\t\t\tfree_commit_list(commit->parents->next);\n+\t\t\tcommit->parents->next = NULL;\n+\t\t}\n+\t\treturn commit->parents;\n+\t}\n+\treturn lookup_decoration(&revs->children, &commit->object);\n+}\n+\n+static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse)\n+{\n+\tstruct commit_list *l = first_scapegoat(revs, commit, reverse);\n+\treturn commit_list_count(l);\n+}\n+\n+/* Distribute collected unsorted blames to the respected sorted lists\n+ * in the various origins.\n+ */\n+static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)\n+{\n+\tblamed = blame_sort(blamed, compare_blame_suspect);\n+\twhile (blamed)\n+\t{\n+\t\tstruct origin *porigin = blamed->suspect;\n+\t\tstruct blame_entry *suspects = NULL;\n+\t\tdo {\n+\t\t\tstruct blame_entry *next = blamed->next;\n+\t\t\tblamed->next = suspects;\n+\t\t\tsuspects = blamed;\n+\t\t\tblamed = next;\n+\t\t} while (blamed && blamed->suspect == porigin);\n+\t\tsuspects = reverse_blame(suspects, NULL);\n+\t\tqueue_blames(sb, porigin, suspects);\n+\t}\n+}\n+\n+#define MAXSG 16\n+\n+static void pass_blame(struct 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+\tstruct blame_entry *toosmall = NULL;\n+\tstruct blame_entry *blames, **blametail = &blames;\n+\n+\tnum_sg = num_scapegoats(revs, commit, sb->reverse);\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 - sb->no_whole_file_rename; pass++) {\n+\t\tstruct origin *(*find)(struct scoreboard *,\n+\t\t\t\t       struct commit *, struct origin *);\n+\t\tfind = pass ? find_rename : find_origin;\n+\n+\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\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 (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {\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    !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) {\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->num_commits++;\n+\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\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\tpass_blame_to_parent(sb, origin, porigin);\n+\t\tif (!origin->suspects)\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\tfilter_small(sb, &toosmall, &origin->suspects, sb->blame_move_score);\n+\t\tif (origin->suspects) {\n+\t\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n+\t\t\t     i < num_sg && sg;\n+\t\t\t     sg = sg->next, i++) {\n+\t\t\t\tstruct origin *porigin = sg_origin[i];\n+\t\t\t\tif (!porigin)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tfind_move_in_parent(sb, &blametail, &toosmall, origin, porigin);\n+\t\t\t\tif (!origin->suspects)\n+\t\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Optionally find copies from parents' files.\n+\t */\n+\tif (opt & PICKAXE_BLAME_COPY) {\n+\t\tif (sb->blame_copy_score > sb->blame_move_score)\n+\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->blame_copy_score);\n+\t\telse if (sb->blame_copy_score < sb->blame_move_score) {\n+\t\t\torigin->suspects = blame_merge(origin->suspects, toosmall);\n+\t\t\ttoosmall = NULL;\n+\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->blame_copy_score);\n+\t\t}\n+\t\tif (!origin->suspects)\n+\t\t\tgoto finish;\n+\n+\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\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\tfind_copy_in_parent(sb, &blametail, &toosmall,\n+\t\t\t\t\t    origin, sg->item, porigin, opt);\n+\t\t\tif (!origin->suspects)\n+\t\t\t\tgoto finish;\n+\t\t}\n+\t}\n+\n+finish:\n+\t*blametail = NULL;\n+\tdistribute_blame(sb, blames);\n+\t/*\n+\t * prepend toosmall to origin->suspects\n+\t *\n+\t * There is no point in sorting: this ends up on a big\n+\t * unsorted list in the caller anyway.\n+\t */\n+\tif (toosmall) {\n+\t\tstruct blame_entry **tail = &toosmall;\n+\t\twhile (*tail)\n+\t\t\ttail = &(*tail)->next;\n+\t\t*tail = origin->suspects;\n+\t\torigin->suspects = toosmall;\n+\t}\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+ * The main loop -- while we have blobs with lines whose true origin\n+ * is still unknown, pick one blob, and allow its lines to pass blames\n+ * to its parents. */\n+void assign_blame(struct scoreboard *sb, int opt)\n+{\n+\tstruct rev_info *revs = sb->revs;\n+\tstruct commit *commit = prio_queue_get(&sb->commits);\n+\n+\twhile (commit) {\n+\t\tstruct blame_entry *ent;\n+\t\tstruct origin *suspect = commit->util;\n+\n+\t\t/* find one suspect to break down */\n+\t\twhile (suspect && !suspect->suspects)\n+\t\t\tsuspect = suspect->next;\n+\n+\t\tif (!suspect) {\n+\t\t\tcommit = prio_queue_get(&sb->commits);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tassert(commit == suspect->commit);\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\tparse_commit(commit);\n+\t\tif (sb->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->show_root)\n+\t\t\tcommit->object.flags |= UNINTERESTING;\n+\n+\t\t/* Take responsibility for the remaining entries */\n+\t\tent = suspect->suspects;\n+\t\tif (ent) {\n+\t\t\tsuspect->guilty = 1;\n+\t\t\tfor (;;) {\n+\t\t\t\tstruct blame_entry *next = ent->next;\n+\t\t\t\tif (sb->found_guilty_entry)\n+\t\t\t\t\tsb->found_guilty_entry(ent, sb->found_guilty_entry_data);\n+\t\t\t\tif (next) {\n+\t\t\t\t\tent = next;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t\tent->next = sb->ent;\n+\t\t\t\tsb->ent = suspect->suspects;\n+\t\t\t\tsuspect->suspects = NULL;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\torigin_decref(suspect);\n+\n+\t\tif (sb->sanity_check) /* sanity */\n+\t\t\tsb->sanity_check(sb);\n+\t}\n+}\ndiff --git a/scoreboard.h b/scoreboard.h\nnew file mode 100644\nindex 0000000..7e9e313\n--- /dev/null\n+++ b/scoreboard.h\n@@ -0,0 +1,74 @@\n+#ifndef SCOREBOARD_H\n+#define SCOREBOARD_H\n+\n+#include \"commit.h\"\n+#include \"revision.h\"\n+#include \"prio-queue.h\"\n+#include \"origin.h\"\n+\n+#define PICKAXE_BLAME_MOVE\t\t01\n+#define PICKAXE_BLAME_COPY\t\t02\n+#define PICKAXE_BLAME_COPY_HARDER\t04\n+#define PICKAXE_BLAME_COPY_HARDEST\t010\n+\n+#define BLAME_DEFAULT_MOVE_SCORE\t20\n+#define BLAME_DEFAULT_COPY_SCORE\t40\n+\n+/*\n+ * The current state of the blame assignment.\n+ */\n+struct scoreboard {\n+\t/* the final commit (i.e. where we started digging from) */\n+\tstruct commit *final;\n+\t/* Priority queue for commits with unassigned blame records */\n+\tstruct prio_queue commits;\n+\tstruct rev_info *revs;\n+\tconst char *path;\n+\n+\t/*\n+\t * The contents in the final image.\n+\t * Used by many functions to obtain contents of the nth line,\n+\t * indexed with scoreboard.lineno[blame_entry.lno].\n+\t */\n+\tconst char *final_buf;\n+\tunsigned long final_buf_size;\n+\n+\t/* linked list of blames */\n+\tstruct blame_entry *ent;\n+\n+\t/* look-up a line in the final buffer */\n+\tint num_lines;\n+\tint *lineno;\n+\n+\t/*\n+\t * blame for a blame_entry with score lower than these thresholds\n+\t * is not passed to the parent using move/copy logic.\n+\t */\n+\tunsigned blame_move_score;\n+\tunsigned blame_copy_score;\n+\n+\t/* stats */\n+\tint num_read_blob;\n+\tint num_get_patch;\n+\tint num_commits;\n+\n+\t/* options */\n+\tint show_root;\n+\tint reverse;\n+\tint xdl_opts;\n+\tint no_whole_file_rename;\n+\n+\t/* callbacks */\n+\tvoid(*sanity_check)(struct scoreboard *);\n+\tvoid(*found_guilty_entry)(struct blame_entry *, void *);\n+\n+\tvoid *found_guilty_entry_data;\n+};\n+\n+void coalesce(struct scoreboard *sb);\n+void blame_sort_final(struct scoreboard *sb);\n+unsigned ent_score(struct scoreboard *sb, struct blame_entry *e);\n+void assign_blame(struct scoreboard *sb, int opt);\n+const char *nth_line(struct scoreboard *sb, long lno);\n+\n+#endif /* SCOREBOARD_H */\n-- \n2.9.3\n\n"},{"id":"318826","messageId":"20170505052729.7576-7-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH 06/10] Move fake_working_tree_commit() to lib","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:25Z","receivedAt":"2017-05-05T05:28:11Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n Makefile        |   1 +\n builtin/blame.c | 198 +------------------------------------------------------\n commit-fake.c   | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n commit-fake.h   |   9 +++\n 4 files changed, 211 insertions(+), 197 deletions(-)\n create mode 100644 commit-fake.c\n create mode 100644 commit-fake.h\n\ndiff --git a/Makefile b/Makefile\nindex 8cbb56c..b197a37 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -727,6 +727,7 @@ LIB_OBJS += color.o\n LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n LIB_OBJS += commit.o\n+LIB_OBJS += commit-fake.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\n LIB_OBJS += config.o\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 7ee84c1..c873cc2 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -16,7 +16,6 @@\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 \"mergesort.h\"\n@@ -29,6 +28,7 @@\n #include \"dir.h\"\n #include \"progress.h\"\n #include \"origin.h\"\n+#include \"commit-fake.h\"\n \n static char blame_usage[] = N_(\"git blame [<options>] [<rev-opts>] [<rev>] [--] <file>\");\n \n@@ -2044,202 +2044,6 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n-static void verify_working_tree_path(struct commit *work_tree, const char *path)\n-{\n-\tstruct commit_list *parents;\n-\tint pos;\n-\n-\tfor (parents = work_tree->parents; parents; parents = parents->next) {\n-\t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n-\t\tstruct object_id blob_oid;\n-\t\tunsigned mode;\n-\n-\t\tif (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&\n-\t\t    sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)\n-\t\t\treturn;\n-\t}\n-\n-\tpos = cache_name_pos(path, strlen(path));\n-\tif (pos >= 0)\n-\t\t; /* path is in the index */\n-\telse if (-1 - pos < active_nr &&\n-\t\t !strcmp(active_cache[-1 - pos]->name, path))\n-\t\t; /* path is in the index, unmerged */\n-\telse\n-\t\tdie(\"no such path '%s' in HEAD\", path);\n-}\n-\n-static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)\n-{\n-\tstruct commit *parent;\n-\n-\tparent = lookup_commit_reference(oid->hash);\n-\tif (!parent)\n-\t\tdie(\"no such commit %s\", oid_to_hex(oid));\n-\treturn &commit_list_insert(parent, tail)->next;\n-}\n-\n-static void append_merge_parents(struct commit_list **tail)\n-{\n-\tint merge_head;\n-\tstruct strbuf line = STRBUF_INIT;\n-\n-\tmerge_head = open(git_path_merge_head(), O_RDONLY);\n-\tif (merge_head < 0) {\n-\t\tif (errno == ENOENT)\n-\t\t\treturn;\n-\t\tdie(\"cannot open '%s' for reading\", git_path_merge_head());\n-\t}\n-\n-\twhile (!strbuf_getwholeline_fd(&line, merge_head, '\\n')) {\n-\t\tstruct object_id oid;\n-\t\tif (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))\n-\t\t\tdie(\"unknown line in '%s': %s\", git_path_merge_head(), line.buf);\n-\t\ttail = append_parent(tail, &oid);\n-\t}\n-\tclose(merge_head);\n-\tstrbuf_release(&line);\n-}\n-\n-/*\n- * This isn't as simple as passing sb->buf and sb->len, because we\n- * want to transfer ownership of the buffer to the commit (so we\n- * must use detach).\n- */\n-static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)\n-{\n-\tsize_t len;\n-\tvoid *buf = strbuf_detach(sb, &len);\n-\tset_commit_buffer(c, buf, len);\n-}\n-\n-/*\n- * Prepare a dummy commit that represents the work tree (or staged) item.\n- * Note that annotating work tree item never works in the reverse.\n- */\n-static struct commit *fake_working_tree_commit(struct diff_options *opt,\n-\t\t\t\t\t       const char *path,\n-\t\t\t\t\t       const char *contents_from)\n-{\n-\tstruct commit *commit;\n-\tstruct origin *origin;\n-\tstruct commit_list **parent_tail, *parent;\n-\tstruct object_id head_oid;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tconst char *ident;\n-\ttime_t now;\n-\tint size, len;\n-\tstruct cache_entry *ce;\n-\tunsigned mode;\n-\tstruct strbuf msg = STRBUF_INIT;\n-\n-\tread_cache();\n-\ttime(&now);\n-\tcommit = alloc_commit_node();\n-\tcommit->object.parsed = 1;\n-\tcommit->date = now;\n-\tparent_tail = &commit->parents;\n-\n-\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, head_oid.hash, NULL))\n-\t\tdie(\"no such ref: HEAD\");\n-\n-\tparent_tail = append_parent(parent_tail, &head_oid);\n-\tappend_merge_parents(parent_tail);\n-\tverify_working_tree_path(commit, path);\n-\n-\torigin = make_origin(commit, path);\n-\n-\tident = fmt_ident(\"Not Committed Yet\", \"not.committed.yet\", NULL, 0);\n-\tstrbuf_addstr(&msg, \"tree 0000000000000000000000000000000000000000\\n\");\n-\tfor (parent = commit->parents; parent; parent = parent->next)\n-\t\tstrbuf_addf(&msg, \"parent %s\\n\",\n-\t\t\t    oid_to_hex(&parent->item->object.oid));\n-\tstrbuf_addf(&msg,\n-\t\t    \"author %s\\n\"\n-\t\t    \"committer %s\\n\\n\"\n-\t\t    \"Version of %s from %s\\n\",\n-\t\t    ident, ident, path,\n-\t\t    (!contents_from ? path :\n-\t\t     (!strcmp(contents_from, \"-\") ? \"standard input\" : contents_from)));\n-\tset_commit_buffer_from_strbuf(commit, &msg);\n-\n-\tif (!contents_from || strcmp(\"-\", contents_from)) {\n-\t\tstruct stat st;\n-\t\tconst char *read_from;\n-\t\tchar *buf_ptr;\n-\t\tunsigned long buf_len;\n-\n-\t\tif (contents_from) {\n-\t\t\tif (stat(contents_from, &st) < 0)\n-\t\t\t\tdie_errno(\"Cannot stat '%s'\", contents_from);\n-\t\t\tread_from = contents_from;\n-\t\t}\n-\t\telse {\n-\t\t\tif (lstat(path, &st) < 0)\n-\t\t\t\tdie_errno(\"Cannot lstat '%s'\", path);\n-\t\t\tread_from = path;\n-\t\t}\n-\t\tmode = canon_mode(st.st_mode);\n-\n-\t\tswitch (st.st_mode & S_IFMT) {\n-\t\tcase S_IFREG:\n-\t\t\tif (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&\n-\t\t\t    textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))\n-\t\t\t\tstrbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);\n-\t\t\telse if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)\n-\t\t\t\tdie_errno(\"cannot open or read '%s'\", read_from);\n-\t\t\tbreak;\n-\t\tcase S_IFLNK:\n-\t\t\tif (strbuf_readlink(&buf, read_from, st.st_size) < 0)\n-\t\t\t\tdie_errno(\"cannot readlink '%s'\", read_from);\n-\t\t\tbreak;\n-\t\tdefault:\n-\t\t\tdie(\"unsupported file type %s\", read_from);\n-\t\t}\n-\t}\n-\telse {\n-\t\t/* Reading from stdin */\n-\t\tmode = 0;\n-\t\tif (strbuf_read(&buf, 0, 0) < 0)\n-\t\t\tdie_errno(\"failed to read from stdin\");\n-\t}\n-\tconvert_to_git(path, buf.buf, buf.len, &buf, 0);\n-\torigin->file.ptr = buf.buf;\n-\torigin->file.size = buf.len;\n-\tpretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);\n-\n-\t/*\n-\t * Read the current index, replace the path entry with\n-\t * origin->blob_sha1 without mucking with its mode or type\n-\t * bits; we are not going to write this index out -- we just\n-\t * want to run \"diff-index --cached\".\n-\t */\n-\tdiscard_cache();\n-\tread_cache();\n-\n-\tlen = strlen(path);\n-\tif (!mode) {\n-\t\tint pos = cache_name_pos(path, len);\n-\t\tif (0 <= pos)\n-\t\t\tmode = active_cache[pos]->ce_mode;\n-\t\telse\n-\t\t\t/* Let's not bother reading from HEAD tree */\n-\t\t\tmode = S_IFREG | 0644;\n-\t}\n-\tsize = cache_entry_size(len);\n-\tce = xcalloc(1, size);\n-\toidcpy(&ce->oid, &origin->blob_oid);\n-\tmemcpy(ce->name, path, len);\n-\tce->ce_flags = create_ce_flags(0);\n-\tce->ce_namelen = len;\n-\tce->ce_mode = create_ce_mode(mode);\n-\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n-\n-\tcache_tree_invalidate_path(&the_index, path);\n-\n-\treturn commit;\n-}\n-\n static struct commit *find_single_final(struct rev_info *revs,\n \t\t\t\t\tconst char **name_p)\n {\ndiff --git a/commit-fake.c b/commit-fake.c\nnew file mode 100644\nindex 0000000..d73e0d8\n--- /dev/null\n+++ b/commit-fake.c\n@@ -0,0 +1,200 @@\n+#include \"refs.h\"\n+#include \"cache-tree.h\"\n+#include \"origin.h\"\n+#include \"commit-fake.h\"\n+\n+static void verify_working_tree_path(struct commit *work_tree, const char *path)\n+{\n+\tstruct commit_list *parents;\n+\tint pos;\n+\n+\tfor (parents = work_tree->parents; parents; parents = parents->next) {\n+\t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n+\t\tstruct object_id blob_oid;\n+\t\tunsigned mode;\n+\n+\t\tif (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&\n+\t\t    sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)\n+\t\t\treturn;\n+\t}\n+\n+\tpos = cache_name_pos(path, strlen(path));\n+\tif (pos >= 0)\n+\t\t; /* path is in the index */\n+\telse if (-1 - pos < active_nr &&\n+\t\t !strcmp(active_cache[-1 - pos]->name, path))\n+\t\t; /* path is in the index, unmerged */\n+\telse\n+\t\tdie(\"no such path '%s' in HEAD\", path);\n+}\n+\n+static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)\n+{\n+\tstruct commit *parent;\n+\n+\tparent = lookup_commit_reference(oid->hash);\n+\tif (!parent)\n+\t\tdie(\"no such commit %s\", oid_to_hex(oid));\n+\treturn &commit_list_insert(parent, tail)->next;\n+}\n+\n+static void append_merge_parents(struct commit_list **tail)\n+{\n+\tint merge_head;\n+\tstruct strbuf line = STRBUF_INIT;\n+\n+\tmerge_head = open(git_path_merge_head(), O_RDONLY);\n+\tif (merge_head < 0) {\n+\t\tif (errno == ENOENT)\n+\t\t\treturn;\n+\t\tdie(\"cannot open '%s' for reading\", git_path_merge_head());\n+\t}\n+\n+\twhile (!strbuf_getwholeline_fd(&line, merge_head, '\\n')) {\n+\t\tstruct object_id oid;\n+\t\tif (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))\n+\t\t\tdie(\"unknown line in '%s': %s\", git_path_merge_head(), line.buf);\n+\t\ttail = append_parent(tail, &oid);\n+\t}\n+\tclose(merge_head);\n+\tstrbuf_release(&line);\n+}\n+\n+/*\n+ * This isn't as simple as passing sb->buf and sb->len, because we\n+ * want to transfer ownership of the buffer to the commit (so we\n+ * must use detach).\n+ */\n+static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)\n+{\n+\tsize_t len;\n+\tvoid *buf = strbuf_detach(sb, &len);\n+\tset_commit_buffer(c, buf, len);\n+}\n+\n+/*\n+ * Prepare a dummy commit that represents the work tree (or staged) item.\n+ * Note that annotating work tree item never works in the reverse.\n+ */\n+struct commit *fake_working_tree_commit(struct diff_options *opt,\n+\t\t\t\t\tconst char *path,\n+\t\t\t\t\tconst char *contents_from)\n+{\n+\tstruct commit *commit;\n+\tstruct origin *origin;\n+\tstruct commit_list **parent_tail, *parent;\n+\tstruct object_id head_oid;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *ident;\n+\ttime_t now;\n+\tint size, len;\n+\tstruct cache_entry *ce;\n+\tunsigned mode;\n+\tstruct strbuf msg = STRBUF_INIT;\n+\n+\tread_cache();\n+\ttime(&now);\n+\tcommit = alloc_commit_node();\n+\tcommit->object.parsed = 1;\n+\tcommit->date = now;\n+\tparent_tail = &commit->parents;\n+\n+\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, head_oid.hash, NULL))\n+\t\tdie(\"no such ref: HEAD\");\n+\n+\tparent_tail = append_parent(parent_tail, &head_oid);\n+\tappend_merge_parents(parent_tail);\n+\tverify_working_tree_path(commit, path);\n+\n+\torigin = make_origin(commit, path);\n+\n+\tident = fmt_ident(\"Not Committed Yet\", \"not.committed.yet\", NULL, 0);\n+\tstrbuf_addstr(&msg, \"tree 0000000000000000000000000000000000000000\\n\");\n+\tfor (parent = commit->parents; parent; parent = parent->next)\n+\t\tstrbuf_addf(&msg, \"parent %s\\n\",\n+\t\t\t    oid_to_hex(&parent->item->object.oid));\n+\tstrbuf_addf(&msg,\n+\t\t    \"author %s\\n\"\n+\t\t    \"committer %s\\n\\n\"\n+\t\t    \"Version of %s from %s\\n\",\n+\t\t    ident, ident, path,\n+\t\t    (!contents_from ? path :\n+\t\t     (!strcmp(contents_from, \"-\") ? \"standard input\" : contents_from)));\n+\tset_commit_buffer_from_strbuf(commit, &msg);\n+\n+\tif (!contents_from || strcmp(\"-\", contents_from)) {\n+\t\tstruct stat st;\n+\t\tconst char *read_from;\n+\t\tchar *buf_ptr;\n+\t\tunsigned long buf_len;\n+\n+\t\tif (contents_from) {\n+\t\t\tif (stat(contents_from, &st) < 0)\n+\t\t\t\tdie_errno(\"Cannot stat '%s'\", contents_from);\n+\t\t\tread_from = contents_from;\n+\t\t}\n+\t\telse {\n+\t\t\tif (lstat(path, &st) < 0)\n+\t\t\t\tdie_errno(\"Cannot lstat '%s'\", path);\n+\t\t\tread_from = path;\n+\t\t}\n+\t\tmode = canon_mode(st.st_mode);\n+\n+\t\tswitch (st.st_mode & S_IFMT) {\n+\t\tcase S_IFREG:\n+\t\t\tif (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&\n+\t\t\t    textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))\n+\t\t\t\tstrbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);\n+\t\t\telse if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)\n+\t\t\t\tdie_errno(\"cannot open or read '%s'\", read_from);\n+\t\t\tbreak;\n+\t\tcase S_IFLNK:\n+\t\t\tif (strbuf_readlink(&buf, read_from, st.st_size) < 0)\n+\t\t\t\tdie_errno(\"cannot readlink '%s'\", read_from);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tdie(\"unsupported file type %s\", read_from);\n+\t\t}\n+\t}\n+\telse {\n+\t\t/* Reading from stdin */\n+\t\tmode = 0;\n+\t\tif (strbuf_read(&buf, 0, 0) < 0)\n+\t\t\tdie_errno(\"failed to read from stdin\");\n+\t}\n+\tconvert_to_git(path, buf.buf, buf.len, &buf, 0);\n+\torigin->file.ptr = buf.buf;\n+\torigin->file.size = buf.len;\n+\tpretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);\n+\n+\t/*\n+\t * Read the current index, replace the path entry with\n+\t * origin->blob_sha1 without mucking with its mode or type\n+\t * bits; we are not going to write this index out -- we just\n+\t * want to run \"diff-index --cached\".\n+\t */\n+\tdiscard_cache();\n+\tread_cache();\n+\n+\tlen = strlen(path);\n+\tif (!mode) {\n+\t\tint pos = cache_name_pos(path, len);\n+\t\tif (0 <= pos)\n+\t\t\tmode = active_cache[pos]->ce_mode;\n+\t\telse\n+\t\t\t/* Let's not bother reading from HEAD tree */\n+\t\t\tmode = S_IFREG | 0644;\n+\t}\n+\tsize = cache_entry_size(len);\n+\tce = xcalloc(1, size);\n+\toidcpy(&ce->oid, &origin->blob_oid);\n+\tmemcpy(ce->name, path, len);\n+\tce->ce_flags = create_ce_flags(0);\n+\tce->ce_namelen = len;\n+\tce->ce_mode = create_ce_mode(mode);\n+\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n+\n+\tcache_tree_invalidate_path(&the_index, path);\n+\n+\treturn commit;\n+}\ndiff --git a/commit-fake.h b/commit-fake.h\nnew file mode 100644\nindex 0000000..8232714\n--- /dev/null\n+++ b/commit-fake.h\n@@ -0,0 +1,9 @@\n+#ifndef COMMIT_FAKE_H\n+#define COMMIT_FAKE_H\n+\n+#include \"commit.h\"\n+#include \"diff.h\"\n+\n+struct commit *fake_working_tree_commit(struct diff_options *opt, const char *path, const char *contents_from);\n+\n+#endif /* COMMIT_FAKE_H */\n-- \n2.9.3\n\n"},{"id":"318827","messageId":"20170505052729.7576-3-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH 02/10] Move textconv_object to be with other textconv methods","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:21Z","receivedAt":"2017-05-05T05:28:12Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin.h          |  2 --\n builtin/blame.c    | 28 ----------------------------\n builtin/cat-file.c |  1 +\n diff.c             | 23 +++++++++++++++++++++++\n diff.h             |  7 +++++++\n 5 files changed, 31 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin.h b/builtin.h\nindex 9e4a898..498ac80 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -25,8 +25,6 @@ struct fmt_merge_msg_opts {\n extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t\t\t struct fmt_merge_msg_opts *);\n \n-extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);\n-\n extern int is_builtin(const char *s);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 42c56eb..c419981 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -147,34 +147,6 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n }\n \n /*\n- * Prepare diff_filespec and convert it using diff textconv API\n- * if the textconv driver exists.\n- * Return 1 if the conversion succeeds, 0 otherwise.\n- */\n-int textconv_object(const char *path,\n-\t\t    unsigned mode,\n-\t\t    const struct object_id *oid,\n-\t\t    int oid_valid,\n-\t\t    char **buf,\n-\t\t    unsigned long *buf_size)\n-{\n-\tstruct diff_filespec *df;\n-\tstruct userdiff_driver *textconv;\n-\n-\tdf = alloc_filespec(path);\n-\tfill_filespec(df, oid->hash, oid_valid, mode);\n-\ttextconv = get_textconv(df);\n-\tif (!textconv) {\n-\t\tfree_filespec(df);\n-\t\treturn 0;\n-\t}\n-\n-\t*buf_size = fill_textconv(textconv, df, buf);\n-\tfree_filespec(df);\n-\treturn 1;\n-}\n-\n-/*\n  * Given an origin, prepare mmfile_t structure to be used by the\n  * diff machinery\n  */\ndiff --git a/builtin/cat-file.c b/builtin/cat-file.c\nindex 1890d7a..79a2c82 100644\n--- a/builtin/cat-file.c\n+++ b/builtin/cat-file.c\n@@ -5,6 +5,7 @@\n  */\n #include \"cache.h\"\n #include \"builtin.h\"\n+#include \"diff.h\"\n #include \"parse-options.h\"\n #include \"userdiff.h\"\n #include \"streaming.h\"\ndiff --git a/diff.c b/diff.c\nindex 11eef1c..a62e989 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -5270,6 +5270,29 @@ size_t fill_textconv(struct userdiff_driver *driver,\n \treturn size;\n }\n \n+int textconv_object(const char *path,\n+\t\t    unsigned mode,\n+\t\t    const struct object_id *oid,\n+\t\t    int oid_valid,\n+\t\t    char **buf,\n+\t\t    unsigned long *buf_size)\n+{\n+\tstruct diff_filespec *df;\n+\tstruct userdiff_driver *textconv;\n+\n+\tdf = alloc_filespec(path);\n+\tfill_filespec(df, oid->hash, oid_valid, mode);\n+\ttextconv = get_textconv(df);\n+\tif (!textconv) {\n+\t\tfree_filespec(df);\n+\t\treturn 0;\n+\t}\n+\n+\t*buf_size = fill_textconv(textconv, df, buf);\n+\tfree_filespec(df);\n+\treturn 1;\n+}\n+\n void setup_diff_pager(struct diff_options *opt)\n {\n \t/*\ndiff --git a/diff.h b/diff.h\nindex 5be1ee7..52ebd54 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -385,6 +385,13 @@ extern size_t fill_textconv(struct userdiff_driver *driver,\n  */\n extern struct userdiff_driver *get_textconv(struct diff_filespec *one);\n \n+/*\n+ * Prepare diff_filespec and convert it using diff textconv API\n+ * if the textconv driver exists.\n+ * Return 1 if the conversion succeeds, 0 otherwise.\n+ */\n+extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);\n+\n extern int parse_rename_score(const char **cp_p);\n \n extern long parse_algorithm_value(const char *value);\n-- \n2.9.3\n\n"},{"id":"318828","messageId":"20170505052729.7576-5-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH 04/10] Remove unused parameter from get_origin()","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:23Z","receivedAt":"2017-05-05T05:28:14Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin/blame.c | 14 ++++++--------\n 1 file changed, 6 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex c419981..cc46f56 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -449,9 +449,7 @@ static struct origin *make_origin(struct commit *commit, const char *path)\n  * Locate an existing origin or create a new one.\n  * This moves the origin to front position in the commit util list.\n  */\n-static struct origin *get_origin(struct scoreboard *sb,\n-\t\t\t\t struct commit *commit,\n-\t\t\t\t const char *path)\n+static struct origin *get_origin(struct commit *commit, const char *path)\n {\n \tstruct origin *o, *l;\n \n@@ -543,7 +541,7 @@ static struct origin *find_origin(struct scoreboard *sb,\n \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\tporigin = get_origin(parent, origin->path);\n \t\toidcpy(&porigin->blob_oid, &origin->blob_oid);\n \t\tporigin->mode = origin->mode;\n \t} else {\n@@ -569,7 +567,7 @@ static struct origin *find_origin(struct scoreboard *sb,\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\tporigin = get_origin(parent, origin->path);\n \t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n \t\t\tporigin->mode = p->one->mode;\n \t\t\tbreak;\n@@ -615,7 +613,7 @@ static struct origin *find_rename(struct scoreboard *sb,\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\tporigin = get_origin(parent, p->one->path);\n \t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n \t\t\tporigin->mode = p->one->mode;\n \t\t\tbreak;\n@@ -1270,7 +1268,7 @@ static void find_copy_in_parent(struct scoreboard *sb,\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\tnorigin = get_origin(parent, p->one->path);\n \t\t\toidcpy(&norigin->blob_oid, &p->one->oid);\n \t\t\tnorigin->mode = p->one->mode;\n \t\t\tfill_origin_blob(&sb->revs->diffopt, norigin, &file_p);\n@@ -2806,7 +2804,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tsb.final_buf_size = o->file.size;\n \t}\n \telse {\n-\t\to = get_origin(&sb, sb.final, path);\n+\t\to = get_origin(sb.final, path);\n \t\tif (fill_blob_sha1_and_mode(o))\n \t\t\tdie(_(\"no such path %s in %s\"), path, final_commit_name);\n \n-- \n2.9.3\n\n"},{"id":"318829","messageId":"20170505052729.7576-11-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH 10/10] Move scoreboard init and setup to lib","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T05:27:29Z","receivedAt":"2017-05-05T05:28:15Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin/blame.c | 274 --------------------------------------------------------\n scoreboard.c    | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n scoreboard.h    |   2 +\n 3 files changed, 275 insertions(+), 274 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 3bde5a6..4e3079f 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -6,11 +6,8 @@\n  */\n \n #include \"cache.h\"\n-#include \"refs.h\"\n #include \"builtin.h\"\n #include \"commit.h\"\n-#include \"tag.h\"\n-#include \"tree-walk.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"quote.h\"\n@@ -25,7 +22,6 @@\n #include \"dir.h\"\n #include \"progress.h\"\n #include \"origin.h\"\n-#include \"commit-fake.h\"\n #include \"scoreboard.h\"\n \n static char blame_usage[] = N_(\"git blame [<options>] [<rev-opts>] [<rev>] [--] <file>\");\n@@ -72,39 +68,6 @@ struct progress_info {\n \tint blamed_lines;\n };\n \n-static int compare_commits_by_reverse_commit_date(const void *a,\n-\t\t\t\t\t\t  const void *b,\n-\t\t\t\t\t\t  void *c)\n-{\n-\treturn -compare_commits_by_commit_date(a, b, c);\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- * This also fills origin->mode for corresponding tree path.\n- */\n-static int fill_blob_sha1_and_mode(struct origin *origin)\n-{\n-\tif (!is_null_oid(&origin->blob_oid))\n-\t\treturn 0;\n-\tif (get_tree_entry(origin->commit->object.oid.hash,\n-\t\t\t   origin->path,\n-\t\t\t   origin->blob_oid.hash, &origin->mode))\n-\t\tgoto error_out;\n-\tif (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)\n-\t\tgoto error_out;\n-\treturn 0;\n- error_out:\n-\toidclr(&origin->blob_oid);\n-\torigin->mode = S_IFINVALID;\n-\treturn -1;\n-}\n-\n static const char *nth_line_cb(void *data, long lno)\n {\n \treturn nth_line((struct scoreboard *)data, lno);\n@@ -512,40 +475,6 @@ static void output(struct scoreboard *sb, int option)\n \t}\n }\n \n-static const char *get_next_line(const char *start, const char *end)\n-{\n-\tconst char *nl = memchr(start, '\\n', end - start);\n-\treturn nl ? nl + 1 : end;\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 scoreboard *sb)\n-{\n-\tconst char *buf = sb->final_buf;\n-\tunsigned long len = sb->final_buf_size;\n-\tconst char *end = buf + len;\n-\tconst char *p;\n-\tint *lineno;\n-\tint num = 0;\n-\n-\tfor (p = buf; p < end; p = get_next_line(p, end))\n-\t\tnum++;\n-\n-\tALLOC_ARRAY(sb->lineno, num + 1);\n-\tlineno = sb->lineno;\n-\n-\tfor (p = buf; p < end; p = get_next_line(p, end))\n-\t\t*lineno++ = p - buf;\n-\n-\t*lineno = len;\n-\n-\tsb->num_lines = num;\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@@ -706,209 +635,6 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n-static struct commit *find_single_final(struct rev_info *revs,\n-\t\t\t\t\tconst char **name_p)\n-{\n-\tint i;\n-\tstruct commit *found = NULL;\n-\tconst char *name = NULL;\n-\n-\tfor (i = 0; i < revs->pending.nr; i++) {\n-\t\tstruct object *obj = revs->pending.objects[i].item;\n-\t\tif (obj->flags & UNINTERESTING)\n-\t\t\tcontinue;\n-\t\tobj = deref_tag(obj, NULL, 0);\n-\t\tif (obj->type != OBJ_COMMIT)\n-\t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n-\t\tif (found)\n-\t\t\tdie(\"More than one commit to dig from %s and %s?\",\n-\t\t\t    revs->pending.objects[i].name, name);\n-\t\tfound = (struct commit *)obj;\n-\t\tname = revs->pending.objects[i].name;\n-\t}\n-\tif (name_p)\n-\t\t*name_p = name;\n-\treturn found;\n-}\n-\n-static struct commit *dwim_reverse_initial(struct rev_info *revs,\n-\t\t\t\t\t   const char **name_p)\n-{\n-\t/*\n-\t * DWIM \"git blame --reverse ONE -- PATH\" as\n-\t * \"git blame --reverse ONE..HEAD -- PATH\" but only do so\n-\t * when it makes sense.\n-\t */\n-\tstruct object *obj;\n-\tstruct commit *head_commit;\n-\tunsigned char head_sha1[20];\n-\n-\tif (revs->pending.nr != 1)\n-\t\treturn NULL;\n-\n-\t/* Is that sole rev a committish? */\n-\tobj = revs->pending.objects[0].item;\n-\tobj = deref_tag(obj, NULL, 0);\n-\tif (obj->type != OBJ_COMMIT)\n-\t\treturn NULL;\n-\n-\t/* Do we have HEAD? */\n-\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, head_sha1, NULL))\n-\t\treturn NULL;\n-\thead_commit = lookup_commit_reference_gently(head_sha1, 1);\n-\tif (!head_commit)\n-\t\treturn NULL;\n-\n-\t/* Turn \"ONE\" into \"ONE..HEAD\" then */\n-\tobj->flags |= UNINTERESTING;\n-\tadd_pending_object(revs, &head_commit->object, \"HEAD\");\n-\n-\tif (name_p)\n-\t\t*name_p = revs->pending.objects[0].name;\n-\treturn (struct commit *)obj;\n-}\n-\n-static struct commit *find_single_initial(struct rev_info *revs,\n-\t\t\t\t\t  const char **name_p)\n-{\n-\tint i;\n-\tstruct commit *found = NULL;\n-\tconst char *name = NULL;\n-\n-\t/*\n-\t * There must be one and only one negative commit, and it must be\n-\t * the boundary.\n-\t */\n-\tfor (i = 0; i < revs->pending.nr; i++) {\n-\t\tstruct object *obj = revs->pending.objects[i].item;\n-\t\tif (!(obj->flags & UNINTERESTING))\n-\t\t\tcontinue;\n-\t\tobj = deref_tag(obj, NULL, 0);\n-\t\tif (obj->type != OBJ_COMMIT)\n-\t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n-\t\tif (found)\n-\t\t\tdie(\"More than one commit to dig up from, %s and %s?\",\n-\t\t\t    revs->pending.objects[i].name, name);\n-\t\tfound = (struct commit *) obj;\n-\t\tname = revs->pending.objects[i].name;\n-\t}\n-\n-\tif (!name)\n-\t\tfound = dwim_reverse_initial(revs, &name);\n-\tif (!name)\n-\t\tdie(\"No commit to dig up from?\");\n-\n-\tif (name_p)\n-\t\t*name_p = name;\n-\treturn found;\n-}\n-\n-void init_scoreboard(struct scoreboard *sb)\n-{\n-\tmemset(sb, 0, sizeof(struct scoreboard));\n-\tsb->blame_move_score = BLAME_DEFAULT_MOVE_SCORE;\n-\tsb->blame_copy_score = BLAME_DEFAULT_COPY_SCORE;\n-}\n-\n-void setup_scoreboard(struct scoreboard *sb, const char *path, struct origin **orig)\n-{\n-\tconst char *final_commit_name = NULL;\n-\tstruct origin *o;\n-\tstruct commit *final_commit = NULL;\n-\tenum object_type type;\n-\n-\tif (sb->reverse && sb->contents_from)\n-\t\tdie(_(\"--contents and --reverse do not blend well.\"));\n-\n-\tif (!sb->reverse) {\n-\t\tsb->final = find_single_final(sb->revs, &final_commit_name);\n-\t\tsb->commits.compare = compare_commits_by_commit_date;\n-\t} else {\n-\t\tsb->final = find_single_initial(sb->revs, &final_commit_name);\n-\t\tsb->commits.compare = compare_commits_by_reverse_commit_date;\n-\t}\n-\n-\tif (sb->final && sb->contents_from)\n-\t\tdie(_(\"cannot use --contents with final commit object name\"));\n-\n-\tif (sb->reverse && sb->revs->first_parent_only)\n-\t\tsb->revs->children.name = NULL;\n-\n-\tif (!sb->final) {\n-\t\t/*\n-\t\t * \"--not A B -- path\" without anything positive;\n-\t\t * do not default to HEAD, but use the working tree\n-\t\t * or \"--contents\".\n-\t\t */\n-\t\tsetup_work_tree();\n-\t\tsb->final = fake_working_tree_commit(&sb->revs->diffopt,\n-\t\t\t\t\t\t     path, sb->contents_from);\n-\t\tadd_pending_object(sb->revs, &(sb->final->object), \":\");\n-\t}\n-\n-\tif (sb->reverse && sb->revs->first_parent_only) {\n-\t\tfinal_commit = find_single_final(sb->revs, NULL);\n-\t\tif (!final_commit)\n-\t\t\tdie(_(\"--reverse and --first-parent together require specified latest commit\"));\n-\t}\n-\n-\t/*\n-\t * If we have bottom, this will mark the ancestors of the\n-\t * bottom commits we would reach while traversing as\n-\t * uninteresting.\n-\t */\n-\tif (prepare_revision_walk(sb->revs))\n-\t\tdie(_(\"revision walk setup failed\"));\n-\n-\tif (sb->reverse && sb->revs->first_parent_only) {\n-\t\tstruct commit *c = final_commit;\n-\n-\t\tsb->revs->children.name = \"children\";\n-\t\twhile (c->parents &&\n-\t\t       oidcmp(&c->object.oid, &sb->final->object.oid)) {\n-\t\t\tstruct commit_list *l = xcalloc(1, sizeof(*l));\n-\n-\t\t\tl->item = c;\n-\t\t\tif (add_decoration(&sb->revs->children,\n-\t\t\t\t\t   &c->parents->item->object, l))\n-\t\t\t\tdie(\"BUG: not unique item in first-parent chain\");\n-\t\t\tc = c->parents->item;\n-\t\t}\n-\n-\t\tif (oidcmp(&c->object.oid, &sb->final->object.oid))\n-\t\t\tdie(_(\"--reverse --first-parent together require range along first-parent chain\"));\n-\t}\n-\n-\tif (is_null_oid(&sb->final->object.oid)) {\n-\t\to = sb->final->util;\n-\t\tsb->final_buf = xmemdupz(o->file.ptr, o->file.size);\n-\t\tsb->final_buf_size = o->file.size;\n-\t}\n-\telse {\n-\t\to = get_origin(sb->final, path);\n-\t\tif (fill_blob_sha1_and_mode(o))\n-\t\t\tdie(_(\"no such path %s in %s\"), path, final_commit_name);\n-\n-\t\tif (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) &&\n-\t\t    textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,\n-\t\t\t\t    &sb->final_buf_size))\n-\t\t\t;\n-\t\telse\n-\t\t\tsb->final_buf = read_sha1_file(o->blob_oid.hash, &type,\n-\t\t\t\t\t\t       &sb->final_buf_size);\n-\n-\t\tif (!sb->final_buf)\n-\t\t\tdie(_(\"cannot read blob %s for path %s\"),\n-\t\t\t    oid_to_hex(&o->blob_oid),\n-\t\t\t    path);\n-\t}\n-\tsb->num_read_blob++;\n-\tprepare_lines(sb);\n-\n-\tif (orig)\n-\t\t*orig = o;\n-}\n-\n static int blame_copy_callback(const struct option *option, const char *arg, int unset)\n {\n \tint *opt = option->value;\ndiff --git a/scoreboard.c b/scoreboard.c\nindex 0463688..01add1d 100644\n--- a/scoreboard.c\n+++ b/scoreboard.c\n@@ -1,6 +1,9 @@\n+#include \"refs.h\"\n+#include \"tag.h\"\n #include \"mergesort.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n+#include \"commit-fake.h\"\n #include \"scoreboard.h\"\n \n static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n@@ -151,6 +154,13 @@ void blame_sort_final(struct scoreboard *sb)\n \tsb->ent = blame_sort(sb->ent, compare_blame_final);\n }\n \n+static int compare_commits_by_reverse_commit_date(const void *a,\n+\t\t\t\t\t\t  const void *b,\n+\t\t\t\t\t\t  void *c)\n+{\n+\treturn -compare_commits_by_commit_date(a, b, c);\n+}\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@@ -201,6 +211,32 @@ static void queue_blames(struct scoreboard *sb, struct origin *porigin,\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+ * This also fills origin->mode for corresponding tree path.\n+ */\n+static int fill_blob_sha1_and_mode(struct origin *origin)\n+{\n+\tif (!is_null_oid(&origin->blob_oid))\n+\t\treturn 0;\n+\tif (get_tree_entry(origin->commit->object.oid.hash,\n+\t\t\t   origin->path,\n+\t\t\t   origin->blob_oid.hash, &origin->mode))\n+\t\tgoto error_out;\n+\tif (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)\n+\t\tgoto error_out;\n+\treturn 0;\n+ error_out:\n+\toidclr(&origin->blob_oid);\n+\torigin->mode = S_IFINVALID;\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@@ -1294,3 +1330,240 @@ void assign_blame(struct scoreboard *sb, int opt)\n \t\t\tsb->sanity_check(sb);\n \t}\n }\n+\n+static const char *get_next_line(const char *start, const char *end)\n+{\n+\tconst char *nl = memchr(start, '\\n', end - start);\n+\treturn nl ? nl + 1 : end;\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 scoreboard *sb)\n+{\n+\tconst char *buf = sb->final_buf;\n+\tunsigned long len = sb->final_buf_size;\n+\tconst char *end = buf + len;\n+\tconst char *p;\n+\tint *lineno;\n+\tint num = 0;\n+\n+\tfor (p = buf; p < end; p = get_next_line(p, end))\n+\t\tnum++;\n+\n+\tALLOC_ARRAY(sb->lineno, num + 1);\n+\tlineno = sb->lineno;\n+\n+\tfor (p = buf; p < end; p = get_next_line(p, end))\n+\t\t*lineno++ = p - buf;\n+\n+\t*lineno = len;\n+\n+\tsb->num_lines = num;\n+\treturn sb->num_lines;\n+}\n+\n+static struct commit *find_single_final(struct rev_info *revs,\n+\t\t\t\t\tconst char **name_p)\n+{\n+\tint i;\n+\tstruct commit *found = NULL;\n+\tconst char *name = NULL;\n+\n+\tfor (i = 0; i < revs->pending.nr; i++) {\n+\t\tstruct object *obj = revs->pending.objects[i].item;\n+\t\tif (obj->flags & UNINTERESTING)\n+\t\t\tcontinue;\n+\t\tobj = deref_tag(obj, NULL, 0);\n+\t\tif (obj->type != OBJ_COMMIT)\n+\t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n+\t\tif (found)\n+\t\t\tdie(\"More than one commit to dig from %s and %s?\",\n+\t\t\t    revs->pending.objects[i].name, name);\n+\t\tfound = (struct commit *)obj;\n+\t\tname = revs->pending.objects[i].name;\n+\t}\n+\tif (name_p)\n+\t\t*name_p = name;\n+\treturn found;\n+}\n+\n+static struct commit *dwim_reverse_initial(struct rev_info *revs,\n+\t\t\t\t\t   const char **name_p)\n+{\n+\t/*\n+\t * DWIM \"git blame --reverse ONE -- PATH\" as\n+\t * \"git blame --reverse ONE..HEAD -- PATH\" but only do so\n+\t * when it makes sense.\n+\t */\n+\tstruct object *obj;\n+\tstruct commit *head_commit;\n+\tunsigned char head_sha1[20];\n+\n+\tif (revs->pending.nr != 1)\n+\t\treturn NULL;\n+\n+\t/* Is that sole rev a committish? */\n+\tobj = revs->pending.objects[0].item;\n+\tobj = deref_tag(obj, NULL, 0);\n+\tif (obj->type != OBJ_COMMIT)\n+\t\treturn NULL;\n+\n+\t/* Do we have HEAD? */\n+\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, head_sha1, NULL))\n+\t\treturn NULL;\n+\thead_commit = lookup_commit_reference_gently(head_sha1, 1);\n+\tif (!head_commit)\n+\t\treturn NULL;\n+\n+\t/* Turn \"ONE\" into \"ONE..HEAD\" then */\n+\tobj->flags |= UNINTERESTING;\n+\tadd_pending_object(revs, &head_commit->object, \"HEAD\");\n+\n+\tif (name_p)\n+\t\t*name_p = revs->pending.objects[0].name;\n+\treturn (struct commit *)obj;\n+}\n+\n+static struct commit *find_single_initial(struct rev_info *revs,\n+\t\t\t\t\t  const char **name_p)\n+{\n+\tint i;\n+\tstruct commit *found = NULL;\n+\tconst char *name = NULL;\n+\n+\t/*\n+\t * There must be one and only one negative commit, and it must be\n+\t * the boundary.\n+\t */\n+\tfor (i = 0; i < revs->pending.nr; i++) {\n+\t\tstruct object *obj = revs->pending.objects[i].item;\n+\t\tif (!(obj->flags & UNINTERESTING))\n+\t\t\tcontinue;\n+\t\tobj = deref_tag(obj, NULL, 0);\n+\t\tif (obj->type != OBJ_COMMIT)\n+\t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n+\t\tif (found)\n+\t\t\tdie(\"More than one commit to dig up from, %s and %s?\",\n+\t\t\t    revs->pending.objects[i].name, name);\n+\t\tfound = (struct commit *) obj;\n+\t\tname = revs->pending.objects[i].name;\n+\t}\n+\n+\tif (!name)\n+\t\tfound = dwim_reverse_initial(revs, &name);\n+\tif (!name)\n+\t\tdie(\"No commit to dig up from?\");\n+\n+\tif (name_p)\n+\t\t*name_p = name;\n+\treturn found;\n+}\n+\n+void init_scoreboard(struct scoreboard *sb)\n+{\n+\tmemset(sb, 0, sizeof(struct scoreboard));\n+\tsb->blame_move_score = BLAME_DEFAULT_MOVE_SCORE;\n+\tsb->blame_copy_score = BLAME_DEFAULT_COPY_SCORE;\n+}\n+\n+void setup_scoreboard(struct scoreboard *sb, const char *path, struct origin **orig)\n+{\n+\tconst char *final_commit_name = NULL;\n+\tstruct origin *o;\n+\tstruct commit *final_commit = NULL;\n+\tenum object_type type;\n+\n+\tif (sb->reverse && sb->contents_from)\n+\t\tdie(_(\"--contents and --reverse do not blend well.\"));\n+\n+\tif (!sb->reverse) {\n+\t\tsb->final = find_single_final(sb->revs, &final_commit_name);\n+\t\tsb->commits.compare = compare_commits_by_commit_date;\n+\t} else {\n+\t\tsb->final = find_single_initial(sb->revs, &final_commit_name);\n+\t\tsb->commits.compare = compare_commits_by_reverse_commit_date;\n+\t}\n+\n+\tif (sb->final && sb->contents_from)\n+\t\tdie(_(\"cannot use --contents with final commit object name\"));\n+\n+\tif (sb->reverse && sb->revs->first_parent_only)\n+\t\tsb->revs->children.name = NULL;\n+\n+\tif (!sb->final) {\n+\t\t/*\n+\t\t * \"--not A B -- path\" without anything positive;\n+\t\t * do not default to HEAD, but use the working tree\n+\t\t * or \"--contents\".\n+\t\t */\n+\t\tsetup_work_tree();\n+\t\tsb->final = fake_working_tree_commit(&sb->revs->diffopt,\n+\t\t\t\t\t\t     path, sb->contents_from);\n+\t\tadd_pending_object(sb->revs, &(sb->final->object), \":\");\n+\t}\n+\n+\tif (sb->reverse && sb->revs->first_parent_only) {\n+\t\tfinal_commit = find_single_final(sb->revs, NULL);\n+\t\tif (!final_commit)\n+\t\t\tdie(_(\"--reverse and --first-parent together require specified latest commit\"));\n+\t}\n+\n+\t/*\n+\t * If we have bottom, this will mark the ancestors of the\n+\t * bottom commits we would reach while traversing as\n+\t * uninteresting.\n+\t */\n+\tif (prepare_revision_walk(sb->revs))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tif (sb->reverse && sb->revs->first_parent_only) {\n+\t\tstruct commit *c = final_commit;\n+\n+\t\tsb->revs->children.name = \"children\";\n+\t\twhile (c->parents &&\n+\t\t       oidcmp(&c->object.oid, &sb->final->object.oid)) {\n+\t\t\tstruct commit_list *l = xcalloc(1, sizeof(*l));\n+\n+\t\t\tl->item = c;\n+\t\t\tif (add_decoration(&sb->revs->children,\n+\t\t\t\t\t   &c->parents->item->object, l))\n+\t\t\t\tdie(\"BUG: not unique item in first-parent chain\");\n+\t\t\tc = c->parents->item;\n+\t\t}\n+\n+\t\tif (oidcmp(&c->object.oid, &sb->final->object.oid))\n+\t\t\tdie(_(\"--reverse --first-parent together require range along first-parent chain\"));\n+\t}\n+\n+\tif (is_null_oid(&sb->final->object.oid)) {\n+\t\to = sb->final->util;\n+\t\tsb->final_buf = xmemdupz(o->file.ptr, o->file.size);\n+\t\tsb->final_buf_size = o->file.size;\n+\t}\n+\telse {\n+\t\to = get_origin(sb->final, path);\n+\t\tif (fill_blob_sha1_and_mode(o))\n+\t\t\tdie(_(\"no such path %s in %s\"), path, final_commit_name);\n+\n+\t\tif (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) &&\n+\t\t    textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,\n+\t\t\t\t    &sb->final_buf_size))\n+\t\t\t;\n+\t\telse\n+\t\t\tsb->final_buf = read_sha1_file(o->blob_oid.hash, &type,\n+\t\t\t\t\t\t       &sb->final_buf_size);\n+\n+\t\tif (!sb->final_buf)\n+\t\t\tdie(_(\"cannot read blob %s for path %s\"),\n+\t\t\t    oid_to_hex(&o->blob_oid),\n+\t\t\t    path);\n+\t}\n+\tsb->num_read_blob++;\n+\tprepare_lines(sb);\n+\n+\tif (orig)\n+\t\t*orig = o;\n+}\ndiff --git a/scoreboard.h b/scoreboard.h\nindex 42d9aac..fb573a5 100644\n--- a/scoreboard.h\n+++ b/scoreboard.h\n@@ -71,5 +71,7 @@ void blame_sort_final(struct scoreboard *sb);\n unsigned ent_score(struct scoreboard *sb, struct blame_entry *e);\n void assign_blame(struct scoreboard *sb, int opt);\n const char *nth_line(struct scoreboard *sb, long lno);\n+void init_scoreboard(struct scoreboard *sb);\n+void setup_scoreboard(struct scoreboard *sb, const char *path, struct origin **orig);\n \n #endif /* SCOREBOARD_H */\n-- \n2.9.3\n\n"},{"id":"318832","messageId":"CACBZZX6EuS86Kp5H+zPf9a_4PrOQqnxPwKdpEdYCXWPspYYp5A@mail.gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-2-whydoubt@gmail.com","subject":"Re: [RFC PATCH 01/10] Remove unneeded dependency on blob.h from blame","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2017-05-05T07:07:41Z","receivedAt":"2017-05-05T07:08:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"For commit message: This was originally added in commit acca687fa9\n(\"git-pickaxe: retire pickaxe\", 2006-11-08), but has never been\nneeded.\n\nOn Fri, May 5, 2017 at 7:27 AM, Jeff Smith <whydoubt@gmail.com> wrote:\n> Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n> ---\n>  builtin/blame.c | 1 -\n>  1 file changed, 1 deletion(-)\n>\n> diff --git a/builtin/blame.c b/builtin/blame.c\n> index 07506a3..42c56eb 100644\n> --- a/builtin/blame.c\n> +++ b/builtin/blame.c\n> @@ -8,7 +8,6 @@\n>  #include \"cache.h\"\n>  #include \"refs.h\"\n>  #include \"builtin.h\"\n> -#include \"blob.h\"\n>  #include \"commit.h\"\n>  #include \"tag.h\"\n>  #include \"tree-walk.h\"\n> --\n> 2.9.3\n>\n"},{"id":"318838","messageId":"CAPX7N=5H9vs4LB8SXd6gVd-1APrfhBxaHbFK+AxsSXeKNqOWfg@mail.gmail.com","threadId":"45873","inReplyTo":"CAPX7N=6tQi+WidagvV1BA-CoaiNJj7OO4U7GYXNE-QzyvD=QRQ@mail.gmail.com","subject":"Re: [RFC PATCH 01/10] Remove unneeded dependency on blob.h from blame","fromName":"Jeffrey Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-05T14:03:48Z","receivedAt":"2017-05-05T14:03:54Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"While it was added there (it was part of builtin pickaxe which was\nrenamed builtin blame), it was actually needed then, but I do see\nwhat you are getting at.\n\nIt was no longer needed in commit 21666f1 (\"convert object type\nhandling from a string to a number\", 2007-02-26) with the removed\nuse of blob_type.  I will update the commit message accordingly.\n\nThanks\n\nOn Fri, May 5, 2017 at 8:52 AM, Jeffrey Smith <whydoubt@gmail.com> wrote:\n> While it was added there (it was part of builtin pickaxe which was\n> renamed builtin blame), it was actually needed then, but I do see\n> what you are getting at.\n>\n> It was no longer needed in commit 21666f1 (\"convert object type\n> handling from a string to a number\", 2007-02-26) with the removed\n> use of blob_type.  I will update the commit message accordingly.\n>\n> Thanks\n>\n> P.S. Sorry for the duplicate message Ævar. I forgot to reply all\n> the first time.\n>\n>\n> On Fri, May 5, 2017 at 2:07 AM, Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> wrote:\n>>\n>> For commit message: This was originally added in commit acca687fa9\n>> (\"git-pickaxe: retire pickaxe\", 2006-11-08), but has never been\n>> needed.\n>>\n>> On Fri, May 5, 2017 at 7:27 AM, Jeff Smith <whydoubt@gmail.com> wrote:\n>> > Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n>> > ---\n>> >  builtin/blame.c | 1 -\n>> >  1 file changed, 1 deletion(-)\n>> >\n>> > diff --git a/builtin/blame.c b/builtin/blame.c\n>> > index 07506a3..42c56eb 100644\n>> > --- a/builtin/blame.c\n>> > +++ b/builtin/blame.c\n>> > @@ -8,7 +8,6 @@\n>> >  #include \"cache.h\"\n>> >  #include \"refs.h\"\n>> >  #include \"builtin.h\"\n>> > -#include \"blob.h\"\n>> >  #include \"commit.h\"\n>> >  #include \"tag.h\"\n>> >  #include \"tree-walk.h\"\n>> > --\n>> > 2.9.3\n>> >\n>\n>\n"},{"id":"318867","messageId":"xmqq1ss38ako.fsf@gitster.mtv.corp.google.com","threadId":"45873","inReplyTo":"20170505052729.7576-4-whydoubt@gmail.com","subject":"Re: [RFC PATCH 03/10] Add some missing definitions to header files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-05T17:42:15Z","receivedAt":"2017-05-05T17:42:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Smith <whydoubt@gmail.com> writes:\n\n> Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n> ---\n>  object.h    | 2 ++\n>  pathspec.h  | 4 ++++\n>  refs.h      | 3 +++\n>  tree-walk.h | 2 ++\n>  4 files changed, 11 insertions(+)\n\nAs the coding rule of this codebase is not to include system headers\nin anything other than git-compat-util.h which should be the first\nthing to be included in the *.c files (cache.h and other selected\nand well-known header files can be the first one in place of\ngit-compat-util, as they include it as the first thing), all changes\nin this patch, except possibly the one to tree-walk.h, are wrong.\n\n>\n> diff --git a/object.h b/object.h\n> index f52957d..9737582 100644\n> --- a/object.h\n> +++ b/object.h\n> @@ -1,6 +1,8 @@\n>  #ifndef OBJECT_H\n>  #define OBJECT_H\n>  \n> +#include \"cache.h\"\n> +\n>  struct object_list {\n>  \tstruct object *item;\n>  \tstruct object_list *next;\n> diff --git a/pathspec.h b/pathspec.h\n> index 55e9769..ea18e24 100644\n> --- a/pathspec.h\n> +++ b/pathspec.h\n> @@ -1,6 +1,10 @@\n>  #ifndef PATHSPEC_H\n>  #define PATHSPEC_H\n>  \n> +#include <stddef.h>\n> +#include <string.h>\n> +#include <strings.h>\n> +\n>  /* Pathspec magic */\n>  #define PATHSPEC_FROMTOP\t(1<<0)\n>  #define PATHSPEC_MAXDEPTH\t(1<<1)\n> diff --git a/refs.h b/refs.h\n> index 07cf4cd..e9d19fd 100644\n> --- a/refs.h\n> +++ b/refs.h\n> @@ -1,6 +1,9 @@\n>  #ifndef REFS_H\n>  #define REFS_H\n>  \n> +#include <stdio.h>\n> +#include <string.h>\n> +\n>  struct object_id;\n>  struct ref_store;\n>  struct strbuf;\n> diff --git a/tree-walk.h b/tree-walk.h\n> index 68bb78b..8b0fccd 100644\n> --- a/tree-walk.h\n> +++ b/tree-walk.h\n> @@ -1,6 +1,8 @@\n>  #ifndef TREE_WALK_H\n>  #define TREE_WALK_H\n>  \n> +struct strbuf;\n> +\n>  struct name_entry {\n>  \tconst struct object_id *oid;\n>  \tconst char *path;\n"},{"id":"318868","messageId":"xmqqwp9v6vw2.fsf@gitster.mtv.corp.google.com","threadId":"45873","inReplyTo":"20170505052729.7576-3-whydoubt@gmail.com","subject":"Re: [RFC PATCH 02/10] Move textconv_object to be with other textconv methods","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-05T17:44:45Z","receivedAt":"2017-05-05T17:44:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Smith <whydoubt@gmail.com> writes:\n\n> Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n> ---\n>  builtin.h          |  2 --\n>  builtin/blame.c    | 28 ----------------------------\n>  builtin/cat-file.c |  1 +\n>  diff.c             | 23 +++++++++++++++++++++++\n>  diff.h             |  7 +++++++\n>  5 files changed, 31 insertions(+), 30 deletions(-)\n\nThis change makes sense regardless of your primary goal of the\nseries.  It was not good that one builtin borrowing a helper from\nanother.  The common helper should live outside builtin/ as a common\ncode, and in this case, diff.[ch] may be an OK place for it.\n\n\n>\n> diff --git a/builtin.h b/builtin.h\n> index 9e4a898..498ac80 100644\n> --- a/builtin.h\n> +++ b/builtin.h\n> @@ -25,8 +25,6 @@ struct fmt_merge_msg_opts {\n>  extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n>  \t\t\t struct fmt_merge_msg_opts *);\n>  \n> -extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);\n> -\n>  extern int is_builtin(const char *s);\n>  \n>  extern int cmd_add(int argc, const char **argv, const char *prefix);\n> diff --git a/builtin/blame.c b/builtin/blame.c\n> index 42c56eb..c419981 100644\n> --- a/builtin/blame.c\n> +++ b/builtin/blame.c\n> @@ -147,34 +147,6 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n>  }\n>  \n>  /*\n> - * Prepare diff_filespec and convert it using diff textconv API\n> - * if the textconv driver exists.\n> - * Return 1 if the conversion succeeds, 0 otherwise.\n> - */\n> -int textconv_object(const char *path,\n> -\t\t    unsigned mode,\n> -\t\t    const struct object_id *oid,\n> -\t\t    int oid_valid,\n> -\t\t    char **buf,\n> -\t\t    unsigned long *buf_size)\n> -{\n> -\tstruct diff_filespec *df;\n> -\tstruct userdiff_driver *textconv;\n> -\n> -\tdf = alloc_filespec(path);\n> -\tfill_filespec(df, oid->hash, oid_valid, mode);\n> -\ttextconv = get_textconv(df);\n> -\tif (!textconv) {\n> -\t\tfree_filespec(df);\n> -\t\treturn 0;\n> -\t}\n> -\n> -\t*buf_size = fill_textconv(textconv, df, buf);\n> -\tfree_filespec(df);\n> -\treturn 1;\n> -}\n> -\n> -/*\n>   * Given an origin, prepare mmfile_t structure to be used by the\n>   * diff machinery\n>   */\n> diff --git a/builtin/cat-file.c b/builtin/cat-file.c\n> index 1890d7a..79a2c82 100644\n> --- a/builtin/cat-file.c\n> +++ b/builtin/cat-file.c\n> @@ -5,6 +5,7 @@\n>   */\n>  #include \"cache.h\"\n>  #include \"builtin.h\"\n> +#include \"diff.h\"\n>  #include \"parse-options.h\"\n>  #include \"userdiff.h\"\n>  #include \"streaming.h\"\n> diff --git a/diff.c b/diff.c\n> index 11eef1c..a62e989 100644\n> --- a/diff.c\n> +++ b/diff.c\n> @@ -5270,6 +5270,29 @@ size_t fill_textconv(struct userdiff_driver *driver,\n>  \treturn size;\n>  }\n>  \n> +int textconv_object(const char *path,\n> +\t\t    unsigned mode,\n> +\t\t    const struct object_id *oid,\n> +\t\t    int oid_valid,\n> +\t\t    char **buf,\n> +\t\t    unsigned long *buf_size)\n> +{\n> +\tstruct diff_filespec *df;\n> +\tstruct userdiff_driver *textconv;\n> +\n> +\tdf = alloc_filespec(path);\n> +\tfill_filespec(df, oid->hash, oid_valid, mode);\n> +\ttextconv = get_textconv(df);\n> +\tif (!textconv) {\n> +\t\tfree_filespec(df);\n> +\t\treturn 0;\n> +\t}\n> +\n> +\t*buf_size = fill_textconv(textconv, df, buf);\n> +\tfree_filespec(df);\n> +\treturn 1;\n> +}\n> +\n>  void setup_diff_pager(struct diff_options *opt)\n>  {\n>  \t/*\n> diff --git a/diff.h b/diff.h\n> index 5be1ee7..52ebd54 100644\n> --- a/diff.h\n> +++ b/diff.h\n> @@ -385,6 +385,13 @@ extern size_t fill_textconv(struct userdiff_driver *driver,\n>   */\n>  extern struct userdiff_driver *get_textconv(struct diff_filespec *one);\n>  \n> +/*\n> + * Prepare diff_filespec and convert it using diff textconv API\n> + * if the textconv driver exists.\n> + * Return 1 if the conversion succeeds, 0 otherwise.\n> + */\n> +extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);\n> +\n>  extern int parse_rename_score(const char **cp_p);\n>  \n>  extern long parse_algorithm_value(const char *value);\n"},{"id":"318870","messageId":"CAGZ79kZ4k7qVKE7OpSEELorwSx5hUgJksLAeNgVGs0w1JC_Ffg@mail.gmail.com","threadId":"45873","inReplyTo":"xmqqwp9v6vw2.fsf@gitster.mtv.corp.google.com","subject":"Re: [RFC PATCH 02/10] Move textconv_object to be with other textconv methods","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-05-05T17:50:55Z","receivedAt":"2017-05-05T17:51:02Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Fri, May 5, 2017 at 10:44 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jeff Smith <whydoubt@gmail.com> writes:\n>\n>> Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n>> ---\n>>  builtin.h          |  2 --\n>>  builtin/blame.c    | 28 ----------------------------\n>>  builtin/cat-file.c |  1 +\n>>  diff.c             | 23 +++++++++++++++++++++++\n>>  diff.h             |  7 +++++++\n>>  5 files changed, 31 insertions(+), 30 deletions(-)\n>\n> This change makes sense regardless of your primary goal of the\n> series.  It was not good that one builtin borrowing a helper from\n> another.  The common helper should live outside builtin/ as a common\n> code, and in this case, diff.[ch] may be an OK place for it.\n>\n\nMy kneejerk reaction to this is would be:\n    Please don't grow diff.[ch] even more. It has\n    5k lines which is a lot IMHO. Although it might be\n    ok for the compiler and from a logical point of view,\n    I'd rather prefer to deal with lots of small files, than\n    with such large files. I am undecided if this hints at\n    bad tooling on my side or at how I think of code\n    and their location.\n\nI guess it is ok for now and in this series, but we may want\nto split up diff.[ch] in the future into multiple finer grained files.\n\nThanks,\nStefan\n"},{"id":"318871","messageId":"xmqqshkj6vim.fsf@gitster.mtv.corp.google.com","threadId":"45873","inReplyTo":"20170505052729.7576-8-whydoubt@gmail.com","subject":"Re: [RFC PATCH 07/10] Break out scoreboard a little better","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-05T17:52:49Z","receivedAt":"2017-05-05T17:52:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Smith <whydoubt@gmail.com> writes:\n\nThis blank space is for you to explain what you mean by \"a little\nbetter\".  What makes the result better?\n\nIt seems to do too many things in a single patch.  For example, I\nguess it may be a good idea that you made various knobs like\n\"show_root\" encapsulated in the scoreboard; that change can and\nshould be on its own single step.  It is unclear what the pupose of\nblame-sort-final thing is, but even if that change were a good idea\n(I am not saying I think it is a bad idea; I am saying I do not see\nsufficient explanation to judge its goodness), it is a change that\nis unrelated to moving \"show_root\" and friends into the scoreboard,\nhence should be a separate patch.\n"},{"id":"318873","messageId":"xmqqr3036vf1.fsf@gitster.mtv.corp.google.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"Re: [RFC PATCH 00/10] Add blame to libgit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-05T17:54:58Z","receivedAt":"2017-05-05T17:55:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Smith <whydoubt@gmail.com> writes:\n\n> Jeff Smith (10):\n>   Remove unneeded dependency on blob.h from blame\n>   Move textconv_object to be with other textconv methods\n>   Add some missing definitions to header files\n>   Remove unused parameter from get_origin()\n>   Split blame origin into its own file\n>   Move fake_working_tree_commit() to lib\n>   Break out scoreboard a little better\n>   Split blame scoreboard into its own file\n>   Break out scoreboard init and setup\n>   Move scoreboard init and setup to lib\n\nMake sure these shortlog entries, when eventually appear along with\nother people's changes, let readers know what these are about\neasily, and formatted similarly to others'.  Write the \"<area>: \"\nprefix in front is a good pratice to help that, e.g.\n\n    blame: remove unneeded dependency on blob.h\n\nIn general I am OK with the idea of splitting builtin/blame.c into\nthe blame Porcelain part (i.e. cmd_blame()) and some form of\nreusable \"blame library\".\n\nHowever, you'd need to be cafreful about names.  Are contents of\n\"commit-fake\", \"origin\" and \"scoreboard\" generally useful outside\nthe context of running a \"blame\"?  They are not good filenames if\nsaying \"origin.c\" in the context of the entire Git codebase, anybody\nwould understand that it is about \"the origin\" concept used by the\nblame engine (similarly for other new files).  I would imagine that\nhaving blame.[ch] at the toplevel instead of creating that many\n\"blame library\" files would be a good way to go (mirrors how other\nparts of the system is laid out, e.g. diff.[ch] and diff-*.[ch] are\nlibrary-ish files, while builtin/diff*.c implements the Porcelain).\n\nHave you renamed the names of structures, functions and variables\nthat used to be private to builtin/blame.c and now shared between\nbuiltin/blame.c and these new files so that they are specific enough\nand are obvious that they are about blame infrastructure?  Names\nthat were perfectly acceptable and appropriate as private ones\ninside a subsystem are often too vague or generic when exposed to\noutside world as part of the library-ish code.\n\n"},{"id":"319032","messageId":"xmqq1ss0cg8t.fsf@gitster.mtv.corp.google.com","threadId":"45873","inReplyTo":"CAGZ79kZ4k7qVKE7OpSEELorwSx5hUgJksLAeNgVGs0w1JC_Ffg@mail.gmail.com","subject":"Re: [RFC PATCH 02/10] Move textconv_object to be with other textconv methods","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-08T01:02:58Z","receivedAt":"2017-05-08T01:03:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> I guess it is ok for now and in this series, but we may want\n> to split up diff.[ch] in the future into multiple finer grained files.\n\nFor what end?  Such a split would require more symbols internal to\ndiff.[ch] to become external, which is a big downside, so we need to\nhave a large reward to compensate it if we were to go there.\n"},{"id":"319125","messageId":"20170508215525.gcyzzntqvm52mqcc@sigill.intra.peff.net","threadId":"45873","inReplyTo":"xmqq1ss0cg8t.fsf@gitster.mtv.corp.google.com","subject":"Re: [RFC PATCH 02/10] Move textconv_object to be with other textconv methods","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-05-08T21:55:25Z","receivedAt":"2017-05-08T21:55:35Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, May 08, 2017 at 10:02:58AM +0900, Junio C Hamano wrote:\n\n> Stefan Beller <sbeller@google.com> writes:\n> \n> > I guess it is ok for now and in this series, but we may want\n> > to split up diff.[ch] in the future into multiple finer grained files.\n> \n> For what end?  Such a split would require more symbols internal to\n> diff.[ch] to become external, which is a big downside, so we need to\n> have a large reward to compensate it if we were to go there.\n\nI think there are two sides to that coin. Let's say you have a file with\nfive functions (and you can replace function with structs, global\nvariables, etc; any unit of complexity that might or might not be\nexternally visible):\n\n  /* called by both a() and b() */\n  static void a_and_b_helper();\n\n  /* called by a() */\n  static void a_helper();\n  void a();\n\n  /* called by b() */\n  static void b_helper();\n  void b();\n\nWhen they are in the same file, b() and b_helper() can see a_helper(),\neven though they don't need it. And that means increased complexity in\ndealing with a_helper(), because its visibility is larger than\nnecessary. We have to worry about what a change to it might do to b()\n(or more realistically, c(), d(), etc).\n\nIf we split this apart, we end up with three files:\n\n   common.c:\n     void a_and_b_helper();\n\n   a.c:\n     static void a_helper();\n     void a();\n\n   b.c:\n     static void b_helper();\n     void b();\n\nThe specific helpers have less visibility, which is good. The public\nfunctions a() and b() were already public, so no change. But now the\ncommon helper is public, too, even though nobody except a() and b() care\nabout it.\n\nSo it's a tradeoff. And the important question is: is the bleed-over\nbetween a() and b() worse than the common bits being made public?  That\ncan't be answered without looking at how many distinct \"a\" and \"b\"-like\nchunks there are in the file, and what the common bits look like. I'm\nnot sure of the answer for diff.c. Without digging, both ends of the\nspectrum seem equally plausible to me: that it is mostly a set of N\ndistinct units which could be split apart, or that it really is a few\npublic functions calling the same common core over and over.\n\nAnd a follow-on question is what we can do to mitigate the cost of\nmaking the common code public. We could advertise a_and_b_helper() only\nin diff-internal.h, and then makes it only semi-public (anybody can\ninclude that, of course, but including diff-internal.h seems like it\nought to be a red flag). That only helps the programmer, though; we'd\nstill be losing out on compiler optimizations and static analysis that\nonly looks at one translation unit at a time.\n\nPhew. That ended up a little long-winded. All of it is to say that I\ndon't think it makes sense to reject a split out-of-hand, but nor is it\nalways a good thing. It depends on what's in the file.\n\n-Peff\n"},{"id":"319126","messageId":"CAGZ79kbKERn5CbD9mVdq6huqLS0TrpH=-Tbk229fD8P8598CbQ@mail.gmail.com","threadId":"45873","inReplyTo":"20170508215525.gcyzzntqvm52mqcc@sigill.intra.peff.net","subject":"Re: [RFC PATCH 02/10] Move textconv_object to be with other textconv methods","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-05-08T22:06:54Z","receivedAt":"2017-05-08T22:07:01Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, May 8, 2017 at 2:55 PM, Jeff King <peff@peff.net> wrote:\n> On Mon, May 08, 2017 at 10:02:58AM +0900, Junio C Hamano wrote:\n>\n>> Stefan Beller <sbeller@google.com> writes:\n>>\n>> > I guess it is ok for now and in this series, but we may want\n>> > to split up diff.[ch] in the future into multiple finer grained files.\n>>\n>> For what end?  Such a split would require more symbols internal to\n>> diff.[ch] to become external, which is a big downside, so we need to\n>> have a large reward to compensate it if we were to go there.\n>\n> I think there are two sides to that coin. Let's say you have a file with\n> five functions (and you can replace function with structs, global\n> variables, etc; any unit of complexity that might or might not be\n> externally visible):\n>\n>   /* called by both a() and b() */\n>   static void a_and_b_helper();\n>\n>   /* called by a() */\n>   static void a_helper();\n>   void a();\n>\n>   /* called by b() */\n>   static void b_helper();\n>   void b();\n>\n> When they are in the same file, b() and b_helper() can see a_helper(),\n> even though they don't need it. And that means increased complexity in\n> dealing with a_helper(), because its visibility is larger than\n> necessary. We have to worry about what a change to it might do to b()\n> (or more realistically, c(), d(), etc).\n>\n> If we split this apart, we end up with three files:\n>\n>    common.c:\n>      void a_and_b_helper();\n>\n>    a.c:\n>      static void a_helper();\n>      void a();\n>\n>    b.c:\n>      static void b_helper();\n>      void b();\n>\n> The specific helpers have less visibility, which is good. The public\n> functions a() and b() were already public, so no change. But now the\n> common helper is public, too, even though nobody except a() and b() care\n> about it.\n>\n> So it's a tradeoff. And the important question is: is the bleed-over\n> between a() and b() worse than the common bits being made public?  That\n> can't be answered without looking at how many distinct \"a\" and \"b\"-like\n> chunks there are in the file, and what the common bits look like. I'm\n> not sure of the answer for diff.c. Without digging, both ends of the\n> spectrum seem equally plausible to me: that it is mostly a set of N\n> distinct units which could be split apart, or that it really is a few\n> public functions calling the same common core over and over.\n>\n> And a follow-on question is what we can do to mitigate the cost of\n> making the common code public. We could advertise a_and_b_helper() only\n> in diff-internal.h, and then makes it only semi-public (anybody can\n> include that, of course, but including diff-internal.h seems like it\n> ought to be a red flag). That only helps the programmer, though; we'd\n> still be losing out on compiler optimizations and static analysis that\n> only looks at one translation unit at a time.\n>\n> Phew. That ended up a little long-winded. All of it is to say that I\n> don't think it makes sense to reject a split out-of-hand, but nor is it\n> always a good thing. It depends on what's in the file.\n\nI agree on this sentiment. It really depends on the content under\ndiscussion whether it makes sense to split.\n\nHaving had some exposure recently for diff.[ch] (and I just picked up\nthat series again, but did not send it out yet), I have the impression that\nwe do have a lot of code in diff.c, which is quite unrelated to each\nother, i.e. a lot of [a,b]_helper()s, and few a_and_b_helper() for the\nalready public functions.\n\nMay first mail was based on perceived unneeded complexity, which\nslows down in achieving a goal.\n\nThanks,\nStefan\n"},{"id":"319134","messageId":"xmqqziem6bq8.fsf@gitster.mtv.corp.google.com","threadId":"45873","inReplyTo":"20170508215525.gcyzzntqvm52mqcc@sigill.intra.peff.net","subject":"Re: [RFC PATCH 02/10] Move textconv_object to be with other textconv methods","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-09T01:49:19Z","receivedAt":"2017-05-09T01:49:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> The specific helpers have less visibility, which is good. The public\n> functions a() and b() were already public, so no change. But now the\n> common helper is public, too, even though nobody except a() and b() care\n> about it.\n>\n> So it's a tradeoff. And the important question is: is the bleed-over\n> between a() and b() worse than the common bits being made public?\n\nAt this point you are saying the same thing as I said ;-) \n\nI haven't touched diff.c for a while, but my impression was that it\nwas already a smallest logical unit, especially since some bits like\ndiff-lib.c (the interface for the front-end programs to drive the\nmachinery via the three standard pairs of sources) and ws.c (diff\nsubsystem to deal with whitespace errors) are already split out (and\nneedless to say, the diffcore transformations are all in their own\nseparate files).  Over time people may have added what does not\nbelong there merely for convenience, which may want to get ejected,\nbut I do not think of many instances of them offhand.\n\n    ... goes and looks ...\n\nIt appears that the textconv related helpers (which was where this\ndiscussion thread started from) could be first restructured so that\nthey do not depend on diff_filespec and turned into a more generic\n\"I have this path and a blob object name, please run textconv to it\nand...\" interface and then moved out of the file into its own\ntextconv.[ch].  It does not need access to quite a few fields in\ndiff_filespec structure (e.g.  it shouldn't care what filemode the\nthing has).  The diff machinery wants the result in a contiguous\npiece of memory and that could be a good starting point, but I said\n\"and...\" above because I am not sure if it is the best generic\nthing the restructured interface can do to the result.\n\n\n\n\n\n"},{"id":"319143","messageId":"20170509023434.hxhmvemmrymgu5ij@sigill.intra.peff.net","threadId":"45873","inReplyTo":"xmqqziem6bq8.fsf@gitster.mtv.corp.google.com","subject":"Re: [RFC PATCH 02/10] Move textconv_object to be with other textconv methods","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-05-09T02:34:34Z","receivedAt":"2017-05-09T02:34:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, May 09, 2017 at 10:49:19AM +0900, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > The specific helpers have less visibility, which is good. The public\n> > functions a() and b() were already public, so no change. But now the\n> > common helper is public, too, even though nobody except a() and b() care\n> > about it.\n> >\n> > So it's a tradeoff. And the important question is: is the bleed-over\n> > between a() and b() worse than the common bits being made public?\n> \n> At this point you are saying the same thing as I said ;-) \n\nMaybe, but I think my hunch is that the answer to the question is\ndifferent than yours. ;)\n\n> I haven't touched diff.c for a while, but my impression was that it\n> was already a smallest logical unit, especially since some bits like\n> diff-lib.c (the interface for the front-end programs to drive the\n> machinery via the three standard pairs of sources) and ws.c (diff\n> subsystem to deal with whitespace errors) are already split out (and\n> needless to say, the diffcore transformations are all in their own\n> separate files).  Over time people may have added what does not\n> belong there merely for convenience, which may want to get ejected,\n> but I do not think of many instances of them offhand.\n\nJust skimming the file, I suspect diff_filespec could be broken out into\na semi-public interface (many of its functions already are public\nanyway), probably dirstat could be broken out, and possibly other\nspecific formats could be broken into their own files. But I didn't\nspend much time on it.\n\n> It appears that the textconv related helpers (which was where this\n> discussion thread started from) could be first restructured so that\n> they do not depend on diff_filespec and turned into a more generic\n> \"I have this path and a blob object name, please run textconv to it\n> and...\" interface and then moved out of the file into its own\n> textconv.[ch].  It does not need access to quite a few fields in\n> diff_filespec structure (e.g.  it shouldn't care what filemode the\n> thing has).  The diff machinery wants the result in a contiguous\n> piece of memory and that could be a good starting point, but I said\n> \"and...\" above because I am not sure if it is the best generic\n> thing the restructured interface can do to the result.\n\nI think some of the users of textconv do have to go through some\ncontortions to turn their buffers into diff_filespecs (e.g., blame and\ngrep), and it would be nice to fix that. But the diff-driver lookup and\ncaching is tied to the diff_filespec, too (which is perhaps a sign of a\nmisfeature in the first place, as the \"diff\" attribute is being used for\nthings like \"grep\" and \"cat-file\"). So we'd have to resolve that.\n\nSo I think there is room for improvement there, but I'm also not sure it\nis worth the trouble. AFAIK neither textconv nor the general length of\ndiff.c is holding up anybody's work or even making it significantly more\ndifficult. If people have spare cycles, they are free to work on\nanything, of course. But I can think of code that is probably in more\ndire need of refactoring. :)\n\n-Peff\n"},{"id":"319671","messageId":"20170514031513.9042-1-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170505052729.7576-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 00/22] Add blame to libgit","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:14:51Z","receivedAt":"2017-05-14T03:16:12Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Rather than duplicate large portions of builtin/blame.c in cgit, it\nwould be better to shift its core functionality into libgit.a.  The\nfunctionality left in builtin/blame.c mostly relates to terminal\npresentation.\n\nSince initial patchset:\n  Made commit titles consistent\n  Broke some commits into more atomic pieces\n  Fleshed out commit message bodies\n  Made public structure and method names more clearly blame-related\n\nJeff Smith (22):\n  blame: remove unneeded dependency on blob.h\n  blame: move textconv_object with related functions\n  blame: remove unused parameters\n  blame: move origin and entry structures to header\n  blame: move scoreboard structure to header\n  blame: move stat counters to scoreboard\n  blame: move copy/move thresholds to scoreboard\n  blame: move contents_from to scoreboard\n  blame: move reverse flag to scoreboard\n  blame: move show_root flag to scoreboard\n  blame: move xdl_opts flags to scoreboard\n  blame: move no_whole_file_rename flag to scoreboard\n  blame: make sanity_check use a callback in scoreboard\n  blame: move progess updates to a scoreboard callback\n  blame: wrap blame_sort and compare_blame_final\n  blame: rework methods that determine 'final' commit\n  blame: move origin-related methods to libgit\n  blame: move fake-commit-related methods to libgit\n  blame: move scoreboard-related methods to libgit\n  blame: create scoreboard init function in libgit\n  blame: create scoreboard setup function in libgit\n  blame: create entry prepend function in libgit\n\n Makefile           |    1 +\n blame.c            | 1863 +++++++++++++++++++++++++++++++++++++++++++++\n blame.h            |  175 +++++\n builtin.h          |    2 -\n builtin/blame.c    | 2130 ++--------------------------------------------------\n builtin/cat-file.c |    1 +\n diff.c             |   23 +\n diff.h             |    7 +\n 8 files changed, 2143 insertions(+), 2059 deletions(-)\n create mode 100644 blame.c\n create mode 100644 blame.h\n\n-- \n2.9.3\n\n"},{"id":"319672","messageId":"20170514031513.9042-2-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 01/22] blame: remove unneeded dependency on blob.h","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:14:52Z","receivedAt":"2017-05-14T03:16:13Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"With commit 21666f1 (\"convert object type handling from a string to a\nnumber\", 2007-02-26), there was no longer a need for blame.c to include\nblob.h but it was not removed.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin/blame.c | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 07506a3..42c56eb 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -8,7 +8,6 @@\n #include \"cache.h\"\n #include \"refs.h\"\n #include \"builtin.h\"\n-#include \"blob.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"tree-walk.h\"\n-- \n2.9.3\n\n"},{"id":"319673","messageId":"20170514031513.9042-4-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 03/22] blame: remove unused parameters","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:14:54Z","receivedAt":"2017-05-14T03:16:18Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Clean up blame code before moving it into libgit\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin/blame.c | 25 ++++++++++---------------\n 1 file changed, 10 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex c419981..e30b3ef 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -449,9 +449,7 @@ static struct origin *make_origin(struct commit *commit, const char *path)\n  * Locate an existing origin or create a new one.\n  * This moves the origin to front position in the commit util list.\n  */\n-static struct origin *get_origin(struct scoreboard *sb,\n-\t\t\t\t struct commit *commit,\n-\t\t\t\t const char *path)\n+static struct origin *get_origin(struct commit *commit, const char *path)\n {\n \tstruct origin *o, *l;\n \n@@ -499,8 +497,7 @@ static int fill_blob_sha1_and_mode(struct origin *origin)\n  * We have an origin -- check if the same path exists in the\n  * parent and return an origin structure to represent it.\n  */\n-static struct origin *find_origin(struct scoreboard *sb,\n-\t\t\t\t  struct commit *parent,\n+static struct origin *find_origin(struct commit *parent,\n \t\t\t\t  struct origin *origin)\n {\n \tstruct origin *porigin;\n@@ -543,7 +540,7 @@ static struct origin *find_origin(struct scoreboard *sb,\n \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\tporigin = get_origin(parent, origin->path);\n \t\toidcpy(&porigin->blob_oid, &origin->blob_oid);\n \t\tporigin->mode = origin->mode;\n \t} else {\n@@ -569,7 +566,7 @@ static struct origin *find_origin(struct scoreboard *sb,\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\tporigin = get_origin(parent, origin->path);\n \t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n \t\t\tporigin->mode = p->one->mode;\n \t\t\tbreak;\n@@ -588,8 +585,7 @@ static struct origin *find_origin(struct scoreboard *sb,\n  * We have an origin -- find the path that corresponds to it in its\n  * parent and return an origin structure to represent it.\n  */\n-static struct origin *find_rename(struct scoreboard *sb,\n-\t\t\t\t  struct commit *parent,\n+static struct origin *find_rename(struct commit *parent,\n \t\t\t\t  struct origin *origin)\n {\n \tstruct origin *porigin = NULL;\n@@ -615,7 +611,7 @@ static struct origin *find_rename(struct scoreboard *sb,\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\tporigin = get_origin(parent, p->one->path);\n \t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n \t\t\tporigin->mode = p->one->mode;\n \t\t\tbreak;\n@@ -1270,7 +1266,7 @@ static void find_copy_in_parent(struct scoreboard *sb,\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\tnorigin = get_origin(parent, p->one->path);\n \t\t\toidcpy(&norigin->blob_oid, &p->one->oid);\n \t\t\tnorigin->mode = p->one->mode;\n \t\t\tfill_origin_blob(&sb->revs->diffopt, norigin, &file_p);\n@@ -1404,8 +1400,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t * common cases, then we look for renames in the second pass.\n \t */\n \tfor (pass = 0; pass < 2 - no_whole_file_rename; pass++) {\n-\t\tstruct origin *(*find)(struct scoreboard *,\n-\t\t\t\t       struct commit *, struct origin *);\n+\t\tstruct origin *(*find)(struct commit *, struct origin *);\n \t\tfind = pass ? find_rename : find_origin;\n \n \t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n@@ -1418,7 +1413,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\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\tporigin = find(p, origin);\n \t\t\tif (!porigin)\n \t\t\t\tcontinue;\n \t\t\tif (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {\n@@ -2806,7 +2801,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tsb.final_buf_size = o->file.size;\n \t}\n \telse {\n-\t\to = get_origin(&sb, sb.final, path);\n+\t\to = get_origin(sb.final, path);\n \t\tif (fill_blob_sha1_and_mode(o))\n \t\t\tdie(_(\"no such path %s in %s\"), path, final_commit_name);\n \n-- \n2.9.3\n\n"},{"id":"319674","messageId":"20170514031513.9042-3-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 02/22] blame: move textconv_object with related functions","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:14:53Z","receivedAt":"2017-05-14T03:16:20Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"textconv_object is used in places other than blame.c and should be moved\nto a more appropriate location.  Other textconv related functions are\nlocated in diff.c so that seems as good a place as any.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin.h          |  2 --\n builtin/blame.c    | 28 ----------------------------\n builtin/cat-file.c |  1 +\n diff.c             | 23 +++++++++++++++++++++++\n diff.h             |  7 +++++++\n 5 files changed, 31 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin.h b/builtin.h\nindex 9e4a898..498ac80 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -25,8 +25,6 @@ struct fmt_merge_msg_opts {\n extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t\t\t struct fmt_merge_msg_opts *);\n \n-extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);\n-\n extern int is_builtin(const char *s);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 42c56eb..c419981 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -147,34 +147,6 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n }\n \n /*\n- * Prepare diff_filespec and convert it using diff textconv API\n- * if the textconv driver exists.\n- * Return 1 if the conversion succeeds, 0 otherwise.\n- */\n-int textconv_object(const char *path,\n-\t\t    unsigned mode,\n-\t\t    const struct object_id *oid,\n-\t\t    int oid_valid,\n-\t\t    char **buf,\n-\t\t    unsigned long *buf_size)\n-{\n-\tstruct diff_filespec *df;\n-\tstruct userdiff_driver *textconv;\n-\n-\tdf = alloc_filespec(path);\n-\tfill_filespec(df, oid->hash, oid_valid, mode);\n-\ttextconv = get_textconv(df);\n-\tif (!textconv) {\n-\t\tfree_filespec(df);\n-\t\treturn 0;\n-\t}\n-\n-\t*buf_size = fill_textconv(textconv, df, buf);\n-\tfree_filespec(df);\n-\treturn 1;\n-}\n-\n-/*\n  * Given an origin, prepare mmfile_t structure to be used by the\n  * diff machinery\n  */\ndiff --git a/builtin/cat-file.c b/builtin/cat-file.c\nindex 1890d7a..79a2c82 100644\n--- a/builtin/cat-file.c\n+++ b/builtin/cat-file.c\n@@ -5,6 +5,7 @@\n  */\n #include \"cache.h\"\n #include \"builtin.h\"\n+#include \"diff.h\"\n #include \"parse-options.h\"\n #include \"userdiff.h\"\n #include \"streaming.h\"\ndiff --git a/diff.c b/diff.c\nindex 74283d9..040fb25 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -5270,6 +5270,29 @@ size_t fill_textconv(struct userdiff_driver *driver,\n \treturn size;\n }\n \n+int textconv_object(const char *path,\n+\t\t    unsigned mode,\n+\t\t    const struct object_id *oid,\n+\t\t    int oid_valid,\n+\t\t    char **buf,\n+\t\t    unsigned long *buf_size)\n+{\n+\tstruct diff_filespec *df;\n+\tstruct userdiff_driver *textconv;\n+\n+\tdf = alloc_filespec(path);\n+\tfill_filespec(df, oid->hash, oid_valid, mode);\n+\ttextconv = get_textconv(df);\n+\tif (!textconv) {\n+\t\tfree_filespec(df);\n+\t\treturn 0;\n+\t}\n+\n+\t*buf_size = fill_textconv(textconv, df, buf);\n+\tfree_filespec(df);\n+\treturn 1;\n+}\n+\n void setup_diff_pager(struct diff_options *opt)\n {\n \t/*\ndiff --git a/diff.h b/diff.h\nindex 5be1ee7..52ebd54 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -385,6 +385,13 @@ extern size_t fill_textconv(struct userdiff_driver *driver,\n  */\n extern struct userdiff_driver *get_textconv(struct diff_filespec *one);\n \n+/*\n+ * Prepare diff_filespec and convert it using diff textconv API\n+ * if the textconv driver exists.\n+ * Return 1 if the conversion succeeds, 0 otherwise.\n+ */\n+extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);\n+\n extern int parse_rename_score(const char **cp_p);\n \n extern long parse_algorithm_value(const char *value);\n-- \n2.9.3\n\n"},{"id":"319675","messageId":"20170514031513.9042-6-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 05/22] blame: move scoreboard structure to header","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:14:56Z","receivedAt":"2017-05-14T03:16:22Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"The scoreboard structure is core to the blame interface. Since\nscoreboard will be more exposed, rename it to blame_scoreboard to\nclarify what it is a part of.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         | 29 ++++++++++++++++++++\n builtin/blame.c | 83 +++++++++++++++++++--------------------------------------\n 2 files changed, 57 insertions(+), 55 deletions(-)\n\ndiff --git a/blame.h b/blame.h\nindex f52d0fc..8d6a6fd 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -4,6 +4,8 @@\n #include \"cache.h\"\n #include \"commit.h\"\n #include \"xdiff-interface.h\"\n+#include \"revision.h\"\n+#include \"prio-queue.h\"\n \n /*\n  * One blob in a commit that is being suspected\n@@ -83,4 +85,31 @@ struct blame_entry {\n \tunsigned score;\n };\n \n+/*\n+ * The current state of the blame assignment.\n+ */\n+struct blame_scoreboard {\n+\t/* the final commit (i.e. where we started digging from) */\n+\tstruct commit *final;\n+\t/* Priority queue for commits with unassigned blame records */\n+\tstruct prio_queue commits;\n+\tstruct rev_info *revs;\n+\tconst char *path;\n+\n+\t/*\n+\t * The contents in the final image.\n+\t * Used by many functions to obtain contents of the nth line,\n+\t * indexed with scoreboard.lineno[blame_entry.lno].\n+\t */\n+\tconst char *final_buf;\n+\tunsigned long final_buf_size;\n+\n+\t/* linked list of blames */\n+\tstruct blame_entry *ent;\n+\n+\t/* look-up a line in the final buffer */\n+\tint num_lines;\n+\tint *lineno;\n+};\n+\n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex caa5ee0..4a1da53 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -273,41 +273,14 @@ static int compare_commits_by_reverse_commit_date(const void *a,\n \treturn -compare_commits_by_commit_date(a, b, c);\n }\n \n-/*\n- * The current state of the blame assignment.\n- */\n-struct scoreboard {\n-\t/* the final commit (i.e. where we started digging from) */\n-\tstruct commit *final;\n-\t/* Priority queue for commits with unassigned blame records */\n-\tstruct prio_queue commits;\n-\tstruct rev_info *revs;\n-\tconst char *path;\n-\n-\t/*\n-\t * The contents in the final image.\n-\t * Used by many functions to obtain contents of the nth line,\n-\t * indexed with scoreboard.lineno[blame_entry.lno].\n-\t */\n-\tconst char *final_buf;\n-\tunsigned long final_buf_size;\n-\n-\t/* linked list of blames */\n-\tstruct blame_entry *ent;\n-\n-\t/* look-up a line in the final buffer */\n-\tint num_lines;\n-\tint *lineno;\n-};\n-\n-static void sanity_check_refcnt(struct scoreboard *);\n+static void sanity_check_refcnt(struct blame_scoreboard *);\n \n /*\n  * If two blame entries that are next to each other came from\n  * contiguous lines in the same origin (i.e. <commit, path> pair),\n  * merge them together.\n  */\n-static void coalesce(struct scoreboard *sb)\n+static void coalesce(struct blame_scoreboard *sb)\n {\n \tstruct blame_entry *ent, *next;\n \n@@ -333,7 +306,7 @@ static void coalesce(struct scoreboard *sb)\n  * the commit priority queue of the score board.\n  */\n \n-static void queue_blames(struct scoreboard *sb, struct blame_origin *porigin,\n+static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porigin,\n \t\t\t struct blame_entry *sorted)\n {\n \tif (porigin->suspects)\n@@ -576,14 +549,14 @@ static void dup_entry(struct blame_entry ***queue,\n \t*queue = &dst->next;\n }\n \n-static const char *nth_line(struct scoreboard *sb, long lno)\n+static const char *nth_line(struct blame_scoreboard *sb, long lno)\n {\n \treturn sb->final_buf + sb->lineno[lno];\n }\n \n static const char *nth_line_cb(void *data, long lno)\n {\n-\treturn nth_line((struct scoreboard *)data, lno);\n+\treturn nth_line((struct blame_scoreboard *)data, lno);\n }\n \n /*\n@@ -842,7 +815,7 @@ static int blame_chunk_cb(long start_a, long count_a,\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 void pass_blame_to_parent(struct scoreboard *sb,\n+static void pass_blame_to_parent(struct blame_scoreboard *sb,\n \t\t\t\t struct blame_origin *target,\n \t\t\t\t struct blame_origin *parent)\n {\n@@ -882,7 +855,7 @@ static void pass_blame_to_parent(struct scoreboard *sb,\n  *\n  * Compute how trivial the lines in the blame_entry are.\n  */\n-static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)\n+static unsigned ent_score(struct blame_scoreboard *sb, struct blame_entry *e)\n {\n \tunsigned score;\n \tconst char *cp, *ep;\n@@ -909,7 +882,7 @@ static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)\n  * so far, by comparing this and best_so_far and copying this into\n  * bst_so_far as needed.\n  */\n-static void copy_split_if_better(struct scoreboard *sb,\n+static void copy_split_if_better(struct blame_scoreboard *sb,\n \t\t\t\t struct blame_entry *best_so_far,\n \t\t\t\t struct blame_entry *this)\n {\n@@ -943,7 +916,7 @@ static void copy_split_if_better(struct scoreboard *sb,\n  *\n  * All line numbers are 0-based.\n  */\n-static void handle_split(struct scoreboard *sb,\n+static void handle_split(struct blame_scoreboard *sb,\n \t\t\t struct blame_entry *ent,\n \t\t\t int tlno, int plno, int same,\n \t\t\t struct blame_origin *parent,\n@@ -962,7 +935,7 @@ static void handle_split(struct scoreboard *sb,\n }\n \n struct handle_split_cb_data {\n-\tstruct scoreboard *sb;\n+\tstruct blame_scoreboard *sb;\n \tstruct blame_entry *ent;\n \tstruct blame_origin *parent;\n \tstruct blame_entry *split;\n@@ -986,7 +959,7 @@ static int handle_split_cb(long start_a, long count_a,\n  * we can pass blames to it.  file_p has the blob contents for\n  * the parent.\n  */\n-static void find_copy_in_blob(struct scoreboard *sb,\n+static void find_copy_in_blob(struct blame_scoreboard *sb,\n \t\t\t      struct blame_entry *ent,\n \t\t\t      struct blame_origin *parent,\n \t\t\t      struct blame_entry *split,\n@@ -1022,7 +995,7 @@ static void find_copy_in_blob(struct scoreboard *sb,\n  * Returns a pointer to the link pointing to the old head of the small list.\n  */\n \n-static struct blame_entry **filter_small(struct scoreboard *sb,\n+static struct blame_entry **filter_small(struct blame_scoreboard *sb,\n \t\t\t\t\t struct blame_entry **small,\n \t\t\t\t\t struct blame_entry **source,\n \t\t\t\t\t unsigned score_min)\n@@ -1049,7 +1022,7 @@ static struct blame_entry **filter_small(struct scoreboard *sb,\n  * See if lines currently target is suspected for can be attributed to\n  * parent.\n  */\n-static void find_move_in_parent(struct scoreboard *sb,\n+static void find_move_in_parent(struct blame_scoreboard *sb,\n \t\t\t\tstruct blame_entry ***blamed,\n \t\t\t\tstruct blame_entry **toosmall,\n \t\t\t\tstruct blame_origin *target,\n@@ -1125,7 +1098,7 @@ static struct blame_list *setup_blame_list(struct blame_entry *unblamed,\n  * across file boundary from the parent commit.  porigin is the path\n  * in the parent we already tried.\n  */\n-static void find_copy_in_parent(struct scoreboard *sb,\n+static void find_copy_in_parent(struct blame_scoreboard *sb,\n \t\t\t\tstruct blame_entry ***blamed,\n \t\t\t\tstruct blame_entry **toosmall,\n \t\t\t\tstruct blame_origin *target,\n@@ -1231,7 +1204,7 @@ static void find_copy_in_parent(struct scoreboard *sb,\n  * The blobs of origin and porigin exactly match, so everything\n  * origin is suspected for can be blamed on the parent.\n  */\n-static void pass_whole_blame(struct scoreboard *sb,\n+static void pass_whole_blame(struct blame_scoreboard *sb,\n \t\t\t     struct blame_origin *origin, struct blame_origin *porigin)\n {\n \tstruct blame_entry *e, *suspects;\n@@ -1279,7 +1252,7 @@ static int num_scapegoats(struct rev_info *revs, struct commit *commit)\n /* Distribute collected unsorted blames to the respected sorted lists\n  * in the various origins.\n  */\n-static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)\n+static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed)\n {\n \tblamed = blame_sort(blamed, compare_blame_suspect);\n \twhile (blamed)\n@@ -1299,7 +1272,7 @@ static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)\n \n #define MAXSG 16\n \n-static void pass_blame(struct scoreboard *sb, struct blame_origin *origin, int opt)\n+static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, int opt)\n {\n \tstruct rev_info *revs = sb->revs;\n \tint i, pass, num_sg;\n@@ -1656,7 +1629,7 @@ static void found_guilty_entry(struct blame_entry *ent,\n  * The main loop -- while we have blobs with lines whose true origin\n  * is still unknown, pick one blob, and allow its lines to pass blames\n  * to its parents. */\n-static void assign_blame(struct scoreboard *sb, int opt)\n+static void assign_blame(struct blame_scoreboard *sb, int opt)\n {\n \tstruct rev_info *revs = sb->revs;\n \tstruct commit *commit = prio_queue_get(&sb->commits);\n@@ -1772,7 +1745,7 @@ static void emit_porcelain_details(struct blame_origin *suspect, int repeat)\n \t\twrite_filename_info(suspect);\n }\n \n-static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,\n+static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,\n \t\t\t   int opt)\n {\n \tint repeat = opt & OUTPUT_LINE_PORCELAIN;\n@@ -1811,7 +1784,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,\n \t\tputchar('\\n');\n }\n \n-static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n+static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt)\n {\n \tint cnt;\n \tconst char *cp;\n@@ -1890,7 +1863,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \tcommit_info_destroy(&ci);\n }\n \n-static void output(struct scoreboard *sb, int option)\n+static void output(struct blame_scoreboard *sb, int option)\n {\n \tstruct blame_entry *ent;\n \n@@ -1929,7 +1902,7 @@ static const char *get_next_line(const char *start, const char *end)\n  * To allow quick access to the contents of nth line in the\n  * final image, prepare an index in the scoreboard.\n  */\n-static int prepare_lines(struct scoreboard *sb)\n+static int prepare_lines(struct blame_scoreboard *sb)\n {\n \tconst char *buf = sb->final_buf;\n \tunsigned long len = sb->final_buf_size;\n@@ -1989,7 +1962,7 @@ static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect)\n  * How many columns do we need to show line numbers, authors,\n  * and filenames?\n  */\n-static void find_alignment(struct scoreboard *sb, int *option)\n+static void find_alignment(struct blame_scoreboard *sb, int *option)\n {\n \tint longest_src_lines = 0;\n \tint longest_dst_lines = 0;\n@@ -2043,7 +2016,7 @@ static void find_alignment(struct scoreboard *sb, int *option)\n  * For debugging -- origin is refcounted, and this asserts that\n  * we do not underflow.\n  */\n-static void sanity_check_refcnt(struct scoreboard *sb)\n+static void sanity_check_refcnt(struct blame_scoreboard *sb)\n {\n \tint baa = 0;\n \tstruct blame_entry *ent;\n@@ -2334,14 +2307,14 @@ static struct commit *find_single_final(struct rev_info *revs,\n \treturn found;\n }\n \n-static char *prepare_final(struct scoreboard *sb)\n+static char *prepare_final(struct blame_scoreboard *sb)\n {\n \tconst char *name;\n \tsb->final = find_single_final(sb->revs, &name);\n \treturn xstrdup_or_null(name);\n }\n \n-static const char *dwim_reverse_initial(struct scoreboard *sb)\n+static const char *dwim_reverse_initial(struct blame_scoreboard *sb)\n {\n \t/*\n \t * DWIM \"git blame --reverse ONE -- PATH\" as\n@@ -2376,7 +2349,7 @@ static const char *dwim_reverse_initial(struct scoreboard *sb)\n \treturn sb->revs->pending.objects[0].name;\n }\n \n-static char *prepare_initial(struct scoreboard *sb)\n+static char *prepare_initial(struct blame_scoreboard *sb)\n {\n \tint i;\n \tconst char *final_commit_name = NULL;\n@@ -2445,7 +2418,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n {\n \tstruct rev_info revs;\n \tconst char *path;\n-\tstruct scoreboard sb;\n+\tstruct blame_scoreboard sb;\n \tstruct blame_origin *o;\n \tstruct blame_entry *ent = NULL;\n \tlong dashdash_pos, lno;\n-- \n2.9.3\n\n"},{"id":"319676","messageId":"20170514031513.9042-7-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 06/22] blame: move stat counters to scoreboard","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:14:57Z","receivedAt":"2017-05-14T03:16:24Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Statistic counters are used in parts of blame that are being moved to\nlibgit, and should be accessible via the scoreboard structure.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         |  5 +++++\n builtin/blame.c | 29 ++++++++++++-----------------\n 2 files changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/blame.h b/blame.h\nindex 8d6a6fd..3438052 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -110,6 +110,11 @@ struct blame_scoreboard {\n \t/* look-up a line in the final buffer */\n \tint num_lines;\n \tint *lineno;\n+\n+\t/* stats */\n+\tint num_read_blob;\n+\tint num_get_patch;\n+\tint num_commits;\n };\n \n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 4a1da53..12add06 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -62,11 +62,6 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP;\n #define DEBUG 0\n #endif\n \n-/* stats */\n-static int num_read_blob;\n-static int num_get_patch;\n-static int num_commits;\n-\n #define PICKAXE_BLAME_MOVE\t\t01\n #define PICKAXE_BLAME_COPY\t\t02\n #define PICKAXE_BLAME_COPY_HARDER\t04\n@@ -108,13 +103,13 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n  * diff machinery\n  */\n static void fill_origin_blob(struct diff_options *opt,\n-\t\t\t     struct blame_origin *o, mmfile_t *file)\n+\t\t\t     struct blame_origin *o, mmfile_t *file, int *num_read_blob)\n {\n \tif (!o->file.ptr) {\n \t\tenum object_type type;\n \t\tunsigned long file_size;\n \n-\t\tnum_read_blob++;\n+\t\t(*num_read_blob)++;\n \t\tif (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&\n \t\t    textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))\n \t\t\t;\n@@ -830,9 +825,9 @@ static void pass_blame_to_parent(struct blame_scoreboard *sb,\n \td.offset = 0;\n \td.dstq = &newdest; d.srcq = &target->suspects;\n \n-\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p);\n-\tfill_origin_blob(&sb->revs->diffopt, target, &file_o);\n-\tnum_get_patch++;\n+\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n+\tfill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob);\n+\tsb->num_get_patch++;\n \n \tif (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d))\n \t\tdie(\"unable to generate diff (%s -> %s)\",\n@@ -1036,7 +1031,7 @@ static void find_move_in_parent(struct blame_scoreboard *sb,\n \tif (!unblamed)\n \t\treturn; /* nothing remains for this target */\n \n-\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p);\n+\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n \tif (!file_p.ptr)\n \t\treturn;\n \n@@ -1165,7 +1160,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb,\n \t\t\tnorigin = get_origin(parent, p->one->path);\n \t\t\toidcpy(&norigin->blob_oid, &p->one->oid);\n \t\t\tnorigin->mode = p->one->mode;\n-\t\t\tfill_origin_blob(&sb->revs->diffopt, norigin, &file_p);\n+\t\t\tfill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob);\n \t\t\tif (!file_p.ptr)\n \t\t\t\tcontinue;\n \n@@ -1330,7 +1325,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\n \t\t}\n \t}\n \n-\tnum_commits++;\n+\tsb->num_commits++;\n \tfor (i = 0, sg = first_scapegoat(revs, commit);\n \t     i < num_sg && sg;\n \t     sg = sg->next, i++) {\n@@ -2714,7 +2709,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\t\t    oid_to_hex(&o->blob_oid),\n \t\t\t    path);\n \t}\n-\tnum_read_blob++;\n+\tsb.num_read_blob++;\n \tlno = prepare_lines(&sb);\n \n \tif (lno && !range_list.nr)\n@@ -2795,9 +2790,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (show_stats) {\n-\t\tprintf(\"num read blob: %d\\n\", num_read_blob);\n-\t\tprintf(\"num get patch: %d\\n\", num_get_patch);\n-\t\tprintf(\"num commits: %d\\n\", num_commits);\n+\t\tprintf(\"num read blob: %d\\n\", sb.num_read_blob);\n+\t\tprintf(\"num get patch: %d\\n\", sb.num_get_patch);\n+\t\tprintf(\"num commits: %d\\n\", sb.num_commits);\n \t}\n \treturn 0;\n }\n-- \n2.9.3\n\n"},{"id":"319677","messageId":"20170514031513.9042-5-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 04/22] blame: move origin and entry structures to header","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:14:55Z","receivedAt":"2017-05-14T03:16:26Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"The origin and blame_entry structures are core to the blame interface\nand reference each other. Since origin will be more exposed, rename it\nto blame_origin to clarify what it is a part of.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         |  86 ++++++++++++++++++++++++++\n builtin/blame.c | 185 +++++++++++++++++---------------------------------------\n 2 files changed, 140 insertions(+), 131 deletions(-)\n create mode 100644 blame.h\n\ndiff --git a/blame.h b/blame.h\nnew file mode 100644\nindex 0000000..f52d0fc\n--- /dev/null\n+++ b/blame.h\n@@ -0,0 +1,86 @@\n+#ifndef BLAME_H\n+#define BLAME_H\n+\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"xdiff-interface.h\"\n+\n+/*\n+ * One blob in a commit that is being suspected\n+ */\n+struct blame_origin {\n+\tint refcnt;\n+\t/* Record preceding blame record for this blob */\n+\tstruct blame_origin *previous;\n+\t/* origins are put in a list linked via `next' hanging off the\n+\t * corresponding commit's util field in order to make finding\n+\t * them fast.  The presence in this chain does not count\n+\t * towards the origin's reference count.  It is tempting to\n+\t * let it count as long as the commit is pending examination,\n+\t * but even under circumstances where the commit will be\n+\t * present multiple times in the priority queue of unexamined\n+\t * commits, processing the first instance will not leave any\n+\t * work requiring the origin data for the second instance.  An\n+\t * interspersed commit changing that would have to be\n+\t * preexisting with a different ancestry and with the same\n+\t * commit date in order to wedge itself between two instances\n+\t * of the same commit in the priority queue _and_ produce\n+\t * blame entries relevant for it.  While we don't want to let\n+\t * us get tripped up by this case, it certainly does not seem\n+\t * worth optimizing for.\n+\t */\n+\tstruct blame_origin *next;\n+\tstruct commit *commit;\n+\t/* `suspects' contains blame entries that may be attributed to\n+\t * this origin's commit or to parent commits.  When a commit\n+\t * is being processed, all suspects will be moved, either by\n+\t * assigning them to an origin in a different commit, or by\n+\t * shipping them to the scoreboard's ent list because they\n+\t * cannot be attributed to a different commit.\n+\t */\n+\tstruct blame_entry *suspects;\n+\tmmfile_t file;\n+\tstruct object_id blob_oid;\n+\tunsigned mode;\n+\t/* guilty gets set when shipping any suspects to the final\n+\t * blame list instead of other commits\n+\t */\n+\tchar guilty;\n+\tchar path[FLEX_ARRAY];\n+};\n+\n+/*\n+ * Each group of lines is described by a blame_entry; it can be split\n+ * as we pass blame to the parents.  They are arranged in linked lists\n+ * kept as `suspects' of some unprocessed origin, or entered (when the\n+ * blame origin has been finalized) into the scoreboard structure.\n+ * While the scoreboard structure is only sorted at the end of\n+ * processing (according to final image line number), the lists\n+ * attached to an origin are sorted by the target line number.\n+ */\n+struct blame_entry {\n+\tstruct blame_entry *next;\n+\n+\t/* the first line of this group in the final image;\n+\t * internally all line numbers are 0 based.\n+\t */\n+\tint lno;\n+\n+\t/* how many lines this group has */\n+\tint num_lines;\n+\n+\t/* the commit that introduced this group into the final image */\n+\tstruct blame_origin *suspect;\n+\n+\t/* the line number of the first line of this group in the\n+\t * suspect's file; internally all line numbers are 0 based.\n+\t */\n+\tint s_lno;\n+\n+\t/* how significant this entry is -- cached to avoid\n+\t * scanning the lines over and over.\n+\t */\n+\tunsigned score;\n+};\n+\n+#endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex e30b3ef..caa5ee0 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -28,6 +28,7 @@\n #include \"line-log.h\"\n #include \"dir.h\"\n #include \"progress.h\"\n+#include \"blame.h\"\n \n static char blame_usage[] = N_(\"git blame [<options>] [<rev-opts>] [<rev>] [--] <file>\");\n \n@@ -84,50 +85,6 @@ static unsigned blame_copy_score;\n #define METAINFO_SHOWN\t\t(1u<<12)\n #define MORE_THAN_ONE_PATH\t(1u<<13)\n \n-/*\n- * One blob in a commit that is being suspected\n- */\n-struct origin {\n-\tint refcnt;\n-\t/* Record preceding blame record for this blob */\n-\tstruct origin *previous;\n-\t/* origins are put in a list linked via `next' hanging off the\n-\t * corresponding commit's util field in order to make finding\n-\t * them fast.  The presence in this chain does not count\n-\t * towards the origin's reference count.  It is tempting to\n-\t * let it count as long as the commit is pending examination,\n-\t * but even under circumstances where the commit will be\n-\t * present multiple times in the priority queue of unexamined\n-\t * commits, processing the first instance will not leave any\n-\t * work requiring the origin data for the second instance.  An\n-\t * interspersed commit changing that would have to be\n-\t * preexisting with a different ancestry and with the same\n-\t * commit date in order to wedge itself between two instances\n-\t * of the same commit in the priority queue _and_ produce\n-\t * blame entries relevant for it.  While we don't want to let\n-\t * us get tripped up by this case, it certainly does not seem\n-\t * worth optimizing for.\n-\t */\n-\tstruct origin *next;\n-\tstruct commit *commit;\n-\t/* `suspects' contains blame entries that may be attributed to\n-\t * this origin's commit or to parent commits.  When a commit\n-\t * is being processed, all suspects will be moved, either by\n-\t * assigning them to an origin in a different commit, or by\n-\t * shipping them to the scoreboard's ent list because they\n-\t * cannot be attributed to a different commit.\n-\t */\n-\tstruct blame_entry *suspects;\n-\tmmfile_t file;\n-\tstruct object_id blob_oid;\n-\tunsigned mode;\n-\t/* guilty gets set when shipping any suspects to the final\n-\t * blame list instead of other commits\n-\t */\n-\tchar guilty;\n-\tchar path[FLEX_ARRAY];\n-};\n-\n struct progress_info {\n \tstruct progress *progress;\n \tint blamed_lines;\n@@ -151,7 +108,7 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n  * diff machinery\n  */\n static void fill_origin_blob(struct diff_options *opt,\n-\t\t\t     struct origin *o, mmfile_t *file)\n+\t\t\t     struct blame_origin *o, mmfile_t *file)\n {\n \tif (!o->file.ptr) {\n \t\tenum object_type type;\n@@ -180,17 +137,17 @@ static void fill_origin_blob(struct diff_options *opt,\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+static inline struct blame_origin *origin_incref(struct blame_origin *o)\n {\n \tif (o)\n \t\to->refcnt++;\n \treturn o;\n }\n \n-static void origin_decref(struct origin *o)\n+static void origin_decref(struct blame_origin *o)\n {\n \tif (o && --o->refcnt <= 0) {\n-\t\tstruct origin *p, *l = NULL;\n+\t\tstruct blame_origin *p, *l = NULL;\n \t\tif (o->previous)\n \t\t\torigin_decref(o->previous);\n \t\tfree(o->file.ptr);\n@@ -209,7 +166,7 @@ static void origin_decref(struct origin *o)\n \t}\n }\n \n-static void drop_origin_blob(struct origin *o)\n+static void drop_origin_blob(struct blame_origin *o)\n {\n \tif (o->file.ptr) {\n \t\tfree(o->file.ptr);\n@@ -218,40 +175,6 @@ static void drop_origin_blob(struct origin *o)\n }\n \n /*\n- * Each group of lines is described by a blame_entry; it can be split\n- * as we pass blame to the parents.  They are arranged in linked lists\n- * kept as `suspects' of some unprocessed origin, or entered (when the\n- * blame origin has been finalized) into the scoreboard structure.\n- * While the scoreboard structure is only sorted at the end of\n- * processing (according to final image line number), the lists\n- * attached to an origin are sorted by the target line number.\n- */\n-struct blame_entry {\n-\tstruct blame_entry *next;\n-\n-\t/* the first line of this group in the final image;\n-\t * internally all line numbers are 0 based.\n-\t */\n-\tint lno;\n-\n-\t/* how many lines this group has */\n-\tint num_lines;\n-\n-\t/* the commit that introduced this group into the final image */\n-\tstruct origin *suspect;\n-\n-\t/* the line number of the first line of this group in the\n-\t * suspect's file; internally all line numbers are 0 based.\n-\t */\n-\tint s_lno;\n-\n-\t/* how significant this entry is -- cached to avoid\n-\t * scanning the lines over and over.\n-\t */\n-\tunsigned score;\n-};\n-\n-/*\n  * Any merge of blames happens on lists of blames that arrived via\n  * different parents in a single suspect.  In this case, we want to\n  * sort according to the suspect line numbers as opposed to the final\n@@ -410,13 +333,13 @@ static void coalesce(struct scoreboard *sb)\n  * the commit priority queue of the score board.\n  */\n \n-static void queue_blames(struct scoreboard *sb, struct origin *porigin,\n+static void queue_blames(struct scoreboard *sb, struct blame_origin *porigin,\n \t\t\t struct blame_entry *sorted)\n {\n \tif (porigin->suspects)\n \t\tporigin->suspects = blame_merge(porigin->suspects, sorted);\n \telse {\n-\t\tstruct origin *o;\n+\t\tstruct blame_origin *o;\n \t\tfor (o = porigin->commit->util; o; o = o->next) {\n \t\t\tif (o->suspects) {\n \t\t\t\tporigin->suspects = sorted;\n@@ -434,9 +357,9 @@ static void queue_blames(struct scoreboard *sb, struct origin *porigin,\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+static struct blame_origin *make_origin(struct commit *commit, const char *path)\n {\n-\tstruct origin *o;\n+\tstruct blame_origin *o;\n \tFLEX_ALLOC_STR(o, path, path);\n \to->commit = commit;\n \to->refcnt = 1;\n@@ -449,9 +372,9 @@ static struct origin *make_origin(struct commit *commit, const char *path)\n  * Locate an existing origin or create a new one.\n  * This moves the origin to front position in the commit util list.\n  */\n-static struct origin *get_origin(struct commit *commit, const char *path)\n+static struct blame_origin *get_origin(struct commit *commit, const char *path)\n {\n-\tstruct origin *o, *l;\n+\tstruct blame_origin *o, *l;\n \n \tfor (o = commit->util, l = NULL; o; l = o, o = o->next) {\n \t\tif (!strcmp(o->path, path)) {\n@@ -476,7 +399,7 @@ static struct origin *get_origin(struct commit *commit, const char *path)\n  *\n  * This also fills origin->mode for corresponding tree path.\n  */\n-static int fill_blob_sha1_and_mode(struct origin *origin)\n+static int fill_blob_sha1_and_mode(struct blame_origin *origin)\n {\n \tif (!is_null_oid(&origin->blob_oid))\n \t\treturn 0;\n@@ -497,10 +420,10 @@ static int fill_blob_sha1_and_mode(struct origin *origin)\n  * We have an origin -- check if the same path exists in the\n  * parent and return an origin structure to represent it.\n  */\n-static struct origin *find_origin(struct commit *parent,\n-\t\t\t\t  struct origin *origin)\n+static struct blame_origin *find_origin(struct commit *parent,\n+\t\t\t\t  struct blame_origin *origin)\n {\n-\tstruct origin *porigin;\n+\tstruct blame_origin *porigin;\n \tstruct diff_options diff_opts;\n \tconst char *paths[2];\n \n@@ -585,10 +508,10 @@ static struct origin *find_origin(struct commit *parent,\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 commit *parent,\n-\t\t\t\t  struct origin *origin)\n+static struct blame_origin *find_rename(struct commit *parent,\n+\t\t\t\t  struct blame_origin *origin)\n {\n-\tstruct origin *porigin = NULL;\n+\tstruct blame_origin *porigin = NULL;\n \tstruct diff_options diff_opts;\n \tint i;\n \n@@ -680,7 +603,7 @@ static const char *nth_line_cb(void *data, long lno)\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+\t\t\t  struct blame_origin *parent)\n {\n \tint chunk_end_lno;\n \tmemset(split, 0, sizeof(struct blame_entry [3]));\n@@ -804,7 +727,7 @@ static struct blame_entry *reverse_blame(struct blame_entry *head,\n  */\n static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,\n \t\t\tint tlno, int offset, int same,\n-\t\t\tstruct origin *parent)\n+\t\t\tstruct blame_origin *parent)\n {\n \tstruct blame_entry *e = **srcq;\n \tstruct blame_entry *samep = NULL, *diffp = NULL;\n@@ -895,7 +818,7 @@ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,\n }\n \n struct blame_chunk_cb_data {\n-\tstruct origin *parent;\n+\tstruct blame_origin *parent;\n \tlong offset;\n \tstruct blame_entry **dstq;\n \tstruct blame_entry **srcq;\n@@ -920,8 +843,8 @@ static int blame_chunk_cb(long start_a, long count_a,\n  * which lines came from parent and pass blame for them.\n  */\n static void pass_blame_to_parent(struct scoreboard *sb,\n-\t\t\t\t struct origin *target,\n-\t\t\t\t struct origin *parent)\n+\t\t\t\t struct blame_origin *target,\n+\t\t\t\t struct blame_origin *parent)\n {\n \tmmfile_t file_p, file_o;\n \tstruct blame_chunk_cb_data d;\n@@ -1023,7 +946,7 @@ static void copy_split_if_better(struct scoreboard *sb,\n static void handle_split(struct 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_origin *parent,\n \t\t\t struct blame_entry *split)\n {\n \tif (ent->num_lines <= tlno)\n@@ -1041,7 +964,7 @@ static void handle_split(struct scoreboard *sb,\n struct handle_split_cb_data {\n \tstruct scoreboard *sb;\n \tstruct blame_entry *ent;\n-\tstruct origin *parent;\n+\tstruct blame_origin *parent;\n \tstruct blame_entry *split;\n \tlong plno;\n \tlong tlno;\n@@ -1065,7 +988,7 @@ static int handle_split_cb(long start_a, long count_a,\n  */\n static void find_copy_in_blob(struct scoreboard *sb,\n \t\t\t      struct blame_entry *ent,\n-\t\t\t      struct origin *parent,\n+\t\t\t      struct blame_origin *parent,\n \t\t\t      struct blame_entry *split,\n \t\t\t      mmfile_t *file_p)\n {\n@@ -1129,8 +1052,8 @@ static struct blame_entry **filter_small(struct scoreboard *sb,\n static void find_move_in_parent(struct scoreboard *sb,\n \t\t\t\tstruct blame_entry ***blamed,\n \t\t\t\tstruct blame_entry **toosmall,\n-\t\t\t\tstruct origin *target,\n-\t\t\t\tstruct origin *parent)\n+\t\t\t\tstruct blame_origin *target,\n+\t\t\t\tstruct blame_origin *parent)\n {\n \tstruct blame_entry *e, split[3];\n \tstruct blame_entry *unblamed = target->suspects;\n@@ -1205,9 +1128,9 @@ static struct blame_list *setup_blame_list(struct blame_entry *unblamed,\n static void find_copy_in_parent(struct scoreboard *sb,\n \t\t\t\tstruct blame_entry ***blamed,\n \t\t\t\tstruct blame_entry **toosmall,\n-\t\t\t\tstruct origin *target,\n+\t\t\t\tstruct blame_origin *target,\n \t\t\t\tstruct commit *parent,\n-\t\t\t\tstruct origin *porigin,\n+\t\t\t\tstruct blame_origin *porigin,\n \t\t\t\tint opt)\n {\n \tstruct diff_options diff_opts;\n@@ -1254,7 +1177,7 @@ static void find_copy_in_parent(struct scoreboard *sb,\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\tstruct blame_origin *norigin;\n \t\t\tmmfile_t file_p;\n \t\t\tstruct blame_entry this[3];\n \n@@ -1309,7 +1232,7 @@ static void find_copy_in_parent(struct scoreboard *sb,\n  * origin is suspected for can be blamed on the parent.\n  */\n static void pass_whole_blame(struct scoreboard *sb,\n-\t\t\t     struct origin *origin, struct origin *porigin)\n+\t\t\t     struct blame_origin *origin, struct blame_origin *porigin)\n {\n \tstruct blame_entry *e, *suspects;\n \n@@ -1361,7 +1284,7 @@ static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)\n \tblamed = blame_sort(blamed, compare_blame_suspect);\n \twhile (blamed)\n \t{\n-\t\tstruct origin *porigin = blamed->suspect;\n+\t\tstruct blame_origin *porigin = blamed->suspect;\n \t\tstruct blame_entry *suspects = NULL;\n \t\tdo {\n \t\t\tstruct blame_entry *next = blamed->next;\n@@ -1376,14 +1299,14 @@ static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)\n \n #define MAXSG 16\n \n-static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n+static void pass_blame(struct scoreboard *sb, struct blame_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+\tstruct blame_origin *sg_buf[MAXSG];\n+\tstruct blame_origin *porigin, **sg_origin = sg_buf;\n \tstruct blame_entry *toosmall = NULL;\n \tstruct blame_entry *blames, **blametail = &blames;\n \n@@ -1400,7 +1323,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t * common cases, then we look for renames in the second pass.\n \t */\n \tfor (pass = 0; pass < 2 - no_whole_file_rename; pass++) {\n-\t\tstruct origin *(*find)(struct commit *, struct origin *);\n+\t\tstruct blame_origin *(*find)(struct commit *, struct blame_origin *);\n \t\tfind = pass ? find_rename : find_origin;\n \n \t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n@@ -1438,7 +1361,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \tfor (i = 0, sg = first_scapegoat(revs, commit);\n \t     i < num_sg && sg;\n \t     sg = sg->next, i++) {\n-\t\tstruct origin *porigin = sg_origin[i];\n+\t\tstruct blame_origin *porigin = sg_origin[i];\n \t\tif (!porigin)\n \t\t\tcontinue;\n \t\tif (!origin->previous) {\n@@ -1459,7 +1382,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n \t\t\t     i < num_sg && sg;\n \t\t\t     sg = sg->next, i++) {\n-\t\t\t\tstruct origin *porigin = sg_origin[i];\n+\t\t\t\tstruct blame_origin *porigin = sg_origin[i];\n \t\t\t\tif (!porigin)\n \t\t\t\t\tcontinue;\n \t\t\t\tfind_move_in_parent(sb, &blametail, &toosmall, origin, porigin);\n@@ -1486,7 +1409,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)\n \t\tfor (i = 0, sg = first_scapegoat(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\tstruct blame_origin *porigin = sg_origin[i];\n \t\t\tfind_copy_in_parent(sb, &blametail, &toosmall,\n \t\t\t\t\t    origin, sg->item, porigin, opt);\n \t\t\tif (!origin->suspects)\n@@ -1665,10 +1588,10 @@ static void get_commit_info(struct commit *commit,\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(struct origin *suspect)\n+static void write_filename_info(struct blame_origin *suspect)\n {\n \tif (suspect->previous) {\n-\t\tstruct origin *prev = suspect->previous;\n+\t\tstruct blame_origin *prev = suspect->previous;\n \t\tprintf(\"previous %s \", oid_to_hex(&prev->commit->object.oid));\n \t\twrite_name_quoted(prev->path, stdout, '\\n');\n \t}\n@@ -1682,7 +1605,7 @@ static void write_filename_info(struct origin *suspect)\n  * the first time each commit appears in the output (unless the\n  * user has specifically asked for us to repeat).\n  */\n-static int emit_one_suspect_detail(struct origin *suspect, int repeat)\n+static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat)\n {\n \tstruct commit_info ci;\n \n@@ -1716,7 +1639,7 @@ static void found_guilty_entry(struct blame_entry *ent,\n \t\t\t   struct progress_info *pi)\n {\n \tif (incremental) {\n-\t\tstruct origin *suspect = ent->suspect;\n+\t\tstruct blame_origin *suspect = ent->suspect;\n \n \t\tprintf(\"%s %d %d %d\\n\",\n \t\t       oid_to_hex(&suspect->commit->object.oid),\n@@ -1745,7 +1668,7 @@ static void assign_blame(struct scoreboard *sb, int opt)\n \n \twhile (commit) {\n \t\tstruct blame_entry *ent;\n-\t\tstruct origin *suspect = commit->util;\n+\t\tstruct blame_origin *suspect = commit->util;\n \n \t\t/* find one suspect to break down */\n \t\twhile (suspect && !suspect->suspects)\n@@ -1842,7 +1765,7 @@ static const char *format_time(unsigned long time, const char *tz_str,\n #define OUTPUT_SHOW_EMAIL\t0400\n #define OUTPUT_LINE_PORCELAIN 01000\n \n-static void emit_porcelain_details(struct origin *suspect, int repeat)\n+static void emit_porcelain_details(struct blame_origin *suspect, int repeat)\n {\n \tif (emit_one_suspect_detail(suspect, repeat) ||\n \t    (suspect->commit->object.flags & MORE_THAN_ONE_PATH))\n@@ -1855,7 +1778,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,\n \tint repeat = opt & OUTPUT_LINE_PORCELAIN;\n \tint cnt;\n \tconst char *cp;\n-\tstruct origin *suspect = ent->suspect;\n+\tstruct blame_origin *suspect = ent->suspect;\n \tchar hex[GIT_MAX_HEXSZ + 1];\n \n \toid_to_hex_r(hex, &suspect->commit->object.oid);\n@@ -1892,7 +1815,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n {\n \tint cnt;\n \tconst char *cp;\n-\tstruct origin *suspect = ent->suspect;\n+\tstruct blame_origin *suspect = ent->suspect;\n \tstruct commit_info ci;\n \tchar hex[GIT_MAX_HEXSZ + 1];\n \tint show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);\n@@ -1974,7 +1897,7 @@ static void output(struct scoreboard *sb, int option)\n \tif (option & OUTPUT_PORCELAIN) {\n \t\tfor (ent = sb->ent; ent; ent = ent->next) {\n \t\t\tint count = 0;\n-\t\t\tstruct origin *suspect;\n+\t\t\tstruct blame_origin *suspect;\n \t\t\tstruct commit *commit = ent->suspect->commit;\n \t\t\tif (commit->object.flags & MORE_THAN_ONE_PATH)\n \t\t\t\tcontinue;\n@@ -2052,7 +1975,7 @@ static int read_ancestry(const char *graft_file)\n \treturn 0;\n }\n \n-static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)\n+static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect)\n {\n \tconst char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash,\n \t\t\t\t\t      auto_abbrev);\n@@ -2076,7 +1999,7 @@ static void find_alignment(struct scoreboard *sb, int *option)\n \tint auto_abbrev = DEFAULT_ABBREV;\n \n \tfor (e = sb->ent; e; e = e->next) {\n-\t\tstruct origin *suspect = e->suspect;\n+\t\tstruct blame_origin *suspect = e->suspect;\n \t\tint num;\n \n \t\tif (compute_auto_abbrev)\n@@ -2268,7 +2191,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,\n \t\t\t\t\t       const char *contents_from)\n {\n \tstruct commit *commit;\n-\tstruct origin *origin;\n+\tstruct blame_origin *origin;\n \tstruct commit_list **parent_tail, *parent;\n \tstruct object_id head_oid;\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -2523,7 +2446,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tstruct rev_info revs;\n \tconst char *path;\n \tstruct scoreboard sb;\n-\tstruct origin *o;\n+\tstruct blame_origin *o;\n \tstruct blame_entry *ent = NULL;\n \tlong dashdash_pos, lno;\n \tchar *final_commit_name = NULL;\n-- \n2.9.3\n\n"},{"id":"319678","messageId":"20170514031513.9042-8-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 07/22] blame: move copy/move thresholds to scoreboard","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:14:58Z","receivedAt":"2017-05-14T03:16:27Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Copy and move score thresholds are used in parts of blame that are being\nmoved to libgit, and should be accessible via the scoreboard structure.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         | 10 ++++++++++\n builtin/blame.c | 36 ++++++++++++++++--------------------\n 2 files changed, 26 insertions(+), 20 deletions(-)\n\ndiff --git a/blame.h b/blame.h\nindex 3438052..fde7d1d 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -7,6 +7,9 @@\n #include \"revision.h\"\n #include \"prio-queue.h\"\n \n+#define BLAME_DEFAULT_MOVE_SCORE\t20\n+#define BLAME_DEFAULT_COPY_SCORE\t40\n+\n /*\n  * One blob in a commit that is being suspected\n  */\n@@ -115,6 +118,13 @@ struct blame_scoreboard {\n \tint num_read_blob;\n \tint num_get_patch;\n \tint num_commits;\n+\n+\t/*\n+\t * blame for a blame_entry with score lower than these thresholds\n+\t * is not passed to the parent using move/copy logic.\n+\t */\n+\tunsigned move_score;\n+\tunsigned copy_score;\n };\n \n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 12add06..949e179 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -67,14 +67,8 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP;\n #define PICKAXE_BLAME_COPY_HARDER\t04\n #define PICKAXE_BLAME_COPY_HARDEST\t010\n \n-/*\n- * blame for a blame_entry with score lower than these thresholds\n- * is not passed to the parent using move/copy logic.\n- */\n static unsigned blame_move_score;\n static unsigned blame_copy_score;\n-#define BLAME_DEFAULT_MOVE_SCORE\t20\n-#define BLAME_DEFAULT_COPY_SCORE\t40\n \n /* Remember to update object flag allocation in object.h */\n #define METAINFO_SHOWN\t\t(1u<<12)\n@@ -1047,7 +1041,7 @@ static void find_move_in_parent(struct blame_scoreboard *sb,\n \t\t\tnext = e->next;\n \t\t\tfind_copy_in_blob(sb, e, parent, split, &file_p);\n \t\t\tif (split[1].suspect &&\n-\t\t\t    blame_move_score < ent_score(sb, &split[1])) {\n+\t\t\t    sb->move_score < ent_score(sb, &split[1])) {\n \t\t\t\tsplit_blame(blamed, &unblamedtail, split, e);\n \t\t\t} else {\n \t\t\t\te->next = leftover;\n@@ -1056,7 +1050,7 @@ static void find_move_in_parent(struct blame_scoreboard *sb,\n \t\t\tdecref_split(split);\n \t\t}\n \t\t*unblamedtail = NULL;\n-\t\ttoosmall = filter_small(sb, toosmall, &unblamed, blame_move_score);\n+\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->move_score);\n \t} while (unblamed);\n \ttarget->suspects = reverse_blame(leftover, NULL);\n }\n@@ -1177,7 +1171,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb,\n \t\tfor (j = 0; j < num_ents; j++) {\n \t\t\tstruct blame_entry *split = blame_list[j].split;\n \t\t\tif (split[1].suspect &&\n-\t\t\t    blame_copy_score < ent_score(sb, &split[1])) {\n+\t\t\t    sb->copy_score < ent_score(sb, &split[1])) {\n \t\t\t\tsplit_blame(blamed, &unblamedtail, split,\n \t\t\t\t\t    blame_list[j].ent);\n \t\t\t} else {\n@@ -1188,7 +1182,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb,\n \t\t}\n \t\tfree(blame_list);\n \t\t*unblamedtail = NULL;\n-\t\ttoosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score);\n+\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score);\n \t} while (unblamed);\n \ttarget->suspects = reverse_blame(leftover, NULL);\n \tdiff_flush(&diff_opts);\n@@ -1345,7 +1339,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\n \t * Optionally find moves in parents' files.\n \t */\n \tif (opt & PICKAXE_BLAME_MOVE) {\n-\t\tfilter_small(sb, &toosmall, &origin->suspects, blame_move_score);\n+\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->move_score);\n \t\tif (origin->suspects) {\n \t\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n \t\t\t     i < num_sg && sg;\n@@ -1364,12 +1358,12 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\n \t * Optionally find copies from parents' files.\n \t */\n \tif (opt & PICKAXE_BLAME_COPY) {\n-\t\tif (blame_copy_score > blame_move_score)\n-\t\t\tfilter_small(sb, &toosmall, &origin->suspects, blame_copy_score);\n-\t\telse if (blame_copy_score < blame_move_score) {\n+\t\tif (sb->copy_score > sb->move_score)\n+\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->copy_score);\n+\t\telse if (sb->copy_score < sb->move_score) {\n \t\t\torigin->suspects = blame_merge(origin->suspects, toosmall);\n \t\t\ttoosmall = NULL;\n-\t\t\tfilter_small(sb, &toosmall, &origin->suspects, blame_copy_score);\n+\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->copy_score);\n \t\t}\n \t\tif (!origin->suspects)\n \t\t\tgoto finish;\n@@ -2566,11 +2560,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\topt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |\n \t\t\tPICKAXE_BLAME_COPY_HARDER);\n \n-\tif (!blame_move_score)\n-\t\tblame_move_score = BLAME_DEFAULT_MOVE_SCORE;\n-\tif (!blame_copy_score)\n-\t\tblame_copy_score = BLAME_DEFAULT_COPY_SCORE;\n-\n \t/*\n \t * We have collected options unknown to us in argv[1..unk]\n \t * which are to be passed to revision machinery if we are\n@@ -2624,6 +2613,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \trevs.disable_stdin = 1;\n \tsetup_revisions(argc, argv, &revs, NULL);\n \tmemset(&sb, 0, sizeof(sb));\n+\tsb.move_score = BLAME_DEFAULT_MOVE_SCORE;\n+\tsb.copy_score = BLAME_DEFAULT_COPY_SCORE;\n \n \tsb.revs = &revs;\n \tif (!reverse) {\n@@ -2762,6 +2753,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tsb.ent = NULL;\n \tsb.path = path;\n \n+\tif (blame_move_score)\n+\t\tsb.move_score = blame_move_score;\n+\tif (blame_copy_score)\n+\t\tsb.copy_score = blame_copy_score;\n+\n \tread_mailmap(&mailmap, NULL);\n \n \tassign_blame(&sb, opt);\n-- \n2.9.3\n\n"},{"id":"319679","messageId":"20170514031513.9042-9-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 08/22] blame: move contents_from to scoreboard","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:14:59Z","receivedAt":"2017-05-14T03:16:29Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"The argument from --contents is used in parts of blame that are being\nmoved to libgit, and should be accessible via the scoreboard structure.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         | 3 +++\n builtin/blame.c | 1 +\n 2 files changed, 4 insertions(+)\n\ndiff --git a/blame.h b/blame.h\nindex fde7d1d..388309d 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -125,6 +125,9 @@ struct blame_scoreboard {\n \t */\n \tunsigned move_score;\n \tunsigned copy_score;\n+\n+\t/* use this file's contents as the final image */\n+\tconst char *contents_from;\n };\n \n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 949e179..032fd15 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -2617,6 +2617,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tsb.copy_score = BLAME_DEFAULT_COPY_SCORE;\n \n \tsb.revs = &revs;\n+\tsb.contents_from = contents_from;\n \tif (!reverse) {\n \t\tfinal_commit_name = prepare_final(&sb);\n \t\tsb.commits.compare = compare_commits_by_commit_date;\n-- \n2.9.3\n\n"},{"id":"319680","messageId":"20170514031513.9042-12-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 11/22] blame: move xdl_opts flags to scoreboard","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:02Z","receivedAt":"2017-05-14T03:16:33Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"The xdl_opts flags are used in parts of blame that are being moved to\nlibgit, and should be accessible via the scoreboard structure.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         | 1 +\n builtin/blame.c | 7 ++++---\n 2 files changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/blame.h b/blame.h\nindex 7691256..42a948d 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -132,6 +132,7 @@ struct blame_scoreboard {\n \t/* flags */\n \tint reverse;\n \tint show_root;\n+\tint xdl_opts;\n };\n \n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 77fb71f..d3af02b 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -80,7 +80,7 @@ struct progress_info {\n };\n \n static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n-\t\t      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)\n+\t\t      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts)\n {\n \txpparam_t xpp = {0};\n \txdemitconf_t xecfg = {0};\n@@ -823,7 +823,7 @@ static void pass_blame_to_parent(struct blame_scoreboard *sb,\n \tfill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob);\n \tsb->num_get_patch++;\n \n-\tif (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d))\n+\tif (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts))\n \t\tdie(\"unable to generate diff (%s -> %s)\",\n \t\t    oid_to_hex(&parent->commit->object.oid),\n \t\t    oid_to_hex(&target->commit->object.oid));\n@@ -972,7 +972,7 @@ static void find_copy_in_blob(struct blame_scoreboard *sb,\n \t * file_p partially may match that image.\n \t */\n \tmemset(split, 0, sizeof(struct blame_entry [3]));\n-\tif (diff_hunks(file_p, &file_o, handle_split_cb, &d))\n+\tif (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts))\n \t\tdie(\"unable to generate diff (%s)\",\n \t\t    oid_to_hex(&parent->commit->object.oid));\n \t/* remainder, if any, all match the preimage */\n@@ -2762,6 +2762,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tsb.copy_score = blame_copy_score;\n \n \tsb.show_root = show_root;\n+\tsb.xdl_opts = xdl_opts;\n \n \tread_mailmap(&mailmap, NULL);\n \n-- \n2.9.3\n\n"},{"id":"319681","messageId":"20170514031513.9042-11-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 10/22] blame: move show_root flag to scoreboard","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:01Z","receivedAt":"2017-05-14T03:16:36Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"The show_root flag is used in parts of blame that are being moved to\nlibgit, and should be accessible via the scoreboard structure.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         | 1 +\n builtin/blame.c | 4 +++-\n 2 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/blame.h b/blame.h\nindex 85703dd..7691256 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -131,6 +131,7 @@ struct blame_scoreboard {\n \n \t/* flags */\n \tint reverse;\n+\tint show_root;\n };\n \n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex ed50eda..77fb71f 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1660,7 +1660,7 @@ static void assign_blame(struct blame_scoreboard *sb, int opt)\n \t\t\t\tmark_parents_uninteresting(commit);\n \t\t}\n \t\t/* treat root commit as boundary */\n-\t\tif (!commit->parents && !show_root)\n+\t\tif (!commit->parents && !sb->show_root)\n \t\t\tcommit->object.flags |= UNINTERESTING;\n \n \t\t/* Take responsibility for the remaining entries */\n@@ -2761,6 +2761,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (blame_copy_score)\n \t\tsb.copy_score = blame_copy_score;\n \n+\tsb.show_root = show_root;\n+\n \tread_mailmap(&mailmap, NULL);\n \n \tassign_blame(&sb, opt);\n-- \n2.9.3\n\n"},{"id":"319682","messageId":"20170514031513.9042-10-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 09/22] blame: move reverse flag to scoreboard","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:00Z","receivedAt":"2017-05-14T03:16:37Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"The reverse flag is used in parts of blame that are being moved to\nlibgit, and should be accessible via the scoreboard structure.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         |  3 +++\n builtin/blame.c | 20 +++++++++++---------\n 2 files changed, 14 insertions(+), 9 deletions(-)\n\ndiff --git a/blame.h b/blame.h\nindex 388309d..85703dd 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -128,6 +128,9 @@ struct blame_scoreboard {\n \n \t/* use this file's contents as the final image */\n \tconst char *contents_from;\n+\n+\t/* flags */\n+\tint reverse;\n };\n \n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 032fd15..ed50eda 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1218,7 +1218,8 @@ static void pass_whole_blame(struct blame_scoreboard *sb,\n  * \"parent\" (and \"porigin\"), but what we mean is to find scapegoat to\n  * exonerate ourselves.\n  */\n-static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)\n+static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit,\n+\t\t\t\t\t   int reverse)\n {\n \tif (!reverse) {\n \t\tif (revs->first_parent_only &&\n@@ -1232,9 +1233,9 @@ static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit\n \treturn lookup_decoration(&revs->children, &commit->object);\n }\n \n-static int num_scapegoats(struct rev_info *revs, struct commit *commit)\n+static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse)\n {\n-\tstruct commit_list *l = first_scapegoat(revs, commit);\n+\tstruct commit_list *l = first_scapegoat(revs, commit, reverse);\n \treturn commit_list_count(l);\n }\n \n@@ -1272,7 +1273,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\n \tstruct blame_entry *toosmall = NULL;\n \tstruct blame_entry *blames, **blametail = &blames;\n \n-\tnum_sg = num_scapegoats(revs, commit);\n+\tnum_sg = num_scapegoats(revs, commit, sb->reverse);\n \tif (!num_sg)\n \t\tgoto finish;\n \telse if (num_sg < ARRAY_SIZE(sg_buf))\n@@ -1288,7 +1289,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\n \t\tstruct blame_origin *(*find)(struct commit *, struct blame_origin *);\n \t\tfind = pass ? find_rename : find_origin;\n \n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n \t\t     i < num_sg && sg;\n \t\t     sg = sg->next, i++) {\n \t\t\tstruct commit *p = sg->item;\n@@ -1320,7 +1321,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\n \t}\n \n \tsb->num_commits++;\n-\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n \t     i < num_sg && sg;\n \t     sg = sg->next, i++) {\n \t\tstruct blame_origin *porigin = sg_origin[i];\n@@ -1341,7 +1342,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\n \tif (opt & PICKAXE_BLAME_MOVE) {\n \t\tfilter_small(sb, &toosmall, &origin->suspects, sb->move_score);\n \t\tif (origin->suspects) {\n-\t\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n \t\t\t     i < num_sg && sg;\n \t\t\t     sg = sg->next, i++) {\n \t\t\t\tstruct blame_origin *porigin = sg_origin[i];\n@@ -1368,7 +1369,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\n \t\tif (!origin->suspects)\n \t\t\tgoto finish;\n \n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit);\n+\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n \t\t     i < num_sg && sg;\n \t\t     sg = sg->next, i++) {\n \t\t\tstruct blame_origin *porigin = sg_origin[i];\n@@ -1649,7 +1650,7 @@ static void assign_blame(struct blame_scoreboard *sb, int opt)\n \t\t */\n \t\torigin_incref(suspect);\n \t\tparse_commit(commit);\n-\t\tif (reverse ||\n+\t\tif (sb->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@@ -2618,6 +2619,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \tsb.revs = &revs;\n \tsb.contents_from = contents_from;\n+\tsb.reverse = reverse;\n \tif (!reverse) {\n \t\tfinal_commit_name = prepare_final(&sb);\n \t\tsb.commits.compare = compare_commits_by_commit_date;\n-- \n2.9.3\n\n"},{"id":"319683","messageId":"20170514031513.9042-19-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 18/22] blame: move fake-commit-related methods to libgit","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:09Z","receivedAt":"2017-05-14T03:16:42Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.c         | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n blame.h         |   4 +-\n builtin/blame.c | 197 ------------------------------------------------------\n 3 files changed, 205 insertions(+), 199 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 4855d6d..8c025bc 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -1,3 +1,6 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+#include \"cache-tree.h\"\n #include \"blame.h\"\n \n void blame_origin_decref(struct blame_origin *o)\n@@ -28,7 +31,7 @@ void blame_origin_decref(struct blame_origin *o)\n  * get_origin() to obtain shared, refcounted copy instead of calling\n  * this function directly.\n  */\n-struct blame_origin *make_origin(struct commit *commit, const char *path)\n+static struct blame_origin *make_origin(struct commit *commit, const char *path)\n {\n \tstruct blame_origin *o;\n \tFLEX_ALLOC_STR(o, path, path);\n@@ -60,3 +63,201 @@ struct blame_origin *get_origin(struct commit *commit, const char *path)\n \t}\n \treturn make_origin(commit, path);\n }\n+\n+\n+\n+static void verify_working_tree_path(struct commit *work_tree, const char *path)\n+{\n+\tstruct commit_list *parents;\n+\tint pos;\n+\n+\tfor (parents = work_tree->parents; parents; parents = parents->next) {\n+\t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n+\t\tstruct object_id blob_oid;\n+\t\tunsigned mode;\n+\n+\t\tif (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&\n+\t\t    sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)\n+\t\t\treturn;\n+\t}\n+\n+\tpos = cache_name_pos(path, strlen(path));\n+\tif (pos >= 0)\n+\t\t; /* path is in the index */\n+\telse if (-1 - pos < active_nr &&\n+\t\t !strcmp(active_cache[-1 - pos]->name, path))\n+\t\t; /* path is in the index, unmerged */\n+\telse\n+\t\tdie(\"no such path '%s' in HEAD\", path);\n+}\n+\n+static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)\n+{\n+\tstruct commit *parent;\n+\n+\tparent = lookup_commit_reference(oid->hash);\n+\tif (!parent)\n+\t\tdie(\"no such commit %s\", oid_to_hex(oid));\n+\treturn &commit_list_insert(parent, tail)->next;\n+}\n+\n+static void append_merge_parents(struct commit_list **tail)\n+{\n+\tint merge_head;\n+\tstruct strbuf line = STRBUF_INIT;\n+\n+\tmerge_head = open(git_path_merge_head(), O_RDONLY);\n+\tif (merge_head < 0) {\n+\t\tif (errno == ENOENT)\n+\t\t\treturn;\n+\t\tdie(\"cannot open '%s' for reading\", git_path_merge_head());\n+\t}\n+\n+\twhile (!strbuf_getwholeline_fd(&line, merge_head, '\\n')) {\n+\t\tstruct object_id oid;\n+\t\tif (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))\n+\t\t\tdie(\"unknown line in '%s': %s\", git_path_merge_head(), line.buf);\n+\t\ttail = append_parent(tail, &oid);\n+\t}\n+\tclose(merge_head);\n+\tstrbuf_release(&line);\n+}\n+\n+/*\n+ * This isn't as simple as passing sb->buf and sb->len, because we\n+ * want to transfer ownership of the buffer to the commit (so we\n+ * must use detach).\n+ */\n+static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)\n+{\n+\tsize_t len;\n+\tvoid *buf = strbuf_detach(sb, &len);\n+\tset_commit_buffer(c, buf, len);\n+}\n+\n+/*\n+ * Prepare a dummy commit that represents the work tree (or staged) item.\n+ * Note that annotating work tree item never works in the reverse.\n+ */\n+struct commit *fake_working_tree_commit(struct diff_options *opt,\n+\t\t\t\t\tconst char *path,\n+\t\t\t\t\tconst char *contents_from)\n+{\n+\tstruct commit *commit;\n+\tstruct blame_origin *origin;\n+\tstruct commit_list **parent_tail, *parent;\n+\tstruct object_id head_oid;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *ident;\n+\ttime_t now;\n+\tint size, len;\n+\tstruct cache_entry *ce;\n+\tunsigned mode;\n+\tstruct strbuf msg = STRBUF_INIT;\n+\n+\tread_cache();\n+\ttime(&now);\n+\tcommit = alloc_commit_node();\n+\tcommit->object.parsed = 1;\n+\tcommit->date = now;\n+\tparent_tail = &commit->parents;\n+\n+\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, head_oid.hash, NULL))\n+\t\tdie(\"no such ref: HEAD\");\n+\n+\tparent_tail = append_parent(parent_tail, &head_oid);\n+\tappend_merge_parents(parent_tail);\n+\tverify_working_tree_path(commit, path);\n+\n+\torigin = make_origin(commit, path);\n+\n+\tident = fmt_ident(\"Not Committed Yet\", \"not.committed.yet\", NULL, 0);\n+\tstrbuf_addstr(&msg, \"tree 0000000000000000000000000000000000000000\\n\");\n+\tfor (parent = commit->parents; parent; parent = parent->next)\n+\t\tstrbuf_addf(&msg, \"parent %s\\n\",\n+\t\t\t    oid_to_hex(&parent->item->object.oid));\n+\tstrbuf_addf(&msg,\n+\t\t    \"author %s\\n\"\n+\t\t    \"committer %s\\n\\n\"\n+\t\t    \"Version of %s from %s\\n\",\n+\t\t    ident, ident, path,\n+\t\t    (!contents_from ? path :\n+\t\t     (!strcmp(contents_from, \"-\") ? \"standard input\" : contents_from)));\n+\tset_commit_buffer_from_strbuf(commit, &msg);\n+\n+\tif (!contents_from || strcmp(\"-\", contents_from)) {\n+\t\tstruct stat st;\n+\t\tconst char *read_from;\n+\t\tchar *buf_ptr;\n+\t\tunsigned long buf_len;\n+\n+\t\tif (contents_from) {\n+\t\t\tif (stat(contents_from, &st) < 0)\n+\t\t\t\tdie_errno(\"Cannot stat '%s'\", contents_from);\n+\t\t\tread_from = contents_from;\n+\t\t}\n+\t\telse {\n+\t\t\tif (lstat(path, &st) < 0)\n+\t\t\t\tdie_errno(\"Cannot lstat '%s'\", path);\n+\t\t\tread_from = path;\n+\t\t}\n+\t\tmode = canon_mode(st.st_mode);\n+\n+\t\tswitch (st.st_mode & S_IFMT) {\n+\t\tcase S_IFREG:\n+\t\t\tif (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&\n+\t\t\t    textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))\n+\t\t\t\tstrbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);\n+\t\t\telse if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)\n+\t\t\t\tdie_errno(\"cannot open or read '%s'\", read_from);\n+\t\t\tbreak;\n+\t\tcase S_IFLNK:\n+\t\t\tif (strbuf_readlink(&buf, read_from, st.st_size) < 0)\n+\t\t\t\tdie_errno(\"cannot readlink '%s'\", read_from);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tdie(\"unsupported file type %s\", read_from);\n+\t\t}\n+\t}\n+\telse {\n+\t\t/* Reading from stdin */\n+\t\tmode = 0;\n+\t\tif (strbuf_read(&buf, 0, 0) < 0)\n+\t\t\tdie_errno(\"failed to read from stdin\");\n+\t}\n+\tconvert_to_git(path, buf.buf, buf.len, &buf, 0);\n+\torigin->file.ptr = buf.buf;\n+\torigin->file.size = buf.len;\n+\tpretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);\n+\n+\t/*\n+\t * Read the current index, replace the path entry with\n+\t * origin->blob_sha1 without mucking with its mode or type\n+\t * bits; we are not going to write this index out -- we just\n+\t * want to run \"diff-index --cached\".\n+\t */\n+\tdiscard_cache();\n+\tread_cache();\n+\n+\tlen = strlen(path);\n+\tif (!mode) {\n+\t\tint pos = cache_name_pos(path, len);\n+\t\tif (0 <= pos)\n+\t\t\tmode = active_cache[pos]->ce_mode;\n+\t\telse\n+\t\t\t/* Let's not bother reading from HEAD tree */\n+\t\t\tmode = S_IFREG | 0644;\n+\t}\n+\tsize = cache_entry_size(len);\n+\tce = xcalloc(1, size);\n+\toidcpy(&ce->oid, &origin->blob_oid);\n+\tmemcpy(ce->name, path, len);\n+\tce->ce_flags = create_ce_flags(0);\n+\tce->ce_namelen = len;\n+\tce->ce_mode = create_ce_mode(mode);\n+\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n+\n+\tcache_tree_invalidate_path(&the_index, path);\n+\n+\treturn commit;\n+}\ndiff --git a/blame.h b/blame.h\nindex 39f8865..f831dec 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -6,6 +6,7 @@\n #include \"xdiff-interface.h\"\n #include \"revision.h\"\n #include \"prio-queue.h\"\n+#include \"diff.h\"\n \n #define BLAME_DEFAULT_MOVE_SCORE\t20\n #define BLAME_DEFAULT_COPY_SCORE\t40\n@@ -155,7 +156,8 @@ static inline struct blame_origin *blame_origin_incref(struct blame_origin *o)\n }\n extern void blame_origin_decref(struct blame_origin *o);\n \n-extern struct blame_origin *make_origin(struct commit *commit, const char *path);\n extern struct blame_origin *get_origin(struct commit *commit, const char *path);\n \n+extern struct commit *fake_working_tree_commit(struct diff_options *opt, const char *path, const char *contents_from);\n+\n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex b60d604..33ede4f 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -16,7 +16,6 @@\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 \"mergesort.h\"\n@@ -2004,202 +2003,6 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n-static void verify_working_tree_path(struct commit *work_tree, const char *path)\n-{\n-\tstruct commit_list *parents;\n-\tint pos;\n-\n-\tfor (parents = work_tree->parents; parents; parents = parents->next) {\n-\t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n-\t\tstruct object_id blob_oid;\n-\t\tunsigned mode;\n-\n-\t\tif (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&\n-\t\t    sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)\n-\t\t\treturn;\n-\t}\n-\n-\tpos = cache_name_pos(path, strlen(path));\n-\tif (pos >= 0)\n-\t\t; /* path is in the index */\n-\telse if (-1 - pos < active_nr &&\n-\t\t !strcmp(active_cache[-1 - pos]->name, path))\n-\t\t; /* path is in the index, unmerged */\n-\telse\n-\t\tdie(\"no such path '%s' in HEAD\", path);\n-}\n-\n-static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)\n-{\n-\tstruct commit *parent;\n-\n-\tparent = lookup_commit_reference(oid->hash);\n-\tif (!parent)\n-\t\tdie(\"no such commit %s\", oid_to_hex(oid));\n-\treturn &commit_list_insert(parent, tail)->next;\n-}\n-\n-static void append_merge_parents(struct commit_list **tail)\n-{\n-\tint merge_head;\n-\tstruct strbuf line = STRBUF_INIT;\n-\n-\tmerge_head = open(git_path_merge_head(), O_RDONLY);\n-\tif (merge_head < 0) {\n-\t\tif (errno == ENOENT)\n-\t\t\treturn;\n-\t\tdie(\"cannot open '%s' for reading\", git_path_merge_head());\n-\t}\n-\n-\twhile (!strbuf_getwholeline_fd(&line, merge_head, '\\n')) {\n-\t\tstruct object_id oid;\n-\t\tif (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))\n-\t\t\tdie(\"unknown line in '%s': %s\", git_path_merge_head(), line.buf);\n-\t\ttail = append_parent(tail, &oid);\n-\t}\n-\tclose(merge_head);\n-\tstrbuf_release(&line);\n-}\n-\n-/*\n- * This isn't as simple as passing sb->buf and sb->len, because we\n- * want to transfer ownership of the buffer to the commit (so we\n- * must use detach).\n- */\n-static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)\n-{\n-\tsize_t len;\n-\tvoid *buf = strbuf_detach(sb, &len);\n-\tset_commit_buffer(c, buf, len);\n-}\n-\n-/*\n- * Prepare a dummy commit that represents the work tree (or staged) item.\n- * Note that annotating work tree item never works in the reverse.\n- */\n-static struct commit *fake_working_tree_commit(struct diff_options *opt,\n-\t\t\t\t\t       const char *path,\n-\t\t\t\t\t       const char *contents_from)\n-{\n-\tstruct commit *commit;\n-\tstruct blame_origin *origin;\n-\tstruct commit_list **parent_tail, *parent;\n-\tstruct object_id head_oid;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tconst char *ident;\n-\ttime_t now;\n-\tint size, len;\n-\tstruct cache_entry *ce;\n-\tunsigned mode;\n-\tstruct strbuf msg = STRBUF_INIT;\n-\n-\tread_cache();\n-\ttime(&now);\n-\tcommit = alloc_commit_node();\n-\tcommit->object.parsed = 1;\n-\tcommit->date = now;\n-\tparent_tail = &commit->parents;\n-\n-\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, head_oid.hash, NULL))\n-\t\tdie(\"no such ref: HEAD\");\n-\n-\tparent_tail = append_parent(parent_tail, &head_oid);\n-\tappend_merge_parents(parent_tail);\n-\tverify_working_tree_path(commit, path);\n-\n-\torigin = make_origin(commit, path);\n-\n-\tident = fmt_ident(\"Not Committed Yet\", \"not.committed.yet\", NULL, 0);\n-\tstrbuf_addstr(&msg, \"tree 0000000000000000000000000000000000000000\\n\");\n-\tfor (parent = commit->parents; parent; parent = parent->next)\n-\t\tstrbuf_addf(&msg, \"parent %s\\n\",\n-\t\t\t    oid_to_hex(&parent->item->object.oid));\n-\tstrbuf_addf(&msg,\n-\t\t    \"author %s\\n\"\n-\t\t    \"committer %s\\n\\n\"\n-\t\t    \"Version of %s from %s\\n\",\n-\t\t    ident, ident, path,\n-\t\t    (!contents_from ? path :\n-\t\t     (!strcmp(contents_from, \"-\") ? \"standard input\" : contents_from)));\n-\tset_commit_buffer_from_strbuf(commit, &msg);\n-\n-\tif (!contents_from || strcmp(\"-\", contents_from)) {\n-\t\tstruct stat st;\n-\t\tconst char *read_from;\n-\t\tchar *buf_ptr;\n-\t\tunsigned long buf_len;\n-\n-\t\tif (contents_from) {\n-\t\t\tif (stat(contents_from, &st) < 0)\n-\t\t\t\tdie_errno(\"Cannot stat '%s'\", contents_from);\n-\t\t\tread_from = contents_from;\n-\t\t}\n-\t\telse {\n-\t\t\tif (lstat(path, &st) < 0)\n-\t\t\t\tdie_errno(\"Cannot lstat '%s'\", path);\n-\t\t\tread_from = path;\n-\t\t}\n-\t\tmode = canon_mode(st.st_mode);\n-\n-\t\tswitch (st.st_mode & S_IFMT) {\n-\t\tcase S_IFREG:\n-\t\t\tif (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&\n-\t\t\t    textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))\n-\t\t\t\tstrbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);\n-\t\t\telse if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)\n-\t\t\t\tdie_errno(\"cannot open or read '%s'\", read_from);\n-\t\t\tbreak;\n-\t\tcase S_IFLNK:\n-\t\t\tif (strbuf_readlink(&buf, read_from, st.st_size) < 0)\n-\t\t\t\tdie_errno(\"cannot readlink '%s'\", read_from);\n-\t\t\tbreak;\n-\t\tdefault:\n-\t\t\tdie(\"unsupported file type %s\", read_from);\n-\t\t}\n-\t}\n-\telse {\n-\t\t/* Reading from stdin */\n-\t\tmode = 0;\n-\t\tif (strbuf_read(&buf, 0, 0) < 0)\n-\t\t\tdie_errno(\"failed to read from stdin\");\n-\t}\n-\tconvert_to_git(path, buf.buf, buf.len, &buf, 0);\n-\torigin->file.ptr = buf.buf;\n-\torigin->file.size = buf.len;\n-\tpretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);\n-\n-\t/*\n-\t * Read the current index, replace the path entry with\n-\t * origin->blob_sha1 without mucking with its mode or type\n-\t * bits; we are not going to write this index out -- we just\n-\t * want to run \"diff-index --cached\".\n-\t */\n-\tdiscard_cache();\n-\tread_cache();\n-\n-\tlen = strlen(path);\n-\tif (!mode) {\n-\t\tint pos = cache_name_pos(path, len);\n-\t\tif (0 <= pos)\n-\t\t\tmode = active_cache[pos]->ce_mode;\n-\t\telse\n-\t\t\t/* Let's not bother reading from HEAD tree */\n-\t\t\tmode = S_IFREG | 0644;\n-\t}\n-\tsize = cache_entry_size(len);\n-\tce = xcalloc(1, size);\n-\toidcpy(&ce->oid, &origin->blob_oid);\n-\tmemcpy(ce->name, path, len);\n-\tce->ce_flags = create_ce_flags(0);\n-\tce->ce_namelen = len;\n-\tce->ce_mode = create_ce_mode(mode);\n-\tadd_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n-\n-\tcache_tree_invalidate_path(&the_index, path);\n-\n-\treturn commit;\n-}\n-\n static struct commit *find_single_final(struct rev_info *revs,\n \t\t\t\t\tconst char **name_p)\n {\n-- \n2.9.3\n\n"},{"id":"319684","messageId":"20170514031513.9042-22-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 21/22] blame: create scoreboard setup function in libgit","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:12Z","receivedAt":"2017-05-14T03:16:49Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.c         | 272 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n blame.h         |   5 +-\n builtin/blame.c | 259 +----------------------------------------------------\n 3 files changed, 271 insertions(+), 265 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 17ebf64..f6c9cb7 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -4,6 +4,7 @@\n #include \"mergesort.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n+#include \"tag.h\"\n #include \"blame.h\"\n \n void blame_origin_decref(struct blame_origin *o)\n@@ -49,7 +50,7 @@ static struct blame_origin *make_origin(struct commit *commit, const char *path)\n  * Locate an existing origin or create a new one.\n  * This moves the origin to front position in the commit util list.\n  */\n-struct blame_origin *get_origin(struct commit *commit, const char *path)\n+static struct blame_origin *get_origin(struct commit *commit, const char *path)\n {\n \tstruct blame_origin *o, *l;\n \n@@ -142,9 +143,9 @@ static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)\n  * Prepare a dummy commit that represents the work tree (or staged) item.\n  * Note that annotating work tree item never works in the reverse.\n  */\n-struct commit *fake_working_tree_commit(struct diff_options *opt,\n-\t\t\t\t\tconst char *path,\n-\t\t\t\t\tconst char *contents_from)\n+static struct commit *fake_working_tree_commit(struct diff_options *opt,\n+\t\t\t\t\t       const char *path,\n+\t\t\t\t\t       const char *contents_from)\n {\n \tstruct commit *commit;\n \tstruct blame_origin *origin;\n@@ -410,6 +411,13 @@ void blame_sort_final(struct blame_scoreboard *sb)\n \t\t\t\t  compare_blame_final);\n }\n \n+static int compare_commits_by_reverse_commit_date(const void *a,\n+\t\t\t\t\t\t  const void *b,\n+\t\t\t\t\t\t  void *c)\n+{\n+\treturn -compare_commits_by_commit_date(a, b, c);\n+}\n+\n /*\n  * For debugging -- origin is refcounted, and this asserts that\n  * we do not underflow.\n@@ -483,6 +491,32 @@ static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porig\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+ * This also fills origin->mode for corresponding tree path.\n+ */\n+static int fill_blob_sha1_and_mode(struct blame_origin *origin)\n+{\n+\tif (!is_null_oid(&origin->blob_oid))\n+\t\treturn 0;\n+\tif (get_tree_entry(origin->commit->object.oid.hash,\n+\t\t\t   origin->path,\n+\t\t\t   origin->blob_oid.hash, &origin->mode))\n+\t\tgoto error_out;\n+\tif (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)\n+\t\tgoto error_out;\n+\treturn 0;\n+ error_out:\n+\toidclr(&origin->blob_oid);\n+\torigin->mode = S_IFINVALID;\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@@ -1575,9 +1609,239 @@ void assign_blame(struct blame_scoreboard *sb, int opt)\n \t}\n }\n \n+static const char *get_next_line(const char *start, const char *end)\n+{\n+\tconst char *nl = memchr(start, '\\n', end - start);\n+\treturn nl ? nl + 1 : end;\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+\tconst char *end = buf + len;\n+\tconst char *p;\n+\tint *lineno;\n+\tint num = 0;\n+\n+\tfor (p = buf; p < end; p = get_next_line(p, end))\n+\t\tnum++;\n+\n+\tALLOC_ARRAY(sb->lineno, num + 1);\n+\tlineno = sb->lineno;\n+\n+\tfor (p = buf; p < end; p = get_next_line(p, end))\n+\t\t*lineno++ = p - buf;\n+\n+\t*lineno = len;\n+\n+\tsb->num_lines = num;\n+\treturn sb->num_lines;\n+}\n+\n+static struct commit *find_single_final(struct rev_info *revs,\n+\t\t\t\t\tconst char **name_p)\n+{\n+\tint i;\n+\tstruct commit *found = NULL;\n+\tconst char *name = NULL;\n+\n+\tfor (i = 0; i < revs->pending.nr; i++) {\n+\t\tstruct object *obj = revs->pending.objects[i].item;\n+\t\tif (obj->flags & UNINTERESTING)\n+\t\t\tcontinue;\n+\t\tobj = deref_tag(obj, NULL, 0);\n+\t\tif (obj->type != OBJ_COMMIT)\n+\t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n+\t\tif (found)\n+\t\t\tdie(\"More than one commit to dig from %s and %s?\",\n+\t\t\t    revs->pending.objects[i].name, name);\n+\t\tfound = (struct commit *)obj;\n+\t\tname = revs->pending.objects[i].name;\n+\t}\n+\tif (name_p)\n+\t\t*name_p = name;\n+\treturn found;\n+}\n+\n+static struct commit *dwim_reverse_initial(struct rev_info *revs,\n+\t\t\t\t\t   const char **name_p)\n+{\n+\t/*\n+\t * DWIM \"git blame --reverse ONE -- PATH\" as\n+\t * \"git blame --reverse ONE..HEAD -- PATH\" but only do so\n+\t * when it makes sense.\n+\t */\n+\tstruct object *obj;\n+\tstruct commit *head_commit;\n+\tunsigned char head_sha1[20];\n+\n+\tif (revs->pending.nr != 1)\n+\t\treturn NULL;\n+\n+\t/* Is that sole rev a committish? */\n+\tobj = revs->pending.objects[0].item;\n+\tobj = deref_tag(obj, NULL, 0);\n+\tif (obj->type != OBJ_COMMIT)\n+\t\treturn NULL;\n+\n+\t/* Do we have HEAD? */\n+\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, head_sha1, NULL))\n+\t\treturn NULL;\n+\thead_commit = lookup_commit_reference_gently(head_sha1, 1);\n+\tif (!head_commit)\n+\t\treturn NULL;\n+\n+\t/* Turn \"ONE\" into \"ONE..HEAD\" then */\n+\tobj->flags |= UNINTERESTING;\n+\tadd_pending_object(revs, &head_commit->object, \"HEAD\");\n+\n+\tif (name_p)\n+\t\t*name_p = revs->pending.objects[0].name;\n+\treturn (struct commit *)obj;\n+}\n+\n+static struct commit *find_single_initial(struct rev_info *revs,\n+\t\t\t\t\t  const char **name_p)\n+{\n+\tint i;\n+\tstruct commit *found = NULL;\n+\tconst char *name = NULL;\n+\n+\t/*\n+\t * There must be one and only one negative commit, and it must be\n+\t * the boundary.\n+\t */\n+\tfor (i = 0; i < revs->pending.nr; i++) {\n+\t\tstruct object *obj = revs->pending.objects[i].item;\n+\t\tif (!(obj->flags & UNINTERESTING))\n+\t\t\tcontinue;\n+\t\tobj = deref_tag(obj, NULL, 0);\n+\t\tif (obj->type != OBJ_COMMIT)\n+\t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n+\t\tif (found)\n+\t\t\tdie(\"More than one commit to dig up from, %s and %s?\",\n+\t\t\t    revs->pending.objects[i].name, name);\n+\t\tfound = (struct commit *) obj;\n+\t\tname = revs->pending.objects[i].name;\n+\t}\n+\n+\tif (!name)\n+\t\tfound = dwim_reverse_initial(revs, &name);\n+\tif (!name)\n+\t\tdie(\"No commit to dig up from?\");\n+\n+\tif (name_p)\n+\t\t*name_p = name;\n+\treturn found;\n+}\n+\n void init_scoreboard(struct blame_scoreboard *sb)\n {\n \tmemset(sb, 0, sizeof(struct blame_scoreboard));\n \tsb->move_score = BLAME_DEFAULT_MOVE_SCORE;\n \tsb->copy_score = BLAME_DEFAULT_COPY_SCORE;\n }\n+\n+void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig)\n+{\n+\tconst char *final_commit_name = NULL;\n+\tstruct blame_origin *o;\n+\tstruct commit *final_commit = NULL;\n+\tenum object_type type;\n+\n+\tif (sb->reverse && sb->contents_from)\n+\t\tdie(_(\"--contents and --reverse do not blend well.\"));\n+\n+\tif (!sb->reverse) {\n+\t\tsb->final = find_single_final(sb->revs, &final_commit_name);\n+\t\tsb->commits.compare = compare_commits_by_commit_date;\n+\t} else {\n+\t\tsb->final = find_single_initial(sb->revs, &final_commit_name);\n+\t\tsb->commits.compare = compare_commits_by_reverse_commit_date;\n+\t}\n+\n+\tif (sb->final && sb->contents_from)\n+\t\tdie(_(\"cannot use --contents with final commit object name\"));\n+\n+\tif (sb->reverse && sb->revs->first_parent_only)\n+\t\tsb->revs->children.name = NULL;\n+\n+\tif (!sb->final) {\n+\t\t/*\n+\t\t * \"--not A B -- path\" without anything positive;\n+\t\t * do not default to HEAD, but use the working tree\n+\t\t * or \"--contents\".\n+\t\t */\n+\t\tsetup_work_tree();\n+\t\tsb->final = fake_working_tree_commit(&sb->revs->diffopt,\n+\t\t\t\t\t\t     path, sb->contents_from);\n+\t\tadd_pending_object(sb->revs, &(sb->final->object), \":\");\n+\t}\n+\n+\tif (sb->reverse && sb->revs->first_parent_only) {\n+\t\tfinal_commit = find_single_final(sb->revs, NULL);\n+\t\tif (!final_commit)\n+\t\t\tdie(_(\"--reverse and --first-parent together require specified latest commit\"));\n+\t}\n+\n+\t/*\n+\t * If we have bottom, this will mark the ancestors of the\n+\t * bottom commits we would reach while traversing as\n+\t * uninteresting.\n+\t */\n+\tif (prepare_revision_walk(sb->revs))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tif (sb->reverse && sb->revs->first_parent_only) {\n+\t\tstruct commit *c = final_commit;\n+\n+\t\tsb->revs->children.name = \"children\";\n+\t\twhile (c->parents &&\n+\t\t       oidcmp(&c->object.oid, &sb->final->object.oid)) {\n+\t\t\tstruct commit_list *l = xcalloc(1, sizeof(*l));\n+\n+\t\t\tl->item = c;\n+\t\t\tif (add_decoration(&sb->revs->children,\n+\t\t\t\t\t   &c->parents->item->object, l))\n+\t\t\t\tdie(\"BUG: not unique item in first-parent chain\");\n+\t\t\tc = c->parents->item;\n+\t\t}\n+\n+\t\tif (oidcmp(&c->object.oid, &sb->final->object.oid))\n+\t\t\tdie(_(\"--reverse --first-parent together require range along first-parent chain\"));\n+\t}\n+\n+\tif (is_null_oid(&sb->final->object.oid)) {\n+\t\to = sb->final->util;\n+\t\tsb->final_buf = xmemdupz(o->file.ptr, o->file.size);\n+\t\tsb->final_buf_size = o->file.size;\n+\t}\n+\telse {\n+\t\to = get_origin(sb->final, path);\n+\t\tif (fill_blob_sha1_and_mode(o))\n+\t\t\tdie(_(\"no such path %s in %s\"), path, final_commit_name);\n+\n+\t\tif (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) &&\n+\t\t    textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,\n+\t\t\t\t    &sb->final_buf_size))\n+\t\t\t;\n+\t\telse\n+\t\t\tsb->final_buf = read_sha1_file(o->blob_oid.hash, &type,\n+\t\t\t\t\t\t       &sb->final_buf_size);\n+\n+\t\tif (!sb->final_buf)\n+\t\t\tdie(_(\"cannot read blob %s for path %s\"),\n+\t\t\t    oid_to_hex(&o->blob_oid),\n+\t\t\t    path);\n+\t}\n+\tsb->num_read_blob++;\n+\tprepare_lines(sb);\n+\n+\tif (orig)\n+\t\t*orig = o;\n+}\ndiff --git a/blame.h b/blame.h\nindex 9477b6e..76fd8ef 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -161,10 +161,6 @@ static inline struct blame_origin *blame_origin_incref(struct blame_origin *o)\n }\n extern void blame_origin_decref(struct blame_origin *o);\n \n-extern struct blame_origin *get_origin(struct commit *commit, const char *path);\n-\n-extern struct commit *fake_working_tree_commit(struct diff_options *opt, const char *path, const char *contents_from);\n-\n extern void blame_coalesce(struct blame_scoreboard *sb);\n extern void blame_sort_final(struct blame_scoreboard *sb);\n extern unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e);\n@@ -172,5 +168,6 @@ extern void assign_blame(struct blame_scoreboard *sb, int opt);\n extern const char *blame_nth_line(struct blame_scoreboard *sb, long lno);\n \n extern void init_scoreboard(struct blame_scoreboard *sb);\n+extern void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig);\n \n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex e01265a..7ec5a73 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -6,11 +6,8 @@\n  */\n \n #include \"cache.h\"\n-#include \"refs.h\"\n #include \"builtin.h\"\n #include \"commit.h\"\n-#include \"tag.h\"\n-#include \"tree-walk.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"quote.h\"\n@@ -70,39 +67,6 @@ struct progress_info {\n \tint blamed_lines;\n };\n \n-static int compare_commits_by_reverse_commit_date(const void *a,\n-\t\t\t\t\t\t  const void *b,\n-\t\t\t\t\t\t  void *c)\n-{\n-\treturn -compare_commits_by_commit_date(a, b, c);\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- * This also fills origin->mode for corresponding tree path.\n- */\n-static int fill_blob_sha1_and_mode(struct blame_origin *origin)\n-{\n-\tif (!is_null_oid(&origin->blob_oid))\n-\t\treturn 0;\n-\tif (get_tree_entry(origin->commit->object.oid.hash,\n-\t\t\t   origin->path,\n-\t\t\t   origin->blob_oid.hash, &origin->mode))\n-\t\tgoto error_out;\n-\tif (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)\n-\t\tgoto error_out;\n-\treturn 0;\n- error_out:\n-\toidclr(&origin->blob_oid);\n-\torigin->mode = S_IFINVALID;\n-\treturn -1;\n-}\n-\n static const char *nth_line_cb(void *data, long lno)\n {\n \treturn blame_nth_line((struct blame_scoreboard *)data, lno);\n@@ -510,40 +474,6 @@ static void output(struct blame_scoreboard *sb, int option)\n \t}\n }\n \n-static const char *get_next_line(const char *start, const char *end)\n-{\n-\tconst char *nl = memchr(start, '\\n', end - start);\n-\treturn nl ? nl + 1 : end;\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-\tconst char *end = buf + len;\n-\tconst char *p;\n-\tint *lineno;\n-\tint num = 0;\n-\n-\tfor (p = buf; p < end; p = get_next_line(p, end))\n-\t\tnum++;\n-\n-\tALLOC_ARRAY(sb->lineno, num + 1);\n-\tlineno = sb->lineno;\n-\n-\tfor (p = buf; p < end; p = get_next_line(p, end))\n-\t\t*lineno++ = p - buf;\n-\n-\t*lineno = len;\n-\n-\tsb->num_lines = num;\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@@ -685,104 +615,6 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n-static struct commit *find_single_final(struct rev_info *revs,\n-\t\t\t\t\tconst char **name_p)\n-{\n-\tint i;\n-\tstruct commit *found = NULL;\n-\tconst char *name = NULL;\n-\n-\tfor (i = 0; i < revs->pending.nr; i++) {\n-\t\tstruct object *obj = revs->pending.objects[i].item;\n-\t\tif (obj->flags & UNINTERESTING)\n-\t\t\tcontinue;\n-\t\tobj = deref_tag(obj, NULL, 0);\n-\t\tif (obj->type != OBJ_COMMIT)\n-\t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n-\t\tif (found)\n-\t\t\tdie(\"More than one commit to dig from %s and %s?\",\n-\t\t\t    revs->pending.objects[i].name, name);\n-\t\tfound = (struct commit *)obj;\n-\t\tname = revs->pending.objects[i].name;\n-\t}\n-\tif (name_p)\n-\t\t*name_p = name;\n-\treturn found;\n-}\n-\n-static struct commit *dwim_reverse_initial(struct rev_info *revs,\n-\t\t\t\t\t   const char **name_p)\n-{\n-\t/*\n-\t * DWIM \"git blame --reverse ONE -- PATH\" as\n-\t * \"git blame --reverse ONE..HEAD -- PATH\" but only do so\n-\t * when it makes sense.\n-\t */\n-\tstruct object *obj;\n-\tstruct commit *head_commit;\n-\tunsigned char head_sha1[20];\n-\n-\tif (revs->pending.nr != 1)\n-\t\treturn NULL;\n-\n-\t/* Is that sole rev a committish? */\n-\tobj = revs->pending.objects[0].item;\n-\tobj = deref_tag(obj, NULL, 0);\n-\tif (obj->type != OBJ_COMMIT)\n-\t\treturn NULL;\n-\n-\t/* Do we have HEAD? */\n-\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, head_sha1, NULL))\n-\t\treturn NULL;\n-\thead_commit = lookup_commit_reference_gently(head_sha1, 1);\n-\tif (!head_commit)\n-\t\treturn NULL;\n-\n-\t/* Turn \"ONE\" into \"ONE..HEAD\" then */\n-\tobj->flags |= UNINTERESTING;\n-\tadd_pending_object(revs, &head_commit->object, \"HEAD\");\n-\n-\tif (name_p)\n-\t\t*name_p = revs->pending.objects[0].name;\n-\treturn (struct commit *)obj;\n-}\n-\n-static struct commit *find_single_initial(struct rev_info *revs,\n-\t\t\t\t\t  const char **name_p)\n-{\n-\tint i;\n-\tconst char *final_commit_name = NULL;\n-\tstruct commit *found = NULL;\n-\n-\t/*\n-\t * There must be one and only one negative commit, and it must be\n-\t * the boundary.\n-\t */\n-\tfor (i = 0; i < revs->pending.nr; i++) {\n-\t\tstruct object *obj = revs->pending.objects[i].item;\n-\t\tif (!(obj->flags & UNINTERESTING))\n-\t\t\tcontinue;\n-\t\tobj = deref_tag(obj, NULL, 0);\n-\t\tif (obj->type != OBJ_COMMIT)\n-\t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n-\t\tif (found)\n-\t\t\tdie(\"More than one commit to dig up from, %s and %s?\",\n-\t\t\t    revs->pending.objects[i].name,\n-\t\t\t    final_commit_name);\n-\t\tfound = (struct commit *) obj;\n-\t\tfinal_commit_name = revs->pending.objects[i].name;\n-\t}\n-\n-\tif (!final_commit_name)\n-\t\tfound = dwim_reverse_initial(revs, &final_commit_name);\n-\tif (!final_commit_name)\n-\t\tdie(\"No commit to dig up from?\");\n-\n-\tif (name_p)\n-\t\t*name_p = final_commit_name;\n-\treturn found;\n-}\n-\n static int blame_copy_callback(const struct option *option, const char *arg, int unset)\n {\n \tint *opt = option->value;\n@@ -824,9 +656,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tstruct blame_origin *o;\n \tstruct blame_entry *ent = NULL;\n \tlong dashdash_pos, lno;\n-\tconst char *final_commit_name = NULL;\n-\tenum object_type type;\n-\tstruct commit *final_commit = NULL;\n \tstruct progress_info pi = { NULL, 0 };\n \n \tstruct string_list range_list = STRING_LIST_INIT_NODUP;\n@@ -1031,92 +860,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tsb.revs = &revs;\n \tsb.contents_from = contents_from;\n \tsb.reverse = reverse;\n-\n-\tif (!reverse) {\n-\t\tsb.final = find_single_final(&revs, &final_commit_name);\n-\t\tsb.commits.compare = compare_commits_by_commit_date;\n-\t}\n-\telse if (contents_from)\n-\t\tdie(_(\"--contents and --reverse do not blend well.\"));\n-\telse {\n-\t\tsb.final = find_single_initial(&revs, &final_commit_name);\n-\t\tsb.commits.compare = compare_commits_by_reverse_commit_date;\n-\t\tif (revs.first_parent_only)\n-\t\t\trevs.children.name = NULL;\n-\t}\n-\n-\tif (!sb.final) {\n-\t\t/*\n-\t\t * \"--not A B -- path\" without anything positive;\n-\t\t * do not default to HEAD, but use the working tree\n-\t\t * or \"--contents\".\n-\t\t */\n-\t\tsetup_work_tree();\n-\t\tsb.final = fake_working_tree_commit(&sb.revs->diffopt,\n-\t\t\t\t\t\t    path, contents_from);\n-\t\tadd_pending_object(&revs, &(sb.final->object), \":\");\n-\t}\n-\telse if (contents_from)\n-\t\tdie(_(\"cannot use --contents with final commit object name\"));\n-\n-\tif (reverse && revs.first_parent_only) {\n-\t\tfinal_commit = find_single_final(sb.revs, NULL);\n-\t\tif (!final_commit)\n-\t\t\tdie(_(\"--reverse and --first-parent together require specified latest commit\"));\n-\t}\n-\n-\t/*\n-\t * If we have bottom, this will mark the ancestors of the\n-\t * bottom commits we would reach while traversing as\n-\t * uninteresting.\n-\t */\n-\tif (prepare_revision_walk(&revs))\n-\t\tdie(_(\"revision walk setup failed\"));\n-\n-\tif (reverse && revs.first_parent_only) {\n-\t\tstruct commit *c = final_commit;\n-\n-\t\tsb.revs->children.name = \"children\";\n-\t\twhile (c->parents &&\n-\t\t       oidcmp(&c->object.oid, &sb.final->object.oid)) {\n-\t\t\tstruct commit_list *l = xcalloc(1, sizeof(*l));\n-\n-\t\t\tl->item = c;\n-\t\t\tif (add_decoration(&sb.revs->children,\n-\t\t\t\t\t   &c->parents->item->object, l))\n-\t\t\t\tdie(\"BUG: not unique item in first-parent chain\");\n-\t\t\tc = c->parents->item;\n-\t\t}\n-\n-\t\tif (oidcmp(&c->object.oid, &sb.final->object.oid))\n-\t\t\tdie(_(\"--reverse --first-parent together require range along first-parent chain\"));\n-\t}\n-\n-\tif (is_null_oid(&sb.final->object.oid)) {\n-\t\to = sb.final->util;\n-\t\tsb.final_buf = xmemdupz(o->file.ptr, o->file.size);\n-\t\tsb.final_buf_size = o->file.size;\n-\t}\n-\telse {\n-\t\to = get_origin(sb.final, path);\n-\t\tif (fill_blob_sha1_and_mode(o))\n-\t\t\tdie(_(\"no such path %s in %s\"), path, final_commit_name);\n-\n-\t\tif (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&\n-\t\t    textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb.final_buf,\n-\t\t\t\t    &sb.final_buf_size))\n-\t\t\t;\n-\t\telse\n-\t\t\tsb.final_buf = read_sha1_file(o->blob_oid.hash, &type,\n-\t\t\t\t\t\t      &sb.final_buf_size);\n-\n-\t\tif (!sb.final_buf)\n-\t\t\tdie(_(\"cannot read blob %s for path %s\"),\n-\t\t\t    oid_to_hex(&o->blob_oid),\n-\t\t\t    path);\n-\t}\n-\tsb.num_read_blob++;\n-\tlno = prepare_lines(&sb);\n+\tsetup_scoreboard(&sb, path, &o);\n+\tlno = sb.num_lines;\n \n \tif (lno && !range_list.nr)\n \t\tstring_list_append(&range_list, \"1\");\n-- \n2.9.3\n\n"},{"id":"319685","messageId":"20170514031513.9042-20-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 19/22] blame: move scoreboard-related methods to libgit","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:10Z","receivedAt":"2017-05-14T03:16:55Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.c         | 1313 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n blame.h         |   11 +\n builtin/blame.c | 1330 +------------------------------------------------------\n 3 files changed, 1330 insertions(+), 1324 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 8c025bc..798e61b 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -1,6 +1,9 @@\n #include \"cache.h\"\n #include \"refs.h\"\n #include \"cache-tree.h\"\n+#include \"mergesort.h\"\n+#include \"diff.h\"\n+#include \"diffcore.h\"\n #include \"blame.h\"\n \n void blame_origin_decref(struct blame_origin *o)\n@@ -261,3 +264,1313 @@ struct commit *fake_working_tree_commit(struct diff_options *opt,\n \n \treturn commit;\n }\n+\n+\n+\n+static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n+\t\t      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts)\n+{\n+\txpparam_t xpp = {0};\n+\txdemitconf_t xecfg = {0};\n+\txdemitcb_t ecb = {NULL};\n+\n+\txpp.flags = xdl_opts;\n+\txecfg.hunk_func = hunk_func;\n+\tecb.priv = cb_data;\n+\treturn xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);\n+}\n+\n+/*\n+ * Given an origin, prepare mmfile_t structure to be used by the\n+ * diff machinery\n+ */\n+static void fill_origin_blob(struct diff_options *opt,\n+\t\t\t     struct blame_origin *o, mmfile_t *file, int *num_read_blob)\n+{\n+\tif (!o->file.ptr) {\n+\t\tenum object_type type;\n+\t\tunsigned long file_size;\n+\n+\t\t(*num_read_blob)++;\n+\t\tif (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&\n+\t\t    textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))\n+\t\t\t;\n+\t\telse\n+\t\t\tfile->ptr = read_sha1_file(o->blob_oid.hash, &type,\n+\t\t\t\t\t\t   &file_size);\n+\t\tfile->size = file_size;\n+\n+\t\tif (!file->ptr)\n+\t\t\tdie(\"Cannot read blob %s for path %s\",\n+\t\t\t    oid_to_hex(&o->blob_oid),\n+\t\t\t    o->path);\n+\t\to->file = *file;\n+\t}\n+\telse\n+\t\t*file = o->file;\n+}\n+\n+static void drop_origin_blob(struct blame_origin *o)\n+{\n+\tif (o->file.ptr) {\n+\t\tfree(o->file.ptr);\n+\t\to->file.ptr = NULL;\n+\t}\n+}\n+\n+/*\n+ * Any merge of blames happens on lists of blames that arrived via\n+ * different parents in a single suspect.  In this case, we want to\n+ * sort according to the suspect line numbers as opposed to the final\n+ * image line numbers.  The function body is somewhat longish because\n+ * it avoids unnecessary writes.\n+ */\n+\n+static struct blame_entry *blame_merge(struct blame_entry *list1,\n+\t\t\t\t       struct blame_entry *list2)\n+{\n+\tstruct blame_entry *p1 = list1, *p2 = list2,\n+\t\t**tail = &list1;\n+\n+\tif (!p1)\n+\t\treturn p2;\n+\tif (!p2)\n+\t\treturn p1;\n+\n+\tif (p1->s_lno <= p2->s_lno) {\n+\t\tdo {\n+\t\t\ttail = &p1->next;\n+\t\t\tif ((p1 = *tail) == NULL) {\n+\t\t\t\t*tail = p2;\n+\t\t\t\treturn list1;\n+\t\t\t}\n+\t\t} while (p1->s_lno <= p2->s_lno);\n+\t}\n+\tfor (;;) {\n+\t\t*tail = p2;\n+\t\tdo {\n+\t\t\ttail = &p2->next;\n+\t\t\tif ((p2 = *tail) == NULL)  {\n+\t\t\t\t*tail = p1;\n+\t\t\t\treturn list1;\n+\t\t\t}\n+\t\t} while (p1->s_lno > p2->s_lno);\n+\t\t*tail = p1;\n+\t\tdo {\n+\t\t\ttail = &p1->next;\n+\t\t\tif ((p1 = *tail) == NULL) {\n+\t\t\t\t*tail = p2;\n+\t\t\t\treturn list1;\n+\t\t\t}\n+\t\t} while (p1->s_lno <= p2->s_lno);\n+\t}\n+}\n+\n+static void *get_next_blame(const void *p)\n+{\n+\treturn ((struct blame_entry *)p)->next;\n+}\n+\n+static void set_next_blame(void *p1, void *p2)\n+{\n+\t((struct blame_entry *)p1)->next = p2;\n+}\n+\n+/*\n+ * Final image line numbers are all different, so we don't need a\n+ * three-way comparison here.\n+ */\n+\n+static int compare_blame_final(const void *p1, const void *p2)\n+{\n+\treturn ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno\n+\t\t? 1 : -1;\n+}\n+\n+static int compare_blame_suspect(const void *p1, const void *p2)\n+{\n+\tconst struct blame_entry *s1 = p1, *s2 = p2;\n+\t/*\n+\t * to allow for collating suspects, we sort according to the\n+\t * respective pointer value as the primary sorting criterion.\n+\t * The actual relation is pretty unimportant as long as it\n+\t * establishes a total order.  Comparing as integers gives us\n+\t * that.\n+\t */\n+\tif (s1->suspect != s2->suspect)\n+\t\treturn (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;\n+\tif (s1->s_lno == s2->s_lno)\n+\t\treturn 0;\n+\treturn s1->s_lno > s2->s_lno ? 1 : -1;\n+}\n+\n+void blame_sort_final(struct blame_scoreboard *sb)\n+{\n+\tsb->ent = llist_mergesort(sb->ent, get_next_blame, set_next_blame,\n+\t\t\t\t  compare_blame_final);\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\toid_to_hex(&ent->suspect->commit->object.oid),\n+\t\t\t\tent->suspect->refcnt);\n+\t\t\tbaa = 1;\n+\t\t}\n+\t}\n+\tif (baa)\n+\t\tsb->on_sanity_fail(sb, baa);\n+}\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 blame_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 (ent->suspect == next->suspect &&\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\tblame_origin_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 (sb->debug) /* sanity */\n+\t\tsanity_check_refcnt(sb);\n+}\n+\n+/*\n+ * Merge the given sorted list of blames into a preexisting origin.\n+ * If there were no previous blames to that commit, it is entered into\n+ * the commit priority queue of the score board.\n+ */\n+\n+static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porigin,\n+\t\t\t struct blame_entry *sorted)\n+{\n+\tif (porigin->suspects)\n+\t\tporigin->suspects = blame_merge(porigin->suspects, sorted);\n+\telse {\n+\t\tstruct blame_origin *o;\n+\t\tfor (o = porigin->commit->util; o; o = o->next) {\n+\t\t\tif (o->suspects) {\n+\t\t\t\tporigin->suspects = sorted;\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t}\n+\t\tporigin->suspects = sorted;\n+\t\tprio_queue_put(&sb->commits, porigin->commit);\n+\t}\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 blame_origin *find_origin(struct commit *parent,\n+\t\t\t\t  struct blame_origin *origin)\n+{\n+\tstruct blame_origin *porigin;\n+\tstruct diff_options diff_opts;\n+\tconst char *paths[2];\n+\n+\t/* First check any existing origins */\n+\tfor (porigin = parent->util; porigin; porigin = porigin->next)\n+\t\tif (!strcmp(porigin->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\treturn blame_origin_incref (porigin);\n+\t\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+\tparse_pathspec(&diff_opts.pathspec,\n+\t\t       PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,\n+\t\t       PATHSPEC_LITERAL_PATH, \"\", paths);\n+\tdiff_setup_done(&diff_opts);\n+\n+\tif (is_null_oid(&origin->commit->object.oid))\n+\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n+\t\t\t       origin->commit->tree->object.oid.hash,\n+\t\t\t       \"\", &diff_opts);\n+\tdiffcore_std(&diff_opts);\n+\n+\tif (!diff_queued_diff.nr) {\n+\t\t/* The path is the same as parent */\n+\t\tporigin = get_origin(parent, origin->path);\n+\t\toidcpy(&porigin->blob_oid, &origin->blob_oid);\n+\t\tporigin->mode = origin->mode;\n+\t} else {\n+\t\t/*\n+\t\t * Since origin->path is a pathspec, if the parent\n+\t\t * commit had it as a directory, we will see a whole\n+\t\t * bunch of deletion of files in the directory that we\n+\t\t * do not care about.\n+\t\t */\n+\t\tint i;\n+\t\tstruct diff_filepair *p = NULL;\n+\t\tfor (i = 0; i < diff_queued_diff.nr; i++) {\n+\t\t\tconst char *name;\n+\t\t\tp = diff_queued_diff.queue[i];\n+\t\t\tname = p->one->path ? p->one->path : p->two->path;\n+\t\t\tif (!strcmp(name, origin->path))\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (!p)\n+\t\t\tdie(\"internal error in blame::find_origin\");\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(parent, origin->path);\n+\t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n+\t\t\tporigin->mode = p->one->mode;\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+\tclear_pathspec(&diff_opts.pathspec);\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 blame_origin *find_rename(struct commit *parent,\n+\t\t\t\t  struct blame_origin *origin)\n+{\n+\tstruct blame_origin *porigin = NULL;\n+\tstruct diff_options diff_opts;\n+\tint i;\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+\tdiff_setup_done(&diff_opts);\n+\n+\tif (is_null_oid(&origin->commit->object.oid))\n+\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n+\t\t\t       origin->commit->tree->object.oid.hash,\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(parent, p->one->path);\n+\t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n+\t\t\tporigin->mode = p->one->mode;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tdiff_flush(&diff_opts);\n+\tclear_pathspec(&diff_opts.pathspec);\n+\treturn porigin;\n+}\n+\n+/*\n+ * Append a new blame entry to a given output queue.\n+ */\n+static void add_blame_entry(struct blame_entry ***queue,\n+\t\t\t    const struct blame_entry *src)\n+{\n+\tstruct blame_entry *e = xmalloc(sizeof(*e));\n+\tmemcpy(e, src, sizeof(*e));\n+\tblame_origin_incref(e->suspect);\n+\n+\te->next = **queue;\n+\t**queue = e;\n+\t*queue = &e->next;\n+}\n+\n+/*\n+ * src typically is on-stack; we want to copy the information in it to\n+ * a malloced blame_entry that gets added to the given queue.  The\n+ * origin of dst loses a refcnt.\n+ */\n+static void dup_entry(struct blame_entry ***queue,\n+\t\t      struct blame_entry *dst, struct blame_entry *src)\n+{\n+\tblame_origin_incref(src->suspect);\n+\tblame_origin_decref(dst->suspect);\n+\tmemcpy(dst, src, sizeof(*src));\n+\tdst->next = **queue;\n+\t**queue = dst;\n+\t*queue = &dst->next;\n+}\n+\n+const char *blame_nth_line(struct blame_scoreboard *sb, long 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 blame_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 = blame_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 = blame_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 = blame_origin_incref(parent);\n+}\n+\n+/*\n+ * split_overlap() divided an existing blame e into up to three parts\n+ * in split.  Any assigned blame is moved to queue to\n+ * reflect the split.\n+ */\n+static void split_blame(struct blame_entry ***blamed,\n+\t\t\tstruct blame_entry ***unblamed,\n+\t\t\tstruct blame_entry *split,\n+\t\t\tstruct blame_entry *e)\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(unblamed, e, &split[0]);\n+\n+\t\t/* The last part -- me */\n+\t\tadd_blame_entry(unblamed, &split[2]);\n+\n+\t\t/* ... and the middle part -- parent */\n+\t\tadd_blame_entry(blamed, &split[1]);\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(blamed, e, &split[1]);\n+\telse if (split[0].suspect) {\n+\t\t/* me and then parent */\n+\t\tdup_entry(unblamed, e, &split[0]);\n+\t\tadd_blame_entry(blamed, &split[1]);\n+\t}\n+\telse {\n+\t\t/* parent and then me */\n+\t\tdup_entry(blamed, e, &split[1]);\n+\t\tadd_blame_entry(unblamed, &split[2]);\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\tblame_origin_decref(split[i].suspect);\n+}\n+\n+/*\n+ * reverse_blame reverses the list given in head, appending tail.\n+ * That allows us to build lists in reverse order, then reverse them\n+ * afterwards.  This can be faster than building the list in proper\n+ * order right away.  The reason is that building in proper order\n+ * requires writing a link in the _previous_ element, while building\n+ * in reverse order just requires placing the list head into the\n+ * _current_ element.\n+ */\n+\n+static struct blame_entry *reverse_blame(struct blame_entry *head,\n+\t\t\t\t\t struct blame_entry *tail)\n+{\n+\twhile (head) {\n+\t\tstruct blame_entry *next = head->next;\n+\t\thead->next = tail;\n+\t\ttail = head;\n+\t\thead = next;\n+\t}\n+\treturn tail;\n+}\n+\n+/*\n+ * Process one hunk from the patch between the current suspect for\n+ * blame_entry e and its parent.  This first blames any unfinished\n+ * entries before the chunk (which is where target and parent start\n+ * differing) on the parent, and then splits blame entries at the\n+ * start and at the end of the difference region.  Since use of -M and\n+ * -C options may lead to overlapping/duplicate source line number\n+ * ranges, all we can rely on from sorting/merging is the order of the\n+ * first suspect line number.\n+ */\n+static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,\n+\t\t\tint tlno, int offset, int same,\n+\t\t\tstruct blame_origin *parent)\n+{\n+\tstruct blame_entry *e = **srcq;\n+\tstruct blame_entry *samep = NULL, *diffp = NULL;\n+\n+\twhile (e && e->s_lno < tlno) {\n+\t\tstruct blame_entry *next = e->next;\n+\t\t/*\n+\t\t * current record starts before differing portion.  If\n+\t\t * it reaches into it, we need to split it up and\n+\t\t * examine the second part separately.\n+\t\t */\n+\t\tif (e->s_lno + e->num_lines > tlno) {\n+\t\t\t/* Move second half to a new record */\n+\t\t\tint len = tlno - e->s_lno;\n+\t\t\tstruct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));\n+\t\t\tn->suspect = e->suspect;\n+\t\t\tn->lno = e->lno + len;\n+\t\t\tn->s_lno = e->s_lno + len;\n+\t\t\tn->num_lines = e->num_lines - len;\n+\t\t\te->num_lines = len;\n+\t\t\te->score = 0;\n+\t\t\t/* Push new record to diffp */\n+\t\t\tn->next = diffp;\n+\t\t\tdiffp = n;\n+\t\t} else\n+\t\t\tblame_origin_decref(e->suspect);\n+\t\t/* Pass blame for everything before the differing\n+\t\t * chunk to the parent */\n+\t\te->suspect = blame_origin_incref(parent);\n+\t\te->s_lno += offset;\n+\t\te->next = samep;\n+\t\tsamep = e;\n+\t\te = next;\n+\t}\n+\t/*\n+\t * As we don't know how much of a common stretch after this\n+\t * diff will occur, the currently blamed parts are all that we\n+\t * can assign to the parent for now.\n+\t */\n+\n+\tif (samep) {\n+\t\t**dstq = reverse_blame(samep, **dstq);\n+\t\t*dstq = &samep->next;\n+\t}\n+\t/*\n+\t * Prepend the split off portions: everything after e starts\n+\t * after the blameable portion.\n+\t */\n+\te = reverse_blame(diffp, e);\n+\n+\t/*\n+\t * Now retain records on the target while parts are different\n+\t * from the parent.\n+\t */\n+\tsamep = NULL;\n+\tdiffp = NULL;\n+\twhile (e && e->s_lno < same) {\n+\t\tstruct blame_entry *next = e->next;\n+\n+\t\t/*\n+\t\t * If current record extends into sameness, need to split.\n+\t\t */\n+\t\tif (e->s_lno + e->num_lines > same) {\n+\t\t\t/*\n+\t\t\t * Move second half to a new record to be\n+\t\t\t * processed by later chunks\n+\t\t\t */\n+\t\t\tint len = same - e->s_lno;\n+\t\t\tstruct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));\n+\t\t\tn->suspect = blame_origin_incref(e->suspect);\n+\t\t\tn->lno = e->lno + len;\n+\t\t\tn->s_lno = e->s_lno + len;\n+\t\t\tn->num_lines = e->num_lines - len;\n+\t\t\te->num_lines = len;\n+\t\t\te->score = 0;\n+\t\t\t/* Push new record to samep */\n+\t\t\tn->next = samep;\n+\t\t\tsamep = n;\n+\t\t}\n+\t\te->next = diffp;\n+\t\tdiffp = e;\n+\t\te = next;\n+\t}\n+\t**srcq = reverse_blame(diffp, reverse_blame(samep, e));\n+\t/* Move across elements that are in the unblamable portion */\n+\tif (diffp)\n+\t\t*srcq = &diffp->next;\n+}\n+\n+struct blame_chunk_cb_data {\n+\tstruct blame_origin *parent;\n+\tlong offset;\n+\tstruct blame_entry **dstq;\n+\tstruct blame_entry **srcq;\n+};\n+\n+/* diff chunks are from parent to target */\n+static int blame_chunk_cb(long start_a, long count_a,\n+\t\t\t  long start_b, long count_b, void *data)\n+{\n+\tstruct blame_chunk_cb_data *d = data;\n+\tif (start_a - start_b != d->offset)\n+\t\tdie(\"internal error in blame::blame_chunk_cb\");\n+\tblame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,\n+\t\t    start_b + count_b, d->parent);\n+\td->offset = start_a + count_a - (start_b + count_b);\n+\treturn 0;\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 void pass_blame_to_parent(struct blame_scoreboard *sb,\n+\t\t\t\t struct blame_origin *target,\n+\t\t\t\t struct blame_origin *parent)\n+{\n+\tmmfile_t file_p, file_o;\n+\tstruct blame_chunk_cb_data d;\n+\tstruct blame_entry *newdest = NULL;\n+\n+\tif (!target->suspects)\n+\t\treturn; /* nothing remains for this target */\n+\n+\td.parent = parent;\n+\td.offset = 0;\n+\td.dstq = &newdest; d.srcq = &target->suspects;\n+\n+\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n+\tfill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob);\n+\tsb->num_get_patch++;\n+\n+\tif (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts))\n+\t\tdie(\"unable to generate diff (%s -> %s)\",\n+\t\t    oid_to_hex(&parent->commit->object.oid),\n+\t\t    oid_to_hex(&target->commit->object.oid));\n+\t/* The rest are the same as the parent */\n+\tblame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);\n+\t*d.dstq = NULL;\n+\tqueue_blames(sb, parent, newdest);\n+\n+\treturn;\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+unsigned blame_entry_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 = blame_nth_line(sb, e->lno);\n+\tep = blame_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 (blame_entry_score(sb, &this[1]) < blame_entry_score(sb, &best_so_far[1]))\n+\t\t\treturn;\n+\t}\n+\n+\tfor (i = 0; i < 3; i++)\n+\t\tblame_origin_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 blame_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 blame_origin *parent;\n+\tstruct blame_entry *split;\n+\tlong plno;\n+\tlong tlno;\n+};\n+\n+static int handle_split_cb(long start_a, long count_a,\n+\t\t\t   long start_b, long count_b, void *data)\n+{\n+\tstruct handle_split_cb_data *d = data;\n+\thandle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,\n+\t\t     d->split);\n+\td->plno = start_a + count_a;\n+\td->tlno = start_b + count_b;\n+\treturn 0;\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 blame_origin *parent,\n+\t\t\t      struct blame_entry *split,\n+\t\t\t      mmfile_t *file_p)\n+{\n+\tconst char *cp;\n+\tmmfile_t file_o;\n+\tstruct handle_split_cb_data d;\n+\n+\tmemset(&d, 0, sizeof(d));\n+\td.sb = sb; d.ent = ent; d.parent = parent; d.split = split;\n+\t/*\n+\t * Prepare mmfile that contains only the lines in ent.\n+\t */\n+\tcp = blame_nth_line(sb, ent->lno);\n+\tfile_o.ptr = (char *) cp;\n+\tfile_o.size = blame_nth_line(sb, ent->lno + ent->num_lines) - cp;\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(split, 0, sizeof(struct blame_entry [3]));\n+\tif (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts))\n+\t\tdie(\"unable to generate diff (%s)\",\n+\t\t    oid_to_hex(&parent->commit->object.oid));\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+/* Move all blame entries from list *source that have a score smaller\n+ * than score_min to the front of list *small.\n+ * Returns a pointer to the link pointing to the old head of the small list.\n+ */\n+\n+static struct blame_entry **filter_small(struct blame_scoreboard *sb,\n+\t\t\t\t\t struct blame_entry **small,\n+\t\t\t\t\t struct blame_entry **source,\n+\t\t\t\t\t unsigned score_min)\n+{\n+\tstruct blame_entry *p = *source;\n+\tstruct blame_entry *oldsmall = *small;\n+\twhile (p) {\n+\t\tif (blame_entry_score(sb, p) <= score_min) {\n+\t\t\t*small = p;\n+\t\t\tsmall = &p->next;\n+\t\t\tp = *small;\n+\t\t} else {\n+\t\t\t*source = p;\n+\t\t\tsource = &p->next;\n+\t\t\tp = *source;\n+\t\t}\n+\t}\n+\t*small = oldsmall;\n+\t*source = NULL;\n+\treturn small;\n+}\n+\n+/*\n+ * See if lines currently target is suspected for can be attributed to\n+ * parent.\n+ */\n+static void find_move_in_parent(struct blame_scoreboard *sb,\n+\t\t\t\tstruct blame_entry ***blamed,\n+\t\t\t\tstruct blame_entry **toosmall,\n+\t\t\t\tstruct blame_origin *target,\n+\t\t\t\tstruct blame_origin *parent)\n+{\n+\tstruct blame_entry *e, split[3];\n+\tstruct blame_entry *unblamed = target->suspects;\n+\tstruct blame_entry *leftover = NULL;\n+\tmmfile_t file_p;\n+\n+\tif (!unblamed)\n+\t\treturn; /* nothing remains for this target */\n+\n+\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n+\tif (!file_p.ptr)\n+\t\treturn;\n+\n+\t/* At each iteration, unblamed has a NULL-terminated list of\n+\t * entries that have not yet been tested for blame.  leftover\n+\t * contains the reversed list of entries that have been tested\n+\t * without being assignable to the parent.\n+\t */\n+\tdo {\n+\t\tstruct blame_entry **unblamedtail = &unblamed;\n+\t\tstruct blame_entry *next;\n+\t\tfor (e = unblamed; e; e = next) {\n+\t\t\tnext = e->next;\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->move_score < blame_entry_score(sb, &split[1])) {\n+\t\t\t\tsplit_blame(blamed, &unblamedtail, split, e);\n+\t\t\t} else {\n+\t\t\t\te->next = leftover;\n+\t\t\t\tleftover = e;\n+\t\t\t}\n+\t\t\tdecref_split(split);\n+\t\t}\n+\t\t*unblamedtail = NULL;\n+\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->move_score);\n+\t} while (unblamed);\n+\ttarget->suspects = reverse_blame(leftover, NULL);\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_entry *unblamed,\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 = unblamed, num_ents = 0; e; e = e->next)\n+\t\tnum_ents++;\n+\tif (num_ents) {\n+\t\tblame_list = xcalloc(num_ents, sizeof(struct blame_list));\n+\t\tfor (e = unblamed, i = 0; e; e = e->next)\n+\t\t\tblame_list[i++].ent = e;\n+\t}\n+\t*num_ents_p = num_ents;\n+\treturn blame_list;\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 void find_copy_in_parent(struct blame_scoreboard *sb,\n+\t\t\t\tstruct blame_entry ***blamed,\n+\t\t\t\tstruct blame_entry **toosmall,\n+\t\t\t\tstruct blame_origin *target,\n+\t\t\t\tstruct commit *parent,\n+\t\t\t\tstruct blame_origin *porigin,\n+\t\t\t\tint opt)\n+{\n+\tstruct diff_options diff_opts;\n+\tint i, j;\n+\tstruct blame_list *blame_list;\n+\tint num_ents;\n+\tstruct blame_entry *unblamed = target->suspects;\n+\tstruct blame_entry *leftover = NULL;\n+\n+\tif (!unblamed)\n+\t\treturn; /* 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+\tdiff_setup_done(&diff_opts);\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_oid(&target->commit->object.oid))\n+\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n+\telse\n+\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n+\t\t\t       target->commit->tree->object.oid.hash,\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+\tdo {\n+\t\tstruct blame_entry **unblamedtail = &unblamed;\n+\t\tblame_list = setup_blame_list(unblamed, &num_ents);\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 blame_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(parent, p->one->path);\n+\t\t\toidcpy(&norigin->blob_oid, &p->one->oid);\n+\t\t\tnorigin->mode = p->one->mode;\n+\t\t\tfill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob);\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\tblame_origin_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->copy_score < blame_entry_score(sb, &split[1])) {\n+\t\t\t\tsplit_blame(blamed, &unblamedtail, split,\n+\t\t\t\t\t    blame_list[j].ent);\n+\t\t\t} else {\n+\t\t\t\tblame_list[j].ent->next = leftover;\n+\t\t\t\tleftover = blame_list[j].ent;\n+\t\t\t}\n+\t\t\tdecref_split(split);\n+\t\t}\n+\t\tfree(blame_list);\n+\t\t*unblamedtail = NULL;\n+\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score);\n+\t} while (unblamed);\n+\ttarget->suspects = reverse_blame(leftover, NULL);\n+\tdiff_flush(&diff_opts);\n+\tclear_pathspec(&diff_opts.pathspec);\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 blame_origin *origin, struct blame_origin *porigin)\n+{\n+\tstruct blame_entry *e, *suspects;\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+\tsuspects = origin->suspects;\n+\torigin->suspects = NULL;\n+\tfor (e = suspects; e; e = e->next) {\n+\t\tblame_origin_incref(porigin);\n+\t\tblame_origin_decref(e->suspect);\n+\t\te->suspect = porigin;\n+\t}\n+\tqueue_blames(sb, porigin, suspects);\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 rev_info *revs, struct commit *commit,\n+\t\t\t\t\tint reverse)\n+{\n+\tif (!reverse) {\n+\t\tif (revs->first_parent_only &&\n+\t\t    commit->parents &&\n+\t\t    commit->parents->next) {\n+\t\t\tfree_commit_list(commit->parents->next);\n+\t\t\tcommit->parents->next = NULL;\n+\t\t}\n+\t\treturn commit->parents;\n+\t}\n+\treturn lookup_decoration(&revs->children, &commit->object);\n+}\n+\n+static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse)\n+{\n+\tstruct commit_list *l = first_scapegoat(revs, commit, reverse);\n+\treturn commit_list_count(l);\n+}\n+\n+/* Distribute collected unsorted blames to the respected sorted lists\n+ * in the various origins.\n+ */\n+static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed)\n+{\n+\tblamed = llist_mergesort(blamed, get_next_blame, set_next_blame,\n+\t\t\t\t compare_blame_suspect);\n+\twhile (blamed)\n+\t{\n+\t\tstruct blame_origin *porigin = blamed->suspect;\n+\t\tstruct blame_entry *suspects = NULL;\n+\t\tdo {\n+\t\t\tstruct blame_entry *next = blamed->next;\n+\t\t\tblamed->next = suspects;\n+\t\t\tsuspects = blamed;\n+\t\t\tblamed = next;\n+\t\t} while (blamed && blamed->suspect == porigin);\n+\t\tsuspects = reverse_blame(suspects, NULL);\n+\t\tqueue_blames(sb, porigin, suspects);\n+\t}\n+}\n+\n+#define MAXSG 16\n+\n+static void pass_blame(struct blame_scoreboard *sb, struct blame_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 blame_origin *sg_buf[MAXSG];\n+\tstruct blame_origin *porigin, **sg_origin = sg_buf;\n+\tstruct blame_entry *toosmall = NULL;\n+\tstruct blame_entry *blames, **blametail = &blames;\n+\n+\tnum_sg = num_scapegoats(revs, commit, sb->reverse);\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 - sb->no_whole_file_rename; pass++) {\n+\t\tstruct blame_origin *(*find)(struct commit *, struct blame_origin *);\n+\t\tfind = pass ? find_rename : find_origin;\n+\n+\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\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(p, origin);\n+\t\t\tif (!porigin)\n+\t\t\t\tcontinue;\n+\t\t\tif (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {\n+\t\t\t\tpass_whole_blame(sb, origin, porigin);\n+\t\t\t\tblame_origin_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    !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) {\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\tblame_origin_decref(porigin);\n+\t\t}\n+\t}\n+\n+\tsb->num_commits++;\n+\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n+\t     i < num_sg && sg;\n+\t     sg = sg->next, i++) {\n+\t\tstruct blame_origin *porigin = sg_origin[i];\n+\t\tif (!porigin)\n+\t\t\tcontinue;\n+\t\tif (!origin->previous) {\n+\t\t\tblame_origin_incref(porigin);\n+\t\t\torigin->previous = porigin;\n+\t\t}\n+\t\tpass_blame_to_parent(sb, origin, porigin);\n+\t\tif (!origin->suspects)\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\tfilter_small(sb, &toosmall, &origin->suspects, sb->move_score);\n+\t\tif (origin->suspects) {\n+\t\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n+\t\t\t     i < num_sg && sg;\n+\t\t\t     sg = sg->next, i++) {\n+\t\t\t\tstruct blame_origin *porigin = sg_origin[i];\n+\t\t\t\tif (!porigin)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tfind_move_in_parent(sb, &blametail, &toosmall, origin, porigin);\n+\t\t\t\tif (!origin->suspects)\n+\t\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Optionally find copies from parents' files.\n+\t */\n+\tif (opt & PICKAXE_BLAME_COPY) {\n+\t\tif (sb->copy_score > sb->move_score)\n+\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->copy_score);\n+\t\telse if (sb->copy_score < sb->move_score) {\n+\t\t\torigin->suspects = blame_merge(origin->suspects, toosmall);\n+\t\t\ttoosmall = NULL;\n+\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->copy_score);\n+\t\t}\n+\t\tif (!origin->suspects)\n+\t\t\tgoto finish;\n+\n+\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n+\t\t     i < num_sg && sg;\n+\t\t     sg = sg->next, i++) {\n+\t\t\tstruct blame_origin *porigin = sg_origin[i];\n+\t\t\tfind_copy_in_parent(sb, &blametail, &toosmall,\n+\t\t\t\t\t    origin, sg->item, porigin, opt);\n+\t\t\tif (!origin->suspects)\n+\t\t\t\tgoto finish;\n+\t\t}\n+\t}\n+\n+finish:\n+\t*blametail = NULL;\n+\tdistribute_blame(sb, blames);\n+\t/*\n+\t * prepend toosmall to origin->suspects\n+\t *\n+\t * There is no point in sorting: this ends up on a big\n+\t * unsorted list in the caller anyway.\n+\t */\n+\tif (toosmall) {\n+\t\tstruct blame_entry **tail = &toosmall;\n+\t\twhile (*tail)\n+\t\t\ttail = &(*tail)->next;\n+\t\t*tail = origin->suspects;\n+\t\torigin->suspects = toosmall;\n+\t}\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\tblame_origin_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+ * The main loop -- while we have blobs with lines whose true origin\n+ * is still unknown, pick one blob, and allow its lines to pass blames\n+ * to its parents. */\n+void assign_blame(struct blame_scoreboard *sb, int opt)\n+{\n+\tstruct rev_info *revs = sb->revs;\n+\tstruct commit *commit = prio_queue_get(&sb->commits);\n+\n+\twhile (commit) {\n+\t\tstruct blame_entry *ent;\n+\t\tstruct blame_origin *suspect = commit->util;\n+\n+\t\t/* find one suspect to break down */\n+\t\twhile (suspect && !suspect->suspects)\n+\t\t\tsuspect = suspect->next;\n+\n+\t\tif (!suspect) {\n+\t\t\tcommit = prio_queue_get(&sb->commits);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tassert(commit == suspect->commit);\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\tblame_origin_incref(suspect);\n+\t\tparse_commit(commit);\n+\t\tif (sb->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->show_root)\n+\t\t\tcommit->object.flags |= UNINTERESTING;\n+\n+\t\t/* Take responsibility for the remaining entries */\n+\t\tent = suspect->suspects;\n+\t\tif (ent) {\n+\t\t\tsuspect->guilty = 1;\n+\t\t\tfor (;;) {\n+\t\t\t\tstruct blame_entry *next = ent->next;\n+\t\t\t\tif (sb->found_guilty_entry)\n+\t\t\t\t\tsb->found_guilty_entry(ent, sb->found_guilty_entry_data);\n+\t\t\t\tif (next) {\n+\t\t\t\t\tent = next;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t\tent->next = sb->ent;\n+\t\t\t\tsb->ent = suspect->suspects;\n+\t\t\t\tsuspect->suspects = NULL;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tblame_origin_decref(suspect);\n+\n+\t\tif (sb->debug) /* sanity */\n+\t\t\tsanity_check_refcnt(sb);\n+\t}\n+}\ndiff --git a/blame.h b/blame.h\nindex f831dec..a3ea677 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -8,6 +8,11 @@\n #include \"prio-queue.h\"\n #include \"diff.h\"\n \n+#define PICKAXE_BLAME_MOVE\t\t01\n+#define PICKAXE_BLAME_COPY\t\t02\n+#define PICKAXE_BLAME_COPY_HARDER\t04\n+#define PICKAXE_BLAME_COPY_HARDEST\t010\n+\n #define BLAME_DEFAULT_MOVE_SCORE\t20\n #define BLAME_DEFAULT_COPY_SCORE\t40\n \n@@ -160,4 +165,10 @@ extern struct blame_origin *get_origin(struct commit *commit, const char *path);\n \n extern struct commit *fake_working_tree_commit(struct diff_options *opt, const char *path, const char *contents_from);\n \n+extern void blame_coalesce(struct blame_scoreboard *sb);\n+extern void blame_sort_final(struct blame_scoreboard *sb);\n+extern unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e);\n+extern void assign_blame(struct blame_scoreboard *sb, int opt);\n+extern const char *blame_nth_line(struct blame_scoreboard *sb, long lno);\n+\n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 33ede4f..207a74d 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -12,13 +12,10 @@\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 \"string-list.h\"\n #include \"mailmap.h\"\n-#include \"mergesort.h\"\n #include \"parse-options.h\"\n #include \"prio-queue.h\"\n #include \"utf8.h\"\n@@ -61,11 +58,6 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP;\n #define DEBUG 0\n #endif\n \n-#define PICKAXE_BLAME_MOVE\t\t01\n-#define PICKAXE_BLAME_COPY\t\t02\n-#define PICKAXE_BLAME_COPY_HARDER\t04\n-#define PICKAXE_BLAME_COPY_HARDEST\t010\n-\n static unsigned blame_move_score;\n static unsigned blame_copy_score;\n \n@@ -78,143 +70,6 @@ struct progress_info {\n \tint blamed_lines;\n };\n \n-static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,\n-\t\t      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts)\n-{\n-\txpparam_t xpp = {0};\n-\txdemitconf_t xecfg = {0};\n-\txdemitcb_t ecb = {NULL};\n-\n-\txpp.flags = xdl_opts;\n-\txecfg.hunk_func = hunk_func;\n-\tecb.priv = cb_data;\n-\treturn xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);\n-}\n-\n-/*\n- * Given an origin, prepare mmfile_t structure to be used by the\n- * diff machinery\n- */\n-static void fill_origin_blob(struct diff_options *opt,\n-\t\t\t     struct blame_origin *o, mmfile_t *file, int *num_read_blob)\n-{\n-\tif (!o->file.ptr) {\n-\t\tenum object_type type;\n-\t\tunsigned long file_size;\n-\n-\t\t(*num_read_blob)++;\n-\t\tif (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&\n-\t\t    textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))\n-\t\t\t;\n-\t\telse\n-\t\t\tfile->ptr = read_sha1_file(o->blob_oid.hash, &type,\n-\t\t\t\t\t\t   &file_size);\n-\t\tfile->size = file_size;\n-\n-\t\tif (!file->ptr)\n-\t\t\tdie(\"Cannot read blob %s for path %s\",\n-\t\t\t    oid_to_hex(&o->blob_oid),\n-\t\t\t    o->path);\n-\t\to->file = *file;\n-\t}\n-\telse\n-\t\t*file = o->file;\n-}\n-\n-static void drop_origin_blob(struct blame_origin *o)\n-{\n-\tif (o->file.ptr) {\n-\t\tfree(o->file.ptr);\n-\t\to->file.ptr = NULL;\n-\t}\n-}\n-\n-/*\n- * Any merge of blames happens on lists of blames that arrived via\n- * different parents in a single suspect.  In this case, we want to\n- * sort according to the suspect line numbers as opposed to the final\n- * image line numbers.  The function body is somewhat longish because\n- * it avoids unnecessary writes.\n- */\n-\n-static struct blame_entry *blame_merge(struct blame_entry *list1,\n-\t\t\t\t       struct blame_entry *list2)\n-{\n-\tstruct blame_entry *p1 = list1, *p2 = list2,\n-\t\t**tail = &list1;\n-\n-\tif (!p1)\n-\t\treturn p2;\n-\tif (!p2)\n-\t\treturn p1;\n-\n-\tif (p1->s_lno <= p2->s_lno) {\n-\t\tdo {\n-\t\t\ttail = &p1->next;\n-\t\t\tif ((p1 = *tail) == NULL) {\n-\t\t\t\t*tail = p2;\n-\t\t\t\treturn list1;\n-\t\t\t}\n-\t\t} while (p1->s_lno <= p2->s_lno);\n-\t}\n-\tfor (;;) {\n-\t\t*tail = p2;\n-\t\tdo {\n-\t\t\ttail = &p2->next;\n-\t\t\tif ((p2 = *tail) == NULL)  {\n-\t\t\t\t*tail = p1;\n-\t\t\t\treturn list1;\n-\t\t\t}\n-\t\t} while (p1->s_lno > p2->s_lno);\n-\t\t*tail = p1;\n-\t\tdo {\n-\t\t\ttail = &p1->next;\n-\t\t\tif ((p1 = *tail) == NULL) {\n-\t\t\t\t*tail = p2;\n-\t\t\t\treturn list1;\n-\t\t\t}\n-\t\t} while (p1->s_lno <= p2->s_lno);\n-\t}\n-}\n-\n-static void *get_next_blame(const void *p)\n-{\n-\treturn ((struct blame_entry *)p)->next;\n-}\n-\n-static void set_next_blame(void *p1, void *p2)\n-{\n-\t((struct blame_entry *)p1)->next = p2;\n-}\n-\n-/*\n- * Final image line numbers are all different, so we don't need a\n- * three-way comparison here.\n- */\n-\n-static int compare_blame_final(const void *p1, const void *p2)\n-{\n-\treturn ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno\n-\t\t? 1 : -1;\n-}\n-\n-static int compare_blame_suspect(const void *p1, const void *p2)\n-{\n-\tconst struct blame_entry *s1 = p1, *s2 = p2;\n-\t/*\n-\t * to allow for collating suspects, we sort according to the\n-\t * respective pointer value as the primary sorting criterion.\n-\t * The actual relation is pretty unimportant as long as it\n-\t * establishes a total order.  Comparing as integers gives us\n-\t * that.\n-\t */\n-\tif (s1->suspect != s2->suspect)\n-\t\treturn (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;\n-\tif (s1->s_lno == s2->s_lno)\n-\t\treturn 0;\n-\treturn s1->s_lno > s2->s_lno ? 1 : -1;\n-}\n-\n static int compare_commits_by_reverse_commit_date(const void *a,\n \t\t\t\t\t\t  const void *b,\n \t\t\t\t\t\t  void *c)\n@@ -222,63 +77,6 @@ static int compare_commits_by_reverse_commit_date(const void *a,\n \treturn -compare_commits_by_commit_date(a, b, c);\n }\n \n-static void blame_sort_final(struct blame_scoreboard *sb)\n-{\n-\tsb->ent = llist_mergesort(sb->ent, get_next_blame, set_next_blame,\n-\t\t\t\t  compare_blame_final);\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 (ent->suspect == next->suspect &&\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\tblame_origin_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 (sb->debug) /* sanity */\n-\t\tsanity_check_refcnt(sb);\n-}\n-\n-/*\n- * Merge the given sorted list of blames into a preexisting origin.\n- * If there were no previous blames to that commit, it is entered into\n- * the commit priority queue of the score board.\n- */\n-\n-static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porigin,\n-\t\t\t struct blame_entry *sorted)\n-{\n-\tif (porigin->suspects)\n-\t\tporigin->suspects = blame_merge(porigin->suspects, sorted);\n-\telse {\n-\t\tstruct blame_origin *o;\n-\t\tfor (o = porigin->commit->util; o; o = o->next) {\n-\t\t\tif (o->suspects) {\n-\t\t\t\tporigin->suspects = sorted;\n-\t\t\t\treturn;\n-\t\t\t}\n-\t\t}\n-\t\tporigin->suspects = sorted;\n-\t\tprio_queue_put(&sb->commits, porigin->commit);\n-\t}\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@@ -305,1034 +103,9 @@ static int fill_blob_sha1_and_mode(struct blame_origin *origin)\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 blame_origin *find_origin(struct commit *parent,\n-\t\t\t\t  struct blame_origin *origin)\n-{\n-\tstruct blame_origin *porigin;\n-\tstruct diff_options diff_opts;\n-\tconst char *paths[2];\n-\n-\t/* First check any existing origins */\n-\tfor (porigin = parent->util; porigin; porigin = porigin->next)\n-\t\tif (!strcmp(porigin->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\treturn blame_origin_incref (porigin);\n-\t\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-\tparse_pathspec(&diff_opts.pathspec,\n-\t\t       PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,\n-\t\t       PATHSPEC_LITERAL_PATH, \"\", paths);\n-\tdiff_setup_done(&diff_opts);\n-\n-\tif (is_null_oid(&origin->commit->object.oid))\n-\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n-\telse\n-\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n-\t\t\t       origin->commit->tree->object.oid.hash,\n-\t\t\t       \"\", &diff_opts);\n-\tdiffcore_std(&diff_opts);\n-\n-\tif (!diff_queued_diff.nr) {\n-\t\t/* The path is the same as parent */\n-\t\tporigin = get_origin(parent, origin->path);\n-\t\toidcpy(&porigin->blob_oid, &origin->blob_oid);\n-\t\tporigin->mode = origin->mode;\n-\t} else {\n-\t\t/*\n-\t\t * Since origin->path is a pathspec, if the parent\n-\t\t * commit had it as a directory, we will see a whole\n-\t\t * bunch of deletion of files in the directory that we\n-\t\t * do not care about.\n-\t\t */\n-\t\tint i;\n-\t\tstruct diff_filepair *p = NULL;\n-\t\tfor (i = 0; i < diff_queued_diff.nr; i++) {\n-\t\t\tconst char *name;\n-\t\t\tp = diff_queued_diff.queue[i];\n-\t\t\tname = p->one->path ? p->one->path : p->two->path;\n-\t\t\tif (!strcmp(name, origin->path))\n-\t\t\t\tbreak;\n-\t\t}\n-\t\tif (!p)\n-\t\t\tdie(\"internal error in blame::find_origin\");\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(parent, origin->path);\n-\t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n-\t\t\tporigin->mode = p->one->mode;\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-\tclear_pathspec(&diff_opts.pathspec);\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 blame_origin *find_rename(struct commit *parent,\n-\t\t\t\t  struct blame_origin *origin)\n-{\n-\tstruct blame_origin *porigin = NULL;\n-\tstruct diff_options diff_opts;\n-\tint i;\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-\tdiff_setup_done(&diff_opts);\n-\n-\tif (is_null_oid(&origin->commit->object.oid))\n-\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n-\telse\n-\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n-\t\t\t       origin->commit->tree->object.oid.hash,\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(parent, p->one->path);\n-\t\t\toidcpy(&porigin->blob_oid, &p->one->oid);\n-\t\t\tporigin->mode = p->one->mode;\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\tdiff_flush(&diff_opts);\n-\tclear_pathspec(&diff_opts.pathspec);\n-\treturn porigin;\n-}\n-\n-/*\n- * Append a new blame entry to a given output queue.\n- */\n-static void add_blame_entry(struct blame_entry ***queue,\n-\t\t\t    const struct blame_entry *src)\n-{\n-\tstruct blame_entry *e = xmalloc(sizeof(*e));\n-\tmemcpy(e, src, sizeof(*e));\n-\tblame_origin_incref(e->suspect);\n-\n-\te->next = **queue;\n-\t**queue = e;\n-\t*queue = &e->next;\n-}\n-\n-/*\n- * src typically is on-stack; we want to copy the information in it to\n- * a malloced blame_entry that gets added to the given queue.  The\n- * origin of dst loses a refcnt.\n- */\n-static void dup_entry(struct blame_entry ***queue,\n-\t\t      struct blame_entry *dst, struct blame_entry *src)\n-{\n-\tblame_origin_incref(src->suspect);\n-\tblame_origin_decref(dst->suspect);\n-\tmemcpy(dst, src, sizeof(*src));\n-\tdst->next = **queue;\n-\t**queue = dst;\n-\t*queue = &dst->next;\n-}\n-\n-static const char *nth_line(struct blame_scoreboard *sb, long lno)\n-{\n-\treturn sb->final_buf + sb->lineno[lno];\n-}\n-\n static const char *nth_line_cb(void *data, long lno)\n {\n-\treturn nth_line((struct blame_scoreboard *)data, 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 blame_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 = blame_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 = blame_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 = blame_origin_incref(parent);\n-}\n-\n-/*\n- * split_overlap() divided an existing blame e into up to three parts\n- * in split.  Any assigned blame is moved to queue to\n- * reflect the split.\n- */\n-static void split_blame(struct blame_entry ***blamed,\n-\t\t\tstruct blame_entry ***unblamed,\n-\t\t\tstruct blame_entry *split,\n-\t\t\tstruct blame_entry *e)\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(unblamed, e, &split[0]);\n-\n-\t\t/* The last part -- me */\n-\t\tadd_blame_entry(unblamed, &split[2]);\n-\n-\t\t/* ... and the middle part -- parent */\n-\t\tadd_blame_entry(blamed, &split[1]);\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(blamed, e, &split[1]);\n-\telse if (split[0].suspect) {\n-\t\t/* me and then parent */\n-\t\tdup_entry(unblamed, e, &split[0]);\n-\t\tadd_blame_entry(blamed, &split[1]);\n-\t}\n-\telse {\n-\t\t/* parent and then me */\n-\t\tdup_entry(blamed, e, &split[1]);\n-\t\tadd_blame_entry(unblamed, &split[2]);\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\tblame_origin_decref(split[i].suspect);\n-}\n-\n-/*\n- * reverse_blame reverses the list given in head, appending tail.\n- * That allows us to build lists in reverse order, then reverse them\n- * afterwards.  This can be faster than building the list in proper\n- * order right away.  The reason is that building in proper order\n- * requires writing a link in the _previous_ element, while building\n- * in reverse order just requires placing the list head into the\n- * _current_ element.\n- */\n-\n-static struct blame_entry *reverse_blame(struct blame_entry *head,\n-\t\t\t\t\t struct blame_entry *tail)\n-{\n-\twhile (head) {\n-\t\tstruct blame_entry *next = head->next;\n-\t\thead->next = tail;\n-\t\ttail = head;\n-\t\thead = next;\n-\t}\n-\treturn tail;\n-}\n-\n-/*\n- * Process one hunk from the patch between the current suspect for\n- * blame_entry e and its parent.  This first blames any unfinished\n- * entries before the chunk (which is where target and parent start\n- * differing) on the parent, and then splits blame entries at the\n- * start and at the end of the difference region.  Since use of -M and\n- * -C options may lead to overlapping/duplicate source line number\n- * ranges, all we can rely on from sorting/merging is the order of the\n- * first suspect line number.\n- */\n-static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,\n-\t\t\tint tlno, int offset, int same,\n-\t\t\tstruct blame_origin *parent)\n-{\n-\tstruct blame_entry *e = **srcq;\n-\tstruct blame_entry *samep = NULL, *diffp = NULL;\n-\n-\twhile (e && e->s_lno < tlno) {\n-\t\tstruct blame_entry *next = e->next;\n-\t\t/*\n-\t\t * current record starts before differing portion.  If\n-\t\t * it reaches into it, we need to split it up and\n-\t\t * examine the second part separately.\n-\t\t */\n-\t\tif (e->s_lno + e->num_lines > tlno) {\n-\t\t\t/* Move second half to a new record */\n-\t\t\tint len = tlno - e->s_lno;\n-\t\t\tstruct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));\n-\t\t\tn->suspect = e->suspect;\n-\t\t\tn->lno = e->lno + len;\n-\t\t\tn->s_lno = e->s_lno + len;\n-\t\t\tn->num_lines = e->num_lines - len;\n-\t\t\te->num_lines = len;\n-\t\t\te->score = 0;\n-\t\t\t/* Push new record to diffp */\n-\t\t\tn->next = diffp;\n-\t\t\tdiffp = n;\n-\t\t} else\n-\t\t\tblame_origin_decref(e->suspect);\n-\t\t/* Pass blame for everything before the differing\n-\t\t * chunk to the parent */\n-\t\te->suspect = blame_origin_incref(parent);\n-\t\te->s_lno += offset;\n-\t\te->next = samep;\n-\t\tsamep = e;\n-\t\te = next;\n-\t}\n-\t/*\n-\t * As we don't know how much of a common stretch after this\n-\t * diff will occur, the currently blamed parts are all that we\n-\t * can assign to the parent for now.\n-\t */\n-\n-\tif (samep) {\n-\t\t**dstq = reverse_blame(samep, **dstq);\n-\t\t*dstq = &samep->next;\n-\t}\n-\t/*\n-\t * Prepend the split off portions: everything after e starts\n-\t * after the blameable portion.\n-\t */\n-\te = reverse_blame(diffp, e);\n-\n-\t/*\n-\t * Now retain records on the target while parts are different\n-\t * from the parent.\n-\t */\n-\tsamep = NULL;\n-\tdiffp = NULL;\n-\twhile (e && e->s_lno < same) {\n-\t\tstruct blame_entry *next = e->next;\n-\n-\t\t/*\n-\t\t * If current record extends into sameness, need to split.\n-\t\t */\n-\t\tif (e->s_lno + e->num_lines > same) {\n-\t\t\t/*\n-\t\t\t * Move second half to a new record to be\n-\t\t\t * processed by later chunks\n-\t\t\t */\n-\t\t\tint len = same - e->s_lno;\n-\t\t\tstruct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));\n-\t\t\tn->suspect = blame_origin_incref(e->suspect);\n-\t\t\tn->lno = e->lno + len;\n-\t\t\tn->s_lno = e->s_lno + len;\n-\t\t\tn->num_lines = e->num_lines - len;\n-\t\t\te->num_lines = len;\n-\t\t\te->score = 0;\n-\t\t\t/* Push new record to samep */\n-\t\t\tn->next = samep;\n-\t\t\tsamep = n;\n-\t\t}\n-\t\te->next = diffp;\n-\t\tdiffp = e;\n-\t\te = next;\n-\t}\n-\t**srcq = reverse_blame(diffp, reverse_blame(samep, e));\n-\t/* Move across elements that are in the unblamable portion */\n-\tif (diffp)\n-\t\t*srcq = &diffp->next;\n-}\n-\n-struct blame_chunk_cb_data {\n-\tstruct blame_origin *parent;\n-\tlong offset;\n-\tstruct blame_entry **dstq;\n-\tstruct blame_entry **srcq;\n-};\n-\n-/* diff chunks are from parent to target */\n-static int blame_chunk_cb(long start_a, long count_a,\n-\t\t\t  long start_b, long count_b, void *data)\n-{\n-\tstruct blame_chunk_cb_data *d = data;\n-\tif (start_a - start_b != d->offset)\n-\t\tdie(\"internal error in blame::blame_chunk_cb\");\n-\tblame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,\n-\t\t    start_b + count_b, d->parent);\n-\td->offset = start_a + count_a - (start_b + count_b);\n-\treturn 0;\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 void pass_blame_to_parent(struct blame_scoreboard *sb,\n-\t\t\t\t struct blame_origin *target,\n-\t\t\t\t struct blame_origin *parent)\n-{\n-\tmmfile_t file_p, file_o;\n-\tstruct blame_chunk_cb_data d;\n-\tstruct blame_entry *newdest = NULL;\n-\n-\tif (!target->suspects)\n-\t\treturn; /* nothing remains for this target */\n-\n-\td.parent = parent;\n-\td.offset = 0;\n-\td.dstq = &newdest; d.srcq = &target->suspects;\n-\n-\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n-\tfill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob);\n-\tsb->num_get_patch++;\n-\n-\tif (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts))\n-\t\tdie(\"unable to generate diff (%s -> %s)\",\n-\t\t    oid_to_hex(&parent->commit->object.oid),\n-\t\t    oid_to_hex(&target->commit->object.oid));\n-\t/* The rest are the same as the parent */\n-\tblame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);\n-\t*d.dstq = NULL;\n-\tqueue_blames(sb, parent, newdest);\n-\n-\treturn;\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\tblame_origin_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 blame_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 blame_origin *parent;\n-\tstruct blame_entry *split;\n-\tlong plno;\n-\tlong tlno;\n-};\n-\n-static int handle_split_cb(long start_a, long count_a,\n-\t\t\t   long start_b, long count_b, void *data)\n-{\n-\tstruct handle_split_cb_data *d = data;\n-\thandle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,\n-\t\t     d->split);\n-\td->plno = start_a + count_a;\n-\td->tlno = start_b + count_b;\n-\treturn 0;\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 blame_origin *parent,\n-\t\t\t      struct blame_entry *split,\n-\t\t\t      mmfile_t *file_p)\n-{\n-\tconst char *cp;\n-\tmmfile_t file_o;\n-\tstruct handle_split_cb_data d;\n-\n-\tmemset(&d, 0, sizeof(d));\n-\td.sb = sb; d.ent = ent; d.parent = parent; d.split = split;\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-\tfile_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;\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(split, 0, sizeof(struct blame_entry [3]));\n-\tif (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts))\n-\t\tdie(\"unable to generate diff (%s)\",\n-\t\t    oid_to_hex(&parent->commit->object.oid));\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-/* Move all blame entries from list *source that have a score smaller\n- * than score_min to the front of list *small.\n- * Returns a pointer to the link pointing to the old head of the small list.\n- */\n-\n-static struct blame_entry **filter_small(struct blame_scoreboard *sb,\n-\t\t\t\t\t struct blame_entry **small,\n-\t\t\t\t\t struct blame_entry **source,\n-\t\t\t\t\t unsigned score_min)\n-{\n-\tstruct blame_entry *p = *source;\n-\tstruct blame_entry *oldsmall = *small;\n-\twhile (p) {\n-\t\tif (ent_score(sb, p) <= score_min) {\n-\t\t\t*small = p;\n-\t\t\tsmall = &p->next;\n-\t\t\tp = *small;\n-\t\t} else {\n-\t\t\t*source = p;\n-\t\t\tsource = &p->next;\n-\t\t\tp = *source;\n-\t\t}\n-\t}\n-\t*small = oldsmall;\n-\t*source = NULL;\n-\treturn small;\n-}\n-\n-/*\n- * See if lines currently target is suspected for can be attributed to\n- * parent.\n- */\n-static void find_move_in_parent(struct blame_scoreboard *sb,\n-\t\t\t\tstruct blame_entry ***blamed,\n-\t\t\t\tstruct blame_entry **toosmall,\n-\t\t\t\tstruct blame_origin *target,\n-\t\t\t\tstruct blame_origin *parent)\n-{\n-\tstruct blame_entry *e, split[3];\n-\tstruct blame_entry *unblamed = target->suspects;\n-\tstruct blame_entry *leftover = NULL;\n-\tmmfile_t file_p;\n-\n-\tif (!unblamed)\n-\t\treturn; /* nothing remains for this target */\n-\n-\tfill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);\n-\tif (!file_p.ptr)\n-\t\treturn;\n-\n-\t/* At each iteration, unblamed has a NULL-terminated list of\n-\t * entries that have not yet been tested for blame.  leftover\n-\t * contains the reversed list of entries that have been tested\n-\t * without being assignable to the parent.\n-\t */\n-\tdo {\n-\t\tstruct blame_entry **unblamedtail = &unblamed;\n-\t\tstruct blame_entry *next;\n-\t\tfor (e = unblamed; e; e = next) {\n-\t\t\tnext = e->next;\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->move_score < ent_score(sb, &split[1])) {\n-\t\t\t\tsplit_blame(blamed, &unblamedtail, split, e);\n-\t\t\t} else {\n-\t\t\t\te->next = leftover;\n-\t\t\t\tleftover = e;\n-\t\t\t}\n-\t\t\tdecref_split(split);\n-\t\t}\n-\t\t*unblamedtail = NULL;\n-\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->move_score);\n-\t} while (unblamed);\n-\ttarget->suspects = reverse_blame(leftover, NULL);\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_entry *unblamed,\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 = unblamed, num_ents = 0; e; e = e->next)\n-\t\tnum_ents++;\n-\tif (num_ents) {\n-\t\tblame_list = xcalloc(num_ents, sizeof(struct blame_list));\n-\t\tfor (e = unblamed, i = 0; e; e = e->next)\n-\t\t\tblame_list[i++].ent = e;\n-\t}\n-\t*num_ents_p = num_ents;\n-\treturn blame_list;\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 void find_copy_in_parent(struct blame_scoreboard *sb,\n-\t\t\t\tstruct blame_entry ***blamed,\n-\t\t\t\tstruct blame_entry **toosmall,\n-\t\t\t\tstruct blame_origin *target,\n-\t\t\t\tstruct commit *parent,\n-\t\t\t\tstruct blame_origin *porigin,\n-\t\t\t\tint opt)\n-{\n-\tstruct diff_options diff_opts;\n-\tint i, j;\n-\tstruct blame_list *blame_list;\n-\tint num_ents;\n-\tstruct blame_entry *unblamed = target->suspects;\n-\tstruct blame_entry *leftover = NULL;\n-\n-\tif (!unblamed)\n-\t\treturn; /* 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-\tdiff_setup_done(&diff_opts);\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_oid(&target->commit->object.oid))\n-\t\tdo_diff_cache(parent->tree->object.oid.hash, &diff_opts);\n-\telse\n-\t\tdiff_tree_sha1(parent->tree->object.oid.hash,\n-\t\t\t       target->commit->tree->object.oid.hash,\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-\tdo {\n-\t\tstruct blame_entry **unblamedtail = &unblamed;\n-\t\tblame_list = setup_blame_list(unblamed, &num_ents);\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 blame_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(parent, p->one->path);\n-\t\t\toidcpy(&norigin->blob_oid, &p->one->oid);\n-\t\t\tnorigin->mode = p->one->mode;\n-\t\t\tfill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob);\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\tblame_origin_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->copy_score < ent_score(sb, &split[1])) {\n-\t\t\t\tsplit_blame(blamed, &unblamedtail, split,\n-\t\t\t\t\t    blame_list[j].ent);\n-\t\t\t} else {\n-\t\t\t\tblame_list[j].ent->next = leftover;\n-\t\t\t\tleftover = blame_list[j].ent;\n-\t\t\t}\n-\t\t\tdecref_split(split);\n-\t\t}\n-\t\tfree(blame_list);\n-\t\t*unblamedtail = NULL;\n-\t\ttoosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score);\n-\t} while (unblamed);\n-\ttarget->suspects = reverse_blame(leftover, NULL);\n-\tdiff_flush(&diff_opts);\n-\tclear_pathspec(&diff_opts.pathspec);\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 blame_origin *origin, struct blame_origin *porigin)\n-{\n-\tstruct blame_entry *e, *suspects;\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-\tsuspects = origin->suspects;\n-\torigin->suspects = NULL;\n-\tfor (e = suspects; e; e = e->next) {\n-\t\tblame_origin_incref(porigin);\n-\t\tblame_origin_decref(e->suspect);\n-\t\te->suspect = porigin;\n-\t}\n-\tqueue_blames(sb, porigin, suspects);\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 rev_info *revs, struct commit *commit,\n-\t\t\t\t\t   int reverse)\n-{\n-\tif (!reverse) {\n-\t\tif (revs->first_parent_only &&\n-\t\t    commit->parents &&\n-\t\t    commit->parents->next) {\n-\t\t\tfree_commit_list(commit->parents->next);\n-\t\t\tcommit->parents->next = NULL;\n-\t\t}\n-\t\treturn commit->parents;\n-\t}\n-\treturn lookup_decoration(&revs->children, &commit->object);\n-}\n-\n-static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse)\n-{\n-\tstruct commit_list *l = first_scapegoat(revs, commit, reverse);\n-\treturn commit_list_count(l);\n-}\n-\n-/* Distribute collected unsorted blames to the respected sorted lists\n- * in the various origins.\n- */\n-static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed)\n-{\n-\tblamed = llist_mergesort(blamed, get_next_blame, set_next_blame,\n-\t\t\t\t compare_blame_suspect);\n-\twhile (blamed)\n-\t{\n-\t\tstruct blame_origin *porigin = blamed->suspect;\n-\t\tstruct blame_entry *suspects = NULL;\n-\t\tdo {\n-\t\t\tstruct blame_entry *next = blamed->next;\n-\t\t\tblamed->next = suspects;\n-\t\t\tsuspects = blamed;\n-\t\t\tblamed = next;\n-\t\t} while (blamed && blamed->suspect == porigin);\n-\t\tsuspects = reverse_blame(suspects, NULL);\n-\t\tqueue_blames(sb, porigin, suspects);\n-\t}\n-}\n-\n-#define MAXSG 16\n-\n-static void pass_blame(struct blame_scoreboard *sb, struct blame_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 blame_origin *sg_buf[MAXSG];\n-\tstruct blame_origin *porigin, **sg_origin = sg_buf;\n-\tstruct blame_entry *toosmall = NULL;\n-\tstruct blame_entry *blames, **blametail = &blames;\n-\n-\tnum_sg = num_scapegoats(revs, commit, sb->reverse);\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 - sb->no_whole_file_rename; pass++) {\n-\t\tstruct blame_origin *(*find)(struct commit *, struct blame_origin *);\n-\t\tfind = pass ? find_rename : find_origin;\n-\n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\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(p, origin);\n-\t\t\tif (!porigin)\n-\t\t\t\tcontinue;\n-\t\t\tif (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {\n-\t\t\t\tpass_whole_blame(sb, origin, porigin);\n-\t\t\t\tblame_origin_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    !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) {\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\tblame_origin_decref(porigin);\n-\t\t}\n-\t}\n-\n-\tsb->num_commits++;\n-\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n-\t     i < num_sg && sg;\n-\t     sg = sg->next, i++) {\n-\t\tstruct blame_origin *porigin = sg_origin[i];\n-\t\tif (!porigin)\n-\t\t\tcontinue;\n-\t\tif (!origin->previous) {\n-\t\t\tblame_origin_incref(porigin);\n-\t\t\torigin->previous = porigin;\n-\t\t}\n-\t\tpass_blame_to_parent(sb, origin, porigin);\n-\t\tif (!origin->suspects)\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\tfilter_small(sb, &toosmall, &origin->suspects, sb->move_score);\n-\t\tif (origin->suspects) {\n-\t\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n-\t\t\t     i < num_sg && sg;\n-\t\t\t     sg = sg->next, i++) {\n-\t\t\t\tstruct blame_origin *porigin = sg_origin[i];\n-\t\t\t\tif (!porigin)\n-\t\t\t\t\tcontinue;\n-\t\t\t\tfind_move_in_parent(sb, &blametail, &toosmall, origin, porigin);\n-\t\t\t\tif (!origin->suspects)\n-\t\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\t/*\n-\t * Optionally find copies from parents' files.\n-\t */\n-\tif (opt & PICKAXE_BLAME_COPY) {\n-\t\tif (sb->copy_score > sb->move_score)\n-\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->copy_score);\n-\t\telse if (sb->copy_score < sb->move_score) {\n-\t\t\torigin->suspects = blame_merge(origin->suspects, toosmall);\n-\t\t\ttoosmall = NULL;\n-\t\t\tfilter_small(sb, &toosmall, &origin->suspects, sb->copy_score);\n-\t\t}\n-\t\tif (!origin->suspects)\n-\t\t\tgoto finish;\n-\n-\t\tfor (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);\n-\t\t     i < num_sg && sg;\n-\t\t     sg = sg->next, i++) {\n-\t\t\tstruct blame_origin *porigin = sg_origin[i];\n-\t\t\tfind_copy_in_parent(sb, &blametail, &toosmall,\n-\t\t\t\t\t    origin, sg->item, porigin, opt);\n-\t\t\tif (!origin->suspects)\n-\t\t\t\tgoto finish;\n-\t\t}\n-\t}\n-\n-finish:\n-\t*blametail = NULL;\n-\tdistribute_blame(sb, blames);\n-\t/*\n-\t * prepend toosmall to origin->suspects\n-\t *\n-\t * There is no point in sorting: this ends up on a big\n-\t * unsorted list in the caller anyway.\n-\t */\n-\tif (toosmall) {\n-\t\tstruct blame_entry **tail = &toosmall;\n-\t\twhile (*tail)\n-\t\t\ttail = &(*tail)->next;\n-\t\t*tail = origin->suspects;\n-\t\torigin->suspects = toosmall;\n-\t}\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\tblame_origin_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+\treturn blame_nth_line((struct blame_scoreboard *)data, lno);\n }\n \n /*\n@@ -1544,74 +317,6 @@ static void found_guilty_entry(struct blame_entry *ent, void *data)\n \tdisplay_progress(pi->progress, pi->blamed_lines);\n }\n \n-/*\n- * The main loop -- while we have blobs with lines whose true origin\n- * is still unknown, pick one blob, and allow its lines to pass blames\n- * to its parents. */\n-static void assign_blame(struct blame_scoreboard *sb, int opt)\n-{\n-\tstruct rev_info *revs = sb->revs;\n-\tstruct commit *commit = prio_queue_get(&sb->commits);\n-\n-\twhile (commit) {\n-\t\tstruct blame_entry *ent;\n-\t\tstruct blame_origin *suspect = commit->util;\n-\n-\t\t/* find one suspect to break down */\n-\t\twhile (suspect && !suspect->suspects)\n-\t\t\tsuspect = suspect->next;\n-\n-\t\tif (!suspect) {\n-\t\t\tcommit = prio_queue_get(&sb->commits);\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tassert(commit == suspect->commit);\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\tblame_origin_incref(suspect);\n-\t\tparse_commit(commit);\n-\t\tif (sb->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->show_root)\n-\t\t\tcommit->object.flags |= UNINTERESTING;\n-\n-\t\t/* Take responsibility for the remaining entries */\n-\t\tent = suspect->suspects;\n-\t\tif (ent) {\n-\t\t\tsuspect->guilty = 1;\n-\t\t\tfor (;;) {\n-\t\t\t\tstruct blame_entry *next = ent->next;\n-\t\t\t\tif (sb->found_guilty_entry)\n-\t\t\t\t\tsb->found_guilty_entry(ent, sb->found_guilty_entry_data);\n-\t\t\t\tif (next) {\n-\t\t\t\t\tent = next;\n-\t\t\t\t\tcontinue;\n-\t\t\t\t}\n-\t\t\t\tent->next = sb->ent;\n-\t\t\t\tsb->ent = suspect->suspects;\n-\t\t\t\tsuspect->suspects = NULL;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t\tblame_origin_decref(suspect);\n-\n-\t\tif (sb->debug) /* sanity */\n-\t\t\tsanity_check_refcnt(sb);\n-\t}\n-}\n-\n static const char *format_time(unsigned long time, const char *tz_str,\n \t\t\t       int show_raw_time)\n {\n@@ -1675,7 +380,7 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,\n \t       ent->num_lines);\n \temit_porcelain_details(suspect, repeat);\n \n-\tcp = nth_line(sb, ent->lno);\n+\tcp = blame_nth_line(sb, ent->lno);\n \tfor (cnt = 0; cnt < ent->num_lines; cnt++) {\n \t\tchar ch;\n \t\tif (cnt) {\n@@ -1709,7 +414,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int\n \tget_commit_info(suspect->commit, &ci, 1);\n \toid_to_hex_r(hex, &suspect->commit->object.oid);\n \n-\tcp = nth_line(sb, ent->lno);\n+\tcp = blame_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) ? GIT_SHA1_HEXSZ : abbrev;\n@@ -1913,8 +618,8 @@ static void find_alignment(struct blame_scoreboard *sb, int *option)\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\tif (largest_score < blame_entry_score(sb, e))\n+\t\t\tlargest_score = blame_entry_score(sb, e);\n \t}\n \tmax_orig_digits = decimal_width(longest_src_lines);\n \tmax_digits = decimal_width(longest_dst_lines);\n@@ -1925,29 +630,6 @@ static void find_alignment(struct blame_scoreboard *sb, int *option)\n \t\tabbrev = auto_abbrev + 1;\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\toid_to_hex(&ent->suspect->commit->object.oid),\n-\t\t\t\tent->suspect->refcnt);\n-\t\t\tbaa = 1;\n-\t\t}\n-\t}\n-\tif (baa)\n-\t\tsb->on_sanity_fail(sb, baa);\n-}\n-\n static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa)\n {\n \tint opt = OUTPUT_SHOW_SCORE | OUTPUT_SHOW_NUMBER | OUTPUT_SHOW_NAME;\n@@ -2519,7 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \tblame_sort_final(&sb);\n \n-\tcoalesce(&sb);\n+\tblame_coalesce(&sb);\n \n \tif (!(output_option & OUTPUT_PORCELAIN))\n \t\tfind_alignment(&sb, &output_option);\n-- \n2.9.3\n\n"},{"id":"319686","messageId":"20170514031513.9042-16-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 15/22] blame: wrap blame_sort and compare_blame_final","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:06Z","receivedAt":"2017-05-14T03:16:58Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"The new method's interface is marginally cleaner than blame_sort, and\nwill avoid the need to expose the compare_blame_final method.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin/blame.c | 17 +++++++++--------\n 1 file changed, 9 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex a40f743..94c9ded 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -249,12 +249,6 @@ static int compare_blame_suspect(const void *p1, const void *p2)\n \treturn s1->s_lno > s2->s_lno ? 1 : -1;\n }\n \n-static struct blame_entry *blame_sort(struct blame_entry *head,\n-\t\t\t\t      int (*compare_fn)(const void *, const void *))\n-{\n-\treturn llist_mergesort (head, get_next_blame, set_next_blame, compare_fn);\n-}\n-\n static int compare_commits_by_reverse_commit_date(const void *a,\n \t\t\t\t\t\t  const void *b,\n \t\t\t\t\t\t  void *c)\n@@ -262,6 +256,12 @@ static int compare_commits_by_reverse_commit_date(const void *a,\n \treturn -compare_commits_by_commit_date(a, b, c);\n }\n \n+static void blame_sort_final(struct blame_scoreboard *sb)\n+{\n+\tsb->ent = llist_mergesort(sb->ent, get_next_blame, set_next_blame,\n+\t\t\t\t  compare_blame_final);\n+}\n+\n static void sanity_check_refcnt(struct blame_scoreboard *);\n \n /*\n@@ -1244,7 +1244,8 @@ static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reve\n  */\n static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed)\n {\n-\tblamed = blame_sort(blamed, compare_blame_suspect);\n+\tblamed = llist_mergesort(blamed, get_next_blame, set_next_blame,\n+\t\t\t\t compare_blame_suspect);\n \twhile (blamed)\n \t{\n \t\tstruct blame_origin *porigin = blamed->suspect;\n@@ -2788,7 +2789,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (incremental)\n \t\treturn 0;\n \n-\tsb.ent = blame_sort(sb.ent, compare_blame_final);\n+\tblame_sort_final(&sb);\n \n \tcoalesce(&sb);\n \n-- \n2.9.3\n\n"},{"id":"319687","messageId":"20170514031513.9042-21-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 20/22] blame: create scoreboard init function in libgit","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:11Z","receivedAt":"2017-05-14T03:17:00Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.c         | 7 +++++++\n blame.h         | 2 ++\n builtin/blame.c | 4 +---\n 3 files changed, 10 insertions(+), 3 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 798e61b..17ebf64 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -1574,3 +1574,10 @@ void assign_blame(struct blame_scoreboard *sb, int opt)\n \t\t\tsanity_check_refcnt(sb);\n \t}\n }\n+\n+void init_scoreboard(struct blame_scoreboard *sb)\n+{\n+\tmemset(sb, 0, sizeof(struct blame_scoreboard));\n+\tsb->move_score = BLAME_DEFAULT_MOVE_SCORE;\n+\tsb->copy_score = BLAME_DEFAULT_COPY_SCORE;\n+}\ndiff --git a/blame.h b/blame.h\nindex a3ea677..9477b6e 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -171,4 +171,6 @@ extern unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entr\n extern void assign_blame(struct blame_scoreboard *sb, int opt);\n extern const char *blame_nth_line(struct blame_scoreboard *sb, long lno);\n \n+extern void init_scoreboard(struct blame_scoreboard *sb);\n+\n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 207a74d..e01265a 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1026,10 +1026,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \trevs.disable_stdin = 1;\n \tsetup_revisions(argc, argv, &revs, NULL);\n-\tmemset(&sb, 0, sizeof(sb));\n-\tsb.move_score = BLAME_DEFAULT_MOVE_SCORE;\n-\tsb.copy_score = BLAME_DEFAULT_COPY_SCORE;\n \n+\tinit_scoreboard(&sb);\n \tsb.revs = &revs;\n \tsb.contents_from = contents_from;\n \tsb.reverse = reverse;\n-- \n2.9.3\n\n"},{"id":"319688","messageId":"20170514031513.9042-23-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 22/22] blame: create entry prepend function in libgit","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:13Z","receivedAt":"2017-05-14T03:17:02Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.c         | 16 ++++++++++++++++\n blame.h         |  2 ++\n builtin/blame.c | 11 +----------\n 3 files changed, 19 insertions(+), 10 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex f6c9cb7..00404b9 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -1845,3 +1845,19 @@ void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blam\n \tif (orig)\n \t\t*orig = o;\n }\n+\n+\n+\n+struct blame_entry *blame_entry_prepend(struct blame_entry *head,\n+\t\t\t\t\tlong start, long end,\n+\t\t\t\t\tstruct blame_origin *o)\n+{\n+\tstruct blame_entry *new_head = xcalloc(1, sizeof(struct blame_entry));\n+\tnew_head->lno = start;\n+\tnew_head->num_lines = end - start;\n+\tnew_head->suspect = o;\n+\tnew_head->s_lno = start;\n+\tnew_head->next = head;\n+\tblame_origin_incref(o);\n+\treturn new_head;\n+}\ndiff --git a/blame.h b/blame.h\nindex 76fd8ef..a6c915c 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -170,4 +170,6 @@ extern const char *blame_nth_line(struct blame_scoreboard *sb, long lno);\n extern void init_scoreboard(struct blame_scoreboard *sb);\n extern void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig);\n \n+extern struct blame_entry *blame_entry_prepend(struct blame_entry *head, long start, long end, struct blame_origin *o);\n+\n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 7ec5a73..8a858b0 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -890,16 +890,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \tfor (range_i = ranges.nr; range_i > 0; --range_i) {\n \t\tconst struct range *r = &ranges.ranges[range_i - 1];\n-\t\tlong bottom = r->start;\n-\t\tlong top = r->end;\n-\t\tstruct blame_entry *next = ent;\n-\t\tent = xcalloc(1, sizeof(*ent));\n-\t\tent->lno = bottom;\n-\t\tent->num_lines = top - bottom;\n-\t\tent->suspect = o;\n-\t\tent->s_lno = bottom;\n-\t\tent->next = next;\n-\t\tblame_origin_incref(o);\n+\t\tent = blame_entry_prepend(ent, r->start, r->end, o);\n \t}\n \n \to->suspects = ent;\n-- \n2.9.3\n\n"},{"id":"319689","messageId":"20170514031513.9042-14-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 13/22] blame: make sanity_check use a callback in scoreboard","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:04Z","receivedAt":"2017-05-14T03:17:04Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Allow the interface user to decide how to handle a failed sanity check,\nwhether that be to output with the current state or to do nothing.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         |  4 ++++\n builtin/blame.c | 23 +++++++++++++++--------\n 2 files changed, 19 insertions(+), 8 deletions(-)\n\ndiff --git a/blame.h b/blame.h\nindex c140f41..e85db06 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -134,6 +134,10 @@ struct blame_scoreboard {\n \tint show_root;\n \tint xdl_opts;\n \tint no_whole_file_rename;\n+\tint debug;\n+\n+\t/* callbacks */\n+\tvoid(*on_sanity_fail)(struct blame_scoreboard *, int);\n };\n \n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex bd295eb..d7b3b5a 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -285,7 +285,7 @@ static void coalesce(struct blame_scoreboard *sb)\n \t\t}\n \t}\n \n-\tif (DEBUG) /* sanity */\n+\tif (sb->debug) /* sanity */\n \t\tsanity_check_refcnt(sb);\n }\n \n@@ -1682,7 +1682,7 @@ static void assign_blame(struct blame_scoreboard *sb, int opt)\n \t\t}\n \t\torigin_decref(suspect);\n \n-\t\tif (DEBUG) /* sanity */\n+\t\tif (sb->debug) /* sanity */\n \t\t\tsanity_check_refcnt(sb);\n \t}\n \n@@ -2021,12 +2021,16 @@ static void sanity_check_refcnt(struct blame_scoreboard *sb)\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+\tif (baa)\n+\t\tsb->on_sanity_fail(sb, baa);\n+}\n+\n+static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa)\n+{\n+\tint opt = OUTPUT_SHOW_SCORE | OUTPUT_SHOW_NUMBER | OUTPUT_SHOW_NAME;\n+\tfind_alignment(sb, &opt);\n+\toutput(sb, opt);\n+\tdie(\"Baa %d!\", baa);\n }\n \n static unsigned parse_score(const char *arg)\n@@ -2761,6 +2765,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (blame_copy_score)\n \t\tsb.copy_score = blame_copy_score;\n \n+\tsb.debug = DEBUG;\n+\tsb.on_sanity_fail = &sanity_check_on_fail;\n+\n \tsb.show_root = show_root;\n \tsb.xdl_opts = xdl_opts;\n \tsb.no_whole_file_rename = no_whole_file_rename;\n-- \n2.9.3\n\n"},{"id":"319690","messageId":"20170514031513.9042-18-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 17/22] blame: move origin-related methods to libgit","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:08Z","receivedAt":"2017-05-14T03:17:13Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n Makefile        |   1 +\n blame.c         |  62 +++++++++++++++++++++++++++++\n blame.h         |  15 +++++++\n builtin/blame.c | 120 ++++++++++++--------------------------------------------\n 4 files changed, 102 insertions(+), 96 deletions(-)\n create mode 100644 blame.c\n\ndiff --git a/Makefile b/Makefile\nindex e35542e..2d795ed 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -718,6 +718,7 @@ LIB_OBJS += argv-array.o\n LIB_OBJS += attr.o\n LIB_OBJS += base85.o\n LIB_OBJS += bisect.o\n+LIB_OBJS += blame.o\n LIB_OBJS += blob.o\n LIB_OBJS += branch.o\n LIB_OBJS += bulk-checkin.o\ndiff --git a/blame.c b/blame.c\nnew file mode 100644\nindex 0000000..4855d6d\n--- /dev/null\n+++ b/blame.c\n@@ -0,0 +1,62 @@\n+#include \"blame.h\"\n+\n+void blame_origin_decref(struct blame_origin *o)\n+{\n+\tif (o && --o->refcnt <= 0) {\n+\t\tstruct blame_origin *p, *l = NULL;\n+\t\tif (o->previous)\n+\t\t\tblame_origin_decref(o->previous);\n+\t\tfree(o->file.ptr);\n+\t\t/* Should be present exactly once in commit chain */\n+\t\tfor (p = o->commit->util; p; l = p, p = p->next) {\n+\t\t\tif (p == o) {\n+\t\t\t\tif (l)\n+\t\t\t\t\tl->next = p->next;\n+\t\t\t\telse\n+\t\t\t\t\to->commit->util = p->next;\n+\t\t\t\tfree(o);\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t}\n+\t\tdie(\"internal error in blame_origin_decref\");\n+\t}\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 blame_origin *make_origin(struct commit *commit, const char *path)\n+{\n+\tstruct blame_origin *o;\n+\tFLEX_ALLOC_STR(o, path, path);\n+\to->commit = commit;\n+\to->refcnt = 1;\n+\to->next = commit->util;\n+\tcommit->util = o;\n+\treturn o;\n+}\n+\n+/*\n+ * Locate an existing origin or create a new one.\n+ * This moves the origin to front position in the commit util list.\n+ */\n+struct blame_origin *get_origin(struct commit *commit, const char *path)\n+{\n+\tstruct blame_origin *o, *l;\n+\n+\tfor (o = commit->util, l = NULL; o; l = o, o = o->next) {\n+\t\tif (!strcmp(o->path, path)) {\n+\t\t\t/* bump to front */\n+\t\t\tif (l) {\n+\t\t\t\tl->next = o->next;\n+\t\t\t\to->next = commit->util;\n+\t\t\t\tcommit->util = o;\n+\t\t\t}\n+\t\t\treturn blame_origin_incref(o);\n+\t\t}\n+\t}\n+\treturn make_origin(commit, path);\n+}\ndiff --git a/blame.h b/blame.h\nindex a0bd91b..39f8865 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -143,4 +143,19 @@ struct blame_scoreboard {\n \tvoid *found_guilty_entry_data;\n };\n \n+/*\n+ * Origin is refcounted and usually we keep the blob contents to be\n+ * reused.\n+ */\n+static inline struct blame_origin *blame_origin_incref(struct blame_origin *o)\n+{\n+\tif (o)\n+\t\to->refcnt++;\n+\treturn o;\n+}\n+extern void blame_origin_decref(struct blame_origin *o);\n+\n+extern struct blame_origin *make_origin(struct commit *commit, const char *path);\n+extern struct blame_origin *get_origin(struct commit *commit, const char *path);\n+\n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 3d85f62..b60d604 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -122,39 +122,6 @@ static void fill_origin_blob(struct diff_options *opt,\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 blame_origin *origin_incref(struct blame_origin *o)\n-{\n-\tif (o)\n-\t\to->refcnt++;\n-\treturn o;\n-}\n-\n-static void origin_decref(struct blame_origin *o)\n-{\n-\tif (o && --o->refcnt <= 0) {\n-\t\tstruct blame_origin *p, *l = NULL;\n-\t\tif (o->previous)\n-\t\t\torigin_decref(o->previous);\n-\t\tfree(o->file.ptr);\n-\t\t/* Should be present exactly once in commit chain */\n-\t\tfor (p = o->commit->util; p; l = p, p = p->next) {\n-\t\t\tif (p == o) {\n-\t\t\t\tif (l)\n-\t\t\t\t\tl->next = p->next;\n-\t\t\t\telse\n-\t\t\t\t\to->commit->util = p->next;\n-\t\t\t\tfree(o);\n-\t\t\t\treturn;\n-\t\t\t}\n-\t\t}\n-\t\tdie(\"internal error in blame::origin_decref\");\n-\t}\n-}\n-\n static void drop_origin_blob(struct blame_origin *o)\n {\n \tif (o->file.ptr) {\n@@ -278,7 +245,7 @@ static void coalesce(struct blame_scoreboard *sb)\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\torigin_decref(next->suspect);\n+\t\t\tblame_origin_decref(next->suspect);\n \t\t\tfree(next);\n \t\t\tent->score = 0;\n \t\t\tnext = ent; /* again */\n@@ -314,45 +281,6 @@ static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porig\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 blame_origin *make_origin(struct commit *commit, const char *path)\n-{\n-\tstruct blame_origin *o;\n-\tFLEX_ALLOC_STR(o, path, path);\n-\to->commit = commit;\n-\to->refcnt = 1;\n-\to->next = commit->util;\n-\tcommit->util = o;\n-\treturn o;\n-}\n-\n-/*\n- * Locate an existing origin or create a new one.\n- * This moves the origin to front position in the commit util list.\n- */\n-static struct blame_origin *get_origin(struct commit *commit, const char *path)\n-{\n-\tstruct blame_origin *o, *l;\n-\n-\tfor (o = commit->util, l = NULL; o; l = o, o = o->next) {\n-\t\tif (!strcmp(o->path, path)) {\n-\t\t\t/* bump to front */\n-\t\t\tif (l) {\n-\t\t\t\tl->next = o->next;\n-\t\t\t\to->next = commit->util;\n-\t\t\t\tcommit->util = o;\n-\t\t\t}\n-\t\t\treturn origin_incref(o);\n-\t\t}\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@@ -396,7 +324,7 @@ static struct blame_origin *find_origin(struct commit *parent,\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\treturn origin_incref (porigin);\n+\t\t\treturn blame_origin_incref (porigin);\n \t\t}\n \n \t/* See if the origin->path is different between parent\n@@ -515,7 +443,7 @@ static void add_blame_entry(struct blame_entry ***queue,\n {\n \tstruct blame_entry *e = xmalloc(sizeof(*e));\n \tmemcpy(e, src, sizeof(*e));\n-\torigin_incref(e->suspect);\n+\tblame_origin_incref(e->suspect);\n \n \te->next = **queue;\n \t**queue = e;\n@@ -530,8 +458,8 @@ static void add_blame_entry(struct blame_entry ***queue,\n static void dup_entry(struct blame_entry ***queue,\n \t\t      struct blame_entry *dst, struct blame_entry *src)\n {\n-\torigin_incref(src->suspect);\n-\torigin_decref(dst->suspect);\n+\tblame_origin_incref(src->suspect);\n+\tblame_origin_decref(dst->suspect);\n \tmemcpy(dst, src, sizeof(*src));\n \tdst->next = **queue;\n \t**queue = dst;\n@@ -572,7 +500,7 @@ static void split_overlap(struct blame_entry *split,\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].suspect = blame_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@@ -586,7 +514,7 @@ static void split_overlap(struct blame_entry *split,\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].suspect = blame_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@@ -602,7 +530,7 @@ static void split_overlap(struct blame_entry *split,\n \t */\n \tif (split[1].num_lines < 1)\n \t\treturn;\n-\tsplit[1].suspect = origin_incref(parent);\n+\tsplit[1].suspect = blame_origin_incref(parent);\n }\n \n /*\n@@ -652,7 +580,7 @@ static void decref_split(struct blame_entry *split)\n \tint i;\n \n \tfor (i = 0; i < 3; i++)\n-\t\torigin_decref(split[i].suspect);\n+\t\tblame_origin_decref(split[i].suspect);\n }\n \n /*\n@@ -715,10 +643,10 @@ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,\n \t\t\tn->next = diffp;\n \t\t\tdiffp = n;\n \t\t} else\n-\t\t\torigin_decref(e->suspect);\n+\t\t\tblame_origin_decref(e->suspect);\n \t\t/* Pass blame for everything before the differing\n \t\t * chunk to the parent */\n-\t\te->suspect = origin_incref(parent);\n+\t\te->suspect = blame_origin_incref(parent);\n \t\te->s_lno += offset;\n \t\te->next = samep;\n \t\tsamep = e;\n@@ -759,7 +687,7 @@ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,\n \t\t\t */\n \t\t\tint len = same - e->s_lno;\n \t\t\tstruct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));\n-\t\t\tn->suspect = origin_incref(e->suspect);\n+\t\t\tn->suspect = blame_origin_incref(e->suspect);\n \t\t\tn->lno = e->lno + len;\n \t\t\tn->s_lno = e->s_lno + len;\n \t\t\tn->num_lines = e->num_lines - len;\n@@ -885,7 +813,7 @@ static void copy_split_if_better(struct blame_scoreboard *sb,\n \t}\n \n \tfor (i = 0; i < 3; i++)\n-\t\torigin_incref(this[i].suspect);\n+\t\tblame_origin_incref(this[i].suspect);\n \tdecref_split(best_so_far);\n \tmemcpy(best_so_far, this, sizeof(struct blame_entry [3]));\n }\n@@ -1165,7 +1093,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb,\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\tblame_origin_decref(norigin);\n \t\t}\n \n \t\tfor (j = 0; j < num_ents; j++) {\n@@ -1206,8 +1134,8 @@ static void pass_whole_blame(struct blame_scoreboard *sb,\n \tsuspects = origin->suspects;\n \torigin->suspects = NULL;\n \tfor (e = suspects; e; e = e->next) {\n-\t\torigin_incref(porigin);\n-\t\torigin_decref(e->suspect);\n+\t\tblame_origin_incref(porigin);\n+\t\tblame_origin_decref(e->suspect);\n \t\te->suspect = porigin;\n \t}\n \tqueue_blames(sb, porigin, suspects);\n@@ -1305,7 +1233,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\n \t\t\t\tcontinue;\n \t\t\tif (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {\n \t\t\t\tpass_whole_blame(sb, origin, porigin);\n-\t\t\t\torigin_decref(porigin);\n+\t\t\t\tblame_origin_decref(porigin);\n \t\t\t\tgoto finish;\n \t\t\t}\n \t\t\tfor (j = same = 0; j < i; j++)\n@@ -1317,7 +1245,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\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\t\tblame_origin_decref(porigin);\n \t\t}\n \t}\n \n@@ -1329,7 +1257,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\n \t\tif (!porigin)\n \t\t\tcontinue;\n \t\tif (!origin->previous) {\n-\t\t\torigin_incref(porigin);\n+\t\t\tblame_origin_incref(porigin);\n \t\t\torigin->previous = porigin;\n \t\t}\n \t\tpass_blame_to_parent(sb, origin, porigin);\n@@ -1400,7 +1328,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\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\tblame_origin_decref(sg_origin[i]);\n \t\t}\n \t}\n \tdrop_origin_blob(origin);\n@@ -1645,7 +1573,7 @@ static void assign_blame(struct blame_scoreboard *sb, int opt)\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\tblame_origin_incref(suspect);\n \t\tparse_commit(commit);\n \t\tif (sb->reverse ||\n \t\t    (!(commit->object.flags & UNINTERESTING) &&\n@@ -1678,7 +1606,7 @@ static void assign_blame(struct blame_scoreboard *sb, int opt)\n \t\t\t\tbreak;\n \t\t\t}\n \t\t}\n-\t\torigin_decref(suspect);\n+\t\tblame_origin_decref(suspect);\n \n \t\tif (sb->debug) /* sanity */\n \t\t\tsanity_check_refcnt(sb);\n@@ -2743,13 +2671,13 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tent->suspect = o;\n \t\tent->s_lno = bottom;\n \t\tent->next = next;\n-\t\torigin_incref(o);\n+\t\tblame_origin_incref(o);\n \t}\n \n \to->suspects = ent;\n \tprio_queue_put(&sb.commits, o->commit);\n \n-\torigin_decref(o);\n+\tblame_origin_decref(o);\n \n \trange_set_release(&ranges);\n \tstring_list_clear(&range_list, 0);\n-- \n2.9.3\n\n"},{"id":"319691","messageId":"20170514031513.9042-17-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 16/22] blame: rework methods that determine 'final' commit","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:07Z","receivedAt":"2017-05-14T03:17:15Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Either prepare_initial or prepare_final is used to determine which\ncommit is marked as 'final'. Call the underlying methods directly to\nmake this more clear.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n builtin/blame.c | 49 +++++++++++++++++++++++--------------------------\n 1 file changed, 23 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 94c9ded..3d85f62 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -2297,14 +2297,8 @@ static struct commit *find_single_final(struct rev_info *revs,\n \treturn found;\n }\n \n-static char *prepare_final(struct blame_scoreboard *sb)\n-{\n-\tconst char *name;\n-\tsb->final = find_single_final(sb->revs, &name);\n-\treturn xstrdup_or_null(name);\n-}\n-\n-static const char *dwim_reverse_initial(struct blame_scoreboard *sb)\n+static struct commit *dwim_reverse_initial(struct rev_info *revs,\n+\t\t\t\t\t   const char **name_p)\n {\n \t/*\n \t * DWIM \"git blame --reverse ONE -- PATH\" as\n@@ -2315,11 +2309,11 @@ static const char *dwim_reverse_initial(struct blame_scoreboard *sb)\n \tstruct commit *head_commit;\n \tunsigned char head_sha1[20];\n \n-\tif (sb->revs->pending.nr != 1)\n+\tif (revs->pending.nr != 1)\n \t\treturn NULL;\n \n \t/* Is that sole rev a committish? */\n-\tobj = sb->revs->pending.objects[0].item;\n+\tobj = revs->pending.objects[0].item;\n \tobj = deref_tag(obj, NULL, 0);\n \tif (obj->type != OBJ_COMMIT)\n \t\treturn NULL;\n@@ -2333,17 +2327,19 @@ static const char *dwim_reverse_initial(struct blame_scoreboard *sb)\n \n \t/* Turn \"ONE\" into \"ONE..HEAD\" then */\n \tobj->flags |= UNINTERESTING;\n-\tadd_pending_object(sb->revs, &head_commit->object, \"HEAD\");\n+\tadd_pending_object(revs, &head_commit->object, \"HEAD\");\n \n-\tsb->final = (struct commit *)obj;\n-\treturn sb->revs->pending.objects[0].name;\n+\tif (name_p)\n+\t\t*name_p = revs->pending.objects[0].name;\n+\treturn (struct commit *)obj;\n }\n \n-static char *prepare_initial(struct blame_scoreboard *sb)\n+static struct commit *find_single_initial(struct rev_info *revs,\n+\t\t\t\t\t  const char **name_p)\n {\n \tint i;\n \tconst char *final_commit_name = NULL;\n-\tstruct rev_info *revs = sb->revs;\n+\tstruct commit *found = NULL;\n \n \t/*\n \t * There must be one and only one negative commit, and it must be\n@@ -2356,19 +2352,22 @@ static char *prepare_initial(struct blame_scoreboard *sb)\n \t\tobj = deref_tag(obj, NULL, 0);\n \t\tif (obj->type != OBJ_COMMIT)\n \t\t\tdie(\"Non commit %s?\", revs->pending.objects[i].name);\n-\t\tif (sb->final)\n+\t\tif (found)\n \t\t\tdie(\"More than one commit to dig up from, %s and %s?\",\n \t\t\t    revs->pending.objects[i].name,\n \t\t\t    final_commit_name);\n-\t\tsb->final = (struct commit *) obj;\n+\t\tfound = (struct commit *) obj;\n \t\tfinal_commit_name = revs->pending.objects[i].name;\n \t}\n \n \tif (!final_commit_name)\n-\t\tfinal_commit_name = dwim_reverse_initial(sb);\n+\t\tfound = dwim_reverse_initial(revs, &final_commit_name);\n \tif (!final_commit_name)\n \t\tdie(\"No commit to dig up from?\");\n-\treturn xstrdup(final_commit_name);\n+\n+\tif (name_p)\n+\t\t*name_p = final_commit_name;\n+\treturn found;\n }\n \n static int blame_copy_callback(const struct option *option, const char *arg, int unset)\n@@ -2412,7 +2411,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tstruct blame_origin *o;\n \tstruct blame_entry *ent = NULL;\n \tlong dashdash_pos, lno;\n-\tchar *final_commit_name = NULL;\n+\tconst char *final_commit_name = NULL;\n \tenum object_type type;\n \tstruct commit *final_commit = NULL;\n \tstruct progress_info pi = { NULL, 0 };\n@@ -2621,14 +2620,15 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tsb.revs = &revs;\n \tsb.contents_from = contents_from;\n \tsb.reverse = reverse;\n+\n \tif (!reverse) {\n-\t\tfinal_commit_name = prepare_final(&sb);\n+\t\tsb.final = find_single_final(&revs, &final_commit_name);\n \t\tsb.commits.compare = compare_commits_by_commit_date;\n \t}\n \telse if (contents_from)\n \t\tdie(_(\"--contents and --reverse do not blend well.\"));\n \telse {\n-\t\tfinal_commit_name = prepare_initial(&sb);\n+\t\tsb.final = find_single_initial(&revs, &final_commit_name);\n \t\tsb.commits.compare = compare_commits_by_reverse_commit_date;\n \t\tif (revs.first_parent_only)\n \t\t\trevs.children.name = NULL;\n@@ -2783,10 +2783,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \tif (!incremental)\n \t\tsetup_pager();\n-\n-\tfree(final_commit_name);\n-\n-\tif (incremental)\n+\telse\n \t\treturn 0;\n \n \tblame_sort_final(&sb);\n-- \n2.9.3\n\n"},{"id":"319692","messageId":"20170514031513.9042-15-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 14/22] blame: move progess updates to a scoreboard callback","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:05Z","receivedAt":"2017-05-14T03:17:19Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Allow the interface user to decide how to handle a progress update.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         |  3 +++\n builtin/blame.c | 24 ++++++++++++++----------\n 2 files changed, 17 insertions(+), 10 deletions(-)\n\ndiff --git a/blame.h b/blame.h\nindex e85db06..a0bd91b 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -138,6 +138,9 @@ struct blame_scoreboard {\n \n \t/* callbacks */\n \tvoid(*on_sanity_fail)(struct blame_scoreboard *, int);\n+\tvoid(*found_guilty_entry)(struct blame_entry *, void *);\n+\n+\tvoid *found_guilty_entry_data;\n };\n \n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex d7b3b5a..a40f743 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1598,9 +1598,10 @@ static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat)\n  * The blame_entry is found to be guilty for the range.\n  * Show it in incremental output.\n  */\n-static void found_guilty_entry(struct blame_entry *ent,\n-\t\t\t   struct progress_info *pi)\n+static void found_guilty_entry(struct blame_entry *ent, void *data)\n {\n+\tstruct progress_info *pi = (struct progress_info *)data;\n+\n \tif (incremental) {\n \t\tstruct blame_origin *suspect = ent->suspect;\n \n@@ -1623,11 +1624,6 @@ static void assign_blame(struct blame_scoreboard *sb, int opt)\n {\n \tstruct rev_info *revs = sb->revs;\n \tstruct commit *commit = prio_queue_get(&sb->commits);\n-\tstruct progress_info pi = { NULL, 0 };\n-\n-\tif (show_progress)\n-\t\tpi.progress = start_progress_delay(_(\"Blaming lines\"),\n-\t\t\t\t\t\t   sb->num_lines, 50, 1);\n \n \twhile (commit) {\n \t\tstruct blame_entry *ent;\n@@ -1669,7 +1665,8 @@ static void assign_blame(struct blame_scoreboard *sb, int opt)\n \t\t\tsuspect->guilty = 1;\n \t\t\tfor (;;) {\n \t\t\t\tstruct blame_entry *next = ent->next;\n-\t\t\t\tfound_guilty_entry(ent, &pi);\n+\t\t\t\tif (sb->found_guilty_entry)\n+\t\t\t\t\tsb->found_guilty_entry(ent, sb->found_guilty_entry_data);\n \t\t\t\tif (next) {\n \t\t\t\t\tent = next;\n \t\t\t\t\tcontinue;\n@@ -1685,8 +1682,6 @@ static void assign_blame(struct blame_scoreboard *sb, int opt)\n \t\tif (sb->debug) /* sanity */\n \t\t\tsanity_check_refcnt(sb);\n \t}\n-\n-\tstop_progress(&pi.progress);\n }\n \n static const char *format_time(unsigned long time, const char *tz_str,\n@@ -2419,6 +2414,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tchar *final_commit_name = NULL;\n \tenum object_type type;\n \tstruct commit *final_commit = NULL;\n+\tstruct progress_info pi = { NULL, 0 };\n \n \tstruct string_list range_list = STRING_LIST_INIT_NODUP;\n \tint output_option = 0, opt = 0;\n@@ -2774,8 +2770,16 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \tread_mailmap(&mailmap, NULL);\n \n+\tsb.found_guilty_entry = &found_guilty_entry;\n+\tsb.found_guilty_entry_data = &pi;\n+\tif (show_progress)\n+\t\tpi.progress = start_progress_delay(_(\"Blaming lines\"),\n+\t\t\t\t\t\t   sb.num_lines, 50, 1);\n+\n \tassign_blame(&sb, opt);\n \n+\tstop_progress(&pi.progress);\n+\n \tif (!incremental)\n \t\tsetup_pager();\n \n-- \n2.9.3\n\n"},{"id":"319693","messageId":"20170514031513.9042-13-whydoubt@gmail.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"[RFC PATCH v2 12/22] blame: move no_whole_file_rename flag to scoreboard","fromName":"Jeff Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-14T03:15:03Z","receivedAt":"2017-05-14T03:17:21Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"The no_whole_file_rename flag is used in parts of blame that are being\nmoved to libgit, and should be accessible via the scoreboard structure.\n\nSigned-off-by: Jeff Smith <whydoubt@gmail.com>\n---\n blame.h         | 1 +\n builtin/blame.c | 3 ++-\n 2 files changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/blame.h b/blame.h\nindex 42a948d..c140f41 100644\n--- a/blame.h\n+++ b/blame.h\n@@ -133,6 +133,7 @@ struct blame_scoreboard {\n \tint reverse;\n \tint show_root;\n \tint xdl_opts;\n+\tint no_whole_file_rename;\n };\n \n #endif /* BLAME_H */\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex d3af02b..bd295eb 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1285,7 +1285,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,\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 - no_whole_file_rename; pass++) {\n+\tfor (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) {\n \t\tstruct blame_origin *(*find)(struct commit *, struct blame_origin *);\n \t\tfind = pass ? find_rename : find_origin;\n \n@@ -2763,6 +2763,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \tsb.show_root = show_root;\n \tsb.xdl_opts = xdl_opts;\n+\tsb.no_whole_file_rename = no_whole_file_rename;\n \n \tread_mailmap(&mailmap, NULL);\n \n-- \n2.9.3\n\n"},{"id":"319723","messageId":"xmqq8tlz504w.fsf@gitster.mtv.corp.google.com","threadId":"45873","inReplyTo":"20170514031513.9042-5-whydoubt@gmail.com","subject":"Re: [RFC PATCH v2 04/22] blame: move origin and entry structures to header","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-14T08:10:55Z","receivedAt":"2017-05-14T08:11:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Smith <whydoubt@gmail.com> writes:\n\n> The origin and blame_entry structures are core to the blame interface\n> and reference each other. Since origin will be more exposed, rename it\n> to blame_origin to clarify what it is a part of.\n>\n> Signed-off-by: Jeff Smith <whydoubt@gmail.com>\n> ---\n>  blame.h         |  86 ++++++++++++++++++++++++++\n>  builtin/blame.c | 185 +++++++++++++++++---------------------------------------\n>  2 files changed, 140 insertions(+), 131 deletions(-)\n>  create mode 100644 blame.h\n>\n> diff --git a/blame.h b/blame.h\n> new file mode 100644\n> index 0000000..f52d0fc\n> --- /dev/null\n> +++ b/blame.h\n> @@ -0,0 +1,86 @@\n> +#ifndef BLAME_H\n> +#define BLAME_H\n> +\n> +#include \"cache.h\"\n> +#include \"commit.h\"\n> +#include \"xdiff-interface.h\"\n> +\n> +/*\n> + * One blob in a commit that is being suspected\n> + */\n> +struct blame_origin {\n> +\tint refcnt;\n> +...\n>   ...\n> -/*\n> - * One blob in a commit that is being suspected\n> - */\n> -struct origin {\n> -\tint refcnt;\n\nI hate to say this AFTER you sent your second attempt, but could you\nhave another preparetory step before this patch, that does many\nrenames of symbols (e.g. s/origin/blame_origin/) and nothing else,\nwhile the lines are still in builtin/blame.c?\n\nTo review a step like yoru 04/22 to make sure nothing else other\nthan moving the lines is going on, an easy way to do so is to run\n\"blame -C\" on the resulting blame.h file---that lets the reviewers'\neyes coast over the lines that came from contiguous region of the\noriginal builtin/blame.c and concentrate on things like what used to\nbe static to the file now getting extern.\n\nIf you rename symbols while moving lines across files, that becomes\nimpossible because many lines are now different (i.e. a line that\ntalked about \"origin\" in builtin/blame.c in the preimage may now\ntalk about \"blame_origin\" in blame.h, hence attributed to the\nnew commit's blame.h).\n\nIt may even be OK to do the \"rename the symbols\" step in a single\npatch (e.g. your 04/22 and 05/22 are separate patches and the former\nrenames \"origin\" while the latter does \"scoreboard\"---I am saying\nthat the preparatory step that renames the symbols without\nintroducing the new \"blame.h\" may not have to have these two as\nseparate patches).\n\nIf rename of symbols are done first while the lines are still in the\noriginal file, mechanical moving of lines gets much easier to\nreview.  Of course, a patch that only renames symbols is also much\neasier to review, even if it is a large one.\n\nThanks.\n\n\n\n\n"},{"id":"319767","messageId":"xmqq7f1iv5fn.fsf@gitster.mtv.corp.google.com","threadId":"45873","inReplyTo":"20170514031513.9042-1-whydoubt@gmail.com","subject":"Re: [RFC PATCH v2 00/22] Add blame to libgit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-15T09:24:12Z","receivedAt":"2017-05-15T09:24:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Smith <whydoubt@gmail.com> writes:\n\n> Rather than duplicate large portions of builtin/blame.c in cgit, it\n> would be better to shift its core functionality into libgit.a.  The\n> functionality left in builtin/blame.c mostly relates to terminal\n> presentation.\n\nAs I said in my review of 04/22, it is a bit hard/tedious to sift\nthe changes to refactoring that actually changes code and pure\nrenaming and movement of lines across files with the current\nsequence of the series, so it is very possible that I may have\nmissed something, but from a cursory read through the series, from\nthe comparison between master and the result of applying all\npatches, and inspection of the resulting builtin/blame.c file, I\nthink what this series does is very sensible in general.  blame.h\ndoes not seem to expose anything that is not needed, which is a good\nsign.\n\n"},{"id":"319784","messageId":"CAPX7N=4jidx5pr3BfFoTL27gbHDT6sdm1q2Z2YxfjsBuN01XaQ@mail.gmail.com","threadId":"45873","inReplyTo":"xmqq7f1iv5fn.fsf@gitster.mtv.corp.google.com","subject":"Re: [RFC PATCH v2 00/22] Add blame to libgit","fromName":"Jeffrey Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-15T13:52:10Z","receivedAt":"2017-05-15T13:52:25Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"I did try to keep any unnecessary changes out of the big movement\npatches (17-19).  Would you prefer I break down 19 further?  I had\nbeen holding back because of the added churn that would introduce from\nlots of changing function visibility in blame.h and blame.c.\n\n(And if gmail would quit trying to make my messages 'rich text' --\na.k.a. HTML -- that would be great...)\n\n\nOn Mon, May 15, 2017 at 4:24 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jeff Smith <whydoubt@gmail.com> writes:\n>\n>> Rather than duplicate large portions of builtin/blame.c in cgit, it\n>> would be better to shift its core functionality into libgit.a.  The\n>> functionality left in builtin/blame.c mostly relates to terminal\n>> presentation.\n>\n> As I said in my review of 04/22, it is a bit hard/tedious to sift\n> the changes to refactoring that actually changes code and pure\n> renaming and movement of lines across files with the current\n> sequence of the series, so it is very possible that I may have\n> missed something, but from a cursory read through the series, from\n> the comparison between master and the result of applying all\n> patches, and inspection of the resulting builtin/blame.c file, I\n> think what this series does is very sensible in general.  blame.h\n> does not seem to expose anything that is not needed, which is a good\n> sign.\n>\n"},{"id":"319853","messageId":"xmqqefvp6458.fsf@gitster.mtv.corp.google.com","threadId":"45873","inReplyTo":"CAPX7N=4jidx5pr3BfFoTL27gbHDT6sdm1q2Z2YxfjsBuN01XaQ@mail.gmail.com","subject":"Re: [RFC PATCH v2 00/22] Add blame to libgit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-16T00:23:31Z","receivedAt":"2017-05-16T00:23:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeffrey Smith <whydoubt@gmail.com> writes:\n\n> On Mon, May 15, 2017 at 4:24 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Jeff Smith <whydoubt@gmail.com> writes:\n>>\n>>> Rather than duplicate large portions of builtin/blame.c in cgit, it\n>>> would be better to shift its core functionality into libgit.a.  The\n>>> functionality left in builtin/blame.c mostly relates to terminal\n>>> presentation.\n>>\n>> As I said in my review of 04/22, it is a bit hard/tedious to sift\n>> the changes to refactoring that actually changes code and pure\n>> renaming and movement of lines across files with the current\n>> sequence of the series, so it is very possible that I may have\n>> missed something, but from a cursory read through the series, from\n>> the comparison between master and the result of applying all\n>> patches, and inspection of the resulting builtin/blame.c file, I\n>> think what this series does is very sensible in general.  blame.h\n>> does not seem to expose anything that is not needed, which is a good\n>> sign.\n>>\n> I did try to keep any unnecessary changes out of the big movement\n> patches (17-19).  Would you prefer I break down 19 further?  I had\n> been holding back because of the added churn that would introduce from\n> lots of changing function visibility in blame.h and blame.c.\n\nTo be honest, the \"rename symbols while moving\" done starting around\n04/22 made the series too noisy to read and I had hard time\nreviewing the later ones like 17-19.  I think they share exactly the\nsame issue as 04/22, e.g. 17/22 renames origin_decref to\nblame_origin_decref while moving the function and some of its\ncallers across files.\n\n>\n> (And if gmail would quit trying to make my messages 'rich text' --\n> a.k.a. HTML -- that would be great...)\n>\n>\n"},{"id":"319865","messageId":"CAPX7N=5=M=wjTbKQoctndqrHxYSb86bEwej8ofoOovsYHWFkKA@mail.gmail.com","threadId":"45873","inReplyTo":"xmqqefvp6458.fsf@gitster.mtv.corp.google.com","subject":"Re: [RFC PATCH v2 00/22] Add blame to libgit","fromName":"Jeffrey Smith","fromEmail":"whydoubt@gmail.com","sentAt":"2017-05-16T02:44:58Z","receivedAt":"2017-05-16T02:45:06Z","isPatch":true,"sender":{"key":"whydoubt@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2654252?v=4"},"body":"Hm, I really thought I had split some of those out.  Patches 4, 5, 17, and 19\ncould all be reasonably split into rename vs move patches.  Patch 18 only\ndoes moving.\n\nI do not see much benefit to splitting up the 'move xyz to scoreboard'\nsubset (6-12) that way as moving an item to scoreboard is already causing\neach use to change (from xyz to sb->xyz).\n\nOn Mon, May 15, 2017 at 7:23 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jeffrey Smith <whydoubt@gmail.com> writes:\n>\n>> On Mon, May 15, 2017 at 4:24 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> Jeff Smith <whydoubt@gmail.com> writes:\n>>>\n>>>> Rather than duplicate large portions of builtin/blame.c in cgit, it\n>>>> would be better to shift its core functionality into libgit.a.  The\n>>>> functionality left in builtin/blame.c mostly relates to terminal\n>>>> presentation.\n>>>\n>>> As I said in my review of 04/22, it is a bit hard/tedious to sift\n>>> the changes to refactoring that actually changes code and pure\n>>> renaming and movement of lines across files with the current\n>>> sequence of the series, so it is very possible that I may have\n>>> missed something, but from a cursory read through the series, from\n>>> the comparison between master and the result of applying all\n>>> patches, and inspection of the resulting builtin/blame.c file, I\n>>> think what this series does is very sensible in general.  blame.h\n>>> does not seem to expose anything that is not needed, which is a good\n>>> sign.\n>>>\n>> I did try to keep any unnecessary changes out of the big movement\n>> patches (17-19).  Would you prefer I break down 19 further?  I had\n>> been holding back because of the added churn that would introduce from\n>> lots of changing function visibility in blame.h and blame.c.\n>\n> To be honest, the \"rename symbols while moving\" done starting around\n> 04/22 made the series too noisy to read and I had hard time\n> reviewing the later ones like 17-19.  I think they share exactly the\n> same issue as 04/22, e.g. 17/22 renames origin_decref to\n> blame_origin_decref while moving the function and some of its\n> callers across files.\n>\n>>\n>> (And if gmail would quit trying to make my messages 'rich text' --\n>> a.k.a. HTML -- that would be great...)\n>>\n>>\n"},{"id":"319868","messageId":"xmqqd1b94hc1.fsf@gitster.mtv.corp.google.com","threadId":"45873","inReplyTo":"CAPX7N=5=M=wjTbKQoctndqrHxYSb86bEwej8ofoOovsYHWFkKA@mail.gmail.com","subject":"Re: [RFC PATCH v2 00/22] Add blame to libgit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-16T03:21:34Z","receivedAt":"2017-05-16T03:21:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeffrey Smith <whydoubt@gmail.com> writes:\n\n> On Mon, May 15, 2017 at 7:23 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Jeffrey Smith <whydoubt@gmail.com> writes:\n>>\n>>> I did try to keep any unnecessary changes out of the big movement\n>>> patches (17-19).  Would you prefer I break down 19 further?  I had\n>>> been holding back because of the added churn that would introduce from\n>>> lots of changing function visibility in blame.h and blame.c.\n>>\n>> To be honest, the \"rename symbols while moving\" done starting around\n>> 04/22 made the series too noisy to read and I had hard time\n>> reviewing the later ones like 17-19.  I think they share exactly the\n>> same issue as 04/22, e.g. 17/22 renames origin_decref to\n>> blame_origin_decref while moving the function and some of its\n>> callers across files.\n>\n> Hm, I really thought I had split some of those out.  Patches 4, 5, 17, and 19\n> could all be reasonably split into rename vs move patches.  Patch 18 only\n> does moving.\n>\n> I do not see much benefit to splitting up the 'move xyz to scoreboard'\n> subset (6-12) that way as moving an item to scoreboard is already causing\n> each use to change (from xyz to sb->xyz).\n\nPerhaps it would be better to repeat and clarify what I suggested\nonce more.\n\n * Keep mechanical changes separate from real changes,\n * Keep each real change separate from each other,\n * Keep the same kind of mechnical changes together, and\n * Keep different kinds of mechanical changes separate.\n\nSo, if you are moving some globals into a structure, we want a patch\nthat does that for a set of related globals and adjust their uses,\nand do nothing else.  You'll probably have a handful of them in the\nend.  For each of these real changes, it is usually better to have\nmore and smaller patches than to have fewer and larger patches.\n\nThen if you are remaing a bunch of structure types and/or variable\nnames, we want a patch that does that mechanical change (i.e. rename\n\"struct foo\" and \"struct bar\" to \"struct blame_foo\" and \"struct\nblame_bar\") that does not do anything else (i.e. do not move lines\nacross files in the same patch).  As I said already, this does not\nhave to be one per struct type.  We can say \"renaming symbols\" is\njust one kind of mechanical changes, which is different from \"moving\nlines across files\".\n\nFinally, move the lines across files (and again, do nothing else).\nHaving to turn \"static something\" into \"extern something\" is part of\nmoving the lines across files, and we do want to see that in the\nsame patch.  But renaming something to blame_something should have\nalready be done while these lines were in builtin/blame.c in an\nearlier step.\n\nThanks.\n"}]}