{"thread":{"id":"899","subject":"[PATCH 2/3] read-tree --emu23.","startedAt":"2005-06-11T01:36:08Z","lastAt":"2005-06-11T01:36:08Z","messageCount":1,"participants":["Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"4844","messageId":"7v7jh17jzr.fsf@assigned-by-dhcp.cox.net","threadId":"899","inReplyTo":null,"subject":"[PATCH 2/3] read-tree --emu23.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-06-11T01:36:08Z","receivedAt":"2005-06-11T01:36:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This new flag causes two-way fast forward to internally use the\nthree-way merge mechanism.  This behaviour is intended to offer\na better fast forward semantics when used in a dirty work tree.\n\nThe new test t1005 is parallel to the existing t1001 \"pure\n2-way\" tests, but some parts that are commented out would fail.\nThese failures are due to three-way merge enforcing too strict\nindex requirements for cases that could succeed.  This problem\nwill be addressed by later patches.\n\nWithout even changing three-way mechanism, the --emu23 two-way\nfast forward already gives the user an easier-to-handle merge\nresult when a file that \"merged head\" updates has local\nmodifications.  This is demonstrated as \"case 16\" test in t1005.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n read-tree.c                       |   81 +++++++++++++++++++++++++++++++++++--\n t/t1001-read-tree-m-2way.sh       |   44 ++++++++++++++++----\n 2 files changed, 113 insertions(+), 12 deletions(-)\n\ndiff --git a/read-tree.c b/read-tree.c\n--- a/read-tree.c\n+++ b/read-tree.c\n@@ -210,6 +210,58 @@ static int twoway_merge(struct cache_ent\n }\n \n /*\n+ * Two-way merge emulated with three-way merge.\n+ *\n+ * This treats \"read-tree -m H M\" by transforming it internally\n+ * into \"read-tree -m H I+H M\", where I+H is a tree that would\n+ * contain the contents of the current index file, overlayed on\n+ * top of H.  Unlike the traditional two-way merge, this leaves\n+ * the stages in the resulting index file and lets the user resolve\n+ * the merge conflicts using standard tools for three-way merge.\n+ *\n+ * This function is just to set-up such an arrangement, and the\n+ * actual merge uses threeway_merge() function.\n+ */\n+static void setup_emu23(void)\n+{\n+\t/* stage0 contains I, stage1 H, stage2 M.\n+\t * move stage2 to stage3, and create stage2 entries\n+\t * by scanning stage0 and stage1 entries.\n+\t */\n+\tint i, namelen, size;\n+\tstruct cache_entry *ce, *stage2;\n+\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tce = active_cache[i];\n+\t\tif (ce_stage(ce) != 2)\n+\t\t\tcontinue;\n+\t\t/* hoist them up to stage 3 */\n+\t\tnamelen = ce_namelen(ce);\n+\t\tce->ce_flags = create_ce_flags(namelen, 3);\n+\t}\n+\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tce = active_cache[i];\n+\t\tif (ce_stage(ce) > 1)\n+\t\t\tcontinue;\n+\t\tnamelen = ce_namelen(ce);\n+\t\tsize = cache_entry_size(namelen);\n+\t\tstage2 = xmalloc(size);\n+\t\tmemcpy(stage2, ce, size);\n+\t\tstage2->ce_flags = create_ce_flags(namelen, 2);\n+\t\tif (add_cache_entry(stage2, ADD_CACHE_OK_TO_ADD) < 0)\n+\t\t\tdie(\"cannot merge index and our head tree\");\n+\n+\t\t/* We are done with this name, so skip to next name */\n+\t\twhile (i < active_nr &&\n+\t\t       ce_namelen(active_cache[i]) == namelen &&\n+\t\t       !memcmp(active_cache[i]->name, ce->name, namelen))\n+\t\t\ti++;\n+\t\ti--; /* compensate for the loop control */\n+\t}\n+}\n+\n+/*\n  * One-way merge.\n  *\n  * The rule is:\n@@ -315,7 +367,7 @@ static struct cache_file cache_file;\n \n int main(int argc, char **argv)\n {\n-\tint i, newfd, merge, reset;\n+\tint i, newfd, merge, reset, emu23;\n \tunsigned char sha1[20];\n \n \tnewfd = hold_index_file_for_update(&cache_file, get_index_file());\n@@ -324,6 +376,7 @@ int main(int argc, char **argv)\n \n \tmerge = 0;\n \treset = 0;\n+\temu23 = 0;\n \tfor (i = 1; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n \n@@ -335,7 +388,7 @@ int main(int argc, char **argv)\n \n \t\t/* This differs from \"-m\" in that we'll silently ignore unmerged entries */\n \t\tif (!strcmp(arg, \"--reset\")) {\n-\t\t\tif (stage || merge)\n+\t\t\tif (stage || merge || emu23)\n \t\t\t\tusage(read_tree_usage);\n \t\t\treset = 1;\n \t\t\tmerge = 1;\n@@ -345,7 +398,7 @@ int main(int argc, char **argv)\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)\n+\t\t\tif (stage || merge || emu23)\n \t\t\t\tusage(read_tree_usage);\n \t\t\tif (read_cache_unmerged())\n \t\t\t\tdie(\"you need to resolve your current index first\");\n@@ -353,6 +406,17 @@ int main(int argc, char **argv)\n \t\t\tmerge = 1;\n \t\t\tcontinue;\n \t\t}\n+\n+\t\t/* \"-emu23\" uses 3-way merge logic to perform fast-forward */\n+\t\tif (!strcmp(arg, \"--emu23\")) {\n+\t\t\tif (stage || merge || emu23)\n+\t\t\t\tusage(read_tree_usage);\n+\t\t\tif (read_cache_unmerged())\n+\t\t\t\tdie(\"you need to resolve your current index first\");\n+\t\t\tmerge = emu23 = stage = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tif (get_sha1(arg, sha1) < 0)\n \t\t\tusage(read_tree_usage);\n \t\tif (stage > 3)\n@@ -369,9 +433,18 @@ int main(int argc, char **argv)\n \t\t\t[2] = twoway_merge,\n \t\t\t[3] = threeway_merge,\n \t\t};\n+\t\tmerge_fn_t fn;\n+\n \t\tif (stage < 2 || stage > 4)\n \t\t\tdie(\"just how do you expect me to merge %d trees?\", stage-1);\n-\t\tmerge_cache(active_cache, active_nr, merge_function[stage-1]);\n+\t\tif (emu23 && stage != 3)\n+\t\t\tdie(\"--emu23 takes only two trees\");\n+\t\tfn = merge_function[stage-1];\n+\t\tif (stage == 3 && emu23) { \n+\t\t\tsetup_emu23();\n+\t\t\tfn = merge_function[3];\n+\t\t}\n+\t\tmerge_cache(active_cache, active_nr, fn);\n \t}\n \tif (write_cache(newfd, active_cache, active_nr) ||\n \t    commit_index_file(&cache_file))\ndiff --git a/t/t1001-read-tree-m-2way.sh b/t/t1005-read-tree-m-2way-emu23.sh\nsimilarity index 86%\ncopy from t/t1001-read-tree-m-2way.sh\ncopy to t/t1005-read-tree-m-2way-emu23.sh\n--- a/t/t1001-read-tree-m-2way.sh\n+++ b/t/t1005-read-tree-m-2way-emu23.sh\n@@ -3,7 +3,7 @@\n # Copyright (c) 2005 Junio C Hamano\n #\n \n-test_description='Two way merge with read-tree -m $H $M\n+test_description='Two way merge with read-tree --emu23 $H $M\n \n This test tries two-way merge (aka fast forward with carry forward).\n \n@@ -23,7 +23,10 @@ In the test, these paths are used:\n . ./test-lib.sh\n \n read_tree_twoway () {\n-    git-read-tree -m \"$1\" \"$2\" && git-ls-files --stage\n+    git-read-tree --emu23 \"$1\" \"$2\" &&\n+    git-ls-files --stage &&\n+    git-merge-cache git-merge-one-file-script -a &&\n+    git-ls-files --stage\n }\n \n _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'\n@@ -51,6 +54,12 @@ check_cache_at () {\n \tesac\n }\n \n+check_stages () {\n+    cat >expected_stages\n+    git-ls-files --stage | sed -e \"s/ $_x40 / X /\" >current_stages\n+    diff -u expected_stages current_stages\n+}\n+\n test_expect_success \\\n     setup \\\n     'echo frotz >frotz &&\n@@ -78,7 +87,7 @@ test_expect_success \\\n      git-ls-files --stage >1-3.out &&\n      diff -u M.out 1-3.out &&\n      check_cache_at bozbar dirty &&\n-     check_cache_at frotz dirty &&\n+     check_cache_at frotz clean && # different from pure 2-way\n      check_cache_at nitfol dirty'\n \n echo '+100644 X 0\tyomin' >expected\n@@ -93,7 +102,9 @@ test_expect_success \\\n      compare_change 4diff.out expected &&\n      check_cache_at yomin clean'\n \n-test_expect_success \\\n+# \"read-tree -m H I+H M\" where !H && !M; so (I+H) not being up-to-date\n+# should not matter, but without #3ALT this does not work.\n+: test_expect_success \\\n     '5 - carry forward local addition.' \\\n     'rm -f .git/index &&\n      echo yomin >yomin &&\n@@ -105,7 +116,10 @@ test_expect_success \\\n      compare_change 5diff.out expected &&\n      check_cache_at yomin dirty'\n \n-test_expect_success \\\n+# \"read-tree -m H I+H M\" where !H && M && (I+H) == M, so this should\n+# succeed (even the entry is clean), but without #5ALT this does not\n+# work.\n+: test_expect_success \\\n     '6 - local addition already has the same.' \\\n     'rm -f .git/index &&\n      git-update-cache --add frotz &&\n@@ -114,7 +128,9 @@ test_expect_success \\\n      diff -u M.out 6.out &&\n      check_cache_at frotz clean'\n \n-test_expect_success \\\n+# Exactly the same pattern as above but with dirty cache.  This also\n+# should succeed, but without #5ALT it does not.\n+: test_expect_success \\\n     '7 - local addition already has the same.' \\\n     'rm -f .git/index &&\n      echo frotz >frotz &&\n@@ -200,12 +216,24 @@ test_expect_success \\\n      compare_change 15diff.out expected &&\n      check_cache_at nitfol dirty'\n \n+# This is different from straight 2-way merge in that it leaves\n+# three stages of bozbar in the index file without failing, so\n+# the user can run git-diff-stages to examine the situation.\n test_expect_success \\\n     '16 - conflicting local change.' \\\n     'rm -f .git/index &&\n      echo bozbar bozbar >bozbar &&\n      git-update-cache --add bozbar &&\n-     if read_tree_twoway $treeH $treeM; then false; else :; fi'\n+     git-read-tree --emu23 $treeH $treeM &&\n+     check_stages' <<\\EOF\n+100644 X 1\tbozbar\n+100644 X 2\tbozbar\n+100644 X 3\tbozbar\n+100644 X 3\tfrotz\n+100644 X 0\tnitfol\n+100644 X 1\trezrov\n+100644 X 2\trezrov\n+EOF\n \n test_expect_success \\\n     '17 - conflicting local change.' \\\n@@ -282,7 +310,7 @@ test_expect_success \\\n      read_tree_twoway $treeDF $treeDFDF &&\n      git-ls-files --stage >DFDFcheck.out &&\n      diff -u DFDF.out DFDFcheck.out &&\n-     check_cache_at DF/DF dirty &&\n+     check_cache_at DF/DF clean && # different from pure 2-way\n      :'\n \n test_done\n------------\n\n"}]}