Inspired by "mkdir -p", this patch allows specifying a "-p" or "--parents" flag which will create all non-existent directories in the destination path before renaming the file. This allows the user to not have to run two commands to move files to a new directory. Signed-off-by: Hugo Sales <hugo@xxxxxxx> --- builtin/mv.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/builtin/mv.c b/builtin/mv.c index c596515ad0..5d64d86179 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -168,7 +168,7 @@ static int empty_dir_has_sparse_contents(const char *name) int cmd_mv(int argc, const char **argv, const char *prefix) { int i, flags, gitmodules_modified = 0; - int verbose = 0, show_only = 0, force = 0, ignore_errors = 0, ignore_sparse = 0; + int verbose = 0, show_only = 0, force = 0, ignore_errors = 0, ignore_sparse = 0, create_parents = 0; struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT__DRY_RUN(&show_only, N_("dry run")), @@ -176,6 +176,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) PARSE_OPT_NOCOMPLETE), OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_BOOL(0, "sparse", &ignore_sparse, N_("allow updating entries outside of the sparse-checkout cone")), + OPT_BOOL('p', "parents", &create_parents, N_("create missing parent directories")), OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; @@ -220,8 +221,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); - else if (!lstat(dest_path[0], &st) && - S_ISDIR(st.st_mode)) { + else if (create_parents || + (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode))) { destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME); } else { if (!path_in_sparse_checkout(dst_w_slash, &the_index) && @@ -381,7 +382,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) bad = _("multiple sources for the same target"); goto act_on_entry; } - if (is_dir_sep(dst[strlen(dst) - 1])) { + + if (!create_parents && is_dir_sep(dst[strlen(dst) - 1])) { bad = _("destination directory does not exist"); goto act_on_entry; } @@ -459,11 +461,18 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (show_only) continue; if (!(mode & (INDEX | SPARSE | SKIP_WORKTREE_DIR)) && - !(dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) && - rename(src, dst) < 0) { - if (ignore_errors) - continue; - die_errno(_("renaming '%s' failed"), src); + !(dst_mode & (SKIP_WORKTREE_DIR | SPARSE))) { + if (create_parents && safe_create_leading_directories_const(dst) < 0) { + if (ignore_errors) + continue; + die_errno(_("creating parent directories for '%s' failed"), dst); + } + + if (rename(src, dst) < 0) { + if (ignore_errors) + continue; + die_errno(_("renaming '%s' failed"), src); + } } if (submodule_gitfile[i]) { if (!update_path_in_gitmodules(src, dst)) -- 2.42.0