Currently the attempt to use "git mv" on a submodule errors out with:
  fatal: source directory is empty, source=<src>, destination=<dest>
The reason is that mv searches for the submodule with a trailing slash in
the index, which it doesn't find (because it is stored without a trailing
slash). As it doesn't find any index entries inside the submodule it
claims the directory would be empty even though it isn't.

Fix that by searching for the name without a trailing slash and continue
if it is a submodule. Then rename() will move the submodule work tree just
like it moves a file.

Signed-off-by: Jens Lehmann <jens.lehm...@web.de>
---
 builtin/mv.c  | 99 +++++++++++++++++++++++++++++++----------------------------
 t/t7001-mv.sh | 34 ++++++++++++++++++++
 2 files changed, 86 insertions(+), 47 deletions(-)

diff --git a/builtin/mv.c b/builtin/mv.c
index 034fec9..361028d 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -117,55 +117,60 @@ int cmd_mv(int argc, const char **argv, const char 
*prefix)
                                && lstat(dst, &st) == 0)
                        bad = _("cannot move directory over file");
                else if (src_is_dir) {
-                       const char *src_w_slash = add_slash(src);
-                       int len_w_slash = length + 1;
-                       int first, last;
-
-                       modes[i] = WORKING_DIRECTORY;
-
-                       first = cache_name_pos(src_w_slash, len_w_slash);
-                       if (first >= 0)
-                               die (_("Huh? %.*s is in index?"),
-                                               len_w_slash, src_w_slash);
-
-                       first = -1 - first;
-                       for (last = first; last < active_nr; last++) {
-                               const char *path = active_cache[last]->name;
-                               if (strncmp(path, src_w_slash, len_w_slash))
-                                       break;
-                       }
-                       free((char *)src_w_slash);
-
-                       if (last - first < 1)
-                               bad = _("source directory is empty");
-                       else {
-                               int j, dst_len;
-
-                               if (last - first > 0) {
-                                       source = xrealloc(source,
-                                                       (argc + last - first)
-                                                       * sizeof(char *));
-                                       destination = xrealloc(destination,
-                                                       (argc + last - first)
-                                                       * sizeof(char *));
-                                       modes = xrealloc(modes,
-                                                       (argc + last - first)
-                                                       * sizeof(enum 
update_mode));
+                       int first = cache_name_pos(src, length);
+                       if (first >= 0) {
+                               if (!S_ISGITLINK(active_cache[first]->ce_mode))
+                                       die (_("Huh? Directory %s is in index 
and no submodule?"), src);
+                       } else {
+                               const char *src_w_slash = add_slash(src);
+                               int last, len_w_slash = length + 1;
+
+                               modes[i] = WORKING_DIRECTORY;
+
+                               first = cache_name_pos(src_w_slash, 
len_w_slash);
+                               if (first >= 0)
+                                       die (_("Huh? %.*s is in index?"),
+                                                       len_w_slash, 
src_w_slash);
+
+                               first = -1 - first;
+                               for (last = first; last < active_nr; last++) {
+                                       const char *path = 
active_cache[last]->name;
+                                       if (strncmp(path, src_w_slash, 
len_w_slash))
+                                               break;
                                }
-
-                               dst = add_slash(dst);
-                               dst_len = strlen(dst);
-
-                               for (j = 0; j < last - first; j++) {
-                                       const char *path =
-                                               active_cache[first + j]->name;
-                                       source[argc + j] = path;
-                                       destination[argc + j] =
-                                               prefix_path(dst, dst_len,
-                                                       path + length + 1);
-                                       modes[argc + j] = INDEX;
+                               free((char *)src_w_slash);
+
+                               if (last - first < 1)
+                                       bad = _("source directory is empty");
+                               else {
+                                       int j, dst_len;
+
+                                       if (last - first > 0) {
+                                               source = xrealloc(source,
+                                                               (argc + last - 
first)
+                                                               * sizeof(char 
*));
+                                               destination = 
xrealloc(destination,
+                                                               (argc + last - 
first)
+                                                               * sizeof(char 
*));
+                                               modes = xrealloc(modes,
+                                                               (argc + last - 
first)
+                                                               * sizeof(enum 
update_mode));
+                                       }
+
+                                       dst = add_slash(dst);
+                                       dst_len = strlen(dst);
+
+                                       for (j = 0; j < last - first; j++) {
+                                               const char *path =
+                                                       active_cache[first + 
j]->name;
+                                               source[argc + j] = path;
+                                               destination[argc + j] =
+                                                       prefix_path(dst, 
dst_len,
+                                                               path + length + 
1);
+                                               modes[argc + j] = INDEX;
+                                       }
+                                       argc += last - first;
                                }
-                               argc += last - first;
                        }
                } else if (cache_name_pos(src, length) < 0)
                        bad = _("not under version control");
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index a845b15..4c57f61 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -255,4 +255,38 @@ test_expect_success SYMLINKS 'git mv should overwrite file 
with a symlink' '

 rm -f moved symlink

+test_expect_success 'setup submodule' '
+       git commit -m initial &&
+       git reset --hard &&
+       git submodule add ./. sub &&
+       echo content >file &&
+       git add file &&
+       git commit -m "added sub and file"
+'
+
+test_expect_success 'git mv cannot move a submodule in a file' '
+       test_must_fail git mv sub file
+'
+
+test_expect_success 'git mv moves a submodule with a .git directory and no 
.gitmodules' '
+       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       git rm .gitmodules &&
+       (
+               cd sub &&
+               rm -f .git &&
+               cp -a ../.git/modules/sub .git &&
+               GIT_WORK_TREE=. git config --unset core.worktree
+       ) &&
+       mkdir mod &&
+       git mv sub mod/sub &&
+       ! test -e sub &&
+       [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+       (
+               cd mod/sub &&
+               git status
+       ) &&
+       git update-index --refresh &&
+       git diff-files --quiet
+'
+
 test_done
-- 
1.8.2.377.g1bdb7d0


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to