{"thread":{"id":"24308","subject":"[PATCHv2 1/5] Add additional testcases for D/F conflicts","startedAt":"2010-07-06T18:51:30Z","lastAt":"2010-07-06T20:19:35Z","messageCount":9,"participants":["newren@gmail.com","Shawn O. Pearce","Elijah Newren"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"144941","messageId":"1278442295-23033-1-git-send-email-newren@gmail.com","threadId":"24308","inReplyTo":null,"subject":"[PATCHv2 0/5] D/F conflict fixes","fromName":"","fromEmail":"newren@gmail.com","sentAt":"2010-07-06T18:51:30Z","receivedAt":"2010-07-06T18:51:30Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"This patch series fixes a number of spurious directory/file conflicts\nand associated bugs appearing in cherry-pick, rebase, merge, and\nfast-import.  It includes testsuite fixes for currently known failures\nin both t6020-merge-df.sh and t6035-merge-dir-to-symlink.sh.\n\nThe right person to review the changes other than the simple\nfast-import one is probably Dscho; in his absence, the next best as\nfar as I can tell is probably Junio or perhaps Shawn.  I hate to\noverwork them even more, so if anyone else has some time to take a\nlook or even do some simple testing, it'd be much appreciated.  Shawn\nis a natural choice for reviewing the (fairly trivial) fast-import\nchange.\n\nChanges since the previous submission:\n  * Significantly extended, clarified, or otherwise modified several\n    of the commit messages\n  * Rebased the series on top of maint (sorry about submitting\n    relative to next!)\n  * Added Alexander's signoff on his testcase\n  * Moved the new rename+D/F conflict testcase to a different file\n    since the testcase uses cherry-pick rather than merge.\n\nAlexander Gladysh (1):\n      Add a rename + D/F conflict testcase\n\nElijah Newren (4):\n      Add additional testcases for D/F conflicts\n      merge-recursive: Fix D/F conflicts\n      merge_recursive: Fix renames across paths below D/F conflicts\n      fast-import: Fix minor data-loss issue with directories becoming symlinks\n\n fast-import.c                   |    5 ++\n merge-recursive.c               |  106 ++++++++++++++++++++++++++++++++-------\n t/t3508-cherry-pick-merge-df.sh |   37 ++++++++++++++\n t/t6020-merge-df.sh             |    2 +-\n t/t6035-merge-dir-to-symlink.sh |   37 +++++++++++++-\n t/t9350-fast-export.sh          |   24 +++++++++\n 6 files changed, 190 insertions(+), 21 deletions(-)\n"},{"id":"144939","messageId":"1278442295-23033-2-git-send-email-newren@gmail.com","threadId":"24308","inReplyTo":"1278442295-23033-1-git-send-email-newren@gmail.com","subject":"[PATCHv2 1/5] Add additional testcases for D/F conflicts","fromName":"","fromEmail":"newren@gmail.com","sentAt":"2010-07-06T18:51:31Z","receivedAt":"2010-07-06T18:51:31Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n t/t6035-merge-dir-to-symlink.sh |   37 +++++++++++++++++++++++++++++++++++--\n t/t9350-fast-export.sh          |   24 ++++++++++++++++++++++++\n 2 files changed, 59 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh\nindex 3202e1d..3c176d7 100755\n--- a/t/t6035-merge-dir-to-symlink.sh\n+++ b/t/t6035-merge-dir-to-symlink.sh\n@@ -56,7 +56,7 @@ test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '\n \ttest -f a/b-2/c/d\n '\n \n-test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' '\n+test_expect_failure 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '\n \tgit reset --hard &&\n \tgit checkout baseline^0 &&\n \tgit merge -s recursive master &&\n@@ -64,6 +64,31 @@ test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' '\n \ttest -f a/b-2/c/d\n '\n \n+test_expect_failure 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '\n+\tgit reset --hard &&\n+\tgit checkout master &&\n+\tgit merge -s recursive baseline^0 &&\n+\ttest -h a/b &&\n+\ttest -f a/b-2/c/d\n+'\n+\n+test_expect_success 'do not lose untracked in merge (recursive)' '\n+\tgit reset --hard &&\n+\tgit checkout baseline^0 &&\n+\ttouch a/b/c/e &&\n+\ttest_must_fail git merge -s recursive master &&\n+\ttest -f a/b/c/e &&\n+\ttest -f a/b-2/c/d\n+'\n+\n+test_expect_success 'do not lose modifications in merge (recursive)' '\n+\tgit add --all &&\n+\tgit reset --hard &&\n+\tgit checkout baseline^0 &&\n+\techo more content >> a/b/c/d &&\n+\ttest_must_fail git merge -s recursive master\n+'\n+\n test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '\n \tgit reset --hard &&\n \tgit checkout start^0 &&\n@@ -82,7 +107,7 @@ test_expect_success 'merge should not have conflicts (resolve)' '\n \ttest -f a/b/c/d\n '\n \n-test_expect_failure 'merge should not have conflicts (recursive)' '\n+test_expect_failure 'merge should not have D/F conflicts (recursive)' '\n \tgit reset --hard &&\n \tgit checkout baseline^0 &&\n \tgit merge -s recursive test2 &&\n@@ -90,4 +115,12 @@ test_expect_failure 'merge should not have conflicts (recursive)' '\n \ttest -f a/b/c/d\n '\n \n+test_expect_failure 'merge should not have F/D conflicts (recursive)' '\n+\tgit reset --hard &&\n+\tgit checkout -b foo test2 &&\n+\tgit merge -s recursive baseline^0 &&\n+\ttest -h a/b-2 &&\n+\ttest -f a/b/c/d\n+'\n+\n test_done\ndiff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh\nindex d43f37c..69179c6 100755\n--- a/t/t9350-fast-export.sh\n+++ b/t/t9350-fast-export.sh\n@@ -376,4 +376,28 @@ test_expect_success 'tree_tag-obj'    'git fast-export tree_tag-obj'\n test_expect_success 'tag-obj_tag'     'git fast-export tag-obj_tag'\n test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'\n \n+test_expect_failure 'directory becomes symlink'        '\n+\tgit init dirtosymlink &&\n+\tgit init result &&\n+\t(\n+\t\tcd dirtosymlink &&\n+\t\tmkdir foo &&\n+\t\tmkdir bar &&\n+\t\techo hello > foo/world &&\n+\t\techo hello > bar/world &&\n+\t\tgit add foo/world bar/world &&\n+\t\tgit commit -q -mone &&\n+\t\tgit rm -r foo &&\n+\t\tln -s bar foo &&\n+\t\tgit add foo &&\n+\t\tgit commit -q -mtwo\n+\t) &&\n+\t(\n+\t\tcd dirtosymlink &&\n+\t\tgit fast-export master -- foo |\n+\t\t(cd ../result && git fast-import --quiet)\n+\t) &&\n+\t(cd result && git show master:foo)\n+'\n+\n test_done\n-- \n1.7.1.1.6.gc3cd6\n"},{"id":"144940","messageId":"1278442295-23033-3-git-send-email-newren@gmail.com","threadId":"24308","inReplyTo":"1278442295-23033-1-git-send-email-newren@gmail.com","subject":"[PATCHv2 2/5] Add a rename + D/F conflict testcase","fromName":"","fromEmail":"newren@gmail.com","sentAt":"2010-07-06T18:51:32Z","receivedAt":"2010-07-06T18:51:32Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Alexander Gladysh <agladysh@gmail.com>\n\nThis is a simple testcase where both sides of the rename are paths involved\nin (separate) D/F merge conflicts\n\nSigned-off-by: Alexander Gladysh <agladysh@gmail.com>\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\nAs noted previously this is simply a testcase Alexander sent to the list\non March 8 which I'm submitting in the form of a testsuite addition.\nHe's happy to have his testcase added to the testsuite and/or even\nsubmit it himself if that makes more sense -- just let us know what is\nwanted.  I'm less familiar with format-patch and am; hopefully this\nsubmission comes across with him recorded as the author.\n\nAlso, I moved the location of this testcase since the last submission;\nlast time I made it part of t/t6020-merge-df.sh, which probably doesn't\nmake sense since the testcase invokes cherry-pick rather than merge.\n\n t/t3508-cherry-pick-merge-df.sh |   34 ++++++++++++++++++++++++++++++++++\n 1 files changed, 34 insertions(+), 0 deletions(-)\n create mode 100755 t/t3508-cherry-pick-merge-df.sh\n\ndiff --git a/t/t3508-cherry-pick-merge-df.sh b/t/t3508-cherry-pick-merge-df.sh\nnew file mode 100755\nindex 0000000..646a56d\n--- /dev/null\n+++ b/t/t3508-cherry-pick-merge-df.sh\n@@ -0,0 +1,34 @@\n+#!/bin/sh\n+\n+test_description='Test cherry-pick with directory/file conflicts'\n+. ./test-lib.sh\n+\n+test_expect_success 'Setup rename across paths each below D/F conflicts' '\n+\tmkdir a &&\n+\ttouch a/f &&\n+\tgit add a &&\n+\tgit commit -m \"a\" &&\n+\n+\tmkdir b &&\n+\tln -s ../a b/a &&\n+\tgit add b &&\n+\tgit commit -m \"b\" &&\n+\n+\tgit checkout -b branch &&\n+\trm b/a &&\n+\tmv a b/ &&\n+\tln -s b/a a &&\n+\tgit add . &&\n+\tgit commit -m \"swap\" &&\n+\n+\ttouch f1 &&\n+\tgit add f1 &&\n+\tgit commit -m \"f1\"\n+'\n+\n+test_expect_failure 'Cherry-pick succeeds with rename across D/F conflicts' '\n+\tgit checkout master &&\n+\tgit cherry-pick branch\n+'\n+\n+test_done\n-- \n1.7.1.1.6.gc3cd6\n"},{"id":"144944","messageId":"1278442295-23033-4-git-send-email-newren@gmail.com","threadId":"24308","inReplyTo":"1278442295-23033-1-git-send-email-newren@gmail.com","subject":"[PATCHv2 3/5] merge-recursive: Fix D/F conflicts","fromName":"","fromEmail":"newren@gmail.com","sentAt":"2010-07-06T18:51:33Z","receivedAt":"2010-07-06T18:51:33Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nThe D/F conflicts that can be automatically resolved (file or directory\nunmodified on one side of history), have the nice property that\nprocess_entry() can correctly handle all subpaths of the D/F conflict.  In\nthe case of D->F conversions, it will correctly delete all non-conflicting\nfiles below the relevant directory and the directory itself (note that both\nuntracked and conflicting files below the directory will prevent its\nremoval).  So if we handle D/F conflicts after all other conflicts, they\nbecome fairly simple to handle -- we just need to check for whether or not\na path (file/directory) is in the way of creating the new content.  We do\nthis by having process_entry() defer handling such entries to a subsequent\nprocess_df_entry() step.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c               |   93 ++++++++++++++++++++++++++++++++-------\n t/t6035-merge-dir-to-symlink.sh |    8 ++--\n 2 files changed, 81 insertions(+), 20 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 206c103..865729a 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1072,6 +1072,7 @@ static int process_entry(struct merge_options *o,\n \tunsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);\n \tunsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);\n \n+\tentry->processed = 1;\n \tif (o_sha && (!a_sha || !b_sha)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_sha && !b_sha) ||\n@@ -1104,33 +1105,28 @@ static int process_entry(struct merge_options *o,\n \t} else if ((!o_sha && a_sha && !b_sha) ||\n \t\t   (!o_sha && !a_sha && b_sha)) {\n \t\t/* Case B: Added in one. */\n-\t\tconst char *add_branch;\n-\t\tconst char *other_branch;\n \t\tunsigned mode;\n \t\tconst unsigned char *sha;\n-\t\tconst char *conf;\n \n \t\tif (a_sha) {\n-\t\t\tadd_branch = o->branch1;\n-\t\t\tother_branch = o->branch2;\n \t\t\tmode = a_mode;\n \t\t\tsha = a_sha;\n-\t\t\tconf = \"file/directory\";\n \t\t} else {\n-\t\t\tadd_branch = o->branch2;\n-\t\t\tother_branch = o->branch1;\n \t\t\tmode = b_mode;\n \t\t\tsha = b_sha;\n-\t\t\tconf = \"directory/file\";\n \t\t}\n \t\tif (string_list_has_string(&o->current_directory_set, path)) {\n-\t\t\tconst char *new_path = unique_path(o, path, add_branch);\n-\t\t\tclean_merge = 0;\n-\t\t\toutput(o, 1, \"CONFLICT (%s): There is a directory with name %s in %s. \"\n-\t\t\t       \"Adding %s as %s\",\n-\t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tremove_file(o, 0, path, 0);\n-\t\t\tupdate_file(o, 0, sha, mode, new_path);\n+\t\t\t/* Handle D->F conflicts after all subfiles */\n+\t\t\tentry->processed = 0;\n+\t\t\t/* But get any file out of the way now, so conflicted\n+\t\t\t * entries below the directory of the same name can\n+\t\t\t * be put in the working directory.\n+\t\t\t */\n+\t\t\tif (a_sha)\n+\t\t\t\toutput(o, 2, \"Removing %s\", path);\n+\t\t\t/* do not touch working file if it did not exist */\n+\t\t\tremove_file(o, 0, path, !a_sha);\n+\t\t\treturn 1; /* Assume clean till processed */\n \t\t} else {\n \t\t\toutput(o, 2, \"Adding %s\", path);\n \t\t\tupdate_file(o, 1, sha, mode, path);\n@@ -1178,6 +1174,64 @@ static int process_entry(struct merge_options *o,\n \treturn clean_merge;\n }\n \n+/* Per entry merge function for D/F conflicts, to be called only after\n+ * all files below dir have been processed.  We do this because in the\n+ * cases we can cleanly resolve D/F conflicts, process_entry() can clean\n+ * out all the files below the directory for us.\n+ */\n+static int process_df_entry(struct merge_options *o,\n+\t\t\t const char *path, struct stage_data *entry)\n+{\n+\tint clean_merge = 1;\n+\tunsigned o_mode = entry->stages[1].mode;\n+\tunsigned a_mode = entry->stages[2].mode;\n+\tunsigned b_mode = entry->stages[3].mode;\n+\tunsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);\n+\tunsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);\n+\tunsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);\n+\n+\t/* We currently only handle D->F cases */\n+\tassert((!o_sha && a_sha && !b_sha) ||\n+\t       (!o_sha && !a_sha && b_sha));\n+\n+\tconst char *add_branch;\n+\tconst char *other_branch;\n+\tunsigned mode;\n+\tconst unsigned char *sha;\n+\tconst char *conf;\n+\n+\tentry->processed = 1;\n+\n+\tif (a_sha) {\n+\t\tadd_branch = o->branch1;\n+\t\tother_branch = o->branch2;\n+\t\tmode = a_mode;\n+\t\tsha = a_sha;\n+\t\tconf = \"file/directory\";\n+\t} else {\n+\t\tadd_branch = o->branch2;\n+\t\tother_branch = o->branch1;\n+\t\tmode = b_mode;\n+\t\tsha = b_sha;\n+\t\tconf = \"directory/file\";\n+\t}\n+\tstruct stat st;\n+\tif (lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) {\n+\t\tconst char *new_path = unique_path(o, path, add_branch);\n+\t\tclean_merge = 0;\n+\t\toutput(o, 1, \"CONFLICT (%s): There is a directory with name %s in %s. \"\n+\t\t       \"Adding %s as %s\",\n+\t\t       conf, path, other_branch, path, new_path);\n+\t\tremove_file(o, 0, path, 0);\n+\t\tupdate_file(o, 0, sha, mode, new_path);\n+\t} else {\n+\t\toutput(o, 2, \"Adding %s\", path);\n+\t\tupdate_file(o, 1, sha, mode, path);\n+\t}\n+\n+\treturn clean_merge;\n+}\n+\n struct unpack_trees_error_msgs get_porcelain_error_msgs(void)\n {\n \tstruct unpack_trees_error_msgs msgs = {\n@@ -1249,6 +1303,13 @@ int merge_trees(struct merge_options *o,\n \t\t\t\t&& !process_entry(o, path, e))\n \t\t\t\tclean = 0;\n \t\t}\n+\t\tfor (i = 0; i < entries->nr; i++) {\n+\t\t\tconst char *path = entries->items[i].string;\n+\t\t\tstruct stage_data *e = entries->items[i].util;\n+\t\t\tif (!e->processed\n+\t\t\t\t&& !process_df_entry(o, path, e))\n+\t\t\t\tclean = 0;\n+\t\t}\n \n \t\tstring_list_clear(re_merge, 0);\n \t\tstring_list_clear(re_head, 0);\ndiff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh\nindex 3c176d7..384547d 100755\n--- a/t/t6035-merge-dir-to-symlink.sh\n+++ b/t/t6035-merge-dir-to-symlink.sh\n@@ -56,7 +56,7 @@ test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '\n \ttest -f a/b-2/c/d\n '\n \n-test_expect_failure 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '\n+test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '\n \tgit reset --hard &&\n \tgit checkout baseline^0 &&\n \tgit merge -s recursive master &&\n@@ -64,7 +64,7 @@ test_expect_failure 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recurs\n \ttest -f a/b-2/c/d\n '\n \n-test_expect_failure 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '\n+test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '\n \tgit reset --hard &&\n \tgit checkout master &&\n \tgit merge -s recursive baseline^0 &&\n@@ -107,7 +107,7 @@ test_expect_success 'merge should not have conflicts (resolve)' '\n \ttest -f a/b/c/d\n '\n \n-test_expect_failure 'merge should not have D/F conflicts (recursive)' '\n+test_expect_success 'merge should not have D/F conflicts (recursive)' '\n \tgit reset --hard &&\n \tgit checkout baseline^0 &&\n \tgit merge -s recursive test2 &&\n@@ -115,7 +115,7 @@ test_expect_failure 'merge should not have D/F conflicts (recursive)' '\n \ttest -f a/b/c/d\n '\n \n-test_expect_failure 'merge should not have F/D conflicts (recursive)' '\n+test_expect_success 'merge should not have F/D conflicts (recursive)' '\n \tgit reset --hard &&\n \tgit checkout -b foo test2 &&\n \tgit merge -s recursive baseline^0 &&\n-- \n1.7.1.1.6.gc3cd6\n"},{"id":"144943","messageId":"1278442295-23033-5-git-send-email-newren@gmail.com","threadId":"24308","inReplyTo":"1278442295-23033-1-git-send-email-newren@gmail.com","subject":"[PATCHv2 4/5] merge_recursive: Fix renames across paths below D/F conflicts","fromName":"","fromEmail":"newren@gmail.com","sentAt":"2010-07-06T18:51:34Z","receivedAt":"2010-07-06T18:51:34Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nThe rename logic in process_renames() handles renames and merging of file\ncontents and then marks files as processed.  However, there may be higher\nstage entries left in the index for other reasons (e.g., due to D/F\nconflicts).  By checking for such cases and marking the entry as not\nprocessed, it allows process_entry() later to look at it and handle those\nhigher stages.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c               |   13 +++++++++++--\n t/t3508-cherry-pick-merge-df.sh |    2 +-\n t/t6020-merge-df.sh             |    2 +-\n 3 files changed, 13 insertions(+), 4 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 865729a..ecddd9e 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1019,14 +1019,23 @@ static int process_renames(struct merge_options *o,\n \n \t\t\t\tif (mfi.clean &&\n \t\t\t\t    sha_eq(mfi.sha, ren1->pair->two->sha1) &&\n-\t\t\t\t    mfi.mode == ren1->pair->two->mode)\n+\t\t\t\t    mfi.mode == ren1->pair->two->mode) {\n \t\t\t\t\t/*\n \t\t\t\t\t * This messaged is part of\n \t\t\t\t\t * t6022 test. If you change\n \t\t\t\t\t * it update the test too.\n \t\t\t\t\t */\n \t\t\t\t\toutput(o, 3, \"Skipped %s (merged same as existing)\", ren1_dst);\n-\t\t\t\telse {\n+\n+\t\t\t\t\t/* There may be higher stage entries left\n+\t\t\t\t\t * in the index (e.g. due to a D/F\n+\t\t\t\t\t * conflict) that need to be resolved.\n+\t\t\t\t\t */\n+\t\t\t\t\tfor (i=1; i<=3; ++i) {\n+\t\t\t\t\t\tif (ren1->dst_entry->stages[i].mode)\n+\t\t\t\t\t\t\tren1->dst_entry->processed = 0;\n+\t\t\t\t\t}\n+\t\t\t\t} else {\n \t\t\t\t\tif (mfi.merge || !mfi.clean)\n \t\t\t\t\t\toutput(o, 1, \"Renaming %s => %s\", ren1_src, ren1_dst);\n \t\t\t\t\tif (mfi.merge)\ndiff --git a/t/t3508-cherry-pick-merge-df.sh b/t/t3508-cherry-pick-merge-df.sh\nindex 646a56d..5895ea3 100755\n--- a/t/t3508-cherry-pick-merge-df.sh\n+++ b/t/t3508-cherry-pick-merge-df.sh\n@@ -26,7 +26,7 @@ test_expect_success 'Setup rename across paths each below D/F conflicts' '\n \tgit commit -m \"f1\"\n '\n \n-test_expect_failure 'Cherry-pick succeeds with rename across D/F conflicts' '\n+test_expect_success 'Cherry-pick succeeds with rename across D/F conflicts' '\n \tgit checkout master &&\n \tgit cherry-pick branch\n '\ndiff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh\nindex e71c687..490d397 100755\n--- a/t/t6020-merge-df.sh\n+++ b/t/t6020-merge-df.sh\n@@ -22,7 +22,7 @@ git commit -m \"File: dir\"'\n \n test_expect_code 1 'Merge with d/f conflicts' 'git merge \"merge msg\" B master'\n \n-test_expect_failure 'F/D conflict' '\n+test_expect_success 'F/D conflict' '\n \tgit reset --hard &&\n \tgit checkout master &&\n \trm .git/index &&\n-- \n1.7.1.1.6.gc3cd6\n"},{"id":"144942","messageId":"1278442295-23033-6-git-send-email-newren@gmail.com","threadId":"24308","inReplyTo":"1278442295-23033-1-git-send-email-newren@gmail.com","subject":"[PATCHv2 5/5] fast-import: Fix minor data-loss issue with directories becoming symlinks","fromName":"","fromEmail":"newren@gmail.com","sentAt":"2010-07-06T18:51:35Z","receivedAt":"2010-07-06T18:51:35Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nWhen fast-export runs across a directory changing to a symlink, it will\noutput the changes in the form\n  M 120000 :239821 dir-changing-to-symlink\n  D dir-changing-to-symlink/filename1\nWhen fast-import sees the first line, it deletes the directory named\ndir-changing-to-symlink (and any files below it) and creates a symlink in\nits place.  When fast-import came across the second line, it was previously\ntrying to remove the file and relevant leading directories in\ntree_content_remove(), and as a side effect it would delete the symlink\nthat was just created.  This resulted in the symlink silently missing from\nthe resulting repository.\n\nWe fix this by ignoring file deletions underneath directory names that\ncorrespond to symlinks.  This can also be viewed as a minor optimization:\nsince there cannot be a symlink and a directory with the same name in the\nsame directory, the file clearly can't exist so nothing needs to be done to\ndelete it.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n fast-import.c          |    5 +++++\n t/t9350-fast-export.sh |    2 +-\n 2 files changed, 6 insertions(+), 1 deletions(-)\n\ndiff --git a/fast-import.c b/fast-import.c\nindex 309f2c5..10462d8 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -1528,6 +1528,11 @@ static int tree_content_remove(\n \tfor (i = 0; i < t->entry_count; i++) {\n \t\te = t->entries[i];\n \t\tif (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {\n+\t\t\tif (slash1 && S_ISLNK(e->versions[1].mode))\n+\t\t\t\t/* If a parent directory of p is a symlink, then\n+\t\t\t\t * p cannot exist and need not be deleted.\n+\t\t\t\t */\n+\t\t\t\treturn 1;\n \t\t\tif (!slash1 || !S_ISDIR(e->versions[1].mode))\n \t\t\t\tgoto del_entry;\n \t\t\tif (!e->tree)\ndiff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh\nindex 69179c6..1ee1461 100755\n--- a/t/t9350-fast-export.sh\n+++ b/t/t9350-fast-export.sh\n@@ -376,7 +376,7 @@ test_expect_success 'tree_tag-obj'    'git fast-export tree_tag-obj'\n test_expect_success 'tag-obj_tag'     'git fast-export tag-obj_tag'\n test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'\n \n-test_expect_failure 'directory becomes symlink'        '\n+test_expect_success 'directory becomes symlink'        '\n \tgit init dirtosymlink &&\n \tgit init result &&\n \t(\n-- \n1.7.1.1.6.gc3cd6\n"},{"id":"144955","messageId":"20100706193455.GA19476@spearce.org","threadId":"24308","inReplyTo":"1278442295-23033-6-git-send-email-newren@gmail.com","subject":"Re: [PATCHv2 5/5] fast-import: Fix minor data-loss issue with directories becoming symlinks","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2010-07-06T19:34:55Z","receivedAt":"2010-07-06T19:34:55Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"newren@gmail.com wrote:\n> From: Elijah Newren <newren@gmail.com>\n> \n> When fast-export runs across a directory changing to a symlink, it will\n> output the changes in the form\n>   M 120000 :239821 dir-changing-to-symlink\n>   D dir-changing-to-symlink/filename1\n> When fast-import sees the first line, it deletes the directory named\n> dir-changing-to-symlink (and any files below it) and creates a symlink in\n> its place.  When fast-import came across the second line, it was previously\n> trying to remove the file and relevant leading directories in\n> tree_content_remove(), and as a side effect it would delete the symlink\n> that was just created.  This resulted in the symlink silently missing from\n> the resulting repository.\n\nUgh.\n\nI'm not against making the input parser more robust, but this is\na violation of the stream format from fast-export.  The stream is\nincremental, a command like 'M' takes place immediately.  It is\nwrong for a frontend to output 'M foo', then 'D foo/bar'.\n\nIMHO, if fast-export is doing what you say above, the bug lies in\nfast-export, and therefore the fix should too.\n \n-- \nShawn.\n"},{"id":"144957","messageId":"AANLkTimrD1LOZBb0YyzlrKgTc7bGWTxdf05cpwPR6WSw@mail.gmail.com","threadId":"24308","inReplyTo":"20100706193455.GA19476@spearce.org","subject":"Re: [PATCHv2 5/5] fast-import: Fix minor data-loss issue with directories becoming symlinks","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2010-07-06T19:48:50Z","receivedAt":"2010-07-06T19:48:50Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jul 6, 2010 at 1:34 PM, Shawn O. Pearce <spearce@spearce.org> wrote:\n> newren@gmail.com wrote:\n>> From: Elijah Newren <newren@gmail.com>\n>>\n>> When fast-export runs across a directory changing to a symlink, it will\n>> output the changes in the form\n>>   M 120000 :239821 dir-changing-to-symlink\n>>   D dir-changing-to-symlink/filename1\n>> When fast-import sees the first line, it deletes the directory named\n>> dir-changing-to-symlink (and any files below it) and creates a symlink in\n>> its place.  When fast-import came across the second line, it was previously\n>> trying to remove the file and relevant leading directories in\n>> tree_content_remove(), and as a side effect it would delete the symlink\n>> that was just created.  This resulted in the symlink silently missing from\n>> the resulting repository.\n>\n> Ugh.\n>\n> I'm not against making the input parser more robust, but this is\n> a violation of the stream format from fast-export.  The stream is\n> incremental, a command like 'M' takes place immediately.  It is\n> wrong for a frontend to output 'M foo', then 'D foo/bar'.\n>\n> IMHO, if fast-export is doing what you say above, the bug lies in\n> fast-export, and therefore the fix should too.\n\nOkay, I'll fix up fast-export.  Do you want me to drop this\nfast-import patch, or does it still make sense as an extra robustness\ncheck?\n\nElijah\n"},{"id":"144961","messageId":"20100706201935.GB19476@spearce.org","threadId":"24308","inReplyTo":"AANLkTimrD1LOZBb0YyzlrKgTc7bGWTxdf05cpwPR6WSw@mail.gmail.com","subject":"Re: [PATCHv2 5/5] fast-import: Fix minor data-loss issue with directories becoming symlinks","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2010-07-06T20:19:35Z","receivedAt":"2010-07-06T20:19:35Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Elijah Newren <newren@gmail.com> wrote:\n> On Tue, Jul 6, 2010 at 1:34 PM, Shawn O. Pearce <spearce@spearce.org> wrote:\n> > newren@gmail.com wrote:\n> >> From: Elijah Newren <newren@gmail.com>\n> >>\n> >> When fast-export runs across a directory changing to a symlink, it will\n> >> output the changes in the form\n> >> ?? M 120000 :239821 dir-changing-to-symlink\n> >> ?? D dir-changing-to-symlink/filename1\n> >> When fast-import sees the first line, it deletes the directory named\n> >> dir-changing-to-symlink (and any files below it) and creates a symlink in\n> >> its place. ??When fast-import came across the second line, it was previously\n> >> trying to remove the file and relevant leading directories in\n> >> tree_content_remove(), and as a side effect it would delete the symlink\n> >> that was just created. ??This resulted in the symlink silently missing from\n> >> the resulting repository.\n> >\n> > Ugh.\n> >\n> > I'm not against making the input parser more robust, but this is\n> > a violation of the stream format from fast-export. ??The stream is\n> > incremental, a command like 'M' takes place immediately. ??It is\n> > wrong for a frontend to output 'M foo', then 'D foo/bar'.\n> >\n> > IMHO, if fast-export is doing what you say above, the bug lies in\n> > fast-export, and therefore the fix should too.\n> \n> Okay, I'll fix up fast-export.  Do you want me to drop this\n> fast-import patch, or does it still make sense as an extra robustness\n> check?\n\nIt probably makes sense to still do this in fast-import, deleting\nsomething that doesn't exist is probably OK, its still going to\nbe deleted in the end anyway.\n\n-- \nShawn.\n"}]}