{"thread":{"id":"38235","subject":"[PATCH] git-new-workdir: support submodules","startedAt":"2014-12-23T02:10:27Z","lastAt":"2014-12-23T19:08:48Z","messageCount":2,"participants":["Craig Silverstein","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"254038","messageId":"1419360673.196271.13944.nullmailer@meta","threadId":"38235","inReplyTo":null,"subject":"[PATCH] git-new-workdir: support submodules","fromName":"Craig Silverstein","fromEmail":"csilvers.mail@gmail.com","sentAt":"2014-12-23T02:10:27Z","receivedAt":"2014-12-23T02:10:27Z","isPatch":true,"sender":{"key":"csilvers.mail@gmail.com","avatar":null},"body":"The basic problem with submodules, from git-new-workdir's point of\nview, is that instead of having a .git directory, they have a .git\nfile with contents `gitdir: <some other path>`.  This is a problem\nbecause the submodule's config file has an entry like `worktree =\n../../../khan-exercises` which is relative to \"<some other path>\"\nrather than to \"submodule_dir/.git\".\n\nAs a result, if we want the new workdir to work properly, it needs to\nkeep the same directory structure as the original repository: it\nshould also contain a .git file with a 'gitdir', and the actual .git\ncontents should be in the place mentioned therein.  (There are other\nways we could have solved this problem, including modifying the\n'config' file, but this seemed most in keeping with the symlink-y\nphilosophy of git-new-branch.)\n\nThis commit implements this, by detecting if the source .git directory\nis actually a .git file with 'gitdir: ...' as its contents, and if so\nreproducing the .git file + gitdir structure in the destination\nwork-tree.\n\nTest plan:\nOn a repo (~/khan/webapp) with a submodule, ran\n\n    git new-workdir ~/khan/webapp /tmp/webapp      # the main module\n    git new-workdir ~/khan/webapp/khan-exercises /tmp/webapp/khan-exercises\n\nand saw that /tmp/webapp/khan-exercises was populated correctly,\n/tmp/webapp/.git/modules/khan-exercises existed with symlinks, and\n/tmp/webapp/khan-exercises/.git was a file with a 'gitdir:' entry\npointing to the .git/modules directory.\n\nSigned-off-by: Craig Silverstein <csilvers@khanacademy.org>\n---\n contrib/workdir/git-new-workdir | 55 +++++++++++++++++++++++++++--------------\n 1 file changed, 36 insertions(+), 19 deletions(-)\n\ndiff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir\nindex 888c34a..3cc50de 100755\n--- a/contrib/workdir/git-new-workdir\n+++ b/contrib/workdir/git-new-workdir\n@@ -52,26 +52,45 @@ then\n \t\t\"a complete repository.\"\n fi\n \n+# don't modify an existing directory, unless it's empty\n+if test -d \"$new_workdir\" && test $(ls -a1 \"$new_workdir/.\" | wc -l) -ne 2\n+then\n+\tdie \"destination directory '$new_workdir' is not empty.\"\n+fi\n+\n # make sure the links in the workdir have full paths to the original repo\n git_dir=$(cd \"$git_dir\" && pwd) || exit 1\n \n-# don't recreate a workdir over an existing directory, unless it's empty\n-if test -d \"$new_workdir\"\n+new_git_dir=\"$new_workdir/.git\"\n+\n+# if $orig_git is a .git file with a 'gitdir' entry (as is the case for\n+# submodules), have the new git dir follow that same pattern.  otherwise\n+# the 'worktree' entry in .git/config, which is a relative path, will\n+# not resolve properly because we're not in the expected subdirectory.\n+gitdir_text=$(sed -ne 's/^gitdir: *//p' \"$orig_git/.git\" 2>/dev/null)\n+if test -n \"$gitdir_text\"; then\n+\tln -s \"$orig_git/.git\" \"$new_workdir/.git\" || failed\n+\tnew_git_dir=\"$new_workdir/$gitdir_text\"\n+fi\n+\n+# if new_workdir already exists, leave it along in case of error\n+if ! test -d \"$new_workdir\"\n then\n-\tif test $(ls -a1 \"$new_workdir/.\" | wc -l) -ne 2\n-\tthen\n-\t\tdie \"destination directory '$new_workdir' is not empty.\"\n-\tfi\n-\tcleandir=\"$new_workdir/.git\"\n-else\n-\tcleandir=\"$new_workdir\"\n+\tclean_new_workdir=true\n fi\n \n-mkdir -p \"$new_workdir/.git\" || failed\n-cleandir=$(cd \"$cleandir\" && pwd) || failed\n+mkdir -p \"$new_git_dir\" || failed\n \n+cleandir=$(cd \"$cleandir\" && pwd) || failed\n cleanup () {\n-\trm -rf \"$cleandir\"\n+\tif test z\"$clean_new_workdir\" = ztrue\n+\tthen\n+\t\trm -rf \"$new_workdir\"\n+\tfi\n+\t# this may (or may not) be a noop if new_workdir was already deleted.\n+\trm -rf \"$new_git_dir\"\n+\t# this is a noop unless .git is a 'gitdir: ...' file.\n+\trm -f \"$new_workdir/.git\"\n }\n siglist=\"0 1 2 15\"\n trap cleanup $siglist\n@@ -84,22 +103,20 @@ do\n \t# create a containing directory if needed\n \tcase $x in\n \t*/*)\n-\t\tmkdir -p \"$new_workdir/.git/${x%/*}\"\n+\t\tmkdir -p \"$new_git_dir/${x%/*}\"\n \t\t;;\n \tesac\n \n-\tln -s \"$git_dir/$x\" \"$new_workdir/.git/$x\" || failed\n+\tln -s \"$git_dir/$x\" \"$new_git_dir/$x\" || failed\n done\n \n-# commands below this are run in the context of the new workdir\n-cd \"$new_workdir\" || failed\n-\n # copy the HEAD from the original repository as a default branch\n-cp \"$git_dir/HEAD\" .git/HEAD || failed\n+cp \"$git_dir/HEAD\" \"$new_git_dir/HEAD\" || failed\n \n # the workdir is set up.  if the checkout fails, the user can fix it.\n trap - $siglist\n \n # checkout the branch (either the same as HEAD from the original repository,\n-# or the one that was asked for)\n+# or the one that was asked for).  we must be in the new workdir for this.\n+cd \"$new_workdir\" || failed\n git checkout -f $branch\n-- \n2.2.1\n"},{"id":"254042","messageId":"xmqqzjae18vz.fsf@gitster.dls.corp.google.com","threadId":"38235","inReplyTo":"1419360673.196271.13944.nullmailer@meta","subject":"Re: [PATCH] git-new-workdir: support submodules","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T19:08:48Z","receivedAt":"2014-12-23T19:08:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Craig Silverstein <csilvers.mail@gmail.com> writes:\n\n> The basic problem with submodules, from git-new-workdir's point of\n> view, is that instead of having a .git directory, they have a .git\n> file with contents `gitdir: <some other path>`.  This is a problem\n> because the submodule's config file has an entry like `worktree =\n> ../../../khan-exercises` which is relative to \"<some other path>\"\n> rather than to \"submodule_dir/.git\".\n>\n> As a result, if we want the new workdir to work properly, it needs to\n> keep the same directory structure as the original repository: it\n> should also contain a .git file with a 'gitdir', and the actual .git\n> contents should be in the place mentioned therein.\n\nHmmmm, does that mean that the submodule S in the original\nrepository O's working tree and its checkout in the secondary\nworking tree W created from O using git-new-workdir share the same\nrepository location?  More specifically:\n\n\tO/.git/                 - original repository\n        O/.git/index            - worktree state in O\n        O/S                     - submodule S's checkout in O\n        O/S/.git                - a gitfile pointing to O/.git/modules/S\n\tO/.git/modules/S        - submodule S's repository contents\n        O/.git/modules/S/config - submodule S's config\n\n\tW/.git/                 - secondary working tree\n        W/.git/config           - symlink to O/.git/config\n        W/.git/index            - worktree state in W (independent of O)\n\tW/S                     - submodule S's checkout in W (independent of O)\n\tW/S/.git                - a gitfile pointing to O/.git/modules/S\n\nDoesn't a submodule checkout keep some state tied to the working\ntree in its repository configuration file?  Wouldn't this change\nintroduce problems by sharing O/.git/modules/S/config between the\ntwo checkouts?\n"}]}