Skip to content

Commit 1150246

Browse files
jlehmanngitster
authored andcommitted
mv: move submodules together with their work trees
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4838c81 commit 1150246

File tree

2 files changed

+86
-47
lines changed

2 files changed

+86
-47
lines changed

builtin/mv.c

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -118,55 +118,60 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
118118
&& lstat(dst, &st) == 0)
119119
bad = _("cannot move directory over file");
120120
else if (src_is_dir) {
121-
const char *src_w_slash = add_slash(src);
122-
int len_w_slash = length + 1;
123-
int first, last;
124-
125-
modes[i] = WORKING_DIRECTORY;
126-
127-
first = cache_name_pos(src_w_slash, len_w_slash);
128-
if (first >= 0)
129-
die (_("Huh? %.*s is in index?"),
130-
len_w_slash, src_w_slash);
131-
132-
first = -1 - first;
133-
for (last = first; last < active_nr; last++) {
134-
const char *path = active_cache[last]->name;
135-
if (strncmp(path, src_w_slash, len_w_slash))
136-
break;
137-
}
138-
free((char *)src_w_slash);
139-
140-
if (last - first < 1)
141-
bad = _("source directory is empty");
142-
else {
143-
int j, dst_len;
144-
145-
if (last - first > 0) {
146-
source = xrealloc(source,
147-
(argc + last - first)
148-
* sizeof(char *));
149-
destination = xrealloc(destination,
150-
(argc + last - first)
151-
* sizeof(char *));
152-
modes = xrealloc(modes,
153-
(argc + last - first)
154-
* sizeof(enum update_mode));
121+
int first = cache_name_pos(src, length);
122+
if (first >= 0) {
123+
if (!S_ISGITLINK(active_cache[first]->ce_mode))
124+
die (_("Huh? Directory %s is in index and no submodule?"), src);
125+
} else {
126+
const char *src_w_slash = add_slash(src);
127+
int last, len_w_slash = length + 1;
128+
129+
modes[i] = WORKING_DIRECTORY;
130+
131+
first = cache_name_pos(src_w_slash, len_w_slash);
132+
if (first >= 0)
133+
die (_("Huh? %.*s is in index?"),
134+
len_w_slash, src_w_slash);
135+
136+
first = -1 - first;
137+
for (last = first; last < active_nr; last++) {
138+
const char *path = active_cache[last]->name;
139+
if (strncmp(path, src_w_slash, len_w_slash))
140+
break;
155141
}
156-
157-
dst = add_slash(dst);
158-
dst_len = strlen(dst);
159-
160-
for (j = 0; j < last - first; j++) {
161-
const char *path =
162-
active_cache[first + j]->name;
163-
source[argc + j] = path;
164-
destination[argc + j] =
165-
prefix_path(dst, dst_len,
166-
path + length + 1);
167-
modes[argc + j] = INDEX;
142+
free((char *)src_w_slash);
143+
144+
if (last - first < 1)
145+
bad = _("source directory is empty");
146+
else {
147+
int j, dst_len;
148+
149+
if (last - first > 0) {
150+
source = xrealloc(source,
151+
(argc + last - first)
152+
* sizeof(char *));
153+
destination = xrealloc(destination,
154+
(argc + last - first)
155+
* sizeof(char *));
156+
modes = xrealloc(modes,
157+
(argc + last - first)
158+
* sizeof(enum update_mode));
159+
}
160+
161+
dst = add_slash(dst);
162+
dst_len = strlen(dst);
163+
164+
for (j = 0; j < last - first; j++) {
165+
const char *path =
166+
active_cache[first + j]->name;
167+
source[argc + j] = path;
168+
destination[argc + j] =
169+
prefix_path(dst, dst_len,
170+
path + length + 1);
171+
modes[argc + j] = INDEX;
172+
}
173+
argc += last - first;
168174
}
169-
argc += last - first;
170175
}
171176
} else if (cache_name_pos(src, length) < 0)
172177
bad = _("not under version control");

t/t7001-mv.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,38 @@ test_expect_success SYMLINKS 'check moved symlink' '
259259

260260
rm -f moved symlink
261261

262+
test_expect_success 'setup submodule' '
263+
git commit -m initial &&
264+
git reset --hard &&
265+
git submodule add ./. sub &&
266+
echo content >file &&
267+
git add file &&
268+
git commit -m "added sub and file"
269+
'
270+
271+
test_expect_success 'git mv cannot move a submodule in a file' '
272+
test_must_fail git mv sub file
273+
'
274+
275+
test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '
276+
entry="$(git ls-files --stage sub | cut -f 1)" &&
277+
git rm .gitmodules &&
278+
(
279+
cd sub &&
280+
rm -f .git &&
281+
cp -a ../.git/modules/sub .git &&
282+
GIT_WORK_TREE=. git config --unset core.worktree
283+
) &&
284+
mkdir mod &&
285+
git mv sub mod/sub &&
286+
! test -e sub &&
287+
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
288+
(
289+
cd mod/sub &&
290+
git status
291+
) &&
292+
git update-index --refresh &&
293+
git diff-files --quiet
294+
'
295+
262296
test_done

0 commit comments

Comments
 (0)