{"thread":{"id":"3618","subject":"[PATCH 0/3] Teach git-blame about renames (take 2)","startedAt":"2006-03-10T09:21:35Z","lastAt":"2006-03-16T00:31:55Z","messageCount":5,"participants":["Fredrik Kuivinen","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"17419","messageId":"20060310092135.24015.26510.stgit@c165","threadId":"3618","inReplyTo":null,"subject":"[PATCH 0/3] Teach git-blame about renames (take 2)","fromName":"Fredrik Kuivinen","fromEmail":"freku045@student.liu.se","sentAt":"2006-03-10T09:21:35Z","receivedAt":"2006-03-10T09:21:35Z","isPatch":true,"sender":{"key":"frekui@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13770967?v=4"},"body":"\nChanges since the previous version:\n\n* Fix the things pointed out by Junio.\n* Some other minor clean-ups\n\n---\n\nThis patch series teaches git-blame about renames. To do this I have\nchanged the revision.h interface a bit. In particular, it is now\npossible for the user of revision.h to specify a\ntry_to_simply_commit-like function. That function can then do the\nrename tracking.\n\nI have also made a small change to sort_in_topological_order to make\nit possible to use the object.util field at the same time as a\ntopological sort is done. Previously the object.util field was\nclobbered by the topological sort. In the new interface the auxiliary\ndata that the topological sort needs to store for each commit object\nis stored with a setter function and retrieved by a getter. Pointers\nto those functions are passed to sort_in_topological_order_fn.\n\n- Fredrik\n"},{"id":"17422","messageId":"20060310092137.24015.45112.stgit@c165","threadId":"3618","inReplyTo":"20060310092135.24015.26510.stgit@c165","subject":"[PATCH 1/3] Make it possible to not clobber object.util in sort_in_topological_order (take 2)","fromName":"Fredrik Kuivinen","fromEmail":"freku045@student.liu.se","sentAt":"2006-03-10T09:21:37Z","receivedAt":"2006-03-10T09:21:37Z","isPatch":true,"sender":{"key":"frekui@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13770967?v=4"},"body":"\n\n\nSigned-off-by: Fredrik Kuivinen <freku045@student.liu.se>\n\n---\n\n commit.c |   30 ++++++++++++++++++++++++------\n commit.h |   16 +++++++++++++++-\n 2 files changed, 39 insertions(+), 7 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex 06d5439..013683a 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -569,11 +569,29 @@ int count_parents(struct commit * commit\n         return count;\n }\n \n+void topo_sort_default_setter(struct commit *c, void *data)\n+{\n+\tc->object.util = data;\n+}\n+\n+void *topo_sort_default_getter(struct commit *c)\n+{\n+\treturn c->object.util;\n+}\n+\n /*\n  * Performs an in-place topological sort on the list supplied.\n  */\n void sort_in_topological_order(struct commit_list ** list, int lifo)\n {\n+\tsort_in_topological_order_fn(list, lifo, topo_sort_default_setter,\n+\t\t\t\t     topo_sort_default_getter);\n+}\n+\n+void sort_in_topological_order_fn(struct commit_list ** list, int lifo,\n+\t\t\t\t  topo_sort_set_fn_t setter,\n+\t\t\t\t  topo_sort_get_fn_t getter)\n+{\n \tstruct commit_list * next = *list;\n \tstruct commit_list * work = NULL, **insert;\n \tstruct commit_list ** pptr = list;\n@@ -596,7 +614,7 @@ void sort_in_topological_order(struct co\n \tnext=*list;\n \twhile (next) {\n \t\tnext_nodes->list_item = next;\n-\t\tnext->item->object.util = next_nodes;\n+\t\tsetter(next->item, next_nodes);\n \t\tnext_nodes++;\n \t\tnext = next->next;\n \t}\n@@ -606,7 +624,7 @@ void sort_in_topological_order(struct co\n \t\tstruct commit_list * parents = next->item->parents;\n \t\twhile (parents) {\n \t\t\tstruct commit * parent=parents->item;\n-\t\t\tstruct sort_node * pn = (struct sort_node *)parent->object.util;\n+\t\t\tstruct sort_node * pn = (struct sort_node *) getter(parent);\n \t\t\t\n \t\t\tif (pn)\n \t\t\t\tpn->indegree++;\n@@ -624,7 +642,7 @@ void sort_in_topological_order(struct co\n \tnext=*list;\n \tinsert = &work;\n \twhile (next) {\n-\t\tstruct sort_node * node = (struct sort_node *)next->item->object.util;\n+\t\tstruct sort_node * node = (struct sort_node *) getter(next->item);\n \n \t\tif (node->indegree == 0) {\n \t\t\tinsert = &commit_list_insert(next->item, insert)->next;\n@@ -637,12 +655,12 @@ void sort_in_topological_order(struct co\n \t\tsort_by_date(&work);\n \twhile (work) {\n \t\tstruct commit * work_item = pop_commit(&work);\n-\t\tstruct sort_node * work_node = (struct sort_node *)work_item->object.util;\n+\t\tstruct sort_node * work_node = (struct sort_node *) getter(work_item);\n \t\tstruct commit_list * parents = work_item->parents;\n \n \t\twhile (parents) {\n \t\t\tstruct commit * parent=parents->item;\n-\t\t\tstruct sort_node * pn = (struct sort_node *)parent->object.util;\n+\t\t\tstruct sort_node * pn = (struct sort_node *) getter(parent);\n \t\t\t\n \t\t\tif (pn) {\n \t\t\t\t/* \n@@ -667,7 +685,7 @@ void sort_in_topological_order(struct co\n \t\t*pptr = work_node->list_item;\n \t\tpptr = &(*pptr)->next;\n \t\t*pptr = NULL;\n-\t\twork_item->object.util = NULL;\n+\t\tsetter(work_item, NULL);\n \t}\n \tfree(nodes);\n }\ndiff --git a/commit.h b/commit.h\nindex 70a7c75..15c798a 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -65,15 +65,29 @@ int count_parents(struct commit * commit\n /*\n  * Performs an in-place topological sort of list supplied.\n  *\n- * Pre-conditions:\n+ * Pre-conditions for sort_in_topological_order:\n  *   all commits in input list and all parents of those\n  *   commits must have object.util == NULL\n  *        \n+ * Pre-conditions for sort_in_topological_order_fn:\n+ *   all commits in input list and all parents of those\n+ *   commits must have getter(commit) == NULL\n+ *\n  * Post-conditions: \n  *   invariant of resulting list is:\n  *      a reachable from b => ord(b) < ord(a)\n  *   in addition, when lifo == 0, commits on parallel tracks are\n  *   sorted in the dates order.\n  */\n+\n+typedef void (*topo_sort_set_fn_t)(struct commit*, void *data);\n+typedef void* (*topo_sort_get_fn_t)(struct commit*);\n+\n+void topo_sort_default_setter(struct commit *c, void *data);\n+void *topo_sort_default_getter(struct commit *c);\n+\n void sort_in_topological_order(struct commit_list ** list, int lifo);\n+void sort_in_topological_order_fn(struct commit_list ** list, int lifo,\n+\t\t\t\t  topo_sort_set_fn_t setter,\n+\t\t\t\t  topo_sort_get_fn_t getter);\n #endif /* COMMIT_H */\n"},{"id":"17420","messageId":"20060310092139.24015.47686.stgit@c165","threadId":"3618","inReplyTo":"20060310092135.24015.26510.stgit@c165","subject":"[PATCH 2/3] rev-lib: Make it easy to do rename tracking (take 2)","fromName":"Fredrik Kuivinen","fromEmail":"freku045@student.liu.se","sentAt":"2006-03-10T09:21:39Z","receivedAt":"2006-03-10T09:21:39Z","isPatch":true,"sender":{"key":"frekui@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13770967?v=4"},"body":"\nprune_fn in the rev_info structure is called in place of\ntry_to_simplify_commit. This makes it possible to do rename tracking\nwith a custom try_to_simplify_commit-like function.\n\nThis commit also introduces init_revisions which initialises the rev_info\nstructure with default values.\n\nSigned-off-by: Fredrik Kuivinen <freku045@student.liu.se>\n\n---\n\n rev-list.c |    6 ++--\n revision.c |  100 ++++++++++++++++++++++++++++++++++--------------------------\n revision.h |   18 ++++++++++-\n 3 files changed, 76 insertions(+), 48 deletions(-)\n\ndiff --git a/rev-list.c b/rev-list.c\nindex 8e4d83e..812d237 100644\n--- a/rev-list.c\n+++ b/rev-list.c\n@@ -190,7 +190,7 @@ static int count_distance(struct commit_\n \n \t\tif (commit->object.flags & (UNINTERESTING | COUNTED))\n \t\t\tbreak;\n-\t\tif (!revs.paths || (commit->object.flags & TREECHANGE))\n+\t\tif (!revs.prune_fn || (commit->object.flags & TREECHANGE))\n \t\t\tnr++;\n \t\tcommit->object.flags |= COUNTED;\n \t\tp = commit->parents;\n@@ -224,7 +224,7 @@ static struct commit_list *find_bisectio\n \tnr = 0;\n \tp = list;\n \twhile (p) {\n-\t\tif (!revs.paths || (p->item->object.flags & TREECHANGE))\n+\t\tif (!revs.prune_fn || (p->item->object.flags & TREECHANGE))\n \t\t\tnr++;\n \t\tp = p->next;\n \t}\n@@ -234,7 +234,7 @@ static struct commit_list *find_bisectio\n \tfor (p = list; p; p = p->next) {\n \t\tint distance;\n \n-\t\tif (revs.paths && !(p->item->object.flags & TREECHANGE))\n+\t\tif (revs.prune_fn && !(p->item->object.flags & TREECHANGE))\n \t\t\tcontinue;\n \n \t\tdistance = count_distance(p);\ndiff --git a/revision.c b/revision.c\nindex 2a33637..b41f961 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -197,31 +197,27 @@ static int everybody_uninteresting(struc\n \treturn 1;\n }\n \n-#define TREE_SAME\t0\n-#define TREE_NEW\t1\n-#define TREE_DIFFERENT\t2\n-static int tree_difference = TREE_SAME;\n+static int tree_difference = REV_TREE_SAME;\n \n static void file_add_remove(struct diff_options *options,\n \t\t    int addremove, unsigned mode,\n \t\t    const unsigned char *sha1,\n \t\t    const char *base, const char *path)\n {\n-\tint diff = TREE_DIFFERENT;\n+\tint diff = REV_TREE_DIFFERENT;\n \n \t/*\n-\t * Is it an add of a new file? It means that\n-\t * the old tree didn't have it at all, so we\n-\t * will turn \"TREE_SAME\" -> \"TREE_NEW\", but\n-\t * leave any \"TREE_DIFFERENT\" alone (and if\n-\t * it already was \"TREE_NEW\", we'll keep it\n-\t * \"TREE_NEW\" of course).\n+\t * Is it an add of a new file? It means that the old tree\n+\t * didn't have it at all, so we will turn \"REV_TREE_SAME\" ->\n+\t * \"REV_TREE_NEW\", but leave any \"REV_TREE_DIFFERENT\" alone\n+\t * (and if it already was \"REV_TREE_NEW\", we'll keep it\n+\t * \"REV_TREE_NEW\" of course).\n \t */\n \tif (addremove == '+') {\n \t\tdiff = tree_difference;\n-\t\tif (diff != TREE_SAME)\n+\t\tif (diff != REV_TREE_SAME)\n \t\t\treturn;\n-\t\tdiff = TREE_NEW;\n+\t\tdiff = REV_TREE_NEW;\n \t}\n \ttree_difference = diff;\n }\n@@ -232,7 +228,7 @@ static void file_change(struct diff_opti\n \t\t const unsigned char *new_sha1,\n \t\t const char *base, const char *path)\n {\n-\ttree_difference = TREE_DIFFERENT;\n+\ttree_difference = REV_TREE_DIFFERENT;\n }\n \n static struct diff_options diff_opt = {\n@@ -241,19 +237,19 @@ static struct diff_options diff_opt = {\n \t.change = file_change,\n };\n \n-static int compare_tree(struct tree *t1, struct tree *t2)\n+int rev_compare_tree(struct tree *t1, struct tree *t2)\n {\n \tif (!t1)\n-\t\treturn TREE_NEW;\n+\t\treturn REV_TREE_NEW;\n \tif (!t2)\n-\t\treturn TREE_DIFFERENT;\n-\ttree_difference = TREE_SAME;\n+\t\treturn REV_TREE_DIFFERENT;\n+\ttree_difference = REV_TREE_SAME;\n \tif (diff_tree_sha1(t1->object.sha1, t2->object.sha1, \"\", &diff_opt) < 0)\n-\t\treturn TREE_DIFFERENT;\n+\t\treturn REV_TREE_DIFFERENT;\n \treturn tree_difference;\n }\n \n-static int same_tree_as_empty(struct tree *t1)\n+int rev_same_tree_as_empty(struct tree *t1)\n {\n \tint retval;\n \tvoid *tree;\n@@ -285,7 +281,7 @@ static void try_to_simplify_commit(struc\n \t\treturn;\n \n \tif (!commit->parents) {\n-\t\tif (!same_tree_as_empty(commit->tree))\n+\t\tif (!rev_same_tree_as_empty(commit->tree))\n \t\t\tcommit->object.flags |= TREECHANGE;\n \t\treturn;\n \t}\n@@ -300,19 +296,20 @@ static void try_to_simplify_commit(struc\n \t\t}\n \n \t\tparse_commit(p);\n-\t\tswitch (compare_tree(p->tree, commit->tree)) {\n-\t\tcase TREE_SAME:\n+\t\tswitch (rev_compare_tree(p->tree, commit->tree)) {\n+\t\tcase REV_TREE_SAME:\n \t\t\tparent->next = NULL;\n \t\t\tcommit->parents = parent;\n \t\t\treturn;\n \n-\t\tcase TREE_NEW:\n-\t\t\tif (revs->remove_empty_trees && same_tree_as_empty(p->tree)) {\n+\t\tcase REV_TREE_NEW:\n+\t\t\tif (revs->remove_empty_trees &&\n+\t\t\t    rev_same_tree_as_empty(p->tree)) {\n \t\t\t\t*pp = parent->next;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t/* fallthrough */\n-\t\tcase TREE_DIFFERENT:\n+\t\tcase REV_TREE_DIFFERENT:\n \t\t\tpp = &parent->next;\n \t\t\tcontinue;\n \t\t}\n@@ -358,8 +355,8 @@ static void add_parents_to_list(struct r\n \t * simplify the commit history and find the parent\n \t * that has no differences in the path set if one exists.\n \t */\n-\tif (revs->paths)\n-\t\ttry_to_simplify_commit(revs, commit);\n+\tif (revs->prune_fn)\n+\t\trevs->prune_fn(revs, commit);\n \n \tparent = commit->parents;\n \twhile (parent) {\n@@ -381,9 +378,6 @@ static void limit_list(struct rev_info *\n \tstruct commit_list *newlist = NULL;\n \tstruct commit_list **p = &newlist;\n \n-\tif (revs->paths)\n-\t\tdiff_tree_setup_paths(revs->paths);\n-\n \twhile (list) {\n \t\tstruct commit_list *entry = list;\n \t\tstruct commit *commit = list->item;\n@@ -435,6 +429,23 @@ static void handle_all(struct rev_info *\n \tfor_each_ref(handle_one_ref);\n }\n \n+void init_revisions(struct rev_info *revs)\n+{\n+\tmemset(revs, 0, sizeof(*revs));\n+\trevs->lifo = 1;\n+\trevs->dense = 1;\n+\trevs->prefix = setup_git_directory();\n+\trevs->max_age = -1;\n+\trevs->min_age = -1;\n+\trevs->max_count = -1;\n+\n+\trevs->prune_fn = NULL;\n+\trevs->prune_data = NULL;\n+\n+\trevs->topo_setter = topo_sort_default_setter;\n+\trevs->topo_getter = topo_sort_default_getter;\n+}\n+\n /*\n  * Parse revision information, filling in the \"rev_info\" structure,\n  * and removing the used arguments from the argument list.\n@@ -448,14 +459,8 @@ int setup_revisions(int argc, const char\n \tconst char **unrecognized = argv + 1;\n \tint left = 1;\n \n-\tmemset(revs, 0, sizeof(*revs));\n-\trevs->lifo = 1;\n-\trevs->dense = 1;\n-\trevs->prefix = setup_git_directory();\n-\trevs->max_age = -1;\n-\trevs->min_age = -1;\n-\trevs->max_count = -1;\n-\n+\tinit_revisions(revs);\n+\t\n \t/* First, search for \"--\" */\n \tseen_dashdash = 0;\n \tfor (i = 1; i < argc; i++) {\n@@ -464,7 +469,7 @@ int setup_revisions(int argc, const char\n \t\t\tcontinue;\n \t\targv[i] = NULL;\n \t\targc = i;\n-\t\trevs->paths = get_pathspec(revs->prefix, argv + i + 1);\n+\t\trevs->prune_data = get_pathspec(revs->prefix, argv + i + 1);\n \t\tseen_dashdash = 1;\n \t\tbreak;\n \t}\n@@ -628,7 +633,7 @@ int setup_revisions(int argc, const char\n \t\t\t\tif (lstat(argv[j], &st) < 0)\n \t\t\t\t\tdie(\"'%s': %s\", arg, strerror(errno));\n \t\t\t}\n-\t\t\trevs->paths = get_pathspec(revs->prefix, argv + i);\n+\t\t\trevs->prune_data = get_pathspec(revs->prefix, argv + i);\n \t\t\tbreak;\n \t\t}\n \t\tcommit = get_commit_reference(revs, arg, sha1, flags ^ local_flags);\n@@ -642,8 +647,13 @@ int setup_revisions(int argc, const char\n \t\tcommit = get_commit_reference(revs, def, sha1, 0);\n \t\tadd_one_commit(commit, revs);\n \t}\n-\tif (revs->paths)\n+\n+\tif (revs->prune_data) {\n+\t\tdiff_tree_setup_paths(revs->prune_data);\n+\t\trevs->prune_fn = try_to_simplify_commit;\n \t\trevs->limited = 1;\n+\t}\n+\n \treturn left;\n }\n \n@@ -653,7 +663,9 @@ void prepare_revision_walk(struct rev_in\n \tif (revs->limited)\n \t\tlimit_list(revs);\n \tif (revs->topo_order)\n-\t\tsort_in_topological_order(&revs->commits, revs->lifo);\n+\t\tsort_in_topological_order_fn(&revs->commits, revs->lifo,\n+\t\t\t\t\t     revs->topo_setter,\n+\t\t\t\t\t     revs->topo_getter);\n }\n \n static int rewrite_one(struct commit **pp)\n@@ -709,7 +721,7 @@ struct commit *get_revision(struct rev_i\n \t\t\treturn NULL;\n \t\tif (revs->no_merges && commit->parents && commit->parents->next)\n \t\t\tgoto next;\n-\t\tif (revs->paths && revs->dense) {\n+\t\tif (revs->prune_fn && revs->dense) {\n \t\t\tif (!(commit->object.flags & TREECHANGE))\n \t\t\t\tgoto next;\n \t\t\trewrite_parents(commit);\ndiff --git a/revision.h b/revision.h\nindex 31e8f61..6c2beca 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -7,6 +7,10 @@\n #define SHOWN\t\t(1u<<3)\n #define TMP_MARK\t(1u<<4) /* for isolated cases; clean after use */\n \n+struct rev_info;\n+\n+typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit);\n+\n struct rev_info {\n \t/* Starting list */\n \tstruct commit_list *commits;\n@@ -14,7 +18,8 @@ struct rev_info {\n \n \t/* Basic information */\n \tconst char *prefix;\n-\tconst char **paths;\n+\tvoid *prune_data;\n+\tprune_fn_t *prune_fn;\n \n \t/* Traversal flags */\n \tunsigned int\tdense:1,\n@@ -33,9 +38,20 @@ struct rev_info {\n \tint max_count;\n \tunsigned long max_age;\n \tunsigned long min_age;\n+\n+\ttopo_sort_set_fn_t topo_setter;\n+\ttopo_sort_get_fn_t topo_getter;\n };\n \n+#define REV_TREE_SAME\t\t0\n+#define REV_TREE_NEW\t\t1\n+#define REV_TREE_DIFFERENT\t2\n+\n /* revision.c */\n+extern int rev_same_tree_as_empty(struct tree *t1);\n+extern int rev_compare_tree(struct tree *t1, struct tree *t2);\n+\n+extern void init_revisions(struct rev_info *revs);\n extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);\n extern void prepare_revision_walk(struct rev_info *revs);\n extern struct commit *get_revision(struct rev_info *revs);\n"},{"id":"17421","messageId":"20060310092141.24015.42482.stgit@c165","threadId":"3618","inReplyTo":"20060310092135.24015.26510.stgit@c165","subject":"[PATCH 3/3] blame: Rename detection (take 2)","fromName":"Fredrik Kuivinen","fromEmail":"freku045@student.liu.se","sentAt":"2006-03-10T09:21:41Z","receivedAt":"2006-03-10T09:21:41Z","isPatch":true,"sender":{"key":"frekui@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13770967?v=4"},"body":"\n\n\nSigned-off-by: Fredrik Kuivinen <freku045@student.liu.se>\n\n---\n\n blame.c |  239 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----------\n 1 files changed, 199 insertions(+), 40 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 90338af..ac57e10 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -14,6 +14,7 @@\n #include \"tree.h\"\n #include \"blob.h\"\n #include \"diff.h\"\n+#include \"diffcore.h\"\n #include \"revision.h\"\n \n #define DEBUG 0\n@@ -34,7 +35,9 @@ struct util_info {\n \tchar *buf;\n \tunsigned long size;\n \tint num_lines;\n-//    const char* path;\n+\tconst char* pathname;\n+\n+\tvoid* topo_data;\n };\n \n struct chunk {\n@@ -342,25 +345,34 @@ static int map_line(struct commit *commi\n \treturn info->line_map[line];\n }\n \n-static int fill_util_info(struct commit *commit, const char *path)\n+static struct util_info* get_util(struct commit *commit)\n {\n-\tstruct util_info *util;\n-\tif (commit->object.util)\n-\t\treturn 0;\n+\tstruct util_info *util = commit->object.util;\n+\n+\tif (util)\n+\t\treturn util;\n \n \tutil = xmalloc(sizeof(struct util_info));\n+\tutil->buf = NULL;\n+\tutil->size = 0;\n+\tutil->line_map = NULL;\n+\tutil->num_lines = -1;\n+\tutil->pathname = NULL;\n+\tcommit->object.util = util;\n+\treturn util;\n+}\n \n-\tif (get_blob_sha1(commit->tree, path, util->sha1)) {\n-\t\tfree(util);\n+static int fill_util_info(struct commit *commit)\n+{\n+\tstruct util_info *util = commit->object.util;\n+\n+\tassert(util);\n+\tassert(util->pathname);\n+\t\n+\tif (get_blob_sha1(commit->tree, util->pathname, util->sha1))\n \t\treturn 1;\n-\t} else {\n-\t\tutil->buf = NULL;\n-\t\tutil->size = 0;\n-\t\tutil->line_map = NULL;\n-\t\tutil->num_lines = -1;\n-\t\tcommit->object.util = util;\n+\telse\n \t\treturn 0;\n-\t}\n }\n \n static void alloc_line_map(struct commit *commit)\n@@ -389,10 +401,11 @@ static void alloc_line_map(struct commit\n \n static void init_first_commit(struct commit* commit, const char* filename)\n {\n-\tstruct util_info* util;\n+\tstruct util_info* util = commit->object.util;\n \tint i;\n \n-\tif (fill_util_info(commit, filename))\n+\tutil->pathname = filename;\n+\tif (fill_util_info(commit))\n \t\tdie(\"fill_util_info failed\");\n \n \talloc_line_map(commit);\n@@ -453,7 +466,7 @@ static void process_commits(struct rev_i\n \t\tif(num_parents == 0)\n \t\t\t*initial = commit;\n \n-\t\tif(fill_util_info(commit, path))\n+\t\tif (fill_util_info(commit))\n \t\t\tcontinue;\n \n \t\talloc_line_map(commit);\n@@ -471,7 +484,7 @@ static void process_commits(struct rev_i\n \t\t\t\tprintf(\"parent: %s\\n\",\n \t\t\t\t       sha1_to_hex(parent->object.sha1));\n \n-\t\t\tif(fill_util_info(parent, path)) {\n+\t\t\tif (fill_util_info(parent)) {\n \t\t\t\tnum_parents--;\n \t\t\t\tcontinue;\n \t\t\t}\n@@ -511,6 +524,135 @@ static void process_commits(struct rev_i\n \t} while ((commit = get_revision(rev)) != NULL);\n }\n \n+\n+static int compare_tree_path(struct rev_info* revs,\n+\t\t\t     struct commit* c1, struct commit* c2)\n+{\n+\tconst char* paths[2];\n+\tstruct util_info* util = c2->object.util;\n+\tpaths[0] = util->pathname;\n+\tpaths[1] = NULL;\n+\n+\tdiff_tree_setup_paths(get_pathspec(revs->prefix, paths));\n+\treturn rev_compare_tree(c1->tree, c2->tree);\n+}\n+\n+\n+static int same_tree_as_empty_path(struct rev_info *revs, struct tree* t1,\n+\t\t\t\t   const char* path)\n+{\n+\tconst char* paths[2];\n+\tpaths[0] = path;\n+\tpaths[1] = NULL;\n+\n+\tdiff_tree_setup_paths(get_pathspec(revs->prefix, paths));\n+\treturn rev_same_tree_as_empty(t1);\n+}\n+\n+static const char* find_rename(struct commit* commit, struct commit* parent)\n+{\n+\tstruct util_info* cutil = commit->object.util;\n+\tstruct diff_options diff_opts;\n+\tconst char *paths[1];\n+\tint i;\n+\n+\tif (DEBUG) {\n+\t\tprintf(\"find_rename commit: %s \",\n+\t\t       sha1_to_hex(commit->object.sha1));\n+\t\tputs(sha1_to_hex(parent->object.sha1));\n+\t}\n+\n+\tdiff_setup(&diff_opts);\n+\tdiff_opts.recursive = 1;\n+\tdiff_opts.detect_rename = DIFF_DETECT_RENAME;\n+\tpaths[0] = NULL;\n+\tdiff_tree_setup_paths(paths);\n+\tif (diff_setup_done(&diff_opts) < 0)\n+\t\tdie(\"diff_setup_done failed\");\n+\n+\tdiff_tree_sha1(commit->tree->object.sha1, parent->tree->object.sha1,\n+\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+\n+\t\tif (p->status == 'R' && !strcmp(p->one->path, cutil->pathname)) {\n+\t\t\tif (DEBUG)\n+\t\t\t\tprintf(\"rename %s -> %s\\n\", p->one->path, p->two->path);\n+\t\t\treturn p->two->path;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void simplify_commit(struct rev_info *revs, struct commit *commit)\n+{\n+\tstruct commit_list **pp, *parent;\n+\n+\tif (!commit->tree)\n+\t\treturn;\n+\n+\tif (!commit->parents) {\n+\t\tstruct util_info* util = commit->object.util;\n+\t\tif (!same_tree_as_empty_path(revs, commit->tree,\n+\t\t\t\t\t     util->pathname))\n+\t\t\tcommit->object.flags |= TREECHANGE;\n+\t\treturn;\n+\t}\n+\n+\tpp = &commit->parents;\n+\twhile ((parent = *pp) != NULL) {\n+\t\tstruct commit *p = parent->item;\n+\n+\t\tif (p->object.flags & UNINTERESTING) {\n+\t\t\tpp = &parent->next;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tparse_commit(p);\n+\t\tswitch (compare_tree_path(revs, p, commit)) {\n+\t\tcase REV_TREE_SAME:\n+\t\t\tparent->next = NULL;\n+\t\t\tcommit->parents = parent;\n+\t\t\tget_util(p)->pathname = get_util(commit)->pathname;\n+\t\t\treturn;\n+\n+\t\tcase REV_TREE_NEW:\n+\t\t{\n+\t\t\t\n+\t\t\tstruct util_info* util = commit->object.util;\n+\t\t\tif (revs->remove_empty_trees &&\n+\t\t\t    same_tree_as_empty_path(revs, p->tree,\n+\t\t\t\t\t\t    util->pathname)) {\n+\t\t\t\tconst char* new_name = find_rename(commit, p);\n+\t\t\t\tif (new_name) {\n+\t\t\t\t\tstruct util_info* putil = get_util(p);\n+\t\t\t\t\tif (!putil->pathname)\n+\t\t\t\t\t\tputil->pathname = strdup(new_name);\n+\t\t\t\t} else {\n+\t\t\t\t\t*pp = parent->next;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\t/* fallthrough */\n+\t\tcase REV_TREE_DIFFERENT:\n+\t\t\tpp = &parent->next;\n+\t\t\tif (!get_util(p)->pathname)\n+\t\t\t\tget_util(p)->pathname =\n+\t\t\t\t\tget_util(commit)->pathname;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tdie(\"bad tree compare for commit %s\",\n+\t\t    sha1_to_hex(commit->object.sha1));\n+\t}\n+\tcommit->object.flags |= TREECHANGE;\n+}\n+\n+\n struct commit_info\n {\n \tchar* author;\n@@ -569,6 +711,18 @@ static const char* format_time(unsigned \n \treturn time_buf;\n }\n \n+static void topo_setter(struct commit* c, void* data)\n+{\n+\tstruct util_info* util = c->object.util;\n+\tutil->topo_data = data;\n+}\n+\n+static void* topo_getter(struct commit* c)\n+{\n+\tstruct util_info* util = c->object.util;\n+\treturn util->topo_data;\n+}\n+\n int main(int argc, const char **argv)\n {\n \tint i;\n@@ -580,8 +734,8 @@ int main(int argc, const char **argv)\n \tint sha1_len = 8;\n \tint compability = 0;\n \tint options = 1;\n+\tstruct commit* start_commit;\n \n-\tint num_args;\n \tconst char* args[10];\n \tstruct rev_info rev;\n \n@@ -634,28 +788,29 @@ int main(int argc, const char **argv)\n \t\tstrcpy(filename_buf, filename);\n \tfilename = filename_buf;\n \n-\t{\n-\t\tstruct commit* c;\n-\t\tif (get_sha1(commit, sha1))\n-\t\t\tdie(\"get_sha1 failed, commit '%s' not found\", commit);\n-\t\tc = lookup_commit_reference(sha1);\n-\n-\t\tif (fill_util_info(c, filename)) {\n-\t\t\tprintf(\"%s not found in %s\\n\", filename, commit);\n-\t\t\treturn 1;\n-\t\t}\n+\tif (get_sha1(commit, sha1))\n+\t\tdie(\"get_sha1 failed, commit '%s' not found\", commit);\t\n+\tstart_commit = lookup_commit_reference(sha1);\n+\tget_util(start_commit)->pathname = filename;\n+\tif (fill_util_info(start_commit)) {\n+\t\tprintf(\"%s not found in %s\\n\", filename, commit);\n+\t\treturn 1;\n \t}\n \n-\tnum_args = 0;\n-\targs[num_args++] = NULL;\n-\targs[num_args++] = \"--topo-order\";\n-\targs[num_args++] = \"--remove-empty\";\n-\targs[num_args++] = commit;\n-\targs[num_args++] = \"--\";\n-\targs[num_args++] = filename;\n-\targs[num_args] = NULL;\n \n-\tsetup_revisions(num_args, args, &rev, \"HEAD\");\n+\tinit_revisions(&rev);\t\n+\trev.remove_empty_trees = 1;\n+\trev.topo_order = 1;\n+\trev.prune_fn = simplify_commit;\n+\trev.topo_setter = topo_setter;\n+\trev.topo_getter = topo_getter;\n+\trev.limited = 1;\n+\n+\tcommit_list_insert(start_commit, &rev.commits);\n+\t\n+\targs[0] = filename;\n+\targs[1] = NULL;\n+\tdiff_tree_setup_paths(args);\n \tprepare_revision_walk(&rev);\n \tprocess_commits(&rev, filename, &initial);\n \n@@ -665,17 +820,21 @@ int main(int argc, const char **argv)\n \n \tfor (i = 0; i < num_blame_lines; i++) {\n \t\tstruct commit *c = blame_lines[i];\n+\t\tstruct util_info* u;\n+\n \t\tif (!c)\n \t\t\tc = initial;\n \n+\t\tu = c->object.util;\n \t\tget_commit_info(c, &ci);\n \t\tfwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout);\n \t\tif(compability)\n \t\t\tprintf(\"\\t(%10s\\t%10s\\t%d)\", ci.author,\n \t\t\t       format_time(ci.author_time, ci.author_tz), i+1);\n \t\telse\n-\t\t\tprintf(\" (%-15.15s %10s %*d) \", ci.author,\n-\t\t\t       format_time(ci.author_time, ci.author_tz),\n+\t\t\tprintf(\" %s (%-15.15s %10s %*d) \", u->pathname,\n+\t\t\t       ci.author, format_time(ci.author_time,\n+\t\t\t\t\t\t      ci.author_tz),\n \t\t\t       max_digits, i+1);\n \n \t\tif(i == num_blame_lines - 1) {\n"},{"id":"17580","messageId":"7vzmjrdybo.fsf@assigned-by-dhcp.cox.net","threadId":"3618","inReplyTo":"20060310092141.24015.42482.stgit@c165","subject":"Re: [PATCH 3/3] blame: Rename detection (take 2)","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-03-16T00:31:55Z","receivedAt":"2006-03-16T00:31:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Very nice.  It even works with that \"insanely fast rename\ndetector\" by Linus ;-)\n\nAn example, from an early part of \"git blame git-format-patch.sh\":\n\n0acfc972 git-format-patch-script (Junio C Hamano  2005-07-05 13:19:05 -0700 \n0acfc972 git-format-patch-script (Junio C Hamano  2005-07-05 13:19:05 -0700 \n0acfc972 git-format-patch-script (Junio C Hamano  2005-07-05 13:19:05 -0700 \n19bb7327 git-format-patch.sh (Mike McCormack  2006-03-06 22:12:12 +0900   6)\n66f04f38 git-format-patch.sh (Andreas Ericsso 2006-02-07 09:37:54 +0000   7)\n66f04f38 git-format-patch.sh (Andreas Ericsso 2006-02-07 09:37:54 +0000   8)\n66f04f38 git-format-patch.sh (Andreas Ericsso 2006-02-07 09:37:54 +0000   9)\n66f04f38 git-format-patch.sh (Andreas Ericsso 2006-02-07 09:37:54 +0000  10)\n\nHowever, I think this underlines that chopping people's names to\nthe first 15 bytes (or any length for that matter) does not buy\nyou much.\n"}]}