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
jch
Junio C Hamano 2026-07-24 16:11:59 -07:00
commit 266ec51bf1
2 changed files with 36 additions and 1 deletions

View File

@ -444,6 +444,27 @@ dir_check:
goto act_on_entry;
}

/*
* If we are going to move SRC to DST on disk, DST's leading
* directories must already exist.
*/
if (!(modes[i] & (INDEX | SPARSE | SKIP_WORKTREE_DIR)) &&
!(dst_mode & (SKIP_WORKTREE_DIR | SPARSE))) {
char *dst_dir = xstrdup(dst);
char *slash = strrchr(dst_dir, '/');

if (slash) {
struct stat dir_st;
*slash = '\0';
if (lstat(dst_dir, &dir_st) < 0 && errno == ENOENT) {
free(dst_dir);
bad = _("destination directory does not exist");
goto act_on_entry;
}
}
free(dst_dir);
}

if (ignore_sparse &&
(dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
index_entry_exists(the_repository->index, dst, strlen(dst))) {
@ -549,7 +570,7 @@ remove_entry:
rename(src, dst) < 0) {
if (ignore_errors)
continue;
die_errno(_("renaming '%s' failed"), src);
die_errno(_("renaming '%s' to '%s' failed"), src, dst);
}
if (submodule_gitfiles[i]) {
if (!update_path_in_gitmodules(src, dst))

View File

@ -114,6 +114,20 @@ test_expect_success 'clean up' '
git reset --hard
'

test_expect_success 'moving to non-existent destination parent directory' '
git reset --hard &&
mkdir -p from &&
echo content >from/file &&
git add from/file &&
test_must_fail git mv from/file no-such-dir/file 2>actual &&
test_grep "destination directory does not exist" actual
'

test_expect_success 'mv --dry-run detects non-existent destination parent directory' '
test_must_fail git mv -n from/file no-such-dir/file 2>actual &&
test_grep "destination directory does not exist" actual
'

test_expect_success 'moving to existing untracked target with trailing slash' '
mkdir path1 &&
git mv path0/ path1/ &&