{"thread":{"id":"4831","subject":"[PATCH] read-tree --rename (WIP)","startedAt":"2006-07-10T12:18:26Z","lastAt":"2006-07-10T12:18:26Z","messageCount":1,"participants":["Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"23541","messageId":"7v1wstd565.fsf@assigned-by-dhcp.cox.net","threadId":"4831","inReplyTo":null,"subject":"[PATCH] read-tree --rename (WIP)","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-07-10T12:18:26Z","receivedAt":"2006-07-10T12:18:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This adds '--rename' flag to 3-way merge done by \"read-tree -m\".\nCurrently it detects the case where one branch renamed but the\nother branch didn't.  It should be taught to notice that both\nbranches renamed the same old path to the same new path.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n * To use this, you would copy git-merge-resolve.sh to create\n   git-merge-rename.sh, replace \"git-read-tree --aggressive\"\n   call with \"git-read-tree --rename\", or something like that.\n\n   I wanted to avoid the changes to threeway_merge().  They were\n   to force the code to leave \"only one side added\" cases\n   unmerged.\n\n builtin-read-tree.c |  150 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 150 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-read-tree.c b/builtin-read-tree.c\nindex 6df5d7c..e7aba0e 100644\n--- a/builtin-read-tree.c\n+++ b/builtin-read-tree.c\n@@ -11,6 +11,8 @@ #include \"object.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"cache-tree.h\"\n+#include \"diff.h\"\n+#include \"diffcore.h\"\n #include <sys/time.h>\n #include <signal.h>\n #include \"builtin.h\"\n@@ -22,6 +24,7 @@ static int index_only = 0;\n static int nontrivial_merge = 0;\n static int trivial_merges_only = 0;\n static int aggressive = 0;\n+static int do_renames = 0;\n static int verbose_update = 0;\n static volatile int progress_update = 0;\n static const char *prefix = NULL;\n@@ -389,6 +392,130 @@ static void check_updates(struct cache_e\n \t}\n }\n \n+static struct cache_entry *renamed_entry(const char *new_name, int namelen, struct cache_entry *old)\n+{\n+\tstruct cache_entry *renamed;\n+\n+\trenamed = xcalloc(1, cache_entry_size(namelen));\n+\trenamed->ce_mode = old->ce_mode;\n+\trenamed->ce_flags = create_ce_flags(namelen, ce_stage(old));\n+\tstrcpy(renamed->name, new_name);\n+\tmemcpy(renamed->sha1, old->sha1, 20);\n+\treturn renamed;\n+}\n+\n+static void handle_renames(void)\n+{\n+\t/* Find entries that have stages #1 and #2 (but not #3)\n+\t * and then entries that have stage #3 only.\n+\t * If #1 of the former and #3 of the latter are similar,\n+\t * that means we did not rename it but they did.\n+\t * Similarly, entries that have stages #1 and #3 can be\n+\t * matched up with entries that have only stage #2, in\n+\t * which case they did not rename but we did.\n+\t * In either case, move the #1 and unmoved stage to the\n+\t * path of the moved stage.\n+\t */\n+\tstruct diff_options opt;\n+\tint i;\n+\n+\tdiff_setup(&opt);\n+\topt.detect_rename = DIFF_DETECT_RENAME;\n+\topt.output_format = DIFF_FORMAT_NO_OUTPUT;\n+\topt.recursive = 1;\n+\tdiff_setup_done(&opt);\n+\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tint j;\n+\t\tif (!ce_stage(ce))\n+\t\t\tcontinue; /* fully merged */\n+\n+\t\tfor (j = i + 1; j < active_nr; j++) {\n+\t\t\tif (strcmp(ce->name, active_cache[j]->name))\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (ce_stage(ce) == 1 && (j - i) == 2)\n+\t\t\t/* This entry has stage #1 and another one\n+\t\t\t * i.e. either #2 or #3 but not both.\n+\t\t\t */\n+\t\t\tdiff_addremove(&opt, '-', ntohl(ce->ce_mode), ce->sha1,\n+\t\t\t\t       ce->name, NULL);\n+\n+\t\tif (1 < ce_stage(ce) && (j - i) == 1)\n+\t\t\t/* This entry has only stage #2 or #3 */\n+\t\t\tdiff_addremove(&opt, '+', ntohl(ce->ce_mode), ce->sha1,\n+\t\t\t\t       ce->name, NULL);\n+\n+\t\ti = j - 1;\n+\t}\n+\tdiffcore_std(&opt);\n+\n+\tif (diff_queued_diff.nr) {\n+\t\t/* We have found the renames and stuff */\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\tconst char *old_name, *new_name;\n+\t\t\tint namelen;\n+\t\t\tstruct cache_entry *e, *e_one, *e_kept, *e_moved;\n+\t\t\tint pos, kept;\n+\n+\t\t\tif (p->status != DIFF_STATUS_RENAMED)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/* one->path has stage #1 and either stage #2\n+\t\t\t * or #3 that was kept on one branch, and\n+\t\t\t * two->path is where it was moved on the other\n+\t\t\t * branch, either stage #2 or stage #3.\n+\t\t\t */\n+\t\t\told_name = p->one->path;\n+\t\t\tnew_name = p->two->path;\n+\t\t\tnamelen = strlen(new_name);\n+\t\t\tpos = cache_name_pos(old_name, strlen(old_name));\n+\t\t\tif (0 <= pos)\n+\t\t\t\tdie(\"oops %s:%d\", __FILE__, __LINE__);\n+\t\t\tpos = -pos - 1;\n+\t\t\te = active_cache[pos++];\n+\t\t\tif (strcmp(e->name, old_name))\n+\t\t\t\tdie(\"oops %s:%d\", __FILE__, __LINE__);\n+\t\t\tif (ce_stage(e) != 1)\n+\t\t\t\tdie(\"oops %s:%d\", __FILE__, __LINE__);\n+\t\t\te_one = renamed_entry(new_name, namelen, e);\n+\t\t\tif (active_nr <= pos)\n+\t\t\t\tdie(\"oops %s:%d\", __FILE__, __LINE__);\n+\t\t\te = active_cache[pos];\n+\t\t\tif (strcmp(e->name, old_name))\n+\t\t\t\tdie(\"oops %s:%d\", __FILE__, __LINE__);\n+\t\t\tkept = ce_stage(e);\n+\t\t\tif (kept < 2)\n+\t\t\t\tdie(\"oops %s:%d\", __FILE__, __LINE__);\n+\t\t\te_kept = renamed_entry(new_name, namelen, e);\n+\n+\t\t\tpos = cache_name_pos(new_name, namelen);\n+\t\t\tif (0 <= pos)\n+\t\t\t\tdie(\"oops %s:%d\", __FILE__, __LINE__);\n+\t\t\tpos = -pos - 1;\n+\t\t\te = active_cache[pos];\n+\t\t\tif (strcmp(e->name, new_name))\n+\t\t\t\tdie(\"oops %s:%d\", __FILE__, __LINE__);\n+\t\t\tif (ce_stage(e) + kept != 5)\n+\t\t\t\t/* If kept was #2 this must be #3 and\n+\t\t\t\t * vice versa.\n+\t\t\t\t */\n+\t\t\t\tdie(\"oops %s:%d\", __FILE__, __LINE__);\n+\t\t\te_moved = renamed_entry(new_name, namelen, e);\n+\n+\t\t\tremove_file_from_cache(old_name);\n+\t\t\tremove_file_from_cache(new_name);\n+\t\t\tadd_cache_entry(e_one, ADD_CACHE_OK_TO_ADD);\n+\t\t\tadd_cache_entry(e_kept, ADD_CACHE_OK_TO_ADD);\n+\t\t\tadd_cache_entry(e_moved, ADD_CACHE_OK_TO_ADD);\n+\t\t}\n+\t}\n+\tdiff_flush(&opt);\n+}\n+\n static int unpack_trees(merge_fn_t fn)\n {\n \tint indpos = 0;\n@@ -412,6 +539,9 @@ static int unpack_trees(merge_fn_t fn)\n \tif (trivial_merges_only && nontrivial_merge)\n \t\tdie(\"Merge requires file-level merging\");\n \n+\tif (merge_size == 3 && do_renames)\n+\t\thandle_renames();\n+\n \tcheck_updates(active_cache, active_nr);\n \treturn 0;\n }\n@@ -606,6 +736,10 @@ static int threeway_merge(struct cache_e\n \t\t}\n \t}\n \n+\tif (do_renames && remote && !head && !index && any_anc_missing)\n+\t\t/* they created, we didn't -- maybe they renamed */\n+\t\treturn keep_entry(remote);\n+\n \t/* We start with cases where the index is allowed to match\n \t * something other than the head: #14(ALT) and #2ALT, where it\n \t * is permitted to match the result instead.\n@@ -625,6 +759,10 @@ static int threeway_merge(struct cache_e\n \t}\n \n \tif (head) {\n+\t\tif (do_renames && !remote && any_anc_missing)\n+\t\t\t/* we created, they didn't -- maybe we renamed */\n+\t\t\treturn keep_entry(head);\n+\n \t\t/* #5ALT, #15 */\n \t\tif (same(head, remote))\n \t\t\treturn merged_entry(head, index);\n@@ -868,6 +1006,9 @@ static void prime_cache_tree(void)\n \n static const char read_tree_usage[] = \"git-read-tree (<sha> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] <sha1> [<sha2> [<sha3>]])\";\n \n+static const char err_aggressive_rename[] =\n+\"--aggressive and --rename are mutually exclusive\";\n+\n static struct lock_file lock_file;\n \n int cmd_read_tree(int argc, const char **argv, char **envp)\n@@ -952,10 +1093,19 @@ int cmd_read_tree(int argc, const char *\n \t\t}\n \n \t\tif (!strcmp(arg, \"--aggressive\")) {\n+\t\t\tif (do_renames)\n+\t\t\t\tdie(err_aggressive_rename);\n \t\t\taggressive = 1;\n \t\t\tcontinue;\n \t\t}\n \n+\t\tif (!strcmp(arg, \"--rename\")) {\n+\t\t\tif (aggressive)\n+\t\t\t\tdie(err_aggressive_rename);\n+\t\t\tdo_renames = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\t/* \"-m\" stands for \"merge\", meaning we start in stage 1 */\n \t\tif (!strcmp(arg, \"-m\")) {\n \t\t\tif (stage || merge || prefix)\n-- \n1.4.1.gf157\n"}]}