Skip to content

Commit 266ec51

Browse files
committed
Merge branch 'lo/mv-missing-dest-dir-check' into jch
'git mv' has been updated to check for a missing destination leading directory during the checking phase, allowing 'git mv -n' to report the failure. The error message when the rename(2) syscall fails has also been improved to name both the source and the destination. * lo/mv-missing-dest-dir-check: mv: check for missing destination directory before renaming mv: name both source and destination when rename fails
2 parents b1ada1c + f9f00ae commit 266ec51

2 files changed

Lines changed: 36 additions & 1 deletion

File tree

builtin/mv.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,27 @@ int cmd_mv(int argc,
444444
goto act_on_entry;
445445
}
446446

447+
/*
448+
* If we are going to move SRC to DST on disk, DST's leading
449+
* directories must already exist.
450+
*/
451+
if (!(modes[i] & (INDEX | SPARSE | SKIP_WORKTREE_DIR)) &&
452+
!(dst_mode & (SKIP_WORKTREE_DIR | SPARSE))) {
453+
char *dst_dir = xstrdup(dst);
454+
char *slash = strrchr(dst_dir, '/');
455+
456+
if (slash) {
457+
struct stat dir_st;
458+
*slash = '\0';
459+
if (lstat(dst_dir, &dir_st) < 0 && errno == ENOENT) {
460+
free(dst_dir);
461+
bad = _("destination directory does not exist");
462+
goto act_on_entry;
463+
}
464+
}
465+
free(dst_dir);
466+
}
467+
447468
if (ignore_sparse &&
448469
(dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
449470
index_entry_exists(the_repository->index, dst, strlen(dst))) {
@@ -549,7 +570,7 @@ int cmd_mv(int argc,
549570
rename(src, dst) < 0) {
550571
if (ignore_errors)
551572
continue;
552-
die_errno(_("renaming '%s' failed"), src);
573+
die_errno(_("renaming '%s' to '%s' failed"), src, dst);
553574
}
554575
if (submodule_gitfiles[i]) {
555576
if (!update_path_in_gitmodules(src, dst))

t/t7001-mv.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,20 @@ test_expect_success 'clean up' '
114114
git reset --hard
115115
'
116116

117+
test_expect_success 'moving to non-existent destination parent directory' '
118+
git reset --hard &&
119+
mkdir -p from &&
120+
echo content >from/file &&
121+
git add from/file &&
122+
test_must_fail git mv from/file no-such-dir/file 2>actual &&
123+
test_grep "destination directory does not exist" actual
124+
'
125+
126+
test_expect_success 'mv --dry-run detects non-existent destination parent directory' '
127+
test_must_fail git mv -n from/file no-such-dir/file 2>actual &&
128+
test_grep "destination directory does not exist" actual
129+
'
130+
117131
test_expect_success 'moving to existing untracked target with trailing slash' '
118132
mkdir path1 &&
119133
git mv path0/ path1/ &&

0 commit comments

Comments
 (0)