@ -42,6 +42,18 @@ static void show_list(const char *label, struct path_list *list)
@@ -42,6 +42,18 @@ static void show_list(const char *label, struct path_list *list)
}
}
static const char *add_slash(const char *path)
{
int len = strlen(path);
if (path[len - 1] != '/') {
char *with_slash = xmalloc(len + 2);
memcpy(with_slash, path, len);
strcat(with_slash + len, "/");
return with_slash;
}
return path;
}
static struct lock_file lock_file;
int cmd_mv(int argc, const char **argv, char **envp)
@ -50,6 +62,7 @@ int cmd_mv(int argc, const char **argv, char **envp)
@@ -50,6 +62,7 @@ int cmd_mv(int argc, const char **argv, char **envp)
int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
const char *prefix = setup_git_directory();
const char **source, **destination, **dest_path;
enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
struct stat st;
struct path_list overwritten = {NULL, 0, 0, 0};
struct path_list src_for_dst = {NULL, 0, 0, 0};
@ -94,11 +107,14 @@ int cmd_mv(int argc, const char **argv, char **envp)
@@ -94,11 +107,14 @@ int cmd_mv(int argc, const char **argv, char **envp)
usage(builtin_mv_usage);
source = copy_pathspec(prefix, argv + i, count, 0);
modes = xcalloc(count, sizeof(enum update_mode));
dest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0);
if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode))
if (!lstat(dest_path[0], &st) &&
S_ISDIR(st.st_mode)) {
dest_path[0] = add_slash(dest_path[0]);
destination = copy_pathspec(dest_path[0], argv + i, count, 1);
else {
} else {
if (count != 1)
usage(builtin_mv_usage);
destination = dest_path;
@ -114,7 +130,64 @@ int cmd_mv(int argc, const char **argv, char **envp)
@@ -114,7 +130,64 @@ int cmd_mv(int argc, const char **argv, char **envp)
if (lstat(source[i], &st) < 0)
bad = "bad source";
else if (lstat(destination[i], &st) == 0) {
if (S_ISDIR(st.st_mode)) {
const char *dir = source[i], *dest_dir = destination[i];
int first, last, len = strlen(dir);
if (lstat(dest_dir, &st) == 0) {
bad = "cannot move directory over file";
goto next;
}
modes[i] = WORKING_DIRECTORY;
first = cache_name_pos(source[i], len);
if (first >= 0)
die ("Huh? %s/ is in index?", dir);
first = -1 - first;
for (last = first; last < active_nr; last++) {
const char *path = active_cache[last]->name;
if (strncmp(path, dir, len) || path[len] != '/')
break;
}
if (last - first < 1)
bad = "source directory is empty";
else if (!bad) {
int j, dst_len = strlen(dest_dir);
if (last - first > 0) {
source = realloc(source,
(count + last - first)
* sizeof(char *));
destination = realloc(destination,
(count + last - first)
* sizeof(char *));
modes = realloc(modes,
(count + last - first)
* sizeof(enum update_mode));
}
dest_dir = add_slash(dest_dir);
for (j = 0; j < last - first; j++) {
const char *path =
active_cache[first + j]->name;
source[count + j] = path;
destination[count + j] =
prefix_path(dest_dir, dst_len,
path + len);
modes[count + j] = INDEX;
}
count += last - first;
}
goto next;
}
if (!bad && lstat(destination[i], &st) == 0) {
bad = "destination exists";
if (force) {
/*
@ -147,6 +220,7 @@ int cmd_mv(int argc, const char **argv, char **envp)
@@ -147,6 +220,7 @@ int cmd_mv(int argc, const char **argv, char **envp)
path_list_insert(destination[i], &src_for_dst);
}
next:
if (bad) {
if (ignore_errors) {
if (--count > 0) {
@ -157,7 +231,7 @@ int cmd_mv(int argc, const char **argv, char **envp)
@@ -157,7 +231,7 @@ int cmd_mv(int argc, const char **argv, char **envp)
(count - i) * sizeof(char *));
}
} else
die ("Error: %s, source=%s, destination=%s",
die ("%s, source=%s, destination=%s",
bad, source[i], destination[i]);
}
}
@ -166,12 +240,15 @@ int cmd_mv(int argc, const char **argv, char **envp)
@@ -166,12 +240,15 @@ int cmd_mv(int argc, const char **argv, char **envp)
if (show_only || verbose)
printf("Renaming %s to %s\n",
source[i], destination[i]);
if (!show_only &&
if (!show_only && modes[i] != INDEX &&
rename(source[i], destination[i]) < 0 &&
!ignore_errors)
die ("renaming %s failed: %s",
source[i], strerror(errno));
if (modes[i] == WORKING_DIRECTORY)
continue;
if (cache_name_pos(source[i], strlen(source[i])) >= 0) {
path_list_insert(source[i], &deleted);