{"thread":{"id":"26816","subject":"[WIP PATCH 02/10] Reminder to fix o->call_depth handling in conflict_rename_rename_1to2","startedAt":"2011-03-21T18:30:54Z","lastAt":"2011-03-21T18:31:04Z","messageCount":11,"participants":["Elijah Newren"],"isPatch":true,"patchVersion":1,"patchTotal":10},"messages":[{"id":"163942","messageId":"1300732264-9638-1-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":null,"subject":"[WIP PATCH 00/10] Work-in-progress merge-recursive work","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:30:54Z","receivedAt":"2011-03-21T18:30:54Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"This is a set of changes I was working on last Sept-Nov; these changes\nare not ready for git.git (other than perhaps patches 1 and 5), but I\nfigured it might be useful to send anyway because (1) it might serve\nas notification to others of various edge and corner case issues that\nexist, and (2) the work won't die if my laptop harddrive does.\n\nI'm expecting to clean these up, though it may take me a month or two\nbefore I get the time.\n\n(Note that patch 5 contains a subset of b9b3eef (merge-recursive:\ntweak magic band-aid 2011-03-17); I could have saved Junio some time\nif I had submitted that earlier, and had remembered that change\ninstead of propagating the mistake.  Sorry about that.)\n\nMost of what is included are new testcases exposing edge and corner\ncase bugs in merge-recursive that I found in a code audit, some of\nwhich are marked as test_expect_success even though the test does not\nsucceed.  There may be a few tests that are just cases I was testing\nfor completeness or that I was worried about breaking with planned\nchanges.  I didn't try to figure out the right place to add all these\ntests (we're out of space in the t60[23]x range and I wasn't sure if I\nwanted to dump all of these into t6036), so I just named them\ntempxx.sh.\n\nElijah Newren (10):\n  merge-recursive: Remove redundant check for removing rename source\n  Reminder to fix o->call_depth handling in conflict_rename_rename_1to2\n  A bunch of fixes and FIXMEs\n  Correct a comment\n  merge-recursive: Fix sorting order and directory change assumptions\n  Add a comment pointing out a bug\n  Good testcases\n  More test scripts\n  Tests and fixes associated with rename/rename conflicts\n  Add new testcase (temp14) showing how undetected renames can cause or\n    spuriously avoid merge conflicts\n\n merge-recursive.c   |   86 ++++++++++++++++++++++--------\n t/t6020-merge-df.sh |   26 ++++++---\n t/temp1.sh          |  146 +++++++++++++++++++++++++++++++++++++++++++++++++++\n t/temp10.sh         |   41 ++++++++++++++\n t/temp11.sh         |   41 ++++++++++++++\n t/temp12.sh         |   59 +++++++++++++++++++++\n t/temp13.sh         |   79 +++++++++++++++++++++++++++\n t/temp14.sh         |   63 ++++++++++++++++++++++\n t/temp2.sh          |   86 ++++++++++++++++++++++++++++++\n t/temp3.sh          |   72 +++++++++++++++++++++++++\n t/temp4.sh          |   96 +++++++++++++++++++++++++++++++++\n t/temp5.sh          |   35 ++++++++++++\n t/temp50.sh         |   38 +++++++++++++\n t/temp6.sh          |   55 +++++++++++++++++++\n t/temp7.sh          |   34 ++++++++++++\n t/temp8.sh          |   37 +++++++++++++\n t/temp9.sh          |   73 +++++++++++++++++++++++++\n 17 files changed, 1036 insertions(+), 31 deletions(-)\n create mode 100644 t/temp1.sh\n create mode 100755 t/temp10.sh\n create mode 100755 t/temp11.sh\n create mode 100755 t/temp12.sh\n create mode 100755 t/temp13.sh\n create mode 100755 t/temp14.sh\n create mode 100755 t/temp2.sh\n create mode 100755 t/temp3.sh\n create mode 100755 t/temp4.sh\n create mode 100755 t/temp5.sh\n create mode 100755 t/temp50.sh\n create mode 100755 t/temp6.sh\n create mode 100755 t/temp7.sh\n create mode 100755 t/temp8.sh\n create mode 100755 t/temp9.sh\n\n-- \n1.7.4\n"},{"id":"163943","messageId":"1300732264-9638-2-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":"1300732264-9638-1-git-send-email-newren@gmail.com","subject":"[WIP PATCH 01/10] merge-recursive: Remove redundant check for removing rename source","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:30:55Z","receivedAt":"2011-03-21T18:30:55Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"If a file is renamed on the side of history being merged into head, the\nfilename serving as the source of that rename needs to be removed from the\nworking directory.  (This statement is about a real merge rather than a\nmerge of merge bases to come up with a virtual ancestor; in the latter\ncase, no working directory changes of any type should be made).\n\nThe path to getting the above statement implemented in merge-recursive took\nseveral steps.  The relevant bits of code may be instructive to keep in\nmind for the explanation, especially since an English-only description\ninvolves double negatives that are hard to follow.  These bits of code are:\n  int remove_file(..., const char *path, int no_wd)\n  {\n    ...\n    int update_working_directory = !o->call_depth && !no_wd;\nand\n  remove_file(o, 1, ren1_src, <expression>);\nWhere the choice for <expression> has morphed over time:\n\n65ac6e9 (merge-recursive: adjust to loosened \"working file clobbered\"\ncheck 2006-10-27), introduced the \"no_wd\" parameter to remove_file() and\nused \"1\" for <expression>.  This meant ren1_src was never deleted, leaving\nit around in the working copy.\n\nIn 8371234 (Remove uncontested renamed files during merge. 2006-12-13),\n<expression> was changed to \"index_only\" (where index_only ==\n!!o->call_depth; see b7fa51da).   This was equivalent to using \"0\" for\n<expression> (due to the early logic in remove_file), and is orthogonal to\nthe condition we actually want to check at this point; it resulted in the\nsource file being removed except when index_only was false.  This was\nproblematic because the file could have been renamed on the side of history\nincluding head, in which case ren1_src could correspond to an untracked\nfile that should not be deleted.\n\nIn 183d797 (Keep untracked files not involved in a merge. 2007-02-04),\n<expression> was changed to \"index_only || stage == 3\".  While this gives\ncorrect behavior, the \"index_only ||\" portion of <expression> is\nunnecessary and makes the code slightly harder to follow.\n\nThere were also two further changes to this expression, though without\nany change in behavior.  First in b7fa51d (merge-recursive: get rid of the\nindex_only global variable 2008-09-02), it was changed to \"o->call_depth\n|| stage == 3\".  (index_only == !!o->call_depth).  Earlier in this series\n(merge-recursive: Small code clarification -- variable name and comments),\nthis was changed to \"o->call_depth || renamed_stage == 2\" (where stage\nwas renamed to other_stage and renamed_stage == other_stage ^ 1).\n\nSo we ended with <expression> being \"o->call_depth || renamed_stage == 2\".\nBut the \"o->call_depth ||\" piece was unnecessary.  Remove it.\n---\n merge-recursive.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 42d52cb..6dc74dc 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1055,7 +1055,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tint renamed_stage = a_renames == renames1 ? 2 : 3;\n \t\t\tint other_stage =   a_renames == renames1 ? 3 : 2;\n \n-\t\t\tremove_file(o, 1, ren1_src, o->call_depth || renamed_stage == 2);\n+\t\t\tremove_file(o, 1, ren1_src, renamed_stage == 2);\n \n \t\t\thashcpy(src_other.sha1, ren1->src_entry->stages[other_stage].sha);\n \t\t\tsrc_other.mode = ren1->src_entry->stages[other_stage].mode;\n-- \n1.7.4\n"},{"id":"163937","messageId":"1300732264-9638-3-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":"1300732264-9638-1-git-send-email-newren@gmail.com","subject":"[WIP PATCH 02/10] Reminder to fix o->call_depth handling in conflict_rename_rename_1to2","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:30:56Z","receivedAt":"2011-03-21T18:30:56Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"---\n merge-recursive.c |    4 ++++\n 1 files changed, 4 insertions(+), 0 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 6dc74dc..8d28a54 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -908,6 +908,10 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t       ren2_dst, branch1, dst_name2);\n \t}\n \tif (o->call_depth) {\n+\t\t/*\n+\t\t * FIXME: This is bogus.  dst_nameX are not in cache in\n+\t\t * case of D/F conflict.\n+\t\t */\n \t\tremove_file_from_cache(dst_name1);\n \t\tremove_file_from_cache(dst_name2);\n \t\t/*\n-- \n1.7.4\n"},{"id":"163938","messageId":"1300732264-9638-4-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":"1300732264-9638-1-git-send-email-newren@gmail.com","subject":"[WIP PATCH 03/10] A bunch of fixes and FIXMEs","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:30:57Z","receivedAt":"2011-03-21T18:30:57Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"---\n merge-recursive.c |   16 ++++++++++++----\n 1 files changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 8d28a54..fde9f9e 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -910,7 +910,8 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \tif (o->call_depth) {\n \t\t/*\n \t\t * FIXME: This is bogus.  dst_nameX are not in cache in\n-\t\t * case of D/F conflict.\n+\t\t * case of D/F conflict.  renX_dst are.  Simple typo, or\n+\t\t * something worse?\n \t\t */\n \t\tremove_file_from_cache(dst_name1);\n \t\tremove_file_from_cache(dst_name2);\n@@ -1290,10 +1291,12 @@ static int merge_content(struct merge_options *o,\n \t\tconst char *new_path;\n \t\tupdate_file_flags(o, mfi.sha, mfi.mode, path,\n \t\t\t\t  o->call_depth || mfi.clean, 0);\n-\t\tnew_path = unique_path(o, path, df_rename_conflict_branch);\n \t\tmfi.clean = 0;\n-\t\toutput(o, 1, \"Adding as %s instead\", new_path);\n-\t\tupdate_file_flags(o, mfi.sha, mfi.mode, new_path, 0, 1);\n+\t\tif (!o->call_depth) {\n+\t\t\tnew_path = unique_path(o, path, df_rename_conflict_branch);\n+\t\t\toutput(o, 1, \"Adding as %s instead\", new_path);\n+\t\t\tupdate_file(o, mfi.clean, mfi.sha, mfi.mode, new_path);\n+\t\t}\n \t} else {\n \t\tupdate_file(o, mfi.clean, mfi.sha, mfi.mode, path);\n \t}\n@@ -1433,6 +1436,11 @@ static int process_df_entry(struct merge_options *o,\n \t\t\t       src, conflict_info->pair2->two->path, conflict_info->branch2,\n \t\t\t       o->call_depth ? \" (left unresolved)\" : \"\");\n \t\t\tif (o->call_depth) {\n+\t\t\t\t/*\n+\t\t\t\t * FIXME: Why remove file from cache, and\n+\t\t\t\t * then immediately readd it?  Why not just\n+\t\t\t\t * overwrite using update_file only?\n+\t\t\t\t */\n \t\t\t\tremove_file_from_cache(src);\n \t\t\t\tupdate_file(o, 0, conflict_info->pair1->one->sha1,\n \t\t\t\t\t    conflict_info->pair1->one->mode, src);\n-- \n1.7.4\n"},{"id":"163939","messageId":"1300732264-9638-5-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":"1300732264-9638-1-git-send-email-newren@gmail.com","subject":"[WIP PATCH 04/10] Correct a comment","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:30:58Z","receivedAt":"2011-03-21T18:30:58Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"---\n merge-recursive.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex fde9f9e..1da385c 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1464,7 +1464,7 @@ static int process_df_entry(struct merge_options *o,\n \t\thandle_delete_modify(o, path, new_path,\n \t\t\t\t     a_sha, a_mode, b_sha, b_mode);\n \t} else if (!o_sha && !!a_sha != !!b_sha) {\n-\t\t/* directory -> (directory, file) */\n+\t\t/* directory -> (directory, file) or <nothing> -> (directory, file) */\n \t\tconst char *add_branch;\n \t\tconst char *other_branch;\n \t\tunsigned mode;\n-- \n1.7.4\n"},{"id":"163941","messageId":"1300732264-9638-6-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":"1300732264-9638-1-git-send-email-newren@gmail.com","subject":"[WIP PATCH 05/10] merge-recursive: Fix sorting order and directory change assumptions","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:30:59Z","receivedAt":"2011-03-21T18:30:59Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"As pointed out in the review of my earlier series, we cannot assume that\ndirectory/file conflicts will appear in sorted order; for example,\n'letters.txt' comes between 'letters' and 'letters/file'.\n\nAlso, we should not make changes to the working directory when merging\nmerge-bases, so removing files to make room for directories of d/f\nconflicts should be skipped in such cases.\n\n(These changes were made last September/October, but were mixed in with\nan update of my series that I never got around to resubmitting.  Now that\nthe original series was accepted, I split these changes back out.)\n---\n merge-recursive.c   |   38 +++++++++++++++++++++++++++++++++-----\n t/t6020-merge-df.sh |   26 ++++++++++++++++++--------\n 2 files changed, 51 insertions(+), 13 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1da385c..7a04ca7 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -344,6 +344,28 @@ static struct string_list *get_unmerged(void)\n \treturn unmerged;\n }\n \n+static int string_list_df_name_compare(const void *a, const void *b)\n+{\n+\tconst struct string_list_item *one = a;\n+\tconst struct string_list_item *two = b;\n+\t/*\n+\t * Here we only care that entries for D/F conflicts are\n+\t * adjacent, in particular with the file of the D/F conflict\n+\t * appearing before files below the corresponding directory.\n+\t * The order of the rest of the list is irrelevant for us.\n+\t *\n+\t * To achieve this, we sort with df_name_compare and provide\n+\t * the mode S_IFDIR so that D/F conflicts will sort correctly.\n+\t * We use the mode S_IFDIR for everything else for simplicity,\n+\t * since in other cases any changes in their order due to\n+\t * sorting cause no problems for us.\n+\t */\n+\treturn df_name_compare(one->string, strlen(one->string), S_IFDIR,\n+\t\t\t       two->string, strlen(two->string), S_IFDIR);\n+}\n+\n+\n+\n static void make_room_for_directories_of_df_conflicts(struct merge_options *o,\n \t\t\t\t\t\t      struct string_list *entries)\n {\n@@ -356,17 +378,23 @@ static void make_room_for_directories_of_df_conflicts(struct merge_options *o,\n \t * otherwise, if the file is not supposed to be removed by the\n \t * merge, the contents of the file will be placed in another\n \t * unique filename.\n-\t *\n-\t * NOTE: This function relies on the fact that entries for a\n-\t * D/F conflict will appear adjacent in the index, with the\n-\t * entries for the file appearing before entries for paths\n-\t * below the corresponding directory.\n \t */\n \tconst char *last_file = NULL;\n \tint last_len = 0;\n \tstruct stage_data *last_e;\n \tint i;\n \n+\t/*\n+\t * If we're merging merge-bases, we don't want to bother with\n+\t * any working directory changes.\n+\t */\n+\tif (o->call_depth)\n+\t\treturn;\n+\n+\t/* Ensure D/F conflicts are adjacent in the entries list. */\n+\tqsort(entries->items, entries->nr, sizeof(*entries->items),\n+\t      string_list_df_name_compare);\n+\n \tfor (i = 0; i < entries->nr; i++) {\n \t\tconst char *path = entries->items[i].string;\n \t\tint len = strlen(path);\ndiff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh\nindex eec8f4e..27c3d73 100755\n--- a/t/t6020-merge-df.sh\n+++ b/t/t6020-merge-df.sh\n@@ -59,15 +59,19 @@ test_expect_success 'setup modify/delete + directory/file conflict' '\n \tgit add letters &&\n \tgit commit -m initial &&\n \n+\t# Throw in letters.txt for sorting order fun\n+\t# (\"letters.txt\" sorts between \"letters\" and \"letters/file\")\n \techo i >>letters &&\n-\tgit add letters &&\n+\techo \"version 2\" >letters.txt &&\n+\tgit add letters letters.txt &&\n \tgit commit -m modified &&\n \n \tgit checkout -b delete HEAD^ &&\n \tgit rm letters &&\n \tmkdir letters &&\n \t>letters/file &&\n-\tgit add letters &&\n+\techo \"version 1\" >letters.txt &&\n+\tgit add letters letters.txt &&\n \tgit commit -m deleted\n '\n \n@@ -75,25 +79,31 @@ test_expect_success 'modify/delete + directory/file conflict' '\n \tgit checkout delete^0 &&\n \ttest_must_fail git merge modify &&\n \n-\ttest 3 = $(git ls-files -s | wc -l) &&\n-\ttest 2 = $(git ls-files -u | wc -l) &&\n-\ttest 1 = $(git ls-files -o | wc -l) &&\n+\ttest 5 -eq $(git ls-files -s | wc -l) &&\n+\ttest 4 -eq $(git ls-files -u | wc -l) &&\n+\ttest 1 -eq $(git ls-files -o | wc -l) &&\n \n \ttest -f letters/file &&\n+\ttest -f letters.txt &&\n \ttest -f letters~modify\n '\n \n test_expect_success 'modify/delete + directory/file conflict; other way' '\n+\t# Yes, we really need the double reset since \"letters\" appears as\n+\t# both a file and a directory.\n+\tgit reset --hard &&\n \tgit reset --hard &&\n \tgit clean -f &&\n \tgit checkout modify^0 &&\n+\n \ttest_must_fail git merge delete &&\n \n-\ttest 3 = $(git ls-files -s | wc -l) &&\n-\ttest 2 = $(git ls-files -u | wc -l) &&\n-\ttest 1 = $(git ls-files -o | wc -l) &&\n+\ttest 5 -eq $(git ls-files -s | wc -l) &&\n+\ttest 4 -eq $(git ls-files -u | wc -l) &&\n+\ttest 1 -eq $(git ls-files -o | wc -l) &&\n \n \ttest -f letters/file &&\n+\ttest -f letters.txt &&\n \ttest -f letters~HEAD\n '\n \n-- \n1.7.4\n"},{"id":"163940","messageId":"1300732264-9638-7-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":"1300732264-9638-1-git-send-email-newren@gmail.com","subject":"[WIP PATCH 06/10] Add a comment pointing out a bug","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:31:00Z","receivedAt":"2011-03-21T18:31:00Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"---\n merge-recursive.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 7a04ca7..544c504 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1064,7 +1064,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t      ren1->dst_entry,\n \t\t\t\t\t\t\t      ren2->dst_entry);\n \t\t\t} else {\n-\t\t\t\tremove_file(o, 1, ren1_src, 1);\n+\t\t\t\tremove_file(o, 1, ren1_src, 1);  // BAD\n \t\t\t\tupdate_stages_and_entry(ren1_dst,\n \t\t\t\t\t\t\tren1->dst_entry,\n \t\t\t\t\t\t\tren1->pair->one,\n-- \n1.7.4\n"},{"id":"163946","messageId":"1300732264-9638-8-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":"1300732264-9638-1-git-send-email-newren@gmail.com","subject":"[WIP PATCH 07/10] Good testcases","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:31:01Z","receivedAt":"2011-03-21T18:31:01Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"---\n t/temp1.sh |  146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n t/temp2.sh |   86 +++++++++++++++++++++++++++++++++++\n t/temp3.sh |   72 +++++++++++++++++++++++++++++\n t/temp4.sh |   96 +++++++++++++++++++++++++++++++++++++++\n t/temp5.sh |   35 ++++++++++++++\n t/temp6.sh |   55 ++++++++++++++++++++++\n t/temp7.sh |   34 ++++++++++++++\n t/temp8.sh |   37 +++++++++++++++\n 8 files changed, 561 insertions(+), 0 deletions(-)\n create mode 100644 t/temp1.sh\n create mode 100755 t/temp2.sh\n create mode 100755 t/temp3.sh\n create mode 100755 t/temp4.sh\n create mode 100755 t/temp5.sh\n create mode 100755 t/temp6.sh\n create mode 100755 t/temp7.sh\n create mode 100755 t/temp8.sh\n\ndiff --git a/t/temp1.sh b/t/temp1.sh\nnew file mode 100644\nindex 0000000..d7075f5\n--- /dev/null\n+++ b/t/temp1.sh\n@@ -0,0 +1,146 @@\n+#!/bin/sh\n+\n+test_description='recursive merge corner cases: d/f conflict (via add/add) + criss-cross merge'\n+\n+. ./test-lib.sh\n+\n+#\n+# Let's make it a bit more coplicated:\n+#   Commit A: Neither file 'a' nor directory 'a/' exist.\n+#   Commit B: Introduce 'a'\n+#   Commit C: Introduce 'a/file'\n+# Two different later cases:\n+#   Commit D1:  Merge B & C, keeping 'a' and deleting 'a/'\n+#   Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'\n+#\n+#   Commit D2:  Merge B & C, keeping a modified 'a' and deleting 'a/'\n+#   Commit E2: Merge B & C, deleting 'a' but keeping a modified 'a/file'\n+# Finally, someone goes to merge D1&E1 or D1&E2 or D2&E1.  What happens?\n+#\n+#      B   D1 or D2\n+#      o---o\n+#     / \\ / \\\n+#  A o   X   ? F\n+#     \\ / \\ /\n+#      o---o\n+#      C   E1 or E2\n+#\n+\n+test_expect_success 'setup differently handled merges of directory/file conflict' '\n+\t>irrelevant-file &&\n+\tgit add irrelevant-file &&\n+\ttest_tick &&\n+\tgit commit -m A &&\n+\n+\tgit branch B &&\n+\tgit checkout -b C &&\n+\tmkdir a &&\n+\techo 10 >a/file &&\n+\tgit add a/file &&\n+\ttest_tick &&\n+\tgit commit -m C &&\n+\n+\tgit checkout B &&\n+\techo 5 >a &&\n+\tgit add a &&\n+\ttest_tick &&\n+\tgit commit -m B &&\n+\n+\tgit checkout B^0 &&\n+\ttest_must_fail git merge C &&\n+\tgit clean -f &&\n+\trm -rf a/ &&\n+\techo 5 >a &&\n+\tgit add a &&\n+\ttest_tick &&\n+\tgit commit -m D &&\n+\tgit tag D &&\n+\n+\tgit checkout C^0 &&\n+\ttest_must_fail git merge B &&\n+\tgit clean -f &&\n+\techo 10 >a/file &&\n+\tgit add a/file &&\n+\tgit rm --cached a &&\n+\ttest_tick &&\n+\tgit commit -m E1 &&\n+\tgit tag E1 &&\n+\n+\tgit checkout C^0 &&\n+\ttest_must_fail git merge B &&\n+\tgit clean -f &&\n+\tprintf \"10\\n11\\n\" >a/file &&\n+\tgit add a/file &&\n+\tgit rm --cached a &&\n+\ttest_tick &&\n+\tgit commit -m E2 &&\n+\tgit tag E2\n+'\n+\n+test_expect_success 'git detects conflict and handles merge of D & E1 correctly' '\n+\tgit reset --hard &&\n+\tgit reset --hard &&\n+\tgit clean -fdqx &&\n+\tgit checkout D^0 &&\n+\n+\ttest_must_fail git merge -s recursive E1^0 &&\n+\n+\ttest 3 -eq $(git ls-files -s | wc -l) &&\n+\ttest 2 -eq $(git ls-files -u | wc -l) &&\n+\ttest 1 -eq $(git ls-files -o | wc -l) &&\n+\n+\ttest $(git rev-parse :2:a) = $(git rev-parse B:a) &&\n+\ttest $(git rev-parse :3:a/file) = $(git rev-parse C:a/file)\n+'\n+\n+test_expect_failure 'git detects conflict and handles merge of E1 & D correctly' '\n+\tgit reset --hard &&\n+\tgit reset --hard &&\n+\tgit clean -fdqx &&\n+\tgit checkout E1^0 &&\n+\n+\ttest_must_fail git merge -s recursive D^0 &&\n+\n+\ttest 3 -eq $(git ls-files -s | wc -l) &&\n+\ttest 2 -eq $(git ls-files -u | wc -l) &&\n+\ttest 1 -eq $(git ls-files -o | wc -l) &&\n+\n+\ttest $(git rev-parse :3:a) = $(git rev-parse B:a) &&\n+\ttest $(git rev-parse :2:a/file) = $(git rev-parse C:a/file)\n+'\n+\n+test_expect_success 'git detects conflict and handles merge of D & E2 correctly' '\n+\tgit reset --hard &&\n+\tgit reset --hard &&\n+\tgit clean -fdqx &&\n+\tgit checkout D^0 &&\n+\n+\ttest_must_fail git merge -s recursive E2^0 &&\n+\n+\ttest 3 -eq $(git ls-files -s | wc -l) &&\n+\ttest 2 -eq $(git ls-files -u | wc -l) &&\n+\ttest 1 -eq $(git ls-files -o | wc -l) &&\n+\n+\ttest $(git rev-parse :2:a) = $(git rev-parse B:a) &&\n+\ttest $(git rev-parse :3:a/file) = $(git rev-parse E1:a/file)\n+\ttest $(git rev-parse :1:a/file) = $(git rev-parse C:a/file)\n+'\n+\n+test_expect_success 'git detects conflict and handles merge of E2 & D correctly' '\n+\tgit reset --hard &&\n+\tgit reset --hard &&\n+\tgit clean -fdqx &&\n+\tgit checkout E2^0 &&\n+\n+\ttest_must_fail git merge -s recursive D^0 &&\n+\n+\ttest 3 -eq $(git ls-files -s | wc -l) &&\n+\ttest 2 -eq $(git ls-files -u | wc -l) &&\n+\ttest 1 -eq $(git ls-files -o | wc -l) &&\n+\n+\ttest $(git rev-parse :3:a) = $(git rev-parse B:a) &&\n+\ttest $(git rev-parse :2:a/file) = $(git rev-parse E1:a/file)\n+\ttest $(git rev-parse :1:a/file) = $(git rev-parse C:a/file)\n+'\n+\n+test_done\ndiff --git a/t/temp2.sh b/t/temp2.sh\nnew file mode 100755\nindex 0000000..46a8c93\n--- /dev/null\n+++ b/t/temp2.sh\n@@ -0,0 +1,86 @@\n+#!/bin/sh\n+\n+test_description='recursive merge corner case: content conflict + criss-cross merge'\n+\n+. ./test-lib.sh\n+\n+#\n+# Let's make a really contrived but simple criss-cross merge case:\n+#\n+#      B   D\n+#      o---o\n+#     / \\ / \\\n+#  A o   X   ? F\n+#     \\ / \\ /\n+#      o---o\n+#      C   E\n+#\n+#   Commit A: file with contents 'A\\n'\n+#   Commit B: file with contents 'B\\n'\n+#   Commit C: file with contents 'C\\n'\n+#   Commit D: file with contents 'D\\n'\n+#   Commit E: file with contents:\n+#      <<<<<<< Temporary merge branch 1\n+#      C\n+#      =======\n+#      B\n+#      >>>>>>> Temporary merge branch 2\n+#\n+# Now, when we merge commits D & E, does git detect the conflict?\n+\n+test_expect_success 'setup differently handled merges of content conflict' '\n+\techo A >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m A &&\n+\n+\tgit branch B &&\n+\tgit checkout -b C &&\n+\techo C >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m C &&\n+\n+\tgit checkout B &&\n+\techo B >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m B &&\n+\n+\tgit checkout B^0 &&\n+\ttest_must_fail git merge C &&\n+\techo D >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m D &&\n+\tgit tag D &&\n+\n+\tgit checkout C^0 &&\n+\ttest_must_fail git merge B &&\n+\tcat <<EOF >file &&\n+<<<<<<< Temporary merge branch 1\n+C\n+=======\n+B\n+>>>>>>> Temporary merge branch 2\n+EOF\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m E &&\n+\tgit tag E\n+'\n+\n+test_expect_success 'git detects conflict merging D & E' '\n+\tgit checkout D^0 &&\n+\n+\ttest_must_fail git merge -s recursive E^0 &&\n+\n+\ttest 3 -eq $(git ls-files -s | wc -l) &&\n+\ttest 3 -eq $(git ls-files -u | wc -l) &&\n+\ttest 0 -eq $(git ls-files -o | wc -l) &&\n+\n+\ttest $(git rev-parse :2:file) = $(git rev-parse D:file) &&\n+\ttest $(git rev-parse :3:file) = $(git rev-parse E:file)\n+'\n+\n+test_done\ndiff --git a/t/temp3.sh b/t/temp3.sh\nnew file mode 100755\nindex 0000000..3f4ceb1\n--- /dev/null\n+++ b/t/temp3.sh\n@@ -0,0 +1,72 @@\n+#!/bin/sh\n+\n+test_description='recursive merge corner case: delete/modify + criss-cross merge'\n+\n+. ./test-lib.sh\n+\n+#\n+# Standard setup:\n+#\n+#      B   D\n+#      o---o\n+#     / \\ / \\\n+#  A o   X   ? F\n+#     \\ / \\ /\n+#      o---o\n+#      C   E\n+#\n+#   Commit A: file with contents 'A\\n'\n+#   Commit B: file with contents 'B\\n'\n+#   Commit C: file not present\n+#   Commit D: file with contents 'B\\n'\n+#   Commit E: file not present\n+#\n+# Now, when we merge commits D & E, does git detect the conflict?\n+\n+test_expect_success 'setup differently handled merges of content conflict' '\n+\techo A >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m A &&\n+\n+\tgit branch B &&\n+\tgit checkout -b C &&\n+\tgit rm file &&\n+\ttest_tick &&\n+\tgit commit -m C &&\n+\n+\tgit checkout B &&\n+\techo B >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m B &&\n+\n+\tgit checkout B^0 &&\n+\ttest_must_fail git merge C &&\n+\techo B >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m D &&\n+\tgit tag D &&\n+\n+\tgit checkout C^0 &&\n+\ttest_must_fail git merge B &&\n+\tgit rm file &&\n+\ttest_tick &&\n+\tgit commit -m E &&\n+\tgit tag E\n+'\n+\n+test_expect_success 'git detects conflict merging D & E' '\n+\tgit checkout D^0 &&\n+\n+\ttest_must_fail git merge -s recursive E^0 &&\n+\n+\ttest 2 -eq $(git ls-files -s | wc -l) &&\n+\ttest 2 -eq $(git ls-files -u | wc -l) &&\n+\n+\ttest $(git rev-parse :1:file) = $(git rev-parse B:file) &&\n+\ttest $(git rev-parse :2:file) = $(git rev-parse B:file)\n+'\n+\n+test_done\ndiff --git a/t/temp4.sh b/t/temp4.sh\nnew file mode 100755\nindex 0000000..2014f5b\n--- /dev/null\n+++ b/t/temp4.sh\n@@ -0,0 +1,96 @@\n+#!/bin/sh\n+\n+test_description='recursive merge rename+content merge+D/F conflict'\n+\n+. ./test-lib.sh\n+\n+# Examples where rename/directory conflicts can cause changes to the file\n+# not on the same side of history as the rename to be dropped.\n+\n+test_expect_success 'setup content merge + rename/directory conflict' '\n+\tprintf \"1\\n2\\n3\\n4\\n5\\n6\\n\" >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m base &&\n+\tgit tag base &&\n+\n+\tgit checkout -b right &&\n+\techo 7 >>file &&\n+\tmkdir newfile &&\n+\techo junk >newfile/realfile &&\n+\tgit add file newfile/realfile &&\n+\ttest_tick &&\n+\tgit commit -m right &&\n+\n+\tgit checkout -b left-conflict base &&\n+\techo 8 >>file &&\n+\tgit add file &&\n+\tgit mv file newfile &&\n+\ttest_tick &&\n+\tgit commit -m left &&\n+\n+\tgit checkout -b left-clean base &&\n+\techo 0 >newfile &&\n+\tcat file >>newfile &&\n+\tgit add newfile &&\n+\tgit rm file &&\n+\ttest_tick &&\n+\tgit commit -m left\n+'\n+\n+test_expect_success 'rename/directory conflict + clean content merge' '\n+\tgit reset --hard &&\n+\tgit reset --hard &&\n+\tgit clean -fdqx &&\n+\n+\tgit checkout left-clean^0 &&\n+\n+\ttest_must_fail git merge -s recursive right^0 &&\n+\n+\ttest 2 -eq $(git ls-files -s | wc -l) &&\n+\ttest 1 -eq $(git ls-files -u | wc -l) &&\n+\ttest 1 -eq $(git ls-files -o | wc -l) &&\n+\n+\techo 0 >expect &&\n+\tgit cat-file -p base:file >>expect &&\n+\techo 7 >>expect &&\n+\ttest_cmp expect newfile~HEAD &&\n+\n+\ttest $(git rev-parse :2:newfile) = $(git hash-object expect) &&\n+\n+\ttest -f newfile/realfile &&\n+\ttest -f newfile~HEAD\n+'\n+\n+test_expect_success 'rename/directory conflict + content merge conflict' '\n+\tgit reset --hard &&\n+\tgit reset --hard &&\n+\tgit clean -fdqx &&\n+\n+\tgit checkout left-conflict^0 &&\n+\n+\ttest_must_fail git merge -s recursive right^0 &&\n+\n+\ttest 4 -eq $(git ls-files -s | wc -l) &&\n+\ttest 3 -eq $(git ls-files -u | wc -l) &&\n+\ttest 1 -eq $(git ls-files -o | wc -l) &&\n+\n+\tgit cat-file -p left:newfile >left &&\n+\tgit cat-file -p base:file    >base &&\n+\tgit cat-file -p right:file   >right &&\n+\ttest_must_fail git merge-file \\\n+\t\t-L \"HEAD:newfile\" \\\n+\t\t-L \"\" \\\n+\t\t-L \"right^0:file\" \\\n+\t\tleft base right &&\n+\ttest_cmp left newfile~HEAD &&\n+\n+\ttest $(git rev-parse :1:newfile) = $(git rev-parse base:file) &&\n+\ttest $(git rev-parse :2:newfile) = $(git rev-parse left:newfile) &&\n+\ttest $(git rev-parse :3:newfile) = $(git rev-parse right:file) &&\n+\n+\ttest -f newfile/realfile &&\n+\ttest -f newfile~HEAD\n+'\n+\n+test_done\ndiff --git a/t/temp5.sh b/t/temp5.sh\nnew file mode 100755\nindex 0000000..bc50780\n--- /dev/null\n+++ b/t/temp5.sh\n@@ -0,0 +1,35 @@\n+#!/bin/sh\n+\n+test_description=\"Does git preserve Gollum's precious artifact?\"\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup abbreviated storyline' '\n+\techo \"A pretty inscription\" >ring &&\n+\tgit add ring &&\n+\ttest_tick &&\n+\tgit commit -m beginning &&\n+\n+\tgit branch people &&\n+\tgit checkout -b rename-the-ring &&\n+\tgit mv ring one-ring-to-rule-them-all &&\n+\ttest_tick &&\n+\tgit commit -m fullname &&\n+\n+\tgit checkout people &&\n+\tgit rm ring &&\n+\techo gollum >owner &&\n+\tgit add owner &&\n+\ttest_tick &&\n+\tgit commit -m track-people-instead-of-objects &&\n+\techo \"Myyy PRECIOUSSS\" >ring\n+'\n+\n+test_expect_success 'Does git preserve the ring?' '\n+\ttest_must_fail git merge -s recursive rename-the-ring &&\n+\n+\t# Make sure git did not delete an untracked file\n+\ttest -f ring\n+'\n+\n+test_done\ndiff --git a/t/temp6.sh b/t/temp6.sh\nnew file mode 100755\nindex 0000000..da00050\n--- /dev/null\n+++ b/t/temp6.sh\n@@ -0,0 +1,55 @@\n+#!/bin/sh\n+\n+test_description='recursive merge rename+content merge+D/F conflict goes away'\n+\n+. ./test-lib.sh\n+\n+# Examples where rename/directory conflicts can cause changes to the file\n+# not on the same side of history as the rename to be dropped.\n+\n+test_expect_success 'setup content merge + rename/directory conflict' '\n+\tmkdir sub &&\n+\tprintf \"1\\n2\\n3\\n4\\n5\\n6\\n\" >sub/file &&\n+\tgit add sub/file &&\n+\ttest_tick &&\n+\tgit commit -m base &&\n+\tgit tag base &&\n+\n+\tgit checkout -b right &&\n+\techo 7 >>sub/file &&\n+\tgit add sub/file &&\n+\ttest_tick &&\n+\tgit commit -m right &&\n+\n+\tgit checkout -b left base &&\n+\techo 0 >newfile &&\n+\tcat sub/file >>newfile &&\n+\tgit rm sub/file &&\n+\tmv newfile sub &&\n+\tgit add sub &&\n+\ttest_tick &&\n+\tgit commit -m left\n+'\n+\n+test_expect_success 'disappearing rename/directory conf.+clean content merge' '\n+\tgit reset --hard &&\n+\tgit reset --hard &&\n+\tgit clean -fdqx &&\n+\n+\tgit checkout left^0 &&\n+\n+\tgit merge -s recursive right^0 &&\n+\n+\ttest 1 -eq $(git ls-files -s | wc -l) &&\n+\ttest 0 -eq $(git ls-files -u | wc -l) &&\n+\ttest 0 -eq $(git ls-files -o | wc -l) &&\n+\n+\techo 0 >expect &&\n+\tgit cat-file -p base:sub/file >>expect &&\n+\techo 7 >>expect &&\n+\ttest_cmp expect sub &&\n+\n+\ttest -f sub\n+'\n+\n+test_done\ndiff --git a/t/temp7.sh b/t/temp7.sh\nnew file mode 100755\nindex 0000000..0cb5946\n--- /dev/null\n+++ b/t/temp7.sh\n@@ -0,0 +1,34 @@\n+#!/bin/sh\n+\n+test_description=\"File renamed identically on both sides + re-add source name\"\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup rename/rename/add resolvable conflict' '\n+\t>a &&\n+\tgit add a &&\n+\ttest_tick &&\n+\tgit commit -m base &&\n+\tgit tag base &&\n+\n+\tgit checkout -b one base &&\n+\tgit mv a b &&\n+\ttest_tick &&\n+\tgit commit -m one &&\n+\n+\tgit checkout -b two base &&\n+\tgit mv a b &&\n+\techo important-info >a &&\n+\ttest_tick &&\n+\tgit commit -m two\n+'\n+\n+test_expect_success 'merge include important info in new a' '\n+\tgit checkout two^0 &&\n+\tgit merge -s recursive one^0 &&\n+\n+\ttest 2 -eq $(git ls-files -s | wc -l) &&\n+\ttest 0 -eq $(git ls-files -o | wc -l)\n+'\n+\n+test_done\ndiff --git a/t/temp8.sh b/t/temp8.sh\nnew file mode 100755\nindex 0000000..0736639\n--- /dev/null\n+++ b/t/temp8.sh\n@@ -0,0 +1,37 @@\n+#!/bin/sh\n+\n+test_description=\"Rename/rename/add conflict\"\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup rename/rename (1to2) + add conflict' '\n+\techo stuff >a &&\n+\tgit add a &&\n+\ttest_tick &&\n+\tgit commit -m base &&\n+\tgit tag base &&\n+\n+\tgit checkout -b one base &&\n+\tgit mv a b &&\n+\techo precious-data >c &&\n+\tgit add c &&\n+\ttest_tick &&\n+\tgit commit -m one &&\n+\n+\tgit checkout -b two base &&\n+\tgit mv a c &&\n+\techo important-info >b &&\n+\tgit add b &&\n+\ttest_tick &&\n+\tgit commit -m two\n+'\n+\n+test_expect_success 'merge maintains important data from one:c and two:b' '\n+\tgit checkout two^0 &&\n+\ttest_must_fail git merge -s recursive one^0 &&\n+\n+\ttest 2 -eq $(git ls-files -u b | wc -l) &&\n+\ttest 2 -eq $(git ls-files -u c | wc -l)\n+'\n+\n+test_done\n-- \n1.7.4\n"},{"id":"163945","messageId":"1300732264-9638-9-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":"1300732264-9638-1-git-send-email-newren@gmail.com","subject":"[WIP PATCH 08/10] More test scripts","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:31:02Z","receivedAt":"2011-03-21T18:31:02Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"---\n t/temp10.sh |   41 +++++++++++++++++++++++++++++++++\n t/temp11.sh |   41 +++++++++++++++++++++++++++++++++\n t/temp50.sh |   38 ++++++++++++++++++++++++++++++\n t/temp9.sh  |   73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 193 insertions(+), 0 deletions(-)\n create mode 100755 t/temp10.sh\n create mode 100755 t/temp11.sh\n create mode 100755 t/temp50.sh\n create mode 100755 t/temp9.sh\n\ndiff --git a/t/temp10.sh b/t/temp10.sh\nnew file mode 100755\nindex 0000000..100c74f\n--- /dev/null\n+++ b/t/temp10.sh\n@@ -0,0 +1,41 @@\n+#!/bin/sh\n+\n+test_description='merge+diff issue: rename/add vs. copy/modify'\n+\n+. ./test-lib.sh\n+\n+# Testcase setup:\n+#   Commit A: new file: a\n+#   Commit B: modify a slightly\n+#   Commit C: rename a->b, add completely different a\n+#\n+# We should be able to merge B & C cleanly\n+\n+test_expect_success 'setup modify + rename/add(source) merge' '\n+\tprintf \"1\\n2\\n3\\n4\\n5\\n6\\n7\\n\" >a &&\n+\tgit add a &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\n+\tgit checkout -b B A &&\n+\techo 8 >>a &&\n+\tgit add a &&\n+\tgit commit -m B &&\n+\n+\tgit checkout -b C A &&\n+\tgit mv a b &&\n+\techo something completely different >a &&\n+\tgit add a &&\n+\tgit commit -m C\n+'\n+\n+test_expect_success 'no conflict merging B & C' '\n+\tgit checkout B^0 &&\n+\n+\tgit merge -s recursive C^0 &&\n+\n+\ttest $(git rev-parse B:a) = $(git rev-parse b) &&\n+\ttest $(git rev-parse C:a) = $(git rev-parse a)\n+'\n+\n+test_done\ndiff --git a/t/temp11.sh b/t/temp11.sh\nnew file mode 100755\nindex 0000000..1d8fb9a\n--- /dev/null\n+++ b/t/temp11.sh\n@@ -0,0 +1,41 @@\n+#!/bin/sh\n+\n+test_description='merge+diff issue: rename/add vs. copy/modify, v2'\n+\n+. ./test-lib.sh\n+\n+# Testcase setup:\n+#   Commit A: new file: a\n+#   Commit B: rename a->b\n+#   Commit C: rename a->c, add completely different a\n+#\n+# Merging of B & C should NOT be clean; there's a rename/rename conflict\n+\n+test_expect_success 'setup modify + rename/add(source) merge' '\n+\tprintf \"1\\n2\\n3\\n4\\n5\\n6\\n7\\n\" >a &&\n+\tgit add a &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\n+\tgit checkout -b B A &&\n+\tgit mv a b &&\n+\tgit commit -m B &&\n+\n+\tgit checkout -b C A &&\n+\tgit mv a c &&\n+\techo something completely different >a &&\n+\tgit add a &&\n+\tgit commit -m C\n+'\n+\n+test_expect_success 'detect conflict merging B & C' '\n+\tgit checkout B^0 &&\n+\n+\ttest_must_fail git merge -s recursive C^0 &&\n+\n+\ttest -f a &&\n+\ttest -f b &&\n+\ttest -f c\n+'\n+\n+test_done\ndiff --git a/t/temp50.sh b/t/temp50.sh\nnew file mode 100755\nindex 0000000..5baebec\n--- /dev/null\n+++ b/t/temp50.sh\n@@ -0,0 +1,38 @@\n+#!/bin/sh\n+\n+test_description=\"Rename/rename conflict doesn't leave files in place for user\"\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup rename/rename (1to2) conflict' '\n+\techo stuff >a &&\n+\tgit add a &&\n+\ttest_tick &&\n+\tgit commit -m base &&\n+\tgit tag base &&\n+\n+\tgit checkout -b one base &&\n+\tgit mv a b &&\n+\ttest_tick &&\n+\tgit commit -m one &&\n+\n+\tgit checkout -b two base &&\n+\tgit mv a c &&\n+\ttest_tick &&\n+\tgit commit -m two\n+'\n+\n+test_expect_success 'merge has correct working tree contents' '\n+\tgit checkout two^0 &&\n+\n+\ttest_must_fail git merge -s recursive one^0 &&\n+\n+\ttest 3 -eq $(git ls-files -s | wc -l) &&\n+\ttest 3 -eq $(git ls-files -u | wc -l) &&\n+\ttest 0 -eq $(git ls-files -o | wc -l) &&\n+\n+\ttest -f b &&\n+\ttest -f c\n+'\n+\n+test_done\ndiff --git a/t/temp9.sh b/t/temp9.sh\nnew file mode 100755\nindex 0000000..3936461\n--- /dev/null\n+++ b/t/temp9.sh\n@@ -0,0 +1,73 @@\n+#!/bin/sh\n+\n+test_description='recursive merge corner case: rename/rename + criss-cross merge + reintroduce file with name of rename source but different contents'\n+\n+. ./test-lib.sh\n+\n+#\n+# Standard setup:\n+#\n+#      B   D\n+#      o---o\n+#     / \\ / \\\n+#  A o   X   ? F\n+#     \\ / \\ /\n+#      o---o\n+#      C   E\n+#\n+#   Commit A: new file: a\n+#   Commit B: rename a->b\n+#   Commit C: rename a->c, add different a\n+#   Commit D: merge B&C, keeping a&b&c, modifying a at the beginning\n+#   Commit E: merge B&C, keeping a&b&c, modifying a at the end\n+#\n+# Now, when we merge commits D & E, there should be no conflict...\n+\n+test_expect_success 'setup rename/rename + criss-cross + new file' '\n+\tprintf \"lots\\nof\\nwords\\nand\\ncontent\\n\" >a &&\n+\tgit add a &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\n+\tgit checkout -b B A &&\n+\tgit mv a b &&\n+\tgit commit -m B &&\n+\n+\tgit checkout -b C A &&\n+\tgit mv a c &&\n+\tprintf \"2\\n3\\n4\\n5\\n6\\n7\\n\" >a &&\n+\tgit add a &&\n+\tgit commit -m C &&\n+\n+\tgit checkout B^0 &&\n+\texit 1\n+\ttest_must_fail git merge C &&\n+\tmv a old_a &&\n+\techo 1 >a &&\n+\tcat old_a >>a &&\n+\trm old_a &&\n+\tgit add -u &&\n+\tgit commit -m D &&\n+\tgit tag D &&\n+\n+\tgit checkout C^0 &&\n+\ttest_must_fail git merge B &&\n+\techo 6 >> a &&\n+\tgit add -u &&\n+\tgit commit -m E &&\n+\tgit tag E\n+'\n+\n+test_expect_success 'no conflict merging D & E' '\n+\tgit checkout D^0 &&\n+\n+\tgit merge -s recursive E^0 &&\n+\n+\ttest 3 -eq $(git ls-files -s | wc -l) &&\n+\ttest 0 -eq $(git ls-files -u | wc -l) &&\n+\ttest 0 -eq $(git ls-files -o | wc -l) &&\n+\n+\ttest 6 -eq $(wc -l < a)\n+'\n+\n+test_done\n-- \n1.7.4\n"},{"id":"163944","messageId":"1300732264-9638-10-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":"1300732264-9638-1-git-send-email-newren@gmail.com","subject":"[WIP PATCH 09/10] Tests and fixes associated with rename/rename conflicts","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:31:03Z","receivedAt":"2011-03-21T18:31:03Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Add a new testcase for basic rename/rename (+modify/modify) -- temp12\nAdd a testcase for tough-to-handle rename/rename + criss-cross merges\n\nFix up most of temp12 testcase, but not yet handling merging of info from\nother side of history.\n---\n merge-recursive.c |   24 ++++++++--------\n t/temp12.sh       |   59 +++++++++++++++++++++++++++++++++++++++\n t/temp13.sh       |   79 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 150 insertions(+), 12 deletions(-)\n create mode 100755 t/temp12.sh\n create mode 100755 t/temp13.sh\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 544c504..cea6e27 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -991,12 +991,12 @@ static int process_renames(struct merge_options *o,\n \tfor (i = 0; i < a_renames->nr; i++) {\n \t\tsre = a_renames->items[i].util;\n \t\tstring_list_insert(&a_by_dst, sre->pair->two->path)->util\n-\t\t\t= sre->dst_entry;\n+\t\t\t= (void*)sre;\n \t}\n \tfor (i = 0; i < b_renames->nr; i++) {\n \t\tsre = b_renames->items[i].util;\n \t\tstring_list_insert(&b_by_dst, sre->pair->two->path)->util\n-\t\t\t= sre->dst_entry;\n+\t\t\t= (void*)sre;\n \t}\n \n \tfor (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {\n@@ -1110,6 +1110,16 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\tclean_merge = 0;\n \t\t\t\t\tconflict_rename_delete(o, ren1->pair, branch1, branch2);\n \t\t\t\t}\n+\t\t\t} else if ((item = string_list_lookup(renames2Dst, ren1_dst))) {\n+\t\t\t\tren2 = item->util;\n+\t\t\t\tclean_merge = 0;\n+\t\t\t\tren2->processed = 1;\n+\t\t\t\toutput(o, 1, \"CONFLICT (rename/rename): \"\n+\t\t\t\t       \"Rename %s->%s in %s. \"\n+\t\t\t\t       \"Rename %s->%s in %s\",\n+\t\t\t\t       ren1_src, ren1_dst, branch1,\n+\t\t\t\t       ren2->pair->one->path, ren2->pair->two->path, branch2);\n+\t\t\t\tconflict_rename_rename_2to1(o, ren1, branch1, ren2, branch2);\n \t\t\t} else if ((dst_other.mode == ren1->pair->two->mode) &&\n \t\t\t\t   sha_eq(dst_other.sha1, ren1->pair->two->sha1)) {\n \t\t\t\t/* Added file on the other side\n@@ -1150,16 +1160,6 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\toutput(o, 1, \"Adding as %s instead\", new_path);\n \t\t\t\t\tupdate_file(o, 0, dst_other.sha1, dst_other.mode, new_path);\n \t\t\t\t}\n-\t\t\t} else if ((item = string_list_lookup(renames2Dst, ren1_dst))) {\n-\t\t\t\tren2 = item->util;\n-\t\t\t\tclean_merge = 0;\n-\t\t\t\tren2->processed = 1;\n-\t\t\t\toutput(o, 1, \"CONFLICT (rename/rename): \"\n-\t\t\t\t       \"Rename %s->%s in %s. \"\n-\t\t\t\t       \"Rename %s->%s in %s\",\n-\t\t\t\t       ren1_src, ren1_dst, branch1,\n-\t\t\t\t       ren2->pair->one->path, ren2->pair->two->path, branch2);\n-\t\t\t\tconflict_rename_rename_2to1(o, ren1, branch1, ren2, branch2);\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \ndiff --git a/t/temp12.sh b/t/temp12.sh\nnew file mode 100755\nindex 0000000..47e657b\n--- /dev/null\n+++ b/t/temp12.sh\n@@ -0,0 +1,59 @@\n+#!/bin/sh\n+\n+test_description='rename/rename (2to1) handling'\n+\n+. ./test-lib.sh\n+\n+# Current git gets all kinds of things wrong with rename/rename (2to1); setup:\n+#   Commit A: new files: a & b\n+#   Commit B: rename a->c, modify b\n+#   Commit C: rename b->c, modify a\n+#\n+# Merging of B & C should NOT be clean.  Questions:\n+#   * Both a & b should be removed by the merge; are they?\n+#   * The two c's should contain modifications to a & b; do they?\n+#   * The index should contain two files, both for c; does it?\n+#   * The working copy should have two files, both of form c~<unique>; does it?\n+#   * Nothing else should be present.  Is anything?\n+\n+test_expect_success 'setup rename/rename (+ modify/modify)' '\n+\tprintf \"1\\n2\\n3\\n4\\n5\\n\" >a &&\n+\tprintf \"5\\n4\\n3\\n2\\n1\\n\" >b &&\n+\tgit add a b &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\n+\tgit checkout -b B A &&\n+\tgit mv a c &&\n+\techo 0 >>b &&\n+\tgit add b &&\n+\tgit commit -m B &&\n+\n+\tgit checkout -b C A &&\n+\tgit mv b c &&\n+\techo 6 >>a &&\n+\tgit add a &&\n+\tgit commit -m C\n+'\n+\n+test_expect_success 'handle rename/rename conflict correctly' '\n+\tgit checkout B^0 &&\n+\n+\ttest_must_fail git merge -s recursive C^0 >out &&\n+\tgrep \"CONFLICT (rename/rename)\" out &&\n+\n+\ttest 2 -eq $(git ls-files -s | wc -l) &&\n+\ttest 2 -eq $(git ls-files -u | wc -l) &&\n+\ttest 2 -eq $(git ls-files -u c | wc -l) &&\n+\ttest 3 -eq $(git ls-files -o | wc -l) &&\n+\n+\ttest ! -f a &&\n+\ttest ! -f b &&\n+\ttest -f c~HEAD &&\n+\ttest -f c~C^0 &&\n+\n+\ttest $(git hash-object c~HEAD) = $(git rev-parse C:a) &&\n+\ttest $(git hash-object c~B) = $(git rev-parse B:b)\n+'\n+\n+test_done\ndiff --git a/t/temp13.sh b/t/temp13.sh\nnew file mode 100755\nindex 0000000..b455a8c\n--- /dev/null\n+++ b/t/temp13.sh\n@@ -0,0 +1,79 @@\n+#!/bin/sh\n+\n+test_description='recursive merge corner case: rename/rename (2to1) + criss-cross merge + introduce funny file on other side of history'\n+\n+. ./test-lib.sh\n+\n+#\n+# Standard setup:\n+#\n+#      B   D\n+#      o---o\n+#     / \\ / \\\n+#  A o   X   ? F\n+#     \\ / \\ /\n+#      o---o\n+#      C   E\n+#\n+#   Commit A: new files: a, b\n+#   Commit B: rename a->c\n+#   Commit C: rename b->c\n+#   Commit D: merge B&C, keeping c1 (a) & c2 (b)\n+#   Commit E: merge B&C, keeping c1 (a) & c2 (b), and introducing c~HEAD, c~C\n+#\n+# Now, when we merge commits D & E, there should be no conflict...\n+\n+test_expect_success 'setup rename/rename + criss-cross + new funny files' '\n+\techo content >a &&\n+\techo other >b &&\n+\tgit add a b &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\n+\tgit checkout -b B A &&\n+\tgit mv a c &&\n+\tgit commit -m B &&\n+\n+\tgit checkout -b C A &&\n+\tgit mv b c &&\n+\tgit commit -m C &&\n+\n+\tgit checkout B^0 &&\n+\ttest_must_fail git merge C &&\n+\tgit clean -f &&\n+\tgit rm -rf . &&\n+\techo content >c1 &&\n+\techo other >c2 &&\n+\tgit add c1 c2 &&\n+\tgit commit -m D &&\n+\tgit tag D &&\n+\n+\tgit checkout C^0 &&\n+\ttest_must_fail git merge B &&\n+\tgit clean -f &&\n+\tgit rm -rf . &&\n+\tgit checkout D -- . &&\n+\tgit add c1 c2 &&\n+\tgit mv c1 \"c~Temporary merge branch 1\" &&\n+\tgit mv c2 \"c~Temporary merge branch 2\" &&\n+\tgit commit -m E &&\n+\tgit tag E\n+'\n+\n+test_expect_success 'differently renamed files conflict detected' '\n+\tgit checkout D^0 &&\n+\n+\t(\n+\t\t# Do not want to use test_must_fail here because we do not want\n+\t\t# a premature fatal exit via \"BUG: There are unmerged index\n+\t\t# entries\" (exit code 128) to count as success.  We want\n+\t\t# the code to run to completion, detect the conflict, and\n+\t\t# return an appropriate (not zero and not 128) exit code.\n+\t\tgit merge -s recursive E^0;\n+\t\texit_code=$? &&\n+\t\ttest $exit_code -gt 0 -a $exit_code -lt 128\n+\t)\n+\n+'\n+\n+test_done\n-- \n1.7.4\n"},{"id":"163947","messageId":"1300732264-9638-11-git-send-email-newren@gmail.com","threadId":"26816","inReplyTo":"1300732264-9638-1-git-send-email-newren@gmail.com","subject":"[WIP PATCH 10/10] Add new testcase (temp14) showing how undetected renames can cause or spuriously avoid merge conflicts","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2011-03-21T18:31:04Z","receivedAt":"2011-03-21T18:31:04Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"---\n t/temp14.sh |   63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 63 insertions(+), 0 deletions(-)\n create mode 100755 t/temp14.sh\n\ndiff --git a/t/temp14.sh b/t/temp14.sh\nnew file mode 100755\nindex 0000000..f82d991\n--- /dev/null\n+++ b/t/temp14.sh\n@@ -0,0 +1,63 @@\n+#!/bin/sh\n+\n+test_description='undetected renames cause merge problems'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup undetected rename causing conflict in merge' '\n+\tprintf \"1\\n2\\n3\\n4\\n5\\n\" >a &&\n+\techo foo >b &&\n+\tgit add a b &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\n+\tgit checkout -b B A &&\n+\tgit mv a c &&\n+\techo \"Completely different content\" >a &&\n+\tgit add a &&\n+\tgit commit -m B &&\n+\n+\tgit checkout -b C A &&\n+\techo 6 >>a &&\n+\tgit add a &&\n+\tgit commit -m C\n+'\n+\n+test_expect_success 'undetected rename causes conflict' '\n+\tgit checkout -q C^0 &&\n+\tgit merge -s recursive B^0 &&\n+\n+\ttest 3 -eq $(git ls-files -s | wc -l) &&\n+\ttest 0 -eq $(git ls-files -u | wc -l) &&\n+\ttest 0 -eq $(git ls-files -o | wc -l) &&\n+\n+\ttest 6 -eq $(wc -l < c) &&\n+\ttest $(git rev-parse HEAD:a) = $(git rev-parse B:a) &&\n+\ttest $(git rev-parse HEAD:b) = $(git rev-parse A:b)\n+'\n+\n+test_expect_success 'setup undetected rename causing missed merge conflict' '\n+\tgit reset --hard &&\n+\tgit clean -f &&\n+\n+\tgit checkout -b D A &&\n+\techo 7 >>a &&\n+\tgit add a &&\n+\tgit mv a c &&\n+\techo \"Completely different content\" >a &&\n+\tgit add a &&\n+\tgit commit -m D &&\n+\n+\tgit checkout -b E A &&\n+\tgit rm a &&\n+\techo \"Completely different content\" >>a &&\n+\tgit add a &&\n+\tgit commit -m E\n+'\n+\n+test_expect_success 'undetected rename causes missed conflict' '\n+\tgit checkout -q E^0 &&\n+\ttest_must_fail git merge -s recursive D^0\n+'\n+\n+test_done\n-- \n1.7.4\n"}]}