{"thread":{"id":"34572","subject":"[PATCH v3 0/5] Teach mv to move submodules","startedAt":"2013-07-30T19:48:15Z","lastAt":"2013-08-08T18:55:13Z","messageCount":18,"participants":["Jens Lehmann","Fredrik Gustafsson","Junio C Hamano"],"isPatch":true,"patchVersion":3,"patchTotal":5},"messages":[{"id":"224314","messageId":"51F8187F.7040603@web.de","threadId":"34572","inReplyTo":null,"subject":"[PATCH v3 0/5] Teach mv to move submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-07-30T19:48:15Z","receivedAt":"2013-07-30T19:48:15Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Here is my third iteration of this series.\n\nChanges to v2 are:\n\n- I resolved the conflict with Duy's pathspec series by replacing the\n  use of common_prefix() with relative_path().\n\n- I separated the functions checking for modified unstaged .gitmodules\n  and staging the changes to that file into another commit, as they\n  are used by both mv and rm.\n\n- mv and rm now die with the message \"Please, stage your changes to\n  .gitmodules or stash them to proceed\" instead of changing and\n  staging a .gitmodules file containing other unstaged modifications.\n\n- Man pages for mv and rm are updated to tell the user what they do\n  with the gitlink and the .gitmodules file in case of submodules.\n\n- Minor changes according to the last review (typos and a bit more\n  efficient coding).\n\nThis series applies cleanly on current pu (and I also ran t3600 and\nt7001 manually to make sure I don't hit the silent breakage my last\nseries showed when I ran the whole test suite).\n\nJens Lehmann (5):\n  Teach mv to move submodules together with their work trees\n  Teach mv to move submodules using a gitfile\n  submodule.c: add .gitmodules staging helper functions\n  Teach mv to update the path entry in .gitmodules for moved submodules\n  rm: delete .gitmodules entry of submodules removed from the work tree\n\n Documentation/git-mv.txt   |  10 ++-\n Documentation/git-rm.txt   |   8 ++-\n builtin/mv.c               | 126 ++++++++++++++++++++++----------------\n builtin/rm.c               |  19 +++++-\n submodule.c                | 147 +++++++++++++++++++++++++++++++++++++++++++++\n submodule.h                |   5 ++\n t/t3600-rm.sh              |  98 ++++++++++++++++++++++++++++--\n t/t7001-mv.sh              | 128 +++++++++++++++++++++++++++++++++++++++\n t/t7400-submodule-basic.sh |  14 ++---\n t/t7610-mergetool.sh       |   6 +-\n 10 files changed, 484 insertions(+), 77 deletions(-)\n\n-- \n1.8.4.rc0.199.g7079aac\n"},{"id":"224315","messageId":"51F818C5.9070507@web.de","threadId":"34572","inReplyTo":"51F8187F.7040603@web.de","subject":"[PATCH v3 1/5] Teach mv to move submodules together with their work trees","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-07-30T19:49:25Z","receivedAt":"2013-07-30T19:49:25Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Currently the attempt to use \"git mv\" on a submodule errors out with:\n  fatal: source directory is empty, source=<src>, destination=<dest>\nThe reason is that mv searches for the submodule with a trailing slash in\nthe index, which it doesn't find (because it is stored without a trailing\nslash). As it doesn't find any index entries inside the submodule it\nclaims the directory would be empty even though it isn't.\n\nFix that by searching for the name without a trailing slash and continue\nif it is a submodule. Then rename() will move the submodule work tree just\nlike it moves a file.\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n builtin/mv.c  | 99 +++++++++++++++++++++++++++++++----------------------------\n t/t7001-mv.sh | 34 ++++++++++++++++++++\n 2 files changed, 86 insertions(+), 47 deletions(-)\n\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex 16ce99b..1d3ef63 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -118,55 +118,60 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\t\t\t&& lstat(dst, &st) == 0)\n \t\t\tbad = _(\"cannot move directory over file\");\n \t\telse if (src_is_dir) {\n-\t\t\tconst char *src_w_slash = add_slash(src);\n-\t\t\tint len_w_slash = length + 1;\n-\t\t\tint first, last;\n-\n-\t\t\tmodes[i] = WORKING_DIRECTORY;\n-\n-\t\t\tfirst = cache_name_pos(src_w_slash, len_w_slash);\n-\t\t\tif (first >= 0)\n-\t\t\t\tdie (_(\"Huh? %.*s is in index?\"),\n-\t\t\t\t\t\tlen_w_slash, src_w_slash);\n-\n-\t\t\tfirst = -1 - first;\n-\t\t\tfor (last = first; last < active_nr; last++) {\n-\t\t\t\tconst char *path = active_cache[last]->name;\n-\t\t\t\tif (strncmp(path, src_w_slash, len_w_slash))\n-\t\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tfree((char *)src_w_slash);\n-\n-\t\t\tif (last - first < 1)\n-\t\t\t\tbad = _(\"source directory is empty\");\n-\t\t\telse {\n-\t\t\t\tint j, dst_len;\n-\n-\t\t\t\tif (last - first > 0) {\n-\t\t\t\t\tsource = xrealloc(source,\n-\t\t\t\t\t\t\t(argc + last - first)\n-\t\t\t\t\t\t\t* sizeof(char *));\n-\t\t\t\t\tdestination = xrealloc(destination,\n-\t\t\t\t\t\t\t(argc + last - first)\n-\t\t\t\t\t\t\t* sizeof(char *));\n-\t\t\t\t\tmodes = xrealloc(modes,\n-\t\t\t\t\t\t\t(argc + last - first)\n-\t\t\t\t\t\t\t* sizeof(enum update_mode));\n+\t\t\tint first = cache_name_pos(src, length);\n+\t\t\tif (first >= 0) {\n+\t\t\t\tif (!S_ISGITLINK(active_cache[first]->ce_mode))\n+\t\t\t\t\tdie (_(\"Huh? Directory %s is in index and no submodule?\"), src);\n+\t\t\t} else {\n+\t\t\t\tconst char *src_w_slash = add_slash(src);\n+\t\t\t\tint last, len_w_slash = length + 1;\n+\n+\t\t\t\tmodes[i] = WORKING_DIRECTORY;\n+\n+\t\t\t\tfirst = cache_name_pos(src_w_slash, len_w_slash);\n+\t\t\t\tif (first >= 0)\n+\t\t\t\t\tdie (_(\"Huh? %.*s is in index?\"),\n+\t\t\t\t\t\t\tlen_w_slash, src_w_slash);\n+\n+\t\t\t\tfirst = -1 - first;\n+\t\t\t\tfor (last = first; last < active_nr; last++) {\n+\t\t\t\t\tconst char *path = active_cache[last]->name;\n+\t\t\t\t\tif (strncmp(path, src_w_slash, len_w_slash))\n+\t\t\t\t\t\tbreak;\n \t\t\t\t}\n-\n-\t\t\t\tdst = add_slash(dst);\n-\t\t\t\tdst_len = strlen(dst);\n-\n-\t\t\t\tfor (j = 0; j < last - first; j++) {\n-\t\t\t\t\tconst char *path =\n-\t\t\t\t\t\tactive_cache[first + j]->name;\n-\t\t\t\t\tsource[argc + j] = path;\n-\t\t\t\t\tdestination[argc + j] =\n-\t\t\t\t\t\tprefix_path(dst, dst_len,\n-\t\t\t\t\t\t\tpath + length + 1);\n-\t\t\t\t\tmodes[argc + j] = INDEX;\n+\t\t\t\tfree((char *)src_w_slash);\n+\n+\t\t\t\tif (last - first < 1)\n+\t\t\t\t\tbad = _(\"source directory is empty\");\n+\t\t\t\telse {\n+\t\t\t\t\tint j, dst_len;\n+\n+\t\t\t\t\tif (last - first > 0) {\n+\t\t\t\t\t\tsource = xrealloc(source,\n+\t\t\t\t\t\t\t\t(argc + last - first)\n+\t\t\t\t\t\t\t\t* sizeof(char *));\n+\t\t\t\t\t\tdestination = xrealloc(destination,\n+\t\t\t\t\t\t\t\t(argc + last - first)\n+\t\t\t\t\t\t\t\t* sizeof(char *));\n+\t\t\t\t\t\tmodes = xrealloc(modes,\n+\t\t\t\t\t\t\t\t(argc + last - first)\n+\t\t\t\t\t\t\t\t* sizeof(enum update_mode));\n+\t\t\t\t\t}\n+\n+\t\t\t\t\tdst = add_slash(dst);\n+\t\t\t\t\tdst_len = strlen(dst);\n+\n+\t\t\t\t\tfor (j = 0; j < last - first; j++) {\n+\t\t\t\t\t\tconst char *path =\n+\t\t\t\t\t\t\tactive_cache[first + j]->name;\n+\t\t\t\t\t\tsource[argc + j] = path;\n+\t\t\t\t\t\tdestination[argc + j] =\n+\t\t\t\t\t\t\tprefix_path(dst, dst_len,\n+\t\t\t\t\t\t\t\tpath + length + 1);\n+\t\t\t\t\t\tmodes[argc + j] = INDEX;\n+\t\t\t\t\t}\n+\t\t\t\t\targc += last - first;\n \t\t\t\t}\n-\t\t\t\targc += last - first;\n \t\t\t}\n \t\t} else if (cache_name_pos(src, length) < 0)\n \t\t\tbad = _(\"not under version control\");\ndiff --git a/t/t7001-mv.sh b/t/t7001-mv.sh\nindex 101816e..15c18b6 100755\n--- a/t/t7001-mv.sh\n+++ b/t/t7001-mv.sh\n@@ -259,4 +259,38 @@ test_expect_success SYMLINKS 'check moved symlink' '\n\n rm -f moved symlink\n\n+test_expect_success 'setup submodule' '\n+\tgit commit -m initial &&\n+\tgit reset --hard &&\n+\tgit submodule add ./. sub &&\n+\techo content >file &&\n+\tgit add file &&\n+\tgit commit -m \"added sub and file\"\n+'\n+\n+test_expect_success 'git mv cannot move a submodule in a file' '\n+\ttest_must_fail git mv sub file\n+'\n+\n+test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '\n+\tentry=\"$(git ls-files --stage sub | cut -f 1)\" &&\n+\tgit rm .gitmodules &&\n+\t(\n+\t\tcd sub &&\n+\t\trm -f .git &&\n+\t\tcp -a ../.git/modules/sub .git &&\n+\t\tGIT_WORK_TREE=. git config --unset core.worktree\n+\t) &&\n+\tmkdir mod &&\n+\tgit mv sub mod/sub &&\n+\t! test -e sub &&\n+\t[ \"$entry\" = \"$(git ls-files --stage mod/sub | cut -f 1)\" ] &&\n+\t(\n+\t\tcd mod/sub &&\n+\t\tgit status\n+\t) &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet\n+'\n+\n test_done\n-- \n1.8.4.rc0.199.g7079aac\n"},{"id":"224316","messageId":"51F818EB.8010101@web.de","threadId":"34572","inReplyTo":"51F8187F.7040603@web.de","subject":"[PATCH v3 2/5] Teach mv to move submodules using a gitfile","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-07-30T19:50:03Z","receivedAt":"2013-07-30T19:50:03Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"When moving a submodule which uses a gitfile to point to the git directory\nstored in .git/modules/<name> of the superproject two changes must be made\nto make the submodule work: the .git file and the core.worktree setting\nmust be adjusted to point from work tree to git directory and back.\n\nAchieve that by remembering which submodule uses a gitfile by storing the\nresult of read_gitfile() of each submodule. If that is not NULL the new\nfunction connect_work_tree_and_git_dir() is called after renaming the\nsubmodule's work tree which updates the two settings to the new values.\n\nExtend the man page to inform the user about that feature (and while at it\nchange the description to not talk about a script anymore, as mv is a\nbuiltin for quite some time now).\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n Documentation/git-mv.txt |  8 +++++++-\n builtin/mv.c             | 19 +++++++++++++++----\n submodule.c              | 31 +++++++++++++++++++++++++++++++\n submodule.h              |  1 +\n t/t7001-mv.sh            | 19 +++++++++++++++++++\n 5 files changed, 73 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt\nindex e93fcb4..1f6fce0 100644\n--- a/Documentation/git-mv.txt\n+++ b/Documentation/git-mv.txt\n@@ -13,7 +13,7 @@ SYNOPSIS\n\n DESCRIPTION\n -----------\n-This script is used to move or rename a file, directory or symlink.\n+Move or rename a file, directory or symlink.\n\n  git mv [-v] [-f] [-n] [-k] <source> <destination>\n  git mv [-v] [-f] [-n] [-k] <source> ... <destination directory>\n@@ -44,6 +44,12 @@ OPTIONS\n --verbose::\n \tReport the names of files as they are moved.\n\n+SUBMODULES\n+----------\n+Moving a submodule using a gitfile (which means they were cloned\n+with a Git version 1.7.8 or newer) will update the gitfile and\n+core.worktree setting to make the submodule work in the new location.\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex 1d3ef63..68b7060 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -9,6 +9,7 @@\n #include \"cache-tree.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"submodule.h\"\n\n static const char * const builtin_mv_usage[] = {\n \tN_(\"git mv [options] <source>... <destination>\"),\n@@ -66,7 +67,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOLEAN('k', NULL, &ignore_errors, N_(\"skip move/rename errors\")),\n \t\tOPT_END(),\n \t};\n-\tconst char **source, **destination, **dest_path;\n+\tconst char **source, **destination, **dest_path, **submodule_gitfile;\n \tenum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;\n \tstruct stat st;\n \tstruct string_list src_for_dst = STRING_LIST_INIT_NODUP;\n@@ -85,6 +86,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \tsource = internal_copy_pathspec(prefix, argv, argc, 0);\n \tmodes = xcalloc(argc, sizeof(enum update_mode));\n \tdest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0);\n+\tsubmodule_gitfile = xcalloc(argc, sizeof(char *));\n\n \tif (dest_path[0][0] == '\\0')\n \t\t/* special case: \".\" was normalized to \"\" */\n@@ -120,8 +122,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\telse if (src_is_dir) {\n \t\t\tint first = cache_name_pos(src, length);\n \t\t\tif (first >= 0) {\n+\t\t\t\tstruct strbuf submodule_dotgit = STRBUF_INIT;\n \t\t\t\tif (!S_ISGITLINK(active_cache[first]->ce_mode))\n \t\t\t\t\tdie (_(\"Huh? Directory %s is in index and no submodule?\"), src);\n+\t\t\t\tstrbuf_addf(&submodule_dotgit, \"%s/.git\", src);\n+\t\t\t\tsubmodule_gitfile[i] = read_gitfile(submodule_dotgit.buf);\n+\t\t\t\tif (submodule_gitfile[i])\n+\t\t\t\t\tsubmodule_gitfile[i] = xstrdup(submodule_gitfile[i]);\n+\t\t\t\tstrbuf_release(&submodule_dotgit);\n \t\t\t} else {\n \t\t\t\tconst char *src_w_slash = add_slash(src);\n \t\t\t\tint last, len_w_slash = length + 1;\n@@ -216,9 +224,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\tint pos;\n \t\tif (show_only || verbose)\n \t\t\tprintf(_(\"Renaming %s to %s\\n\"), src, dst);\n-\t\tif (!show_only && mode != INDEX &&\n-\t\t\t\trename(src, dst) < 0 && !ignore_errors)\n-\t\t\tdie_errno (_(\"renaming '%s' failed\"), src);\n+\t\tif (!show_only && mode != INDEX) {\n+\t\t\tif (rename(src, dst) < 0 && !ignore_errors)\n+\t\t\t\tdie_errno (_(\"renaming '%s' failed\"), src);\n+\t\t\tif (submodule_gitfile[i])\n+\t\t\t\tconnect_work_tree_and_git_dir(dst, submodule_gitfile[i]);\n+\t\t}\n\n \t\tif (mode == WORKING_DIRECTORY)\n \t\t\tcontinue;\ndiff --git a/submodule.c b/submodule.c\nindex 3f0a3f9..d96d187 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1004,3 +1004,34 @@ int merge_submodule(unsigned char result[20], const char *path,\n \tfree(merges.objects);\n \treturn 0;\n }\n+\n+/* Update gitfile and core.worktree setting to connect work tree and git dir */\n+void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)\n+{\n+\tstruct strbuf file_name = STRBUF_INIT;\n+\tstruct strbuf rel_path = STRBUF_INIT;\n+\tconst char *real_work_tree = xstrdup(real_path(work_tree));\n+\tFILE *fp;\n+\n+\t/* Update gitfile */\n+\tstrbuf_addf(&file_name, \"%s/.git\", work_tree);\n+\tfp = fopen(file_name.buf, \"w\");\n+\tif (!fp)\n+\t\tdie(_(\"Could not create git link %s\"), file_name.buf);\n+\tfprintf(fp, \"gitdir: %s\\n\", relative_path(git_dir, real_work_tree,\n+\t\t\t\t\t\t  &rel_path));\n+\tfclose(fp);\n+\n+\t/* Update core.worktree setting */\n+\tstrbuf_reset(&file_name);\n+\tstrbuf_addf(&file_name, \"%s/config\", git_dir);\n+\tif (git_config_set_in_file(file_name.buf, \"core.worktree\",\n+\t\t\t\t   relative_path(real_work_tree, git_dir,\n+\t\t\t\t\t\t &rel_path)))\n+\t\tdie(_(\"Could not set core.worktree in %s\"),\n+\t\t    file_name.buf);\n+\n+\tstrbuf_release(&file_name);\n+\tstrbuf_release(&rel_path);\n+\tfree((void *)real_work_tree);\n+}\ndiff --git a/submodule.h b/submodule.h\nindex c7ffc7c..29e9658 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -36,5 +36,6 @@ int merge_submodule(unsigned char result[20], const char *path, const unsigned c\n int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,\n \t\tstruct string_list *needs_pushing);\n int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);\n+void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);\n\n #endif\ndiff --git a/t/t7001-mv.sh b/t/t7001-mv.sh\nindex 15c18b6..b99177f 100755\n--- a/t/t7001-mv.sh\n+++ b/t/t7001-mv.sh\n@@ -293,4 +293,23 @@ test_expect_success 'git mv moves a submodule with a .git directory and no .gitm\n \tgit diff-files --quiet\n '\n\n+test_expect_success 'git mv moves a submodule with gitfile' '\n+\trm -rf mod/sub &&\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tentry=\"$(git ls-files --stage sub | cut -f 1)\" &&\n+\t(\n+\t\tcd mod &&\n+\t\tgit mv ../sub/ .\n+\t) &&\n+\t! test -e sub &&\n+\t[ \"$entry\" = \"$(git ls-files --stage mod/sub | cut -f 1)\" ] &&\n+\t(\n+\t\tcd mod/sub &&\n+\t\tgit status\n+\t) &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet\n+'\n+\n test_done\n-- \n1.8.4.rc0.199.g7079aac\n"},{"id":"224317","messageId":"51F8190A.3030002@web.de","threadId":"34572","inReplyTo":"51F8187F.7040603@web.de","subject":"[PATCH v3 3/5] submodule.c: add .gitmodules staging helper functions","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-07-30T19:50:34Z","receivedAt":"2013-07-30T19:50:34Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Add the new is_staging_gitmodules_ok() and stage_updated_gitmodules()\nfunctions to submodule.c. The first makes it possible for call sites to\nsee if the .gitmodules file did contain any unstaged modifications they\nwould accidentally stage in addition to those they intend to stage\nthemselves. The second function stages all modifications to the\n.gitmodules file, both will be used by subsequent patches for the mv\nand rm commands.\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n submodule.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++\n submodule.h |  2 ++\n 2 files changed, 53 insertions(+)\n\ndiff --git a/submodule.c b/submodule.c\nindex d96d187..584f7de 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -10,6 +10,7 @@\n #include \"string-list.h\"\n #include \"sha1-array.h\"\n #include \"argv-array.h\"\n+#include \"blob.h\"\n\n static struct string_list config_name_for_path;\n static struct string_list config_fetch_recurse_submodules_for_name;\n@@ -30,6 +31,51 @@ static struct sha1_array ref_tips_after_fetch;\n  */\n static int gitmodules_is_unmerged;\n\n+/*\n+ * This flag is set if the .gitmodules file had unstaged modifications on\n+ * startup. This must be checked before allowing modifications to the\n+ * .gitmodules file with the intention to stage them later, because when\n+ * continuing we would stage the modifications the user didn't stage herself\n+ * too. That might change in a future version when we learn to stage the\n+ * changes we do ourselves without staging any previous modifications.\n+ */\n+static int gitmodules_is_modified;\n+\n+\n+int is_staging_gitmodules_ok()\n+{\n+\treturn !gitmodules_is_modified;\n+}\n+\n+void stage_updated_gitmodules(void)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct stat st;\n+\tint pos;\n+\tstruct cache_entry *ce;\n+\tint namelen = strlen(\".gitmodules\");\n+\n+\tpos = cache_name_pos(\".gitmodules\", namelen);\n+\tif (pos < 0) {\n+\t\twarning(_(\"could not find .gitmodules in index\"));\n+\t\treturn;\n+\t}\n+\tce = active_cache[pos];\n+\tce->ce_flags = namelen;\n+\tif (strbuf_read_file(&buf, \".gitmodules\", 0) < 0)\n+\t\tdie(_(\"reading updated .gitmodules failed\"));\n+\tif (lstat(\".gitmodules\", &st) < 0)\n+\t\tdie_errno(_(\"unable to stat updated .gitmodules\"));\n+\tfill_stat_cache_info(ce, &st);\n+\tce->ce_mode = ce_mode_from_stat(ce, st.st_mode);\n+\tif (remove_cache_entry_at(pos) < 0)\n+\t\tdie(_(\"unable to remove .gitmodules from index\"));\n+\tif (write_sha1_file(buf.buf, buf.len, blob_type, ce->sha1))\n+\t\tdie(_(\"adding updated .gitmodules failed\"));\n+\tif (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))\n+\t\tdie(_(\"staging updated .gitmodules failed\"));\n+}\n+\n static int add_submodule_odb(const char *path)\n {\n \tstruct strbuf objects_directory = STRBUF_INIT;\n@@ -116,6 +162,11 @@ void gitmodules_config(void)\n \t\t\t\t    !memcmp(ce->name, \".gitmodules\", 11))\n \t\t\t\t\tgitmodules_is_unmerged = 1;\n \t\t\t}\n+\t\t} else if (pos < active_nr) {\n+\t\t\tstruct stat st;\n+\t\t\tif (lstat(\".gitmodules\", &st) == 0 &&\n+\t\t\t    ce_match_stat(active_cache[pos], &st, 0) & DATA_CHANGED)\n+\t\t\t\tgitmodules_is_modified = 1;\n \t\t}\n\n \t\tif (!gitmodules_is_unmerged)\ndiff --git a/submodule.h b/submodule.h\nindex 29e9658..244d5f5 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -11,6 +11,8 @@ enum {\n \tRECURSE_SUBMODULES_ON = 2\n };\n\n+int is_staging_gitmodules_ok();\n+void stage_updated_gitmodules(void);\n void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,\n \t\tconst char *path);\n int submodule_config(const char *var, const char *value, void *cb);\n-- \n1.8.4.rc0.199.g7079aac\n"},{"id":"224318","messageId":"51F81929.1030706@web.de","threadId":"34572","inReplyTo":"51F8187F.7040603@web.de","subject":"[PATCH v3 4/5] Teach mv to update the path entry in .gitmodules for moved submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-07-30T19:51:05Z","receivedAt":"2013-07-30T19:51:05Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Currently using \"git mv\" on a submodule moves the submodule's work tree in\nthat of the superproject. But the submodule's path setting in .gitmodules\nis left untouched, which is now inconsistent with the work tree and makes\ngit commands that rely on the proper path -> name mapping (like status and\ndiff) behave strangely.\n\nLet \"git mv\" help here by not only moving the submodule's work tree but\nalso updating the \"submodule.<submodule name>.path\" setting from the\n.gitmodules file and stage both. This doesn't happen when no .gitmodules\nfile is found and only issues a warning when it doesn't have a section for\nthis submodule. This is because the user might just use plain gitlinks\nwithout the .gitmodules file or has already updated the path setting by\nhand before issuing the \"git mv\" command (in which case the warning\nreminds him that mv would have done that for him). Only when .gitmodules\nis found and contains merge conflicts the mv command will fail and tell\nthe user to resolve the conflict before trying again.\n\nAlso extend the man page to inform the user about this new feature.\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n Documentation/git-mv.txt |  2 ++\n builtin/mv.c             | 10 ++++++-\n submodule.c              | 33 +++++++++++++++++++++\n submodule.h              |  1 +\n t/t7001-mv.sh            | 75 ++++++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 120 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt\nindex 1f6fce0..b1f7988 100644\n--- a/Documentation/git-mv.txt\n+++ b/Documentation/git-mv.txt\n@@ -49,6 +49,8 @@ SUBMODULES\n Moving a submodule using a gitfile (which means they were cloned\n with a Git version 1.7.8 or newer) will update the gitfile and\n core.worktree setting to make the submodule work in the new location.\n+It also will attempt to update the submodule.<name>.path setting in\n+the linkgit:gitmodules[5] file and stage that file (unless -n is used).\n\n GIT\n ---\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex 68b7060..7dd6bb4 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -58,7 +58,7 @@ static struct lock_file lock_file;\n\n int cmd_mv(int argc, const char **argv, const char *prefix)\n {\n-\tint i, newfd;\n+\tint i, newfd, gitmodules_modified = 0;\n \tint verbose = 0, show_only = 0, force = 0, ignore_errors = 0;\n \tstruct option builtin_mv_options[] = {\n \t\tOPT__VERBOSE(&verbose, N_(\"be verbose\")),\n@@ -72,6 +72,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \tstruct stat st;\n \tstruct string_list src_for_dst = STRING_LIST_INIT_NODUP;\n\n+\tgitmodules_config();\n \tgit_config(git_default_config, NULL);\n\n \targc = parse_options(argc, argv, prefix, builtin_mv_options,\n@@ -125,6 +126,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\t\t\tstruct strbuf submodule_dotgit = STRBUF_INIT;\n \t\t\t\tif (!S_ISGITLINK(active_cache[first]->ce_mode))\n \t\t\t\t\tdie (_(\"Huh? Directory %s is in index and no submodule?\"), src);\n+\t\t\t\tif (!is_staging_gitmodules_ok())\n+\t\t\t\t\tdie (_(\"Please, stage your changes to .gitmodules or stash them to proceed\"));\n \t\t\t\tstrbuf_addf(&submodule_dotgit, \"%s/.git\", src);\n \t\t\t\tsubmodule_gitfile[i] = read_gitfile(submodule_dotgit.buf);\n \t\t\t\tif (submodule_gitfile[i])\n@@ -229,6 +232,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\t\t\tdie_errno (_(\"renaming '%s' failed\"), src);\n \t\t\tif (submodule_gitfile[i])\n \t\t\t\tconnect_work_tree_and_git_dir(dst, submodule_gitfile[i]);\n+\t\t\tif (!update_path_in_gitmodules(src, dst))\n+\t\t\t\tgitmodules_modified = 1;\n \t\t}\n\n \t\tif (mode == WORKING_DIRECTORY)\n@@ -240,6 +245,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\t\trename_cache_entry_at(pos, dst);\n \t}\n\n+\tif (gitmodules_modified)\n+\t\tstage_updated_gitmodules();\n+\n \tif (active_cache_changed) {\n \t\tif (write_cache(newfd, active_cache, active_nr) ||\n \t\t    commit_locked_index(&lock_file))\ndiff --git a/submodule.c b/submodule.c\nindex 584f7de..b210685 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -47,6 +47,39 @@ int is_staging_gitmodules_ok()\n \treturn !gitmodules_is_modified;\n }\n\n+/*\n+ * Try to update the \"path\" entry in the \"submodule.<name>\" section of the\n+ * .gitmodules file. Return 0 only if a .gitmodules file was found, a section\n+ * with the correct path=<oldpath> setting was found and we could update it.\n+ */\n+int update_path_in_gitmodules(const char *oldpath, const char *newpath)\n+{\n+\tstruct strbuf entry = STRBUF_INIT;\n+\tstruct string_list_item *path_option;\n+\n+\tif (!file_exists(\".gitmodules\")) /* Do nothing without .gitmodules */\n+\t\treturn -1;\n+\n+\tif (gitmodules_is_unmerged)\n+\t\tdie(_(\"Cannot change unmerged .gitmodules, resolve merge conflicts first\"));\n+\n+\tpath_option = unsorted_string_list_lookup(&config_name_for_path, oldpath);\n+\tif (!path_option) {\n+\t\twarning(_(\"Could not find section in .gitmodules where path=%s\"), oldpath);\n+\t\treturn -1;\n+\t}\n+\tstrbuf_addstr(&entry, \"submodule.\");\n+\tstrbuf_addstr(&entry, path_option->util);\n+\tstrbuf_addstr(&entry, \".path\");\n+\tif (git_config_set_in_file(\".gitmodules\", entry.buf, newpath) < 0) {\n+\t\t/* Maybe the user already did that, don't error out here */\n+\t\twarning(_(\"Could not update .gitmodules entry %s\"), entry.buf);\n+\t\treturn -1;\n+\t}\n+\tstrbuf_release(&entry);\n+\treturn 0;\n+}\n+\n void stage_updated_gitmodules(void)\n {\n \tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/submodule.h b/submodule.h\nindex 244d5f5..570d4d0 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -12,6 +12,7 @@ enum {\n };\n\n int is_staging_gitmodules_ok();\n+int update_path_in_gitmodules(const char *oldpath, const char *newpath);\n void stage_updated_gitmodules(void);\n void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,\n \t\tconst char *path);\ndiff --git a/t/t7001-mv.sh b/t/t7001-mv.sh\nindex b99177f..d432f42 100755\n--- a/t/t7001-mv.sh\n+++ b/t/t7001-mv.sh\n@@ -308,8 +308,83 @@ test_expect_success 'git mv moves a submodule with gitfile' '\n \t\tcd mod/sub &&\n \t\tgit status\n \t) &&\n+\techo mod/sub >expected &&\n+\tgit config -f .gitmodules submodule.sub.path >actual &&\n+\ttest_cmp expected actual &&\n \tgit update-index --refresh &&\n \tgit diff-files --quiet\n '\n\n+test_expect_success 'mv does not complain when no .gitmodules file is found' '\n+\trm -rf mod/sub &&\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit rm .gitmodules &&\n+\tentry=\"$(git ls-files --stage sub | cut -f 1)\" &&\n+\tgit mv sub mod/sub 2>actual.err &&\n+\t! test -s actual.err &&\n+\t! test -e sub &&\n+\t[ \"$entry\" = \"$(git ls-files --stage mod/sub | cut -f 1)\" ] &&\n+\t(\n+\t\tcd mod/sub &&\n+\t\tgit status\n+\t) &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet\n+'\n+\n+test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '\n+\trm -rf mod/sub &&\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit config -f .gitmodules foo.bar true &&\n+\tentry=\"$(git ls-files --stage sub | cut -f 1)\" &&\n+\ttest_must_fail git mv sub mod/sub 2>actual.err &&\n+\ttest -s actual.err &&\n+\ttest -e sub &&\n+\tgit diff-files --quiet -- sub &&\n+\tgit add .gitmodules &&\n+\tgit mv sub mod/sub 2>actual.err &&\n+\t! test -s actual.err &&\n+\t! test -e sub &&\n+\t[ \"$entry\" = \"$(git ls-files --stage mod/sub | cut -f 1)\" ] &&\n+\t(\n+\t\tcd mod/sub &&\n+\t\tgit status\n+\t) &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet\n+'\n+\n+test_expect_success 'mv issues a warning when section is not found in .gitmodules' '\n+\trm -rf mod/sub &&\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit config -f .gitmodules --remove-section submodule.sub &&\n+\tgit add .gitmodules &&\n+\tentry=\"$(git ls-files --stage sub | cut -f 1)\" &&\n+\techo \"warning: Could not find section in .gitmodules where path=sub\" >expect.err &&\n+\tgit mv sub mod/sub 2>actual.err &&\n+\ttest_i18ncmp expect.err actual.err &&\n+\t! test -e sub &&\n+\t[ \"$entry\" = \"$(git ls-files --stage mod/sub | cut -f 1)\" ] &&\n+\t(\n+\t\tcd mod/sub &&\n+\t\tgit status\n+\t) &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet\n+'\n+\n+test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '\n+\trm -rf mod/sub &&\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit mv -n sub mod/sub 2>actual.err &&\n+\ttest -f sub/.git &&\n+\tgit diff-index --exit-code HEAD &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet -- sub .gitmodules\n+'\n+\n test_done\n-- \n1.8.4.rc0.199.g7079aac\n"},{"id":"224319","messageId":"51F81957.1000709@web.de","threadId":"34572","inReplyTo":"51F8187F.7040603@web.de","subject":"[PATCH v3 5/5] rm: delete .gitmodules entry of submodules removed from the work tree","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-07-30T19:51:51Z","receivedAt":"2013-07-30T19:51:51Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Currently using \"git rm\" on a submodule removes the submodule's work tree\nfrom that of the superproject and the gitlink from the index. But the\nsubmodule's section in .gitmodules is left untouched, which is a leftover\nof the now removed submodule and might irritate users (as opposed to the\nsetting in .git/config, this must stay as a reminder that the user showed\ninterest in this submodule so it will be repopulated later when an older\ncommit is checked out).\n\nLet \"git rm\" help the user by not only removing the submodule from the\nwork tree but by also removing the \"submodule.<submodule name>\" section\nfrom the .gitmodules file and stage both. This doesn't happen when the\n\"--cached\" option is used, as it would modify the work tree. This also\nsilently does nothing when no .gitmodules file is found and only issues a\nwarning when it doesn't have a section for this submodule. This is because\nthe user might just use plain gitlinks without the .gitmodules file or has\nalready removed the section by hand before issuing the \"git rm\" command\n(in which case the warning reminds him that rm would have done that for\nhim). Only when .gitmodules is found and contains merge conflicts the rm\ncommand will fail and tell the user to resolve the conflict before trying\nagain.\n\nAlso extend the man page to inform the user about this new feature. While\nat it promote the submodule sub-section to a chapter as it made not much\nsense under \"REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM\".\n\nIn t7610 three uses of \"git rm submod\" had to be replaced with \"git rm\n--cached submod\" because that test expects .gitmodules and the work tree\nto stay untouched. Also in t7400 the tests for the remaining settings in\nthe .gitmodules file had to be changed to assert that these settings are\nmissing.\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n Documentation/git-rm.txt   |  8 ++--\n builtin/rm.c               | 19 +++++++--\n submodule.c                | 32 +++++++++++++++\n submodule.h                |  1 +\n t/t3600-rm.sh              | 98 +++++++++++++++++++++++++++++++++++++++++++---\n t/t7400-submodule-basic.sh | 14 ++-----\n t/t7610-mergetool.sh       |  6 +--\n 7 files changed, 153 insertions(+), 25 deletions(-)\n\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex 1d876c2..9d731b4 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -134,14 +134,16 @@ use the following command:\n git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached\n ----------------\n\n-Submodules\n-~~~~~~~~~~\n+SUBMODULES\n+----------\n Only submodules using a gitfile (which means they were cloned\n with a Git version 1.7.8 or newer) will be removed from the work\n tree, as their repository lives inside the .git directory of the\n superproject. If a submodule (or one of those nested inside it)\n still uses a .git directory, `git rm` will fail - no matter if forced\n-or not - to protect the submodule's history.\n+or not - to protect the submodule's history. If it exists the\n+submodule.<name> section in the linkgit:gitmodules[5] file will also\n+be removed and that file will be staged (unless --cached or -n are used).\n\n A submodule is considered up-to-date when the HEAD is the same as\n recorded in the index, no tracked files are modified and no untracked\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex df85f98..a9d45dd 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -283,6 +283,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \tstruct pathspec pathspec;\n \tchar *seen;\n\n+\tgitmodules_config();\n \tgit_config(git_default_config, NULL);\n\n \targc = parse_options(argc, argv, prefix, builtin_rm_options,\n@@ -324,7 +325,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \t\t\tcontinue;\n \t\tALLOC_GROW(list.entry, list.nr + 1, list.alloc);\n \t\tlist.entry[list.nr].name = ce->name;\n-\t\tlist.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode);\n+\t\tlist.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);\n+\t\tif (list.entry[list.nr++].is_submodule &&\n+\t\t    !is_staging_gitmodules_ok())\n+\t\t\tdie (_(\"Please, stage your changes to .gitmodules or stash them to proceed\"));\n \t}\n\n \tif (pathspec.nr) {\n@@ -396,13 +400,15 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \t * in the middle)\n \t */\n \tif (!index_only) {\n-\t\tint removed = 0;\n+\t\tint removed = 0, gitmodules_modified = 0;\n \t\tfor (i = 0; i < list.nr; i++) {\n \t\t\tconst char *path = list.entry[i].name;\n \t\t\tif (list.entry[i].is_submodule) {\n \t\t\t\tif (is_empty_dir(path)) {\n \t\t\t\t\tif (!rmdir(path)) {\n \t\t\t\t\t\tremoved = 1;\n+\t\t\t\t\t\tif (!remove_path_from_gitmodules(path))\n+\t\t\t\t\t\t\tgitmodules_modified = 1;\n \t\t\t\t\t\tcontinue;\n \t\t\t\t\t}\n \t\t\t\t} else {\n@@ -410,9 +416,14 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tstrbuf_addstr(&buf, path);\n \t\t\t\t\tif (!remove_dir_recursively(&buf, 0)) {\n \t\t\t\t\t\tremoved = 1;\n+\t\t\t\t\t\tif (!remove_path_from_gitmodules(path))\n+\t\t\t\t\t\t\tgitmodules_modified = 1;\n \t\t\t\t\t\tstrbuf_release(&buf);\n \t\t\t\t\t\tcontinue;\n-\t\t\t\t\t}\n+\t\t\t\t\t} else if (!file_exists(path))\n+\t\t\t\t\t\t/* Submodule was removed by user */\n+\t\t\t\t\t\tif (!remove_path_from_gitmodules(path))\n+\t\t\t\t\t\t\tgitmodules_modified = 1;\n \t\t\t\t\tstrbuf_release(&buf);\n \t\t\t\t\t/* Fallthrough and let remove_path() fail. */\n \t\t\t\t}\n@@ -424,6 +435,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \t\t\tif (!removed)\n \t\t\t\tdie_errno(\"git rm: '%s'\", path);\n \t\t}\n+\t\tif (gitmodules_modified)\n+\t\t\tstage_updated_gitmodules();\n \t}\n\n \tif (active_cache_changed) {\ndiff --git a/submodule.c b/submodule.c\nindex b210685..5cd8845 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -80,6 +80,38 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)\n \treturn 0;\n }\n\n+/*\n+ * Try to remove the \"submodule.<name>\" section from .gitmodules where the given\n+ * path is configured. Return 0 only if a .gitmodules file was found, a section\n+ * with the correct path=<path> setting was found and we could remove it.\n+ */\n+int remove_path_from_gitmodules(const char *path)\n+{\n+\tstruct strbuf sect = STRBUF_INIT;\n+\tstruct string_list_item *path_option;\n+\n+\tif (!file_exists(\".gitmodules\")) /* Do nothing without .gitmodules */\n+\t\treturn -1;\n+\n+\tif (gitmodules_is_unmerged)\n+\t\tdie(_(\"Cannot change unmerged .gitmodules, resolve merge conflicts first\"));\n+\n+\tpath_option = unsorted_string_list_lookup(&config_name_for_path, path);\n+\tif (!path_option) {\n+\t\twarning(_(\"Could not find section in .gitmodules where path=%s\"), path);\n+\t\treturn -1;\n+\t}\n+\tstrbuf_addstr(&sect, \"submodule.\");\n+\tstrbuf_addstr(&sect, path_option->util);\n+\tif (git_config_rename_section_in_file(\".gitmodules\", sect.buf, NULL) < 0) {\n+\t\t/* Maybe the user already did that, don't error out here */\n+\t\twarning(_(\"Could not remove .gitmodules entry for %s\"), path);\n+\t\treturn -1;\n+\t}\n+\tstrbuf_release(&sect);\n+\treturn 0;\n+}\n+\n void stage_updated_gitmodules(void)\n {\n \tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/submodule.h b/submodule.h\nindex 570d4d0..eca1d11 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -13,6 +13,7 @@ enum {\n\n int is_staging_gitmodules_ok();\n int update_path_in_gitmodules(const char *oldpath, const char *newpath);\n+int remove_path_from_gitmodules(const char *path);\n void stage_updated_gitmodules(void);\n void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,\n \t\tconst char *path);\ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex 5c87b55..639cb70 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -263,6 +263,7 @@ test_expect_success 'rm removes subdirectories recursively' '\n '\n\n cat >expect <<EOF\n+M  .gitmodules\n D  submod\n EOF\n\n@@ -270,6 +271,15 @@ cat >expect.modified <<EOF\n  M submod\n EOF\n\n+cat >expect.cached <<EOF\n+D  submod\n+EOF\n+\n+cat >expect.both_deleted<<EOF\n+D  .gitmodules\n+D  submod\n+EOF\n+\n test_expect_success 'rm removes empty submodules from work tree' '\n \tmkdir submod &&\n \tgit update-index --add --cacheinfo 160000 $(git rev-parse HEAD) submod &&\n@@ -281,16 +291,20 @@ test_expect_success 'rm removes empty submodules from work tree' '\n \tgit rm submod &&\n \ttest ! -e submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n '\n\n-test_expect_success 'rm removes removed submodule from index' '\n+test_expect_success 'rm removes removed submodule from index and .gitmodules' '\n \tgit reset --hard &&\n \tgit submodule update &&\n \trm -rf submod &&\n \tgit rm submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n '\n\n test_expect_success 'rm removes work tree of unmodified submodules' '\n@@ -299,7 +313,9 @@ test_expect_success 'rm removes work tree of unmodified submodules' '\n \tgit rm submod &&\n \ttest ! -d submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n '\n\n test_expect_success 'rm removes a submodule with a trailing /' '\n@@ -333,6 +349,72 @@ test_expect_success 'rm of a populated submodule with different HEAD fails unles\n \tgit rm -f submod &&\n \ttest ! -d submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n+'\n+\n+test_expect_success 'rm --cached leaves work tree of populated submodules and .gitmodules alone' '\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit rm --cached submod &&\n+\ttest -d submod &&\n+\ttest -f submod/.git &&\n+\tgit status -s -uno >actual &&\n+\ttest_cmp expect.cached actual &&\n+\tgit config -f .gitmodules submodule.sub.url &&\n+\tgit config -f .gitmodules submodule.sub.path\n+'\n+\n+test_expect_success 'rm --dry-run does not touch the submodule or .gitmodules' '\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit rm -n submod &&\n+\ttest -f submod/.git &&\n+\tgit diff-index --exit-code HEAD\n+'\n+\n+test_expect_success 'rm does not complain when no .gitmodules file is found' '\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit rm .gitmodules &&\n+\tgit rm submod >actual 2>actual.err &&\n+\t! test -s actual.err &&\n+\t! test -d submod &&\n+\t! test -f submod/.git &&\n+\tgit status -s -uno >actual &&\n+\ttest_cmp expect.both_deleted actual\n+'\n+\n+test_expect_success 'rm will error out on a modified .gitmodules file unless staged' '\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit config -f .gitmodules foo.bar true &&\n+\ttest_must_fail git rm submod >actual 2>actual.err &&\n+\ttest -s actual.err &&\n+\ttest -d submod &&\n+\ttest -f submod/.git &&\n+\tgit diff-files --quiet -- submod &&\n+\tgit add .gitmodules &&\n+\tgit rm submod >actual 2>actual.err &&\n+\t! test -s actual.err &&\n+\t! test -d submod &&\n+\t! test -f submod/.git &&\n+\tgit status -s -uno >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rm issues a warning when section is not found in .gitmodules' '\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit config -f .gitmodules --remove-section submodule.sub &&\n+\tgit add .gitmodules &&\n+\techo \"warning: Could not find section in .gitmodules where path=submod\" >expect.err &&\n+\tgit rm submod >actual 2>actual.err &&\n+\ttest_i18ncmp expect.err actual.err &&\n+\t! test -d submod &&\n+\t! test -f submod/.git &&\n+\tgit status -s -uno >actual &&\n \ttest_cmp expect actual\n '\n\n@@ -427,7 +509,9 @@ test_expect_success 'rm of a conflicted populated submodule with different HEAD\n \tgit rm -f submod &&\n \ttest ! -d submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n '\n\n test_expect_success 'rm of a conflicted populated submodule with modifications fails unless forced' '\n@@ -446,7 +530,9 @@ test_expect_success 'rm of a conflicted populated submodule with modifications f\n \tgit rm -f submod &&\n \ttest ! -d submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n '\n\n test_expect_success 'rm of a conflicted populated submodule with untracked files fails unless forced' '\ndiff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh\nindex 5ee97b0..88293e6 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -773,13 +773,11 @@ test_expect_success 'submodule add --name allows to replace a submodule with ano\n \t\t\ttest_cmp expect .git\n \t\t) &&\n \t\techo \"repo\" >expect &&\n-\t\tgit config -f .gitmodules submodule.repo.path >actual &&\n-\t\ttest_cmp expect actual &&\n+\t\ttest_must_fail git config -f .gitmodules submodule.repo.path &&\n \t\tgit config -f .gitmodules submodule.repo_new.path >actual &&\n \t\ttest_cmp expect actual&&\n \t\techo \"$submodurl/repo\" >expect &&\n-\t\tgit config -f .gitmodules submodule.repo.url >actual &&\n-\t\ttest_cmp expect actual &&\n+\t\ttest_must_fail git config -f .gitmodules submodule.repo.url &&\n \t\techo \"$submodurl/bare.git\" >expect &&\n \t\tgit config -f .gitmodules submodule.repo_new.url >actual &&\n \t\ttest_cmp expect actual &&\n@@ -799,12 +797,8 @@ test_expect_success 'submodule add with an existing name fails unless forced' '\n \t\tgit rm repo &&\n \t\ttest_must_fail git submodule add -q --name repo_new \"$submodurl/repo.git\" repo &&\n \t\ttest ! -d repo &&\n-\t\techo \"repo\" >expect &&\n-\t\tgit config -f .gitmodules submodule.repo_new.path >actual &&\n-\t\ttest_cmp expect actual&&\n-\t\techo \"$submodurl/bare.git\" >expect &&\n-\t\tgit config -f .gitmodules submodule.repo_new.url >actual &&\n-\t\ttest_cmp expect actual &&\n+\t\ttest_must_fail git config -f .gitmodules submodule.repo_new.path &&\n+\t\ttest_must_fail git config -f .gitmodules submodule.repo_new.url &&\n \t\techo \"$submodurl/bare.git\" >expect &&\n \t\tgit config submodule.repo_new.url >actual &&\n \t\ttest_cmp expect actual &&\ndiff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\nindex d526b1d..05d9db0 100755\n--- a/t/t7610-mergetool.sh\n+++ b/t/t7610-mergetool.sh\n@@ -253,7 +253,7 @@ 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 rm --cached submod &&\n     git commit -m \"Submodule deleted from branch\" &&\n     git checkout -b test6.a test6 &&\n     test_must_fail git merge master &&\n@@ -322,7 +322,7 @@ 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+    git rm --cached submod &&\n     echo not a submodule >submod &&\n     git add submod &&\n     git commit -m \"Submodule path becomes file\" &&\n@@ -453,7 +453,7 @@ test_expect_success 'submodule in subdirectory' '\n test_expect_success 'directory vs modified submodule' '\n     git checkout -b test11 branch1 &&\n     mv submod submod-movedaside &&\n-    git rm submod &&\n+    git rm --cached submod &&\n     mkdir submod &&\n     echo not a submodule >submod/file16 &&\n     git add submod/file16 &&\n-- \n1.8.4.rc0.199.g7079aac\n"},{"id":"224321","messageId":"20130730201511.GC19369@paksenarrion.iveqy.com","threadId":"34572","inReplyTo":"51F81957.1000709@web.de","subject":"Re: [PATCH v3 5/5] rm: delete .gitmodules entry of submodules removed from the work tree","fromName":"Fredrik Gustafsson","fromEmail":"iveqy@iveqy.com","sentAt":"2013-07-30T20:15:11Z","receivedAt":"2013-07-30T20:15:11Z","isPatch":true,"sender":{"key":"iveqy@iveqy.com","avatar":"https://avatars.githubusercontent.com/u/761743?v=4"},"body":"On Tue, Jul 30, 2013 at 09:51:51PM +0200, Jens Lehmann wrote:\n> +/*\n> + * Try to remove the \"submodule.<name>\" section from .gitmodules where the given\n> + * path is configured. Return 0 only if a .gitmodules file was found, a section\n> + * with the correct path=<path> setting was found and we could remove it.\n> + */\n> +int remove_path_from_gitmodules(const char *path)\n> +{\n> +\tstruct strbuf sect = STRBUF_INIT;\n> +\tstruct string_list_item *path_option;\n> +\n> +\tif (!file_exists(\".gitmodules\")) /* Do nothing without .gitmodules */\n> +\t\treturn -1;\n> +\n> +\tif (gitmodules_is_unmerged)\n> +\t\tdie(_(\"Cannot change unmerged .gitmodules, resolve merge conflicts first\"));\n> +\n> +\tpath_option = unsorted_string_list_lookup(&config_name_for_path, path);\n> +\tif (!path_option) {\n> +\t\twarning(_(\"Could not find section in .gitmodules where path=%s\"), path);\n> +\t\treturn -1;\n> +\t}\n> +\tstrbuf_addstr(&sect, \"submodule.\");\n> +\tstrbuf_addstr(&sect, path_option->util);\n> +\tif (git_config_rename_section_in_file(\".gitmodules\", sect.buf, NULL) < 0) {\n> +\t\t/* Maybe the user already did that, don't error out here */\n> +\t\twarning(_(\"Could not remove .gitmodules entry for %s\"), path);\n> +\t\treturn -1;\n> +\t}\n> +\tstrbuf_release(&sect);\n> +\treturn 0;\n> +}\n\nThis question applies for this function and a few more functions in this\npatch that has the same characteristics.\n\nIf we're in a state when we need to return non-zero, we don't do any\ncleaning (that is strbuf_release()). Since this file is in the part\ncalled libgit AFAIK, shouldn't we always clean after us?\n\nWould it make sense to have different return values for different\nerrors?\n\nI do like the comments above the function, more functions (at least\nnon-static ones) should follow this good style IMHO.\n-- \nMed vänliga hälsningar\nFredrik Gustafsson\n\ntel: 0733-608274\ne-post: iveqy@iveqy.com\n"},{"id":"224325","messageId":"7vsiyvvidl.fsf@alter.siamese.dyndns.org","threadId":"34572","inReplyTo":"51F8190A.3030002@web.de","subject":"Re: [PATCH v3 3/5] submodule.c: add .gitmodules staging helper functions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-07-30T21:37:58Z","receivedAt":"2013-07-30T21:37:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jens Lehmann <Jens.Lehmann@web.de> writes:\n\n> +int is_staging_gitmodules_ok()\n\nWill tweak this to:\n\n\tint is_staging_gitmodules_ok(void)\n\nand fix this as well:\n\n> +int is_staging_gitmodules_ok();\n> +void stage_updated_gitmodules(void);\n\n\nbefore queuing.\n\nThanks.\n"},{"id":"224329","messageId":"51F846FA.8030808@web.de","threadId":"34572","inReplyTo":"20130730201511.GC19369@paksenarrion.iveqy.com","subject":"Re: [PATCH v3 5/5] rm: delete .gitmodules entry of submodules removed from the work tree","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-07-30T23:06:34Z","receivedAt":"2013-07-30T23:06:34Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Am 30.07.2013 22:15, schrieb Fredrik Gustafsson:\n> On Tue, Jul 30, 2013 at 09:51:51PM +0200, Jens Lehmann wrote:\n>> +/*\n>> + * Try to remove the \"submodule.<name>\" section from .gitmodules where the given\n>> + * path is configured. Return 0 only if a .gitmodules file was found, a section\n>> + * with the correct path=<path> setting was found and we could remove it.\n>> + */\n>> +int remove_path_from_gitmodules(const char *path)\n>> +{\n>> +\tstruct strbuf sect = STRBUF_INIT;\n>> +\tstruct string_list_item *path_option;\n>> +\n>> +\tif (!file_exists(\".gitmodules\")) /* Do nothing without .gitmodules */\n>> +\t\treturn -1;\n>> +\n>> +\tif (gitmodules_is_unmerged)\n>> +\t\tdie(_(\"Cannot change unmerged .gitmodules, resolve merge conflicts first\"));\n>> +\n>> +\tpath_option = unsorted_string_list_lookup(&config_name_for_path, path);\n>> +\tif (!path_option) {\n>> +\t\twarning(_(\"Could not find section in .gitmodules where path=%s\"), path);\n>> +\t\treturn -1;\n>> +\t}\n>> +\tstrbuf_addstr(&sect, \"submodule.\");\n>> +\tstrbuf_addstr(&sect, path_option->util);\n>> +\tif (git_config_rename_section_in_file(\".gitmodules\", sect.buf, NULL) < 0) {\n>> +\t\t/* Maybe the user already did that, don't error out here */\n>> +\t\twarning(_(\"Could not remove .gitmodules entry for %s\"), path);\n>> +\t\treturn -1;\n>> +\t}\n>> +\tstrbuf_release(&sect);\n>> +\treturn 0;\n>> +}\n> \n> This question applies for this function and a few more functions in this\n> patch that has the same characteristics.\n> \n> If we're in a state when we need to return non-zero, we don't do any\n> cleaning (that is strbuf_release()). Since this file is in the part\n> called libgit AFAIK, shouldn't we always clean after us?\n\nRight you are, thanks for bringing that up. The last return needs a\nstrbuf_release(), will fix that in v4.\n\n> Would it make sense to have different return values for different\n> errors?\n\nI don't think so. The caller only wants to know if the modification\nof the .gitmodules file happened or not, so he can decide if that\nneeds to be staged.\n"},{"id":"224330","messageId":"51F84881.9040204@web.de","threadId":"34572","inReplyTo":"7vsiyvvidl.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 3/5] submodule.c: add .gitmodules staging helper functions","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-07-30T23:13:05Z","receivedAt":"2013-07-30T23:13:05Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Am 30.07.2013 23:37, schrieb Junio C Hamano:\n> Jens Lehmann <Jens.Lehmann@web.de> writes:\n> \n>> +int is_staging_gitmodules_ok()\n> \n> Will tweak this to:\n> \n> \tint is_staging_gitmodules_ok(void)\n> \n> and fix this as well:\n> \n>> +int is_staging_gitmodules_ok();\n>> +void stage_updated_gitmodules(void);\n> \n> \n> before queuing.\n\nThanks. As Frederik already noticed, this series is missing at least\none strbuf_release(). Will fix both in v4.\n"},{"id":"224345","messageId":"20130731094314.GD19369@paksenarrion.iveqy.com","threadId":"34572","inReplyTo":"51F818EB.8010101@web.de","subject":"Re: [PATCH v3 2/5] Teach mv to move submodules using a gitfile","fromName":"Fredrik Gustafsson","fromEmail":"iveqy@iveqy.com","sentAt":"2013-07-31T09:43:14Z","receivedAt":"2013-07-31T09:43:14Z","isPatch":true,"sender":{"key":"iveqy@iveqy.com","avatar":"https://avatars.githubusercontent.com/u/761743?v=4"},"body":"On Tue, Jul 30, 2013 at 09:50:03PM +0200, Jens Lehmann wrote:\n> +/* Update gitfile and core.worktree setting to connect work tree and git dir */\n> +void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)\n> +{\n> +\tstruct strbuf file_name = STRBUF_INIT;\n> +\tstruct strbuf rel_path = STRBUF_INIT;\n> +\tconst char *real_work_tree = xstrdup(real_path(work_tree));\n> +\tFILE *fp;\n> +\n> +\t/* Update gitfile */\n> +\tstrbuf_addf(&file_name, \"%s/.git\", work_tree);\n> +\tfp = fopen(file_name.buf, \"w\");\n> +\tif (!fp)\n> +\t\tdie(_(\"Could not create git link %s\"), file_name.buf);\n> +\tfprintf(fp, \"gitdir: %s\\n\", relative_path(git_dir, real_work_tree,\n> +\t\t\t\t\t\t  &rel_path));\n> +\tfclose(fp);\n> +\n> +\t/* Update core.worktree setting */\n> +\tstrbuf_reset(&file_name);\n> +\tstrbuf_addf(&file_name, \"%s/config\", git_dir);\n> +\tif (git_config_set_in_file(file_name.buf, \"core.worktree\",\n> +\t\t\t\t   relative_path(real_work_tree, git_dir,\n> +\t\t\t\t\t\t &rel_path)))\n> +\t\tdie(_(\"Could not set core.worktree in %s\"),\n> +\t\t    file_name.buf);\n> +\n> +\tstrbuf_release(&file_name);\n> +\tstrbuf_release(&rel_path);\n> +\tfree((void *)real_work_tree);\n> +}\n\nWould it make sense to return an int here and do the dying in\nbuiltin/mv.c instead? Again thinking of \"libgit\" and the die_errno for\nnon-submodule errors are in mv.c and not in rename().\n\nIf that's the case the same applies for stage_updated_gitmodules() and\nupdate_path_in_gitmodules().\n\nupdate_path_in_gitmodules() also needs a strbuf_release() if you haven't\nalready found it.\n\n-- \nMed vänliga hälsningar\nFredrik Gustafsson\n\ntel: 0733-608274\ne-post: iveqy@iveqy.com\n"},{"id":"224703","messageId":"52014B3F.9070208@web.de","threadId":"34572","inReplyTo":"51F81929.1030706@web.de","subject":"Re: [PATCH v4 4/5] Teach mv to update the path entry in .gitmodules for moved submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-08-06T19:15:11Z","receivedAt":"2013-08-06T19:15:11Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Currently using \"git mv\" on a submodule moves the submodule's work tree in\nthat of the superproject. But the submodule's path setting in .gitmodules\nis left untouched, which is now inconsistent with the work tree and makes\ngit commands that rely on the proper path -> name mapping (like status and\ndiff) behave strangely.\n\nLet \"git mv\" help here by not only moving the submodule's work tree but\nalso updating the \"submodule.<submodule name>.path\" setting from the\n.gitmodules file and stage both. This doesn't happen when no .gitmodules\nfile is found and only issues a warning when it doesn't have a section for\nthis submodule. This is because the user might just use plain gitlinks\nwithout the .gitmodules file or has already updated the path setting by\nhand before issuing the \"git mv\" command (in which case the warning\nreminds him that mv would have done that for him). Only when .gitmodules\nis found and contains merge conflicts the mv command will fail and tell\nthe user to resolve the conflict before trying again.\n\nAlso extend the man page to inform the user about this new feature.\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n\nThis version fixes the missing strbuf_release() noticed by Frederik.\n\n\n Documentation/git-mv.txt |  2 ++\n builtin/mv.c             | 10 ++++++-\n submodule.c              | 34 ++++++++++++++++++++++\n submodule.h              |  1 +\n t/t7001-mv.sh            | 75 ++++++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 121 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt\nindex 1f6fce0..b1f7988 100644\n--- a/Documentation/git-mv.txt\n+++ b/Documentation/git-mv.txt\n@@ -49,6 +49,8 @@ SUBMODULES\n Moving a submodule using a gitfile (which means they were cloned\n with a Git version 1.7.8 or newer) will update the gitfile and\n core.worktree setting to make the submodule work in the new location.\n+It also will attempt to update the submodule.<name>.path setting in\n+the linkgit:gitmodules[5] file and stage that file (unless -n is used).\n\n GIT\n ---\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex 68b7060..7dd6bb4 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -58,7 +58,7 @@ static struct lock_file lock_file;\n\n int cmd_mv(int argc, const char **argv, const char *prefix)\n {\n-\tint i, newfd;\n+\tint i, newfd, gitmodules_modified = 0;\n \tint verbose = 0, show_only = 0, force = 0, ignore_errors = 0;\n \tstruct option builtin_mv_options[] = {\n \t\tOPT__VERBOSE(&verbose, N_(\"be verbose\")),\n@@ -72,6 +72,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \tstruct stat st;\n \tstruct string_list src_for_dst = STRING_LIST_INIT_NODUP;\n\n+\tgitmodules_config();\n \tgit_config(git_default_config, NULL);\n\n \targc = parse_options(argc, argv, prefix, builtin_mv_options,\n@@ -125,6 +126,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\t\t\tstruct strbuf submodule_dotgit = STRBUF_INIT;\n \t\t\t\tif (!S_ISGITLINK(active_cache[first]->ce_mode))\n \t\t\t\t\tdie (_(\"Huh? Directory %s is in index and no submodule?\"), src);\n+\t\t\t\tif (!is_staging_gitmodules_ok())\n+\t\t\t\t\tdie (_(\"Please, stage your changes to .gitmodules or stash them to proceed\"));\n \t\t\t\tstrbuf_addf(&submodule_dotgit, \"%s/.git\", src);\n \t\t\t\tsubmodule_gitfile[i] = read_gitfile(submodule_dotgit.buf);\n \t\t\t\tif (submodule_gitfile[i])\n@@ -229,6 +232,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\t\t\tdie_errno (_(\"renaming '%s' failed\"), src);\n \t\t\tif (submodule_gitfile[i])\n \t\t\t\tconnect_work_tree_and_git_dir(dst, submodule_gitfile[i]);\n+\t\t\tif (!update_path_in_gitmodules(src, dst))\n+\t\t\t\tgitmodules_modified = 1;\n \t\t}\n\n \t\tif (mode == WORKING_DIRECTORY)\n@@ -240,6 +245,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \t\t\trename_cache_entry_at(pos, dst);\n \t}\n\n+\tif (gitmodules_modified)\n+\t\tstage_updated_gitmodules();\n+\n \tif (active_cache_changed) {\n \t\tif (write_cache(newfd, active_cache, active_nr) ||\n \t\t    commit_locked_index(&lock_file))\ndiff --git a/submodule.c b/submodule.c\nindex 5874d08..1c2714f 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -47,6 +47,40 @@ int is_staging_gitmodules_ok(void)\n \treturn !gitmodules_is_modified;\n }\n\n+/*\n+ * Try to update the \"path\" entry in the \"submodule.<name>\" section of the\n+ * .gitmodules file. Return 0 only if a .gitmodules file was found, a section\n+ * with the correct path=<oldpath> setting was found and we could update it.\n+ */\n+int update_path_in_gitmodules(const char *oldpath, const char *newpath)\n+{\n+\tstruct strbuf entry = STRBUF_INIT;\n+\tstruct string_list_item *path_option;\n+\n+\tif (!file_exists(\".gitmodules\")) /* Do nothing without .gitmodules */\n+\t\treturn -1;\n+\n+\tif (gitmodules_is_unmerged)\n+\t\tdie(_(\"Cannot change unmerged .gitmodules, resolve merge conflicts first\"));\n+\n+\tpath_option = unsorted_string_list_lookup(&config_name_for_path, oldpath);\n+\tif (!path_option) {\n+\t\twarning(_(\"Could not find section in .gitmodules where path=%s\"), oldpath);\n+\t\treturn -1;\n+\t}\n+\tstrbuf_addstr(&entry, \"submodule.\");\n+\tstrbuf_addstr(&entry, path_option->util);\n+\tstrbuf_addstr(&entry, \".path\");\n+\tif (git_config_set_in_file(\".gitmodules\", entry.buf, newpath) < 0) {\n+\t\t/* Maybe the user already did that, don't error out here */\n+\t\twarning(_(\"Could not update .gitmodules entry %s\"), entry.buf);\n+\t\tstrbuf_release(&entry);\n+\t\treturn -1;\n+\t}\n+\tstrbuf_release(&entry);\n+\treturn 0;\n+}\n+\n void stage_updated_gitmodules(void)\n {\n \tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/submodule.h b/submodule.h\nindex 5501354..e339891 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -12,6 +12,7 @@ enum {\n };\n\n int is_staging_gitmodules_ok(void);\n+int update_path_in_gitmodules(const char *oldpath, const char *newpath);\n void stage_updated_gitmodules(void);\n void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,\n \t\tconst char *path);\ndiff --git a/t/t7001-mv.sh b/t/t7001-mv.sh\nindex b99177f..d432f42 100755\n--- a/t/t7001-mv.sh\n+++ b/t/t7001-mv.sh\n@@ -308,8 +308,83 @@ test_expect_success 'git mv moves a submodule with gitfile' '\n \t\tcd mod/sub &&\n \t\tgit status\n \t) &&\n+\techo mod/sub >expected &&\n+\tgit config -f .gitmodules submodule.sub.path >actual &&\n+\ttest_cmp expected actual &&\n \tgit update-index --refresh &&\n \tgit diff-files --quiet\n '\n\n+test_expect_success 'mv does not complain when no .gitmodules file is found' '\n+\trm -rf mod/sub &&\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit rm .gitmodules &&\n+\tentry=\"$(git ls-files --stage sub | cut -f 1)\" &&\n+\tgit mv sub mod/sub 2>actual.err &&\n+\t! test -s actual.err &&\n+\t! test -e sub &&\n+\t[ \"$entry\" = \"$(git ls-files --stage mod/sub | cut -f 1)\" ] &&\n+\t(\n+\t\tcd mod/sub &&\n+\t\tgit status\n+\t) &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet\n+'\n+\n+test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '\n+\trm -rf mod/sub &&\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit config -f .gitmodules foo.bar true &&\n+\tentry=\"$(git ls-files --stage sub | cut -f 1)\" &&\n+\ttest_must_fail git mv sub mod/sub 2>actual.err &&\n+\ttest -s actual.err &&\n+\ttest -e sub &&\n+\tgit diff-files --quiet -- sub &&\n+\tgit add .gitmodules &&\n+\tgit mv sub mod/sub 2>actual.err &&\n+\t! test -s actual.err &&\n+\t! test -e sub &&\n+\t[ \"$entry\" = \"$(git ls-files --stage mod/sub | cut -f 1)\" ] &&\n+\t(\n+\t\tcd mod/sub &&\n+\t\tgit status\n+\t) &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet\n+'\n+\n+test_expect_success 'mv issues a warning when section is not found in .gitmodules' '\n+\trm -rf mod/sub &&\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit config -f .gitmodules --remove-section submodule.sub &&\n+\tgit add .gitmodules &&\n+\tentry=\"$(git ls-files --stage sub | cut -f 1)\" &&\n+\techo \"warning: Could not find section in .gitmodules where path=sub\" >expect.err &&\n+\tgit mv sub mod/sub 2>actual.err &&\n+\ttest_i18ncmp expect.err actual.err &&\n+\t! test -e sub &&\n+\t[ \"$entry\" = \"$(git ls-files --stage mod/sub | cut -f 1)\" ] &&\n+\t(\n+\t\tcd mod/sub &&\n+\t\tgit status\n+\t) &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet\n+'\n+\n+test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '\n+\trm -rf mod/sub &&\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit mv -n sub mod/sub 2>actual.err &&\n+\ttest -f sub/.git &&\n+\tgit diff-index --exit-code HEAD &&\n+\tgit update-index --refresh &&\n+\tgit diff-files --quiet -- sub .gitmodules\n+'\n+\n test_done\n-- \n1.8.4.rc0.199.g3f237fc\n"},{"id":"224704","messageId":"52014B4D.3090602@web.de","threadId":"34572","inReplyTo":"51F81957.1000709@web.de","subject":"Re: [PATCH v4 5/5] rm: delete .gitmodules entry of submodules removed from the work tree","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-08-06T19:15:25Z","receivedAt":"2013-08-06T19:15:25Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Currently using \"git rm\" on a submodule removes the submodule's work tree\nfrom that of the superproject and the gitlink from the index. But the\nsubmodule's section in .gitmodules is left untouched, which is a leftover\nof the now removed submodule and might irritate users (as opposed to the\nsetting in .git/config, this must stay as a reminder that the user showed\ninterest in this submodule so it will be repopulated later when an older\ncommit is checked out).\n\nLet \"git rm\" help the user by not only removing the submodule from the\nwork tree but by also removing the \"submodule.<submodule name>\" section\nfrom the .gitmodules file and stage both. This doesn't happen when the\n\"--cached\" option is used, as it would modify the work tree. This also\nsilently does nothing when no .gitmodules file is found and only issues a\nwarning when it doesn't have a section for this submodule. This is because\nthe user might just use plain gitlinks without the .gitmodules file or has\nalready removed the section by hand before issuing the \"git rm\" command\n(in which case the warning reminds him that rm would have done that for\nhim). Only when .gitmodules is found and contains merge conflicts the rm\ncommand will fail and tell the user to resolve the conflict before trying\nagain.\n\nAlso extend the man page to inform the user about this new feature. While\nat it promote the submodule sub-section to a chapter as it made not much\nsense under \"REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM\".\n\nIn t7610 three uses of \"git rm submod\" had to be replaced with \"git rm\n--cached submod\" because that test expects .gitmodules and the work tree\nto stay untouched. Also in t7400 the tests for the remaining settings in\nthe .gitmodules file had to be changed to assert that these settings are\nmissing.\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n\nThis version fixes the missing strbuf_release() noticed by Frederik.\n\n\n Documentation/git-rm.txt   |  8 ++--\n builtin/rm.c               | 19 +++++++--\n submodule.c                | 33 ++++++++++++++++\n submodule.h                |  1 +\n t/t3600-rm.sh              | 98 +++++++++++++++++++++++++++++++++++++++++++---\n t/t7400-submodule-basic.sh | 14 ++-----\n t/t7610-mergetool.sh       |  6 +--\n 7 files changed, 154 insertions(+), 25 deletions(-)\n\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex 1d876c2..9d731b4 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -134,14 +134,16 @@ use the following command:\n git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached\n ----------------\n\n-Submodules\n-~~~~~~~~~~\n+SUBMODULES\n+----------\n Only submodules using a gitfile (which means they were cloned\n with a Git version 1.7.8 or newer) will be removed from the work\n tree, as their repository lives inside the .git directory of the\n superproject. If a submodule (or one of those nested inside it)\n still uses a .git directory, `git rm` will fail - no matter if forced\n-or not - to protect the submodule's history.\n+or not - to protect the submodule's history. If it exists the\n+submodule.<name> section in the linkgit:gitmodules[5] file will also\n+be removed and that file will be staged (unless --cached or -n are used).\n\n A submodule is considered up-to-date when the HEAD is the same as\n recorded in the index, no tracked files are modified and no untracked\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex df85f98..a9d45dd 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -283,6 +283,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \tstruct pathspec pathspec;\n \tchar *seen;\n\n+\tgitmodules_config();\n \tgit_config(git_default_config, NULL);\n\n \targc = parse_options(argc, argv, prefix, builtin_rm_options,\n@@ -324,7 +325,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \t\t\tcontinue;\n \t\tALLOC_GROW(list.entry, list.nr + 1, list.alloc);\n \t\tlist.entry[list.nr].name = ce->name;\n-\t\tlist.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode);\n+\t\tlist.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);\n+\t\tif (list.entry[list.nr++].is_submodule &&\n+\t\t    !is_staging_gitmodules_ok())\n+\t\t\tdie (_(\"Please, stage your changes to .gitmodules or stash them to proceed\"));\n \t}\n\n \tif (pathspec.nr) {\n@@ -396,13 +400,15 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \t * in the middle)\n \t */\n \tif (!index_only) {\n-\t\tint removed = 0;\n+\t\tint removed = 0, gitmodules_modified = 0;\n \t\tfor (i = 0; i < list.nr; i++) {\n \t\t\tconst char *path = list.entry[i].name;\n \t\t\tif (list.entry[i].is_submodule) {\n \t\t\t\tif (is_empty_dir(path)) {\n \t\t\t\t\tif (!rmdir(path)) {\n \t\t\t\t\t\tremoved = 1;\n+\t\t\t\t\t\tif (!remove_path_from_gitmodules(path))\n+\t\t\t\t\t\t\tgitmodules_modified = 1;\n \t\t\t\t\t\tcontinue;\n \t\t\t\t\t}\n \t\t\t\t} else {\n@@ -410,9 +416,14 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tstrbuf_addstr(&buf, path);\n \t\t\t\t\tif (!remove_dir_recursively(&buf, 0)) {\n \t\t\t\t\t\tremoved = 1;\n+\t\t\t\t\t\tif (!remove_path_from_gitmodules(path))\n+\t\t\t\t\t\t\tgitmodules_modified = 1;\n \t\t\t\t\t\tstrbuf_release(&buf);\n \t\t\t\t\t\tcontinue;\n-\t\t\t\t\t}\n+\t\t\t\t\t} else if (!file_exists(path))\n+\t\t\t\t\t\t/* Submodule was removed by user */\n+\t\t\t\t\t\tif (!remove_path_from_gitmodules(path))\n+\t\t\t\t\t\t\tgitmodules_modified = 1;\n \t\t\t\t\tstrbuf_release(&buf);\n \t\t\t\t\t/* Fallthrough and let remove_path() fail. */\n \t\t\t\t}\n@@ -424,6 +435,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \t\t\tif (!removed)\n \t\t\t\tdie_errno(\"git rm: '%s'\", path);\n \t\t}\n+\t\tif (gitmodules_modified)\n+\t\t\tstage_updated_gitmodules();\n \t}\n\n \tif (active_cache_changed) {\ndiff --git a/submodule.c b/submodule.c\nindex 1c2714f..35c745e 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -81,6 +81,39 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)\n \treturn 0;\n }\n\n+/*\n+ * Try to remove the \"submodule.<name>\" section from .gitmodules where the given\n+ * path is configured. Return 0 only if a .gitmodules file was found, a section\n+ * with the correct path=<path> setting was found and we could remove it.\n+ */\n+int remove_path_from_gitmodules(const char *path)\n+{\n+\tstruct strbuf sect = STRBUF_INIT;\n+\tstruct string_list_item *path_option;\n+\n+\tif (!file_exists(\".gitmodules\")) /* Do nothing without .gitmodules */\n+\t\treturn -1;\n+\n+\tif (gitmodules_is_unmerged)\n+\t\tdie(_(\"Cannot change unmerged .gitmodules, resolve merge conflicts first\"));\n+\n+\tpath_option = unsorted_string_list_lookup(&config_name_for_path, path);\n+\tif (!path_option) {\n+\t\twarning(_(\"Could not find section in .gitmodules where path=%s\"), path);\n+\t\treturn -1;\n+\t}\n+\tstrbuf_addstr(&sect, \"submodule.\");\n+\tstrbuf_addstr(&sect, path_option->util);\n+\tif (git_config_rename_section_in_file(\".gitmodules\", sect.buf, NULL) < 0) {\n+\t\t/* Maybe the user already did that, don't error out here */\n+\t\twarning(_(\"Could not remove .gitmodules entry for %s\"), path);\n+\t\tstrbuf_release(&sect);\n+\t\treturn -1;\n+\t}\n+\tstrbuf_release(&sect);\n+\treturn 0;\n+}\n+\n void stage_updated_gitmodules(void)\n {\n \tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/submodule.h b/submodule.h\nindex e339891..7beec48 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -13,6 +13,7 @@ enum {\n\n int is_staging_gitmodules_ok(void);\n int update_path_in_gitmodules(const char *oldpath, const char *newpath);\n+int remove_path_from_gitmodules(const char *path);\n void stage_updated_gitmodules(void);\n void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,\n \t\tconst char *path);\ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex 5c87b55..639cb70 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -263,6 +263,7 @@ test_expect_success 'rm removes subdirectories recursively' '\n '\n\n cat >expect <<EOF\n+M  .gitmodules\n D  submod\n EOF\n\n@@ -270,6 +271,15 @@ cat >expect.modified <<EOF\n  M submod\n EOF\n\n+cat >expect.cached <<EOF\n+D  submod\n+EOF\n+\n+cat >expect.both_deleted<<EOF\n+D  .gitmodules\n+D  submod\n+EOF\n+\n test_expect_success 'rm removes empty submodules from work tree' '\n \tmkdir submod &&\n \tgit update-index --add --cacheinfo 160000 $(git rev-parse HEAD) submod &&\n@@ -281,16 +291,20 @@ test_expect_success 'rm removes empty submodules from work tree' '\n \tgit rm submod &&\n \ttest ! -e submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n '\n\n-test_expect_success 'rm removes removed submodule from index' '\n+test_expect_success 'rm removes removed submodule from index and .gitmodules' '\n \tgit reset --hard &&\n \tgit submodule update &&\n \trm -rf submod &&\n \tgit rm submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n '\n\n test_expect_success 'rm removes work tree of unmodified submodules' '\n@@ -299,7 +313,9 @@ test_expect_success 'rm removes work tree of unmodified submodules' '\n \tgit rm submod &&\n \ttest ! -d submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n '\n\n test_expect_success 'rm removes a submodule with a trailing /' '\n@@ -333,6 +349,72 @@ test_expect_success 'rm of a populated submodule with different HEAD fails unles\n \tgit rm -f submod &&\n \ttest ! -d submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n+'\n+\n+test_expect_success 'rm --cached leaves work tree of populated submodules and .gitmodules alone' '\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit rm --cached submod &&\n+\ttest -d submod &&\n+\ttest -f submod/.git &&\n+\tgit status -s -uno >actual &&\n+\ttest_cmp expect.cached actual &&\n+\tgit config -f .gitmodules submodule.sub.url &&\n+\tgit config -f .gitmodules submodule.sub.path\n+'\n+\n+test_expect_success 'rm --dry-run does not touch the submodule or .gitmodules' '\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit rm -n submod &&\n+\ttest -f submod/.git &&\n+\tgit diff-index --exit-code HEAD\n+'\n+\n+test_expect_success 'rm does not complain when no .gitmodules file is found' '\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit rm .gitmodules &&\n+\tgit rm submod >actual 2>actual.err &&\n+\t! test -s actual.err &&\n+\t! test -d submod &&\n+\t! test -f submod/.git &&\n+\tgit status -s -uno >actual &&\n+\ttest_cmp expect.both_deleted actual\n+'\n+\n+test_expect_success 'rm will error out on a modified .gitmodules file unless staged' '\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit config -f .gitmodules foo.bar true &&\n+\ttest_must_fail git rm submod >actual 2>actual.err &&\n+\ttest -s actual.err &&\n+\ttest -d submod &&\n+\ttest -f submod/.git &&\n+\tgit diff-files --quiet -- submod &&\n+\tgit add .gitmodules &&\n+\tgit rm submod >actual 2>actual.err &&\n+\t! test -s actual.err &&\n+\t! test -d submod &&\n+\t! test -f submod/.git &&\n+\tgit status -s -uno >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rm issues a warning when section is not found in .gitmodules' '\n+\tgit reset --hard &&\n+\tgit submodule update &&\n+\tgit config -f .gitmodules --remove-section submodule.sub &&\n+\tgit add .gitmodules &&\n+\techo \"warning: Could not find section in .gitmodules where path=submod\" >expect.err &&\n+\tgit rm submod >actual 2>actual.err &&\n+\ttest_i18ncmp expect.err actual.err &&\n+\t! test -d submod &&\n+\t! test -f submod/.git &&\n+\tgit status -s -uno >actual &&\n \ttest_cmp expect actual\n '\n\n@@ -427,7 +509,9 @@ test_expect_success 'rm of a conflicted populated submodule with different HEAD\n \tgit rm -f submod &&\n \ttest ! -d submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n '\n\n test_expect_success 'rm of a conflicted populated submodule with modifications fails unless forced' '\n@@ -446,7 +530,9 @@ test_expect_success 'rm of a conflicted populated submodule with modifications f\n \tgit rm -f submod &&\n \ttest ! -d submod &&\n \tgit status -s -uno --ignore-submodules=none > actual &&\n-\ttest_cmp expect actual\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.url &&\n+\ttest_must_fail git config -f .gitmodules submodule.sub.path\n '\n\n test_expect_success 'rm of a conflicted populated submodule with untracked files fails unless forced' '\ndiff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh\nindex 5ee97b0..88293e6 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -773,13 +773,11 @@ test_expect_success 'submodule add --name allows to replace a submodule with ano\n \t\t\ttest_cmp expect .git\n \t\t) &&\n \t\techo \"repo\" >expect &&\n-\t\tgit config -f .gitmodules submodule.repo.path >actual &&\n-\t\ttest_cmp expect actual &&\n+\t\ttest_must_fail git config -f .gitmodules submodule.repo.path &&\n \t\tgit config -f .gitmodules submodule.repo_new.path >actual &&\n \t\ttest_cmp expect actual&&\n \t\techo \"$submodurl/repo\" >expect &&\n-\t\tgit config -f .gitmodules submodule.repo.url >actual &&\n-\t\ttest_cmp expect actual &&\n+\t\ttest_must_fail git config -f .gitmodules submodule.repo.url &&\n \t\techo \"$submodurl/bare.git\" >expect &&\n \t\tgit config -f .gitmodules submodule.repo_new.url >actual &&\n \t\ttest_cmp expect actual &&\n@@ -799,12 +797,8 @@ test_expect_success 'submodule add with an existing name fails unless forced' '\n \t\tgit rm repo &&\n \t\ttest_must_fail git submodule add -q --name repo_new \"$submodurl/repo.git\" repo &&\n \t\ttest ! -d repo &&\n-\t\techo \"repo\" >expect &&\n-\t\tgit config -f .gitmodules submodule.repo_new.path >actual &&\n-\t\ttest_cmp expect actual&&\n-\t\techo \"$submodurl/bare.git\" >expect &&\n-\t\tgit config -f .gitmodules submodule.repo_new.url >actual &&\n-\t\ttest_cmp expect actual &&\n+\t\ttest_must_fail git config -f .gitmodules submodule.repo_new.path &&\n+\t\ttest_must_fail git config -f .gitmodules submodule.repo_new.url &&\n \t\techo \"$submodurl/bare.git\" >expect &&\n \t\tgit config submodule.repo_new.url >actual &&\n \t\ttest_cmp expect actual &&\ndiff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\nindex d526b1d..05d9db0 100755\n--- a/t/t7610-mergetool.sh\n+++ b/t/t7610-mergetool.sh\n@@ -253,7 +253,7 @@ 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 rm --cached submod &&\n     git commit -m \"Submodule deleted from branch\" &&\n     git checkout -b test6.a test6 &&\n     test_must_fail git merge master &&\n@@ -322,7 +322,7 @@ 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+    git rm --cached submod &&\n     echo not a submodule >submod &&\n     git add submod &&\n     git commit -m \"Submodule path becomes file\" &&\n@@ -453,7 +453,7 @@ test_expect_success 'submodule in subdirectory' '\n test_expect_success 'directory vs modified submodule' '\n     git checkout -b test11 branch1 &&\n     mv submod submod-movedaside &&\n-    git rm submod &&\n+    git rm --cached submod &&\n     mkdir submod &&\n     echo not a submodule >submod/file16 &&\n     git add submod/file16 &&\n-- \n1.8.4.rc0.199.g3f237fc\n"},{"id":"224711","messageId":"7v8v0efrs3.fsf@alter.siamese.dyndns.org","threadId":"34572","inReplyTo":"52014B4D.3090602@web.de","subject":"Re: [PATCH v4 5/5] rm: delete .gitmodules entry of submodules removed from the work tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-08-06T21:11:56Z","receivedAt":"2013-08-06T21:11:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks, will replace the top two commits and queue.  Looks like we\nare getting ready for 'next'?\n"},{"id":"224763","messageId":"52027B1E.3080101@web.de","threadId":"34572","inReplyTo":"7v8v0efrs3.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v4 5/5] rm: delete .gitmodules entry of submodules removed from the work tree","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-08-07T16:51:42Z","receivedAt":"2013-08-07T16:51:42Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Am 06.08.2013 23:11, schrieb Junio C Hamano:\n> Thanks, will replace the top two commits and queue.  Looks like we\n> are getting ready for 'next'?\n\nI hope so, I'm not aware of any open issues.\n"},{"id":"224766","messageId":"20130807182809.GA15123@paksenarrion.iveqy.com","threadId":"34572","inReplyTo":"7v8v0efrs3.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v4 5/5] rm: delete .gitmodules entry of submodules removed from the work tree","fromName":"Fredrik Gustafsson","fromEmail":"iveqy@iveqy.com","sentAt":"2013-08-07T18:28:09Z","receivedAt":"2013-08-07T18:28:09Z","isPatch":true,"sender":{"key":"iveqy@iveqy.com","avatar":"https://avatars.githubusercontent.com/u/761743?v=4"},"body":"On Tue, Aug 06, 2013 at 02:11:56PM -0700, Junio C Hamano wrote:\n> Thanks, will replace the top two commits and queue.  Looks like we\n> are getting ready for 'next'?\n\nI'm a bit curious about if we should move towards a reentrent libgit\n(which would for example make multithreading easier) or not.\n\nIf so, I suggest that this patch only use die() in builtin/. However I\nknow that there's a lot of die() all over libgit today, I'm curious\nabout what direction we're heading.\n\n-- \nMed vänliga hälsningar\nFredrik Gustafsson\n\ntel: 0733-608274\ne-post: iveqy@iveqy.com\n"},{"id":"224828","messageId":"5203D128.7030705@web.de","threadId":"34572","inReplyTo":"20130807182809.GA15123@paksenarrion.iveqy.com","subject":"Re: [PATCH v4 5/5] rm: delete .gitmodules entry of submodules removed from the work tree","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2013-08-08T17:11:04Z","receivedAt":"2013-08-08T17:11:04Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"Am 07.08.2013 20:28, schrieb Fredrik Gustafsson:\n> On Tue, Aug 06, 2013 at 02:11:56PM -0700, Junio C Hamano wrote:\n>> Thanks, will replace the top two commits and queue.  Looks like we\n>> are getting ready for 'next'?\n> \n> I'm a bit curious about if we should move towards a reentrent libgit\n> (which would for example make multithreading easier) or not.\n\nI'm not aware of such an effort in core Git (I always thought that\nlibgit2 is the project doing what you seem to aim for).\n\n> If so, I suggest that this patch only use die() in builtin/. However I\n> know that there's a lot of die() all over libgit today, I'm curious\n> about what direction we're heading.\n\nThe die() calls are just one part. Global variables are another issue,\nwe have memory which is implicitly freed on exit ... so unless we\ncommit ourselves to fix all those issues I see no point in moving the\ndie() calls into builtin/ in my series.\n"},{"id":"224842","messageId":"20130808185513.GC24549@paksenarrion.iveqy.com","threadId":"34572","inReplyTo":"5203D128.7030705@web.de","subject":"Re: [PATCH v4 5/5] rm: delete .gitmodules entry of submodules removed from the work tree","fromName":"Fredrik Gustafsson","fromEmail":"iveqy@iveqy.com","sentAt":"2013-08-08T18:55:13Z","receivedAt":"2013-08-08T18:55:13Z","isPatch":true,"sender":{"key":"iveqy@iveqy.com","avatar":"https://avatars.githubusercontent.com/u/761743?v=4"},"body":"On Thu, Aug 08, 2013 at 07:11:04PM +0200, Jens Lehmann wrote:\n> Am 07.08.2013 20:28, schrieb Fredrik Gustafsson:\n> > On Tue, Aug 06, 2013 at 02:11:56PM -0700, Junio C Hamano wrote:\n> >> Thanks, will replace the top two commits and queue.  Looks like we\n> >> are getting ready for 'next'?\n> > \n> > I'm a bit curious about if we should move towards a reentrent libgit\n> > (which would for example make multithreading easier) or not.\n> \n> I'm not aware of such an effort in core Git (I always thought that\n> libgit2 is the project doing what you seem to aim for).\n> \n> > If so, I suggest that this patch only use die() in builtin/. However I\n> > know that there's a lot of die() all over libgit today, I'm curious\n> > about what direction we're heading.\n> \n> The die() calls are just one part. Global variables are another issue,\n> we have memory which is implicitly freed on exit ... so unless we\n> commit ourselves to fix all those issues I see no point in moving the\n> die() calls into builtin/ in my series.\n\nOkay, thanks for your answer.\n\n-- \nMed vänliga hälsningar\nFredrik Gustafsson\n\ntel: 0733-608274\ne-post: iveqy@iveqy.com\n"}]}