git mv: do not keep slash in `git mv dir non-existing-dir/`

When calling `rename("dir", "non-existing-dir/")` on Linux, it silently succeeds, stripping the trailing slash of the second argument. This is all good and dandy but this behavior disagrees with the specs at http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html that state clearly regarding the 2nd parameter (called `new`): If the `new` argument does not resolve to an existing directory entry for a file of type directory and the `new` argument contains at least one non- <slash> character and ends with one or more trailing <slash> characters after all symbolic links have been processed, `rename()` shall fail. Of course, we would like `git mv dir non-existing-dir/` to succeed (and rename the directory "dir" to "non-existing-dir"). Let's be extra careful to remove the trailing slash in that case. This lets t7001-mv.sh pass in Bash on Windows. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Aug 5, 2016 at 16:41 UTC 189d035e67b1f8cdbb1dbd388efd1b7434f34b04
1 file changed +7 -4
builtin/mv.c
+7 -4
@@ -104,7 +104,7 @@ static int index_range_of_same_dir(const char *src, int length,
104
105 int cmd_mv(int argc, const char **argv, const char *prefix)
106 {
107 - int i, gitmodules_modified = 0;
107 + int i, flags, gitmodules_modified = 0;
108 int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
109 struct option builtin_mv_options[] = {
110 OPT__VERBOSE(&verbose, N_("be verbose")),
@@ -134,10 +134,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
134 modes = xcalloc(argc, sizeof(enum update_mode));
135 /*
136 * Keep trailing slash, needed to let
137 - * "git mv file no-such-dir/" error out.
137 + * "git mv file no-such-dir/" error out, except in the case
138 + * "git mv directory no-such-dir/".
139 */
139 - dest_path = internal_copy_pathspec(prefix, argv + argc, 1,
140 - KEEP_TRAILING_SLASH);
140 + flags = KEEP_TRAILING_SLASH;
141 + if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1]))
142 + flags = 0;
143 + dest_path = internal_copy_pathspec(prefix, argv + argc, 1, flags);
144 submodule_gitfile = xcalloc(argc, sizeof(char *));
145
146 if (dest_path[0][0] == '\0')