{"thread":{"id":"27043","subject":"[PATCH] mergetool: Teach about submodules","startedAt":"2011-04-09T03:59:30Z","lastAt":"2011-04-13T10:00:48Z","messageCount":7,"participants":["Jonathon Mah","David Aguilar","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"165473","messageId":"1302321570-85987-1-git-send-email-me@JonathonMah.com","threadId":"27043","inReplyTo":null,"subject":"[PATCH] mergetool: Teach about submodules","fromName":"Jonathon Mah","fromEmail":"me@jonathonmah.com","sentAt":"2011-04-09T03:59:30Z","receivedAt":"2011-04-09T03:59:30Z","isPatch":true,"sender":{"key":"me@jonathonmah.com","avatar":"https://avatars.githubusercontent.com/u/2748?v=4"},"body":"Mergetool mildly clobbered submodules, attempting to move and copy their\ndirectories. It now recognizes submodules and offers a resolution:\n\nSubmodule merge conflict for 'Shared':\n  {local}: commit ad9f12e3e6205381bf2163a793d1e596a9e211d0\n  {remote}: commit f5893fb70ec5646efcd9aa643c5136753ac89253\nUse (l)ocal or (r)emote, or (a)bort?\n\nSelecting a commit will stage it, but not update the submodule (as it\nwould had there been no conflict). Type changes are also supported,\nshould the path be a submodule on one side, and a file on the other.\n\nSigned-off-by: Jonathon Mah <me@JonathonMah.com>\n---\n git-mergetool.sh |   67 +++++++++++++++++++++++++++++++++++++++++++++++++++--\n 1 files changed, 64 insertions(+), 3 deletions(-)\n\ndiff --git a/git-mergetool.sh b/git-mergetool.sh\nindex bacbda2..83351d6 100755\n--- a/git-mergetool.sh\n+++ b/git-mergetool.sh\n@@ -21,6 +21,10 @@ is_symlink () {\n     test \"$1\" = 120000\n }\n \n+is_submodule () {\n+    test \"$1\" = 160000\n+}\n+\n local_present () {\n     test -n \"$local_mode\"\n }\n@@ -52,6 +56,8 @@ describe_file () {\n \techo \"deleted\"\n     elif is_symlink \"$mode\" ; then\n \techo \"a symbolic link -> '$(cat \"$file\")'\"\n+    elif is_submodule \"$mode\" ; then\n+\techo \"commit $file\"\n     else\n \tif base_present; then\n \t    echo \"modified\"\n@@ -112,6 +118,51 @@ resolve_deleted_merge () {\n \tdone\n }\n \n+resolve_submodule_merge () {\n+    while true; do\n+\tprintf \"Use (l)ocal or (r)emote, or (a)bort? \"\n+\tread ans\n+\tcase \"$ans\" in\n+\t    [lL]*)\n+\t\tlocal_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $1;}')\n+\t\tif is_submodule \"$local_mode\"; then\n+\t\t    stage_submodule \"$MERGED\" $(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n+\t\telse\n+\t\t    git checkout-index -f --stage=2 -- \"$MERGED\"\n+\t\t    git add -- \"$MERGED\"\n+\t\tfi\n+\t\treturn 0\n+\t\t;;\n+\t    [rR]*)\n+\t\tremote_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $1;}')\n+\t\tif is_submodule \"$remote_mode\"; then\n+\t\t    stage_submodule \"$MERGED\" $(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $2;}')\n+\t\telse\n+\t\t    git checkout-index -f --stage=2 -- \"$MERGED\"\n+\t\t    git add -- \"$MERGED\"\n+\t\tfi\n+\t\treturn 0\n+\t\t;;\n+\t    [aA]*)\n+\t\treturn 1\n+\t\t;;\n+\t    esac\n+\tdone\n+}\n+\n+stage_submodule () {\n+    path=\"$1\"\n+    submodule_sha1=\"$2\"\n+\n+    submodule_basename=$(basename \"$path\")\n+    tree_with_module=$(echo \"160000 commit $submodule_sha1\t\\\"$submodule_basename\\\"\" | git mktree --missing 2>/dev/null)\n+    if test -z \"$tree_with_module\" ; then\n+\techo \"$path: unable to stage commit $sha1\"\n+\treturn 1\n+    fi\n+    git checkout $tree_with_module -- \"$path\"\n+}\n+\n checkout_staged_file () {\n     tmpfile=$(expr \"$(git checkout-index --temp --stage=\"$1\" \"$2\")\" : '\\([^\t]*\\)\t')\n \n@@ -139,13 +190,23 @@ merge_file () {\n     REMOTE=\"./$MERGED.REMOTE.$ext\"\n     BASE=\"./$MERGED.BASE.$ext\"\n \n-    mv -- \"$MERGED\" \"$BACKUP\"\n-    cp -- \"$BACKUP\" \"$MERGED\"\n-\n     base_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==1) print $1;}')\n     local_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $1;}')\n     remote_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $1;}')\n \n+    if is_submodule \"$local_mode\" || is_submodule \"$remote_mode\"; then\n+\techo \"Submodule merge conflict for '$MERGED':\"\n+\tlocal_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n+\tremote_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $2;}')\n+\tdescribe_file \"$local_mode\" \"local\" \"$local_sha1\"\n+\tdescribe_file \"$remote_mode\" \"remote\" \"$remote_sha1\"\n+\tresolve_submodule_merge\n+\treturn\n+    fi\n+\n+    mv -- \"$MERGED\" \"$BACKUP\"\n+    cp -- \"$BACKUP\" \"$MERGED\"\n+\n     base_present   && checkout_staged_file 1 \"$MERGED\" \"$BASE\"\n     local_present  && checkout_staged_file 2 \"$MERGED\" \"$LOCAL\"\n     remote_present && checkout_staged_file 3 \"$MERGED\" \"$REMOTE\"\n-- \n1.7.5.rc1.1.g64431\n"},{"id":"165485","messageId":"20110409120301.GA1369@gmail.com","threadId":"27043","inReplyTo":"1302321570-85987-1-git-send-email-me@JonathonMah.com","subject":"Re: [PATCH] mergetool: Teach about submodules","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2011-04-09T12:03:03Z","receivedAt":"2011-04-09T12:03:03Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"I added Charles Bailey to the cc list.\n\nOn Fri, Apr 08, 2011 at 08:59:30PM -0700, Jonathon Mah wrote:\n> Mergetool mildly clobbered submodules, attempting to move and copy their\n> directories. It now recognizes submodules and offers a resolution:\n> \n> Submodule merge conflict for 'Shared':\n>   {local}: commit ad9f12e3e6205381bf2163a793d1e596a9e211d0\n>   {remote}: commit f5893fb70ec5646efcd9aa643c5136753ac89253\n> Use (l)ocal or (r)emote, or (a)bort?\n> \n> Selecting a commit will stage it, but not update the submodule (as it\n> would had there been no conflict). Type changes are also supported,\n> should the path be a submodule on one side, and a file on the other.\n> \n> Signed-off-by: Jonathon Mah <me@JonathonMah.com>\n> ---\n\nThis is a nice patch.  It fixes a bug by introducing a great\nnew feature.  Thank you.\n\nOne thing that could make it better, though, would be if it\nalso added tests for the feature to t/t7610-mergetool.sh.\nThat will help prevent someone (like me) from accidentally\nbreaking it in the future.\n\nCheers,\n-- \n\t\t\t\t\tDavid\n\n>  git-mergetool.sh |   67 +++++++++++++++++++++++++++++++++++++++++++++++++++--\n>  1 files changed, 64 insertions(+), 3 deletions(-)\n> \n> diff --git a/git-mergetool.sh b/git-mergetool.sh\n> index bacbda2..83351d6 100755\n> --- a/git-mergetool.sh\n> +++ b/git-mergetool.sh\n> @@ -21,6 +21,10 @@ is_symlink () {\n>      test \"$1\" = 120000\n>  }\n>  \n> +is_submodule () {\n> +    test \"$1\" = 160000\n> +}\n> +\n>  local_present () {\n>      test -n \"$local_mode\"\n>  }\n> @@ -52,6 +56,8 @@ describe_file () {\n>  \techo \"deleted\"\n>      elif is_symlink \"$mode\" ; then\n>  \techo \"a symbolic link -> '$(cat \"$file\")'\"\n> +    elif is_submodule \"$mode\" ; then\n> +\techo \"commit $file\"\n>      else\n>  \tif base_present; then\n>  \t    echo \"modified\"\n> @@ -112,6 +118,51 @@ resolve_deleted_merge () {\n>  \tdone\n>  }\n>  \n> +resolve_submodule_merge () {\n> +    while true; do\n> +\tprintf \"Use (l)ocal or (r)emote, or (a)bort? \"\n> +\tread ans\n> +\tcase \"$ans\" in\n> +\t    [lL]*)\n> +\t\tlocal_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $1;}')\n> +\t\tif is_submodule \"$local_mode\"; then\n> +\t\t    stage_submodule \"$MERGED\" $(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n> +\t\telse\n> +\t\t    git checkout-index -f --stage=2 -- \"$MERGED\"\n> +\t\t    git add -- \"$MERGED\"\n> +\t\tfi\n> +\t\treturn 0\n> +\t\t;;\n> +\t    [rR]*)\n> +\t\tremote_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $1;}')\n> +\t\tif is_submodule \"$remote_mode\"; then\n> +\t\t    stage_submodule \"$MERGED\" $(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $2;}')\n> +\t\telse\n> +\t\t    git checkout-index -f --stage=2 -- \"$MERGED\"\n> +\t\t    git add -- \"$MERGED\"\n> +\t\tfi\n> +\t\treturn 0\n> +\t\t;;\n> +\t    [aA]*)\n> +\t\treturn 1\n> +\t\t;;\n> +\t    esac\n> +\tdone\n> +}\n> +\n> +stage_submodule () {\n> +    path=\"$1\"\n> +    submodule_sha1=\"$2\"\n> +\n> +    submodule_basename=$(basename \"$path\")\n> +    tree_with_module=$(echo \"160000 commit $submodule_sha1\t\\\"$submodule_basename\\\"\" | git mktree --missing 2>/dev/null)\n> +    if test -z \"$tree_with_module\" ; then\n> +\techo \"$path: unable to stage commit $sha1\"\n> +\treturn 1\n> +    fi\n> +    git checkout $tree_with_module -- \"$path\"\n> +}\n> +\n>  checkout_staged_file () {\n>      tmpfile=$(expr \"$(git checkout-index --temp --stage=\"$1\" \"$2\")\" : '\\([^\t]*\\)\t')\n>  \n> @@ -139,13 +190,23 @@ merge_file () {\n>      REMOTE=\"./$MERGED.REMOTE.$ext\"\n>      BASE=\"./$MERGED.BASE.$ext\"\n>  \n> -    mv -- \"$MERGED\" \"$BACKUP\"\n> -    cp -- \"$BACKUP\" \"$MERGED\"\n> -\n>      base_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==1) print $1;}')\n>      local_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $1;}')\n>      remote_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $1;}')\n>  \n> +    if is_submodule \"$local_mode\" || is_submodule \"$remote_mode\"; then\n> +\techo \"Submodule merge conflict for '$MERGED':\"\n> +\tlocal_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n> +\tremote_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $2;}')\n> +\tdescribe_file \"$local_mode\" \"local\" \"$local_sha1\"\n> +\tdescribe_file \"$remote_mode\" \"remote\" \"$remote_sha1\"\n> +\tresolve_submodule_merge\n> +\treturn\n> +    fi\n> +\n> +    mv -- \"$MERGED\" \"$BACKUP\"\n> +    cp -- \"$BACKUP\" \"$MERGED\"\n> +\n>      base_present   && checkout_staged_file 1 \"$MERGED\" \"$BASE\"\n>      local_present  && checkout_staged_file 2 \"$MERGED\" \"$LOCAL\"\n>      remote_present && checkout_staged_file 3 \"$MERGED\" \"$REMOTE\"\n> -- \n> 1.7.5.rc1.1.g64431\n"},{"id":"165539","messageId":"39B95643-D7FB-436F-B407-D716D43C3922@JonathonMah.com","threadId":"27043","inReplyTo":"20110409120301.GA1369@gmail.com","subject":"Re: [PATCH] mergetool: Teach about submodules","fromName":"Jonathon Mah","fromEmail":"me@jonathonmah.com","sentAt":"2011-04-10T10:15:24Z","receivedAt":"2011-04-10T10:15:24Z","isPatch":true,"sender":{"key":"me@jonathonmah.com","avatar":"https://avatars.githubusercontent.com/u/2748?v=4"},"body":"Hi David,\n\nOn 2011-04-09, at 05:03, David Aguilar wrote:\n\n> One thing that could make it better, though, would be if it\n> also added tests for the feature to t/t7610-mergetool.sh.\n> That will help prevent someone (like me) from accidentally\n> breaking it in the future.\n\nThanks for the feedback. I've shoehorned some tests into the existing ones, and the patch is imminent. It can be squashed into the previous patch if that's nicer.\n\nIt would obviously be nice if you could tell mergetool to merge those submodule commits, and then have it recurse into the submodule. Submodules don't feel quite robust enough to me to automate to that extent yet, but I might keep tinkering should the urge emerge. :)\n\n\n\nJonathon Mah\nme@JonathonMah.com\n"},{"id":"165540","messageId":"1302430704-22754-1-git-send-email-me@JonathonMah.com","threadId":"27043","inReplyTo":"39B95643-D7FB-436F-B407-D716D43C3922@JonathonMah.com","subject":"[PATCH] mergetool: Added tests for submodule","fromName":"Jonathon Mah","fromEmail":"me@jonathonmah.com","sentAt":"2011-04-10T10:18:24Z","receivedAt":"2011-04-10T10:18:24Z","isPatch":true,"sender":{"key":"me@jonathonmah.com","avatar":"https://avatars.githubusercontent.com/u/2748?v=4"},"body":"---\n t/t7610-mergetool.sh |   50 +++++++++++++++++++++++++++++++++++++++++++++++---\n 1 files changed, 47 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\nindex dc838c9..96d7d9b 100755\n--- a/t/t7610-mergetool.sh\n+++ b/t/t7610-mergetool.sh\n@@ -22,26 +22,53 @@ test_expect_success 'setup' '\n     echo master file14 >file14 &&\n     mkdir subdir &&\n     echo master sub >subdir/file3 &&\n-    git add file1 file1[1-4] subdir/file3 &&\n+    test_create_repo submod &&\n+    (\n+\tcd submod &&\n+\t: >foo &&\n+\tgit add foo &&\n+\tgit commit -m \"Add foo\"\n+    ) &&\n+    git config -f .gitmodules submodule.submod.path submod &&\n+    git config -f .gitmodules submodule.submod.url git://example.com/submod &&\n+    git config submodule.submod.url git://example.com/submod &&\n+    git config fetch.recurseSubmodules false &&\n+    git add file1 file1[1-4] subdir/file3 .gitmodules submod &&\n     git commit -m \"add initial versions\" &&\n \n     git checkout -b branch1 master &&\n+    git submodule update -N &&\n     echo branch1 change >file1 &&\n     echo branch1 newfile >file2 &&\n     echo branch1 change file11 >file11 &&\n     echo branch1 change file13 >file13 &&\n     echo branch1 sub >subdir/file3 &&\n-    git add file1 file11 file13 file2 subdir/file3 &&\n+    (\n+\tcd submod &&\n+\techo branch1 submodule >bar &&\n+\tgit add bar &&\n+\tgit commit -m \"Add bar on branch1\" &&\n+\tgit checkout -b submod-branch1\n+    ) &&\n+    git add file1 file11 file13 file2 subdir/file3 submod &&\n     git rm file12 &&\n     git commit -m \"branch1 changes\" &&\n \n     git checkout master &&\n+    git submodule update -N &&\n     echo master updated >file1 &&\n     echo master new >file2 &&\n     echo master updated file12 >file12 &&\n     echo master updated file14 >file14 &&\n     echo master new sub >subdir/file3 &&\n-    git add file1 file12 file14 file2 subdir/file3 &&\n+    (\n+\tcd submod &&\n+\techo master submodule >bar &&\n+\tgit add bar &&\n+\tgit commit -m \"Add bar on master\" &&\n+\tgit checkout -b submod-master\n+    ) &&\n+    git add file1 file12 file14 file2 subdir/file3 submod &&\n     git rm file11 &&\n     git commit -m \"master updates\" &&\n \n@@ -52,15 +79,18 @@ test_expect_success 'setup' '\n \n test_expect_success 'custom mergetool' '\n     git checkout -b test1 branch1 &&\n+    git submodule update -N &&\n     test_must_fail git merge master >/dev/null 2>&1 &&\n     ( yes \"\" | git mergetool file1 >/dev/null 2>&1 ) &&\n     ( yes \"\" | git mergetool file2 >/dev/null 2>&1 ) &&\n     ( yes \"\" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&\n     ( yes \"d\" | git mergetool file11 >/dev/null 2>&1 ) &&\n     ( yes \"d\" | git mergetool file12 >/dev/null 2>&1 ) &&\n+    ( yes \"l\" | git mergetool submod >/dev/null 2>&1 ) &&\n     test \"$(cat file1)\" = \"master updated\" &&\n     test \"$(cat file2)\" = \"master new\" &&\n     test \"$(cat subdir/file3)\" = \"master new sub\" &&\n+    test \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n     git commit -m \"branch1 resolved with mergetool\"\n '\n \n@@ -73,9 +103,12 @@ test_expect_success 'mergetool crlf' '\n     ( yes \"\" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&\n     ( yes \"d\" | git mergetool file11 >/dev/null 2>&1 ) &&\n     ( yes \"d\" | git mergetool file12 >/dev/null 2>&1 ) &&\n+    ( yes \"r\" | git mergetool submod >/dev/null 2>&1 ) &&\n     test \"$(printf x | cat file1 -)\" = \"$(printf \"master updated\\r\\nx\")\" &&\n     test \"$(printf x | cat file2 -)\" = \"$(printf \"master new\\r\\nx\")\" &&\n     test \"$(printf x | cat subdir/file3 -)\" = \"$(printf \"master new sub\\r\\nx\")\" &&\n+    git submodule update -N &&\n+    test \"$(cat submod/bar)\" = \"master submodule\" &&\n     git commit -m \"branch1 resolved with mergetool - autocrlf\" &&\n     git config core.autocrlf false &&\n     git reset --hard\n@@ -83,6 +116,7 @@ test_expect_success 'mergetool crlf' '\n \n test_expect_success 'mergetool in subdir' '\n     git checkout -b test3 branch1 &&\n+    git submodule update -N &&\n     (\n \tcd subdir &&\n \ttest_must_fail git merge master >/dev/null 2>&1 &&\n@@ -98,18 +132,22 @@ test_expect_success 'mergetool on file in parent dir' '\n \t( yes \"\" | git mergetool ../file2 >/dev/null 2>&1 ) &&\n \t( yes \"d\" | git mergetool ../file11 >/dev/null 2>&1 ) &&\n \t( yes \"d\" | git mergetool ../file12 >/dev/null 2>&1 ) &&\n+\t( yes \"l\" | git mergetool ../submod >/dev/null 2>&1 ) &&\n \ttest \"$(cat ../file1)\" = \"master updated\" &&\n \ttest \"$(cat ../file2)\" = \"master new\" &&\n+\ttest \"$(cat ../submod/bar)\" = \"branch1 submodule\" &&\n \tgit commit -m \"branch1 resolved with mergetool - subdir\"\n     )\n '\n \n test_expect_success 'mergetool skips autoresolved' '\n     git checkout -b test4 branch1 &&\n+    git submodule update -N &&\n     test_must_fail git merge master &&\n     test -n \"$(git ls-files -u)\" &&\n     ( yes \"d\" | git mergetool file11 >/dev/null 2>&1 ) &&\n     ( yes \"d\" | git mergetool file12 >/dev/null 2>&1 ) &&\n+    ( yes \"l\" | git mergetool submod >/dev/null 2>&1 ) &&\n     output=\"$(git mergetool --no-prompt)\" &&\n     test \"$output\" = \"No files need merging\" &&\n     git reset --hard\n@@ -120,10 +158,13 @@ test_expect_success 'mergetool merges all from subdir' '\n \tcd subdir &&\n \tgit config rerere.enabled false &&\n \ttest_must_fail git merge master &&\n+\t( yes \"r\" | git mergetool ../submod ) &&\n \t( yes \"d\" \"d\" | git mergetool --no-prompt ) &&\n \ttest \"$(cat ../file1)\" = \"master updated\" &&\n \ttest \"$(cat ../file2)\" = \"master new\" &&\n \ttest \"$(cat file3)\" = \"master new sub\" &&\n+\t( cd .. && git submodule update -N ) &&\n+\ttest \"$(cat ../submod/bar)\" = \"master submodule\" &&\n \tgit commit -m \"branch2 resolved by mergetool from subdir\"\n     )\n '\n@@ -132,8 +173,11 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' '\n     git config rerere.enabled true &&\n     rm -rf .git/rr-cache &&\n     git checkout -b test5 branch1\n+    git submodule update -N &&\n     test_must_fail git merge master >/dev/null 2>&1 &&\n+    ( yes \"l\" | git mergetool --no-prompt submod >/dev/null 2>&1 ) &&\n     ( yes \"d\" \"d\" | git mergetool --no-prompt >/dev/null 2>&1 ) &&\n+    git submodule update -N &&\n     output=\"$(yes \"n\" | git mergetool --no-prompt)\" &&\n     test \"$output\" = \"No files need merging\" &&\n     git reset --hard\n-- \n1.7.5.rc1.1.g64431\n"},{"id":"165644","messageId":"7v62qkwomk.fsf@alter.siamese.dyndns.org","threadId":"27043","inReplyTo":"1302321570-85987-1-git-send-email-me@JonathonMah.com","subject":"Re: [PATCH] mergetool: Teach about submodules","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-11T19:53:55Z","receivedAt":"2011-04-11T19:53:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathon Mah <me@JonathonMah.com> writes:\n\n> Mergetool mildly clobbered submodules, attempting to move and copy their\n> directories. It now recognizes submodules and offers a resolution:\n> Submodule merge conflict for 'Shared':\n>   {local}: commit ad9f12e3e6205381bf2163a793d1e596a9e211d0\n>   {remote}: commit f5893fb70ec5646efcd9aa643c5136753ac89253\n> Use (l)ocal or (r)emote, or (a)bort?\n\nI was confused when I first read the first sentence, because \"when/under\nwhat condition\" was missing.  I also suspect that we don't even have to\nsay \"mildly\".  The reviewers can judge the severity themselves.\n\nWhen you can, please make the statement of the problem and the description\nof the solution into separate paragraphs.  It also makes it easier to read\nif you indent illustration (e.g. sample transcript) from your description.\n\nPerhaps like this:\n\n    When a merge in the superproject results in conflict at a submodule,\n    mergetool used to mildly clobber submodules, attempting to move and\n    copy their directories.\n\n    Recognize submodules and offer a resolution instead:\n\n      Submodule merge conflict for 'Shared':\n        {local}: commit ad9f12e3e6205381bf2163a793d1e596a9e211d0\n        {remote}: commit f5893fb70ec5646efcd9aa643c5136753ac89253\n      Use (l)ocal or (r)emote, or (a)bort?\n\n> Selecting a commit will stage it, but not update the submodule (as it\n> would had there been no conflict). Type changes are also supported,\n> should the path be a submodule on one side, and a file on the other.\n>\n> Signed-off-by: Jonathon Mah <me@JonathonMah.com>\n> ...\n> +resolve_submodule_merge () {\n> +    while true; do\n> +\tprintf \"Use (l)ocal or (r)emote, or (a)bort? \"\n> +\tread ans\n> +\tcase \"$ans\" in\n> +\t    [lL]*)\n> +\t\tlocal_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $1;}')\n> +\t\tif is_submodule \"$local_mode\"; then\n> +\t\t    stage_submodule \"$MERGED\" $(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n\nIf the version we had checked out and merging into has a submodule at the\npath, use that.  This part of the logic seem sensible.\n\nDon't you already have local_mode from the caller here?  For that matter,\ndon't you also have access to local_sha1 the caller already has computed?\n\n> +\t\telse\n> +\t\t    git checkout-index -f --stage=2 -- \"$MERGED\"\n> +\t\t    git add -- \"$MERGED\"\n\nIf what we had is not a submodule, then do a checkout-index.  Here you\nassume that we _must_ have a stage #2 entry, but is that always the case?\n\nCan we be in delete/modify conflict, where we had a submodule at the\ncommon ancestor, we removed the submodule while the other branch modified\nit?  What does this \"else\" clause do in such a case?\n\nThe same comment applies symmetrically to the \"remote\" case, of course.\n\n> +stage_submodule () {\n> +    path=\"$1\"\n> +    submodule_sha1=\"$2\"\n> +\n> +    submodule_basename=$(basename \"$path\")\n> +    tree_with_module=$(echo \"160000 commit $submodule_sha1\t\\\"$submodule_basename\\\"\" | git mktree --missing 2>/dev/null)\n> +    if test -z \"$tree_with_module\" ; then\n> +\techo \"$path: unable to stage commit $sha1\"\n> +\treturn 1\n> +    fi\n> +    git checkout $tree_with_module -- \"$path\"\n\nAre you looking for \"git update-index --cacheinfo 160000 $sha1 $name\"\nhere, or is there something deeper going on?\n\nIf not, please don't use the \"primarily for debugging and hacking\" command\nmktree for something like this to create a garbage tree object.\n\n>  checkout_staged_file () {\n>      tmpfile=$(expr \"$(git checkout-index --temp --stage=\"$1\" \"$2\")\" : '\\([^\t]*\\)\t')\n>  \n> +    if is_submodule \"$local_mode\" || is_submodule \"$remote_mode\"; then\n> +\techo \"Submodule merge conflict for '$MERGED':\"\n> +\tlocal_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n> +\tremote_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $2;}')\n> +\tdescribe_file \"$local_mode\" \"local\" \"$local_sha1\"\n> +\tdescribe_file \"$remote_mode\" \"remote\" \"$remote_sha1\"\n> +\tresolve_submodule_merge\n> +\treturn\n\nI really hate these repeated \"awk\" invocations, here and then inside the\ncallee.  As the script seems to use these as global variables, the callee\nshouldn't have to recompute local/remote-mode/sha1, no?\n\nThanks.\n"},{"id":"165733","messageId":"C411FA8C-8E26-4DB8-BC2A-B95CDBB2CF38@jonathonmah.com","threadId":"27043","inReplyTo":"7v62qkwomk.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] mergetool: Teach about submodules","fromName":"Jonathon Mah","fromEmail":"me@jonathonmah.com","sentAt":"2011-04-13T10:00:15Z","receivedAt":"2011-04-13T10:00:15Z","isPatch":true,"sender":{"key":"me@jonathonmah.com","avatar":"https://avatars.githubusercontent.com/u/2748?v=4"},"body":"Hi Junio,\n\nOn 2011-04-11, at 12:53, Junio C Hamano wrote:\n\n>> +resolve_submodule_merge () {\n>> +    while true; do\n>> +\tprintf \"Use (l)ocal or (r)emote, or (a)bort? \"\n>> +\tread ans\n>> +\tcase \"$ans\" in\n>> +\t    [lL]*)\n>> +\t\tlocal_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $1;}')\n>> +\t\tif is_submodule \"$local_mode\"; then\n>> +\t\t    stage_submodule \"$MERGED\" $(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n> \n> If the version we had checked out and merging into has a submodule at the\n> path, use that.  This part of the logic seem sensible.\n> \n> Don't you already have local_mode from the caller here?  For that matter,\n> don't you also have access to local_sha1 the caller already has computed?\n\nThanks for the feedback. I'm not particularly experienced with shell scripts, so I don't usually think of the lack of variable scope.\n\n>> +\t\telse\n>> +\t\t    git checkout-index -f --stage=2 -- \"$MERGED\"\n>> +\t\t    git add -- \"$MERGED\"\n> \n> If what we had is not a submodule, then do a checkout-index.  Here you\n> assume that we _must_ have a stage #2 entry, but is that always the case?\n\nIndeed, it may not have. A v2 patch is following that handles the submodule path without a stage 2 (either because the submodule was deleted, or turned into a regular directory).\n\n>> +stage_submodule () {\n>> +    path=\"$1\"\n>> +    submodule_sha1=\"$2\"\n>> +\n>> +    submodule_basename=$(basename \"$path\")\n>> +    tree_with_module=$(echo \"160000 commit $submodule_sha1\t\\\"$submodule_basename\\\"\" | git mktree --missing 2>/dev/null)\n>> +    if test -z \"$tree_with_module\" ; then\n>> +\techo \"$path: unable to stage commit $sha1\"\n>> +\treturn 1\n>> +    fi\n>> +    git checkout $tree_with_module -- \"$path\"\n> \n> Are you looking for \"git update-index --cacheinfo 160000 $sha1 $name\"\n\nYes, this is the command I was intending. I needed it a while back when I was converting a monolithic repo into a super/submodule configuration, but couldn't find a better way than the kludge above. I had hoped someone would show me the clean way to do it!\n\n\n\nJonathon Mah\nme@JonathonMah.com\n"},{"id":"165734","messageId":"1302688848-30518-1-git-send-email-me@JonathonMah.com","threadId":"27043","inReplyTo":"7v62qkwomk.fsf@alter.siamese.dyndns.org","subject":"[PATCH v2] mergetool: Teach about submodules","fromName":"Jonathon Mah","fromEmail":"me@jonathonmah.com","sentAt":"2011-04-13T10:00:48Z","receivedAt":"2011-04-13T10:00:48Z","isPatch":true,"sender":{"key":"me@jonathonmah.com","avatar":"https://avatars.githubusercontent.com/u/2748?v=4"},"body":"When the index has conflicted submodules, mergetool used to mildly\nclobber the module, renaming it to mymodule.BACKUP.nnnn, then failing to\ncopy it non-recursively.\n\nRecognize submodules and offer a resolution instead:\n\n  Submodule merge conflict for 'Shared':\n    {local}: submodule commit ad9f12e3e6205381bf2163a793d1e596a9e211d0\n    {remote}: submodule commit f5893fb70ec5646efcd9aa643c5136753ac89253\n  Use (l)ocal or (r)emote, or (a)bort?\n\nSelecting a commit will stage it, but not update the submodule (as git\ndoes had there been no conflict). Type changes are also supported,\nshould the path be a submodule on one side, and a file, symlink,\ndirectory, or deleted on the other.\n\nSigned-off-by: Jonathon Mah <me@JonathonMah.com>\n---\n git-mergetool.sh     |   90 +++++++++++++++-\n t/t7610-mergetool.sh |  290 +++++++++++++++++++++++++++++++++++++++++++++++++-\n 2 files changed, 371 insertions(+), 9 deletions(-)\n\ndiff --git a/git-mergetool.sh b/git-mergetool.sh\nindex bacbda2..3aab5aa 100755\n--- a/git-mergetool.sh\n+++ b/git-mergetool.sh\n@@ -21,6 +21,10 @@ is_symlink () {\n     test \"$1\" = 120000\n }\n \n+is_submodule () {\n+    test \"$1\" = 160000\n+}\n+\n local_present () {\n     test -n \"$local_mode\"\n }\n@@ -35,7 +39,8 @@ base_present () {\n \n cleanup_temp_files () {\n     if test \"$1\" = --save-backup ; then\n-\tmv -- \"$BACKUP\" \"$MERGED.orig\"\n+\trm -rf -- \"$MERGED.orig\"\n+\ttest -e \"$BACKUP\" && mv -- \"$BACKUP\" \"$MERGED.orig\"\n \trm -f -- \"$LOCAL\" \"$REMOTE\" \"$BASE\"\n     else\n \trm -f -- \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$BACKUP\"\n@@ -52,11 +57,13 @@ describe_file () {\n \techo \"deleted\"\n     elif is_symlink \"$mode\" ; then\n \techo \"a symbolic link -> '$(cat \"$file\")'\"\n+    elif is_submodule \"$mode\" ; then\n+\techo \"submodule commit $file\"\n     else\n \tif base_present; then\n-\t    echo \"modified\"\n+\t    echo \"modified file\"\n \telse\n-\t    echo \"created\"\n+\t    echo \"created file\"\n \tfi\n     fi\n }\n@@ -112,6 +119,67 @@ resolve_deleted_merge () {\n \tdone\n }\n \n+resolve_submodule_merge () {\n+    while true; do\n+\tprintf \"Use (l)ocal or (r)emote, or (a)bort? \"\n+\tread ans\n+\tcase \"$ans\" in\n+\t    [lL]*)\n+\t\tif ! local_present; then\n+\t\t    if test -n \"$(git ls-tree HEAD -- \"$MERGED\")\"; then\n+\t\t\t# Local isn't present, but it's a subdirectory\n+\t\t\tgit ls-tree --full-name -r HEAD -- \"$MERGED\" | git update-index --index-info || exit $?\n+\t\t    else\n+\t\t\ttest -e \"$MERGED\" && mv -- \"$MERGED\" \"$BACKUP\"\n+\t\t\tgit update-index --force-remove \"$MERGED\"\n+\t\t\tcleanup_temp_files --save-backup\n+\t\t    fi\n+\t\telif is_submodule \"$local_mode\"; then\n+\t\t    stage_submodule \"$MERGED\" \"$local_sha1\"\n+\t\telse\n+\t\t    git checkout-index -f --stage=2 -- \"$MERGED\"\n+\t\t    git add -- \"$MERGED\"\n+\t\tfi\n+\t\treturn 0\n+\t\t;;\n+\t    [rR]*)\n+\t\tif ! remote_present; then\n+\t\t    if test -n \"$(git ls-tree MERGE_HEAD -- \"$MERGED\")\"; then\n+\t\t\t# Remote isn't present, but it's a subdirectory\n+\t\t\tgit ls-tree --full-name -r MERGE_HEAD -- \"$MERGED\" | git update-index --index-info || exit $?\n+\t\t    else\n+\t\t\ttest -e \"$MERGED\" && mv -- \"$MERGED\" \"$BACKUP\"\n+\t\t\tgit update-index --force-remove \"$MERGED\"\n+\t\t    fi\n+\t\telif is_submodule \"$remote_mode\"; then\n+\t\t    ! is_submodule \"$local_mode\" && test -e \"$MERGED\" && mv -- \"$MERGED\" \"$BACKUP\"\n+\t\t    stage_submodule \"$MERGED\" \"$remote_sha1\"\n+\t\telse\n+\t\t    test -e \"$MERGED\" && mv -- \"$MERGED\" \"$BACKUP\"\n+\t\t    git checkout-index -f --stage=3 -- \"$MERGED\"\n+\t\t    git add -- \"$MERGED\"\n+\t\tfi\n+\t\tcleanup_temp_files --save-backup\n+\t\treturn 0\n+\t\t;;\n+\t    [aA]*)\n+\t\treturn 1\n+\t\t;;\n+\t    esac\n+\tdone\n+}\n+\n+stage_submodule () {\n+    path=\"$1\"\n+    submodule_sha1=\"$2\"\n+    mkdir -p \"$path\" || die \"fatal: unable to create directory for module at $path\"\n+    # Find $path relative to work tree\n+    work_tree_root=$(cd_to_toplevel && pwd)\n+    work_rel_path=$(cd \"$path\" && GIT_WORK_TREE=\"${work_tree_root}\" git rev-parse --show-prefix)\n+    test -n \"$work_rel_path\" || die \"fatal: unable to get path of module $path relative to work tree\"\n+    git update-index --add --replace --cacheinfo 160000 \"$submodule_sha1\" \"${work_rel_path%/}\" || die\n+}\n+\n checkout_staged_file () {\n     tmpfile=$(expr \"$(git checkout-index --temp --stage=\"$1\" \"$2\")\" : '\\([^\t]*\\)\t')\n \n@@ -139,13 +207,23 @@ merge_file () {\n     REMOTE=\"./$MERGED.REMOTE.$ext\"\n     BASE=\"./$MERGED.BASE.$ext\"\n \n-    mv -- \"$MERGED\" \"$BACKUP\"\n-    cp -- \"$BACKUP\" \"$MERGED\"\n-\n     base_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==1) print $1;}')\n     local_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $1;}')\n     remote_mode=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $1;}')\n \n+    if is_submodule \"$local_mode\" || is_submodule \"$remote_mode\"; then\n+\techo \"Submodule merge conflict for '$MERGED':\"\n+\tlocal_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==2) print $2;}')\n+\tremote_sha1=$(git ls-files -u -- \"$MERGED\" | awk '{if ($3==3) print $2;}')\n+\tdescribe_file \"$local_mode\" \"local\" \"$local_sha1\"\n+\tdescribe_file \"$remote_mode\" \"remote\" \"$remote_sha1\"\n+\tresolve_submodule_merge\n+\treturn\n+    fi\n+\n+    mv -- \"$MERGED\" \"$BACKUP\"\n+    cp -- \"$BACKUP\" \"$MERGED\"\n+\n     base_present   && checkout_staged_file 1 \"$MERGED\" \"$BASE\"\n     local_present  && checkout_staged_file 2 \"$MERGED\" \"$LOCAL\"\n     remote_present && checkout_staged_file 3 \"$MERGED\" \"$REMOTE\"\ndiff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\nindex dc838c9..cbc08e3 100755\n--- a/t/t7610-mergetool.sh\n+++ b/t/t7610-mergetool.sh\n@@ -22,26 +22,50 @@ test_expect_success 'setup' '\n     echo master file14 >file14 &&\n     mkdir subdir &&\n     echo master sub >subdir/file3 &&\n-    git add file1 file1[1-4] subdir/file3 &&\n+    test_create_repo submod &&\n+    (\n+\tcd submod &&\n+\t: >foo &&\n+\tgit add foo &&\n+\tgit commit -m \"Add foo\"\n+    ) &&\n+    git submodule add git://example.com/submod submod &&\n+    git add file1 file1[1-4] subdir/file3 .gitmodules submod &&\n     git commit -m \"add initial versions\" &&\n \n     git checkout -b branch1 master &&\n+    git submodule update -N &&\n     echo branch1 change >file1 &&\n     echo branch1 newfile >file2 &&\n     echo branch1 change file11 >file11 &&\n     echo branch1 change file13 >file13 &&\n     echo branch1 sub >subdir/file3 &&\n-    git add file1 file11 file13 file2 subdir/file3 &&\n+    (\n+\tcd submod &&\n+\techo branch1 submodule >bar &&\n+\tgit add bar &&\n+\tgit commit -m \"Add bar on branch1\" &&\n+\tgit checkout -b submod-branch1\n+    ) &&\n+    git add file1 file11 file13 file2 subdir/file3 submod &&\n     git rm file12 &&\n     git commit -m \"branch1 changes\" &&\n \n     git checkout master &&\n+    git submodule update -N &&\n     echo master updated >file1 &&\n     echo master new >file2 &&\n     echo master updated file12 >file12 &&\n     echo master updated file14 >file14 &&\n     echo master new sub >subdir/file3 &&\n-    git add file1 file12 file14 file2 subdir/file3 &&\n+    (\n+\tcd submod &&\n+\techo master submodule >bar &&\n+\tgit add bar &&\n+\tgit commit -m \"Add bar on master\" &&\n+\tgit checkout -b submod-master\n+    ) &&\n+    git add file1 file12 file14 file2 subdir/file3 submod &&\n     git rm file11 &&\n     git commit -m \"master updates\" &&\n \n@@ -52,15 +76,18 @@ test_expect_success 'setup' '\n \n test_expect_success 'custom mergetool' '\n     git checkout -b test1 branch1 &&\n+    git submodule update -N &&\n     test_must_fail git merge master >/dev/null 2>&1 &&\n     ( yes \"\" | git mergetool file1 >/dev/null 2>&1 ) &&\n     ( yes \"\" | git mergetool file2 >/dev/null 2>&1 ) &&\n     ( yes \"\" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&\n     ( yes \"d\" | git mergetool file11 >/dev/null 2>&1 ) &&\n     ( yes \"d\" | git mergetool file12 >/dev/null 2>&1 ) &&\n+    ( yes \"l\" | git mergetool submod >/dev/null 2>&1 ) &&\n     test \"$(cat file1)\" = \"master updated\" &&\n     test \"$(cat file2)\" = \"master new\" &&\n     test \"$(cat subdir/file3)\" = \"master new sub\" &&\n+    test \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n     git commit -m \"branch1 resolved with mergetool\"\n '\n \n@@ -73,9 +100,12 @@ test_expect_success 'mergetool crlf' '\n     ( yes \"\" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&\n     ( yes \"d\" | git mergetool file11 >/dev/null 2>&1 ) &&\n     ( yes \"d\" | git mergetool file12 >/dev/null 2>&1 ) &&\n+    ( yes \"r\" | git mergetool submod >/dev/null 2>&1 ) &&\n     test \"$(printf x | cat file1 -)\" = \"$(printf \"master updated\\r\\nx\")\" &&\n     test \"$(printf x | cat file2 -)\" = \"$(printf \"master new\\r\\nx\")\" &&\n     test \"$(printf x | cat subdir/file3 -)\" = \"$(printf \"master new sub\\r\\nx\")\" &&\n+    git submodule update -N &&\n+    test \"$(cat submod/bar)\" = \"master submodule\" &&\n     git commit -m \"branch1 resolved with mergetool - autocrlf\" &&\n     git config core.autocrlf false &&\n     git reset --hard\n@@ -83,6 +113,7 @@ test_expect_success 'mergetool crlf' '\n \n test_expect_success 'mergetool in subdir' '\n     git checkout -b test3 branch1 &&\n+    git submodule update -N &&\n     (\n \tcd subdir &&\n \ttest_must_fail git merge master >/dev/null 2>&1 &&\n@@ -98,18 +129,22 @@ test_expect_success 'mergetool on file in parent dir' '\n \t( yes \"\" | git mergetool ../file2 >/dev/null 2>&1 ) &&\n \t( yes \"d\" | git mergetool ../file11 >/dev/null 2>&1 ) &&\n \t( yes \"d\" | git mergetool ../file12 >/dev/null 2>&1 ) &&\n+\t( yes \"l\" | git mergetool ../submod >/dev/null 2>&1 ) &&\n \ttest \"$(cat ../file1)\" = \"master updated\" &&\n \ttest \"$(cat ../file2)\" = \"master new\" &&\n+\ttest \"$(cat ../submod/bar)\" = \"branch1 submodule\" &&\n \tgit commit -m \"branch1 resolved with mergetool - subdir\"\n     )\n '\n \n test_expect_success 'mergetool skips autoresolved' '\n     git checkout -b test4 branch1 &&\n+    git submodule update -N &&\n     test_must_fail git merge master &&\n     test -n \"$(git ls-files -u)\" &&\n     ( yes \"d\" | git mergetool file11 >/dev/null 2>&1 ) &&\n     ( yes \"d\" | git mergetool file12 >/dev/null 2>&1 ) &&\n+    ( yes \"l\" | git mergetool submod >/dev/null 2>&1 ) &&\n     output=\"$(git mergetool --no-prompt)\" &&\n     test \"$output\" = \"No files need merging\" &&\n     git reset --hard\n@@ -120,10 +155,13 @@ test_expect_success 'mergetool merges all from subdir' '\n \tcd subdir &&\n \tgit config rerere.enabled false &&\n \ttest_must_fail git merge master &&\n+\t( yes \"r\" | git mergetool ../submod ) &&\n \t( yes \"d\" \"d\" | git mergetool --no-prompt ) &&\n \ttest \"$(cat ../file1)\" = \"master updated\" &&\n \ttest \"$(cat ../file2)\" = \"master new\" &&\n \ttest \"$(cat file3)\" = \"master new sub\" &&\n+\t( cd .. && git submodule update -N ) &&\n+\ttest \"$(cat ../submod/bar)\" = \"master submodule\" &&\n \tgit commit -m \"branch2 resolved by mergetool from subdir\"\n     )\n '\n@@ -132,11 +170,257 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' '\n     git config rerere.enabled true &&\n     rm -rf .git/rr-cache &&\n     git checkout -b test5 branch1\n+    git submodule update -N &&\n     test_must_fail git merge master >/dev/null 2>&1 &&\n+    ( yes \"l\" | git mergetool --no-prompt submod >/dev/null 2>&1 ) &&\n     ( yes \"d\" \"d\" | git mergetool --no-prompt >/dev/null 2>&1 ) &&\n+    git submodule update -N &&\n     output=\"$(yes \"n\" | git mergetool --no-prompt)\" &&\n     test \"$output\" = \"No files need merging\" &&\n     git reset --hard\n '\n \n+test_expect_success 'deleted vs modified submodule' '\n+    git checkout -b test6 branch1 &&\n+    git submodule update -N &&\n+    mv submod submod-movedaside &&\n+    git rm submod &&\n+    git commit -m \"Submodule deleted from branch\" &&\n+    git checkout -b test6.a test6 &&\n+    test_must_fail git merge master &&\n+    test -n \"$(git ls-files -u)\" &&\n+    ( yes \"\" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&\n+    ( yes \"d\" | git mergetool file11 file12 >/dev/null 2>&1 ) &&\n+    ( yes \"r\" | git mergetool submod ) &&\n+    rmdir submod && mv submod-movedaside submod &&\n+    test \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n+    git submodule update -N &&\n+    test \"$(cat submod/bar)\" = \"master submodule\" &&\n+    output=\"$(git mergetool --no-prompt)\" &&\n+    test \"$output\" = \"No files need merging\" &&\n+    git commit -m \"Merge resolved by keeping module\" &&\n+\n+    mv submod submod-movedaside &&\n+    git checkout -b test6.b test6 &&\n+    git submodule update -N &&\n+    test_must_fail git merge master &&\n+    test -n \"$(git ls-files -u)\" &&\n+    ( yes \"\" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&\n+    ( yes \"d\" | git mergetool file11 file12 >/dev/null 2>&1 ) &&\n+    ( yes \"l\" | git mergetool submod ) &&\n+    test ! -e submod &&\n+    output=\"$(git mergetool --no-prompt)\" &&\n+    test \"$output\" = \"No files need merging\" &&\n+    git commit -m \"Merge resolved by deleting module\" &&\n+\n+    mv submod-movedaside submod &&\n+    git checkout -b test6.c master &&\n+    git submodule update -N &&\n+    test_must_fail git merge test6 &&\n+    test -n \"$(git ls-files -u)\" &&\n+    ( yes \"\" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&\n+    ( yes \"d\" | git mergetool file11 file12 >/dev/null 2>&1 ) &&\n+    ( yes \"r\" | git mergetool submod ) &&\n+    test ! -e submod &&\n+    test -d submod.orig &&\n+    git submodule update -N &&\n+    output=\"$(git mergetool --no-prompt)\" &&\n+    test \"$output\" = \"No files need merging\" &&\n+    git commit -m \"Merge resolved by deleting module\" &&\n+    mv submod.orig submod &&\n+\n+    git checkout -b test6.d master &&\n+    git submodule update -N &&\n+    test_must_fail git merge test6 &&\n+    test -n \"$(git ls-files -u)\" &&\n+    ( yes \"\" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&\n+    ( yes \"d\" | git mergetool file11 file12 >/dev/null 2>&1 ) &&\n+    ( yes \"l\" | git mergetool submod ) &&\n+    test \"$(cat submod/bar)\" = \"master submodule\" &&\n+    git submodule update -N &&\n+    test \"$(cat submod/bar)\" = \"master submodule\" &&\n+    output=\"$(git mergetool --no-prompt)\" &&\n+    test \"$output\" = \"No files need merging\" &&\n+    git commit -m \"Merge resolved by keeping module\" &&\n+    git reset --hard HEAD\n+'\n+\n+test_expect_success 'file vs modified submodule' '\n+    git checkout -b test7 branch1 &&\n+    git submodule update -N &&\n+    mv submod submod-movedaside &&\n+    git rm submod &&\n+    echo not a submodule >submod &&\n+    git add submod &&\n+    git commit -m \"Submodule path becomes file\" &&\n+    git checkout -b test7.a branch1 &&\n+    test_must_fail git merge master &&\n+    test -n \"$(git ls-files -u)\" &&\n+    ( yes \"\" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&\n+    ( yes \"d\" | git mergetool file11 file12 >/dev/null 2>&1 ) &&\n+    ( yes \"r\" | git mergetool submod ) &&\n+    rmdir submod && mv submod-movedaside submod &&\n+    test \"$(cat submod/bar)\" = \"branch1 submodule\" &&\n+    git submodule update -N &&\n+    test \"$(cat submod/bar)\" = \"master submodule\" &&\n+    output=\"$(git mergetool --no-prompt)\" &&\n+    test \"$output\" = \"No files need merging\" &&\n+    git commit -m \"Merge resolved by keeping module\" &&\n+\n+    mv submod submod-movedaside &&\n+    git checkout -b test7.b test7 &&\n+    test_must_fail git merge master &&\n+    test -n \"$(git ls-files -u)\" &&\n+    ( yes \"\" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&\n+    ( yes \"d\" | git mergetool file11 file12 >/dev/null 2>&1 ) &&\n+    ( yes \"l\" | git mergetool submod ) &&\n+    git submodule update -N &&\n+    test \"$(cat submod)\" = \"not a submodule\" &&\n+    output=\"$(git mergetool --no-prompt)\" &&\n+    test \"$output\" = \"No files need merging\" &&\n+    git commit -m \"Merge resolved by keeping file\" &&\n+\n+    git checkout -b test7.c master &&\n+    rmdir submod && mv submod-movedaside submod &&\n+    test ! -e submod.orig &&\n+    git submodule update -N &&\n+    test_must_fail git merge test7 &&\n+    test -n \"$(git ls-files -u)\" &&\n+    ( yes \"\" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&\n+    ( yes \"d\" | git mergetool file11 file12 >/dev/null 2>&1 ) &&\n+    ( yes \"r\" | git mergetool submod ) &&\n+    test -d submod.orig &&\n+    git submodule update -N &&\n+    test \"$(cat submod)\" = \"not a submodule\" &&\n+    output=\"$(git mergetool --no-prompt)\" &&\n+    test \"$output\" = \"No files need merging\" &&\n+    git commit -m \"Merge resolved by keeping file\" &&\n+\n+    git checkout -b test7.d master &&\n+    rmdir submod && mv submod.orig submod &&\n+    git submodule update -N &&\n+    test_must_fail git merge test7 &&\n+    test -n \"$(git ls-files -u)\" &&\n+    ( yes \"\" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) &&\n+    ( yes \"d\" | git mergetool file11 file12 >/dev/null 2>&1 ) &&\n+    ( yes \"l\" | git mergetool submod ) &&\n+    test \"$(cat submod/bar)\" = \"master submodule\" &&\n+    git submodule update -N &&\n+    test \"$(cat submod/bar)\" = \"master submodule\" &&\n+    output=\"$(git mergetool --no-prompt)\" &&\n+    test \"$output\" = \"No files need merging\" &&\n+    git commit -m \"Merge resolved by keeping module\"\n+'\n+\n+test_expect_success 'submodule in subdirectory' '\n+    git checkout -b test10 branch1 &&\n+    git submodule update -N &&\n+    (\n+\tcd subdir &&\n+\ttest_create_repo subdir_module &&\n+\t(\n+\t    cd subdir_module &&\n+\t    : >file15 &&\n+\t    git add file15 &&\n+\t    git commit -m \"add initial versions\"\n+\t)\n+    ) &&\n+    git submodule add git://example.com/subsubmodule subdir/subdir_module &&\n+    git add subdir/subdir_module &&\n+    git commit -m \"add submodule in subdirectory\" &&\n+\n+    git checkout -b test10.a test10 &&\n+    git submodule update -N &&\n+    (\n+\tcd subdir/subdir_module &&\n+\tgit checkout -b super10.a &&\n+\techo test10.a >file15 &&\n+\tgit add file15 &&\n+\tgit commit -m \"on branch 10.a\"\n+    ) &&\n+    git add subdir/subdir_module &&\n+    git commit -m \"change submodule in subdirectory on test10.a\" &&\n+\n+    git checkout -b test10.b test10 &&\n+    git submodule update -N &&\n+    (\n+\tcd subdir/subdir_module &&\n+\tgit checkout -b super10.b &&\n+\techo test10.b >file15 &&\n+\tgit add file15 &&\n+\tgit commit -m \"on branch 10.b\"\n+    ) &&\n+    git add subdir/subdir_module &&\n+    git commit -m \"change submodule in subdirectory on test10.b\" &&\n+\n+    test_must_fail git merge test10.a >/dev/null 2>&1 &&\n+    (\n+\tcd subdir &&\n+\t( yes \"l\" | git mergetool subdir_module )\n+    ) &&\n+    test \"$(cat subdir/subdir_module/file15)\" = \"test10.b\" &&\n+    git submodule update -N &&\n+    test \"$(cat subdir/subdir_module/file15)\" = \"test10.b\" &&\n+    git reset --hard &&\n+    git submodule update -N &&\n+\n+    test_must_fail git merge test10.a >/dev/null 2>&1 &&\n+    ( yes \"r\" | git mergetool subdir/subdir_module ) &&\n+    test \"$(cat subdir/subdir_module/file15)\" = \"test10.b\" &&\n+    git submodule update -N &&\n+    test \"$(cat subdir/subdir_module/file15)\" = \"test10.a\" &&\n+    git commit -m \"branch1 resolved with mergetool\" &&\n+    rm -rf subdir/subdir_module\n+'\n+\n+test_expect_success 'directory vs modified submodule' '\n+    git checkout -b test11 branch1 &&\n+    mv submod submod-movedaside &&\n+    git rm submod &&\n+    mkdir submod &&\n+    echo not a submodule >submod/file16 &&\n+    git add submod/file16 &&\n+    git commit -m \"Submodule path becomes directory\" &&\n+\n+    test_must_fail git merge master &&\n+    test -n \"$(git ls-files -u)\" &&\n+    ( yes \"l\" | git mergetool submod ) &&\n+    test \"$(cat submod/file16)\" = \"not a submodule\" &&\n+    rm -rf submod.orig &&\n+\n+    git reset --hard &&\n+    test_must_fail git merge master &&\n+    test -n \"$(git ls-files -u)\" &&\n+    test ! -e submod.orig &&\n+    ( yes \"r\" | git mergetool submod ) &&\n+    test -d submod.orig &&\n+    test \"$(cat submod.orig/file16)\" = \"not a submodule\" &&\n+    rm -r submod.orig &&\n+    mv submod-movedaside/.git submod &&\n+    ( cd submod && git clean -f && git reset --hard ) &&\n+    git submodule update -N &&\n+    test \"$(cat submod/bar)\" = \"master submodule\" &&\n+    git reset --hard && rm -rf submod-movedaside &&\n+\n+    git checkout -b test11.c master &&\n+    git submodule update -N &&\n+    test_must_fail git merge test11 &&\n+    test -n \"$(git ls-files -u)\" &&\n+    ( yes \"l\" | git mergetool submod ) &&\n+    git submodule update -N &&\n+    test \"$(cat submod/bar)\" = \"master submodule\" &&\n+\n+    git reset --hard &&\n+    git submodule update -N &&\n+    test_must_fail git merge test11 &&\n+    test -n \"$(git ls-files -u)\" &&\n+    test ! -e submod.orig &&\n+    ( yes \"r\" | git mergetool submod ) &&\n+    test \"$(cat submod/file16)\" = \"not a submodule\" &&\n+\n+    git reset --hard master &&\n+    ( cd submod && git clean -f && git reset --hard ) &&\n+    git submodule update -N\n+'\n+\n test_done\n-- \n1.7.5.rc1.1.g64431\n"}]}