Skip to content

Commit ada6ebb

Browse files
committed
Merge branch 'mm/mv-file-to-no-such-dir-with-slash' into maint
"git mv A B/", when B does not exist as a directory, should error out, but it didn't. * mm/mv-file-to-no-such-dir-with-slash: mv: let 'git mv file no-such-dir/' error out on Windows, too mv: let 'git mv file no-such-dir/' error out
2 parents be941a2 + a893346 commit ada6ebb

File tree

2 files changed

+47
-7
lines changed

2 files changed

+47
-7
lines changed

builtin/mv.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ static const char * const builtin_mv_usage[] = {
1616
NULL
1717
};
1818

19+
#define DUP_BASENAME 1
20+
#define KEEP_TRAILING_SLASH 2
21+
1922
static const char **internal_copy_pathspec(const char *prefix,
2023
const char **pathspec,
21-
int count, int base_name)
24+
int count, unsigned flags)
2225
{
2326
int i;
2427
const char **result = xmalloc((count + 1) * sizeof(const char *));
@@ -27,11 +30,12 @@ static const char **internal_copy_pathspec(const char *prefix,
2730
for (i = 0; i < count; i++) {
2831
int length = strlen(result[i]);
2932
int to_copy = length;
30-
while (to_copy > 0 && is_dir_sep(result[i][to_copy - 1]))
33+
while (!(flags & KEEP_TRAILING_SLASH) &&
34+
to_copy > 0 && is_dir_sep(result[i][to_copy - 1]))
3135
to_copy--;
32-
if (to_copy != length || base_name) {
36+
if (to_copy != length || flags & DUP_BASENAME) {
3337
char *it = xmemdupz(result[i], to_copy);
34-
if (base_name) {
38+
if (flags & DUP_BASENAME) {
3539
result[i] = xstrdup(basename(it));
3640
free(it);
3741
} else
@@ -87,16 +91,21 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
8791

8892
source = internal_copy_pathspec(prefix, argv, argc, 0);
8993
modes = xcalloc(argc, sizeof(enum update_mode));
90-
dest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0);
94+
/*
95+
* Keep trailing slash, needed to let
96+
* "git mv file no-such-dir/" error out.
97+
*/
98+
dest_path = internal_copy_pathspec(prefix, argv + argc, 1,
99+
KEEP_TRAILING_SLASH);
91100
submodule_gitfile = xcalloc(argc, sizeof(char *));
92101

93102
if (dest_path[0][0] == '\0')
94103
/* special case: "." was normalized to "" */
95-
destination = internal_copy_pathspec(dest_path[0], argv, argc, 1);
104+
destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
96105
else if (!lstat(dest_path[0], &st) &&
97106
S_ISDIR(st.st_mode)) {
98107
dest_path[0] = add_slash(dest_path[0]);
99-
destination = internal_copy_pathspec(dest_path[0], argv, argc, 1);
108+
destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
100109
} else {
101110
if (argc != 1)
102111
die("destination '%s' is not a directory", dest_path[0]);
@@ -205,6 +214,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
205214
}
206215
} else if (string_list_has_string(&src_for_dst, dst))
207216
bad = _("multiple sources for the same target");
217+
else if (is_dir_sep(dst[strlen(dst) - 1]))
218+
bad = _("destination directory does not exist");
208219
else
209220
string_list_insert(&src_for_dst, dst);
210221

t/t7001-mv.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,35 @@ test_expect_success \
7070
rm -f idontexist untracked1 untracked2 \
7171
path0/idontexist path0/untracked1 path0/untracked2 \
7272
.git/index.lock
73+
rmdir path1
74+
75+
test_expect_success \
76+
'moving to absent target with trailing slash' \
77+
'test_must_fail git mv path0/COPYING no-such-dir/ &&
78+
test_must_fail git mv path0/COPYING no-such-dir// &&
79+
git mv path0/ no-such-dir/ &&
80+
test_path_is_dir no-such-dir'
81+
82+
test_expect_success \
83+
'clean up' \
84+
'git reset --hard'
85+
86+
test_expect_success \
87+
'moving to existing untracked target with trailing slash' \
88+
'mkdir path1 &&
89+
git mv path0/ path1/ &&
90+
test_path_is_dir path1/path0/'
91+
92+
test_expect_success \
93+
'moving to existing tracked target with trailing slash' \
94+
'mkdir path2 &&
95+
>path2/file && git add path2/file &&
96+
git mv path1/path0/ path2/ &&
97+
test_path_is_dir path2/path0/'
98+
99+
test_expect_success \
100+
'clean up' \
101+
'git reset --hard'
73102

74103
test_expect_success \
75104
'adding another file' \

0 commit comments

Comments
 (0)