Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 1 | /* |
| 2 | * "git mv" builtin command |
| 3 | * |
| 4 | * Copyright (C) 2006 Johannes Schindelin |
| 5 | */ |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 6 | #include "cache.h" |
| 7 | #include "builtin.h" |
| 8 | #include "dir.h" |
| 9 | #include "cache-tree.h" |
| 10 | #include "path-list.h" |
| 11 | |
| 12 | static const char builtin_mv_usage[] = |
| 13 | "git-mv [-n] [-f] (<source> <destination> | [-k] <source>... <destination>)"; |
| 14 | |
| 15 | static const char **copy_pathspec(const char *prefix, const char **pathspec, |
| 16 | int count, int base_name) |
| 17 | { |
Johannes Schindelin | d78b0f3 | 2006-08-16 10:44:02 +0200 | [diff] [blame] | 18 | int i; |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 19 | const char **result = xmalloc((count + 1) * sizeof(const char *)); |
| 20 | memcpy(result, pathspec, count * sizeof(const char *)); |
| 21 | result[count] = NULL; |
Johannes Schindelin | d78b0f3 | 2006-08-16 10:44:02 +0200 | [diff] [blame] | 22 | for (i = 0; i < count; i++) { |
| 23 | int length = strlen(result[i]); |
| 24 | if (length > 0 && result[i][length - 1] == '/') { |
| 25 | char *without_slash = xmalloc(length); |
| 26 | memcpy(without_slash, result[i], length - 1); |
Johannes Schindelin | 6e17886 | 2006-08-21 22:22:25 +0200 | [diff] [blame] | 27 | without_slash[length - 1] = '\0'; |
Johannes Schindelin | d78b0f3 | 2006-08-16 10:44:02 +0200 | [diff] [blame] | 28 | result[i] = without_slash; |
| 29 | } |
| 30 | if (base_name) { |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 31 | const char *last_slash = strrchr(result[i], '/'); |
| 32 | if (last_slash) |
| 33 | result[i] = last_slash + 1; |
| 34 | } |
| 35 | } |
| 36 | return get_pathspec(prefix, result); |
| 37 | } |
| 38 | |
| 39 | static void show_list(const char *label, struct path_list *list) |
| 40 | { |
| 41 | if (list->nr > 0) { |
| 42 | int i; |
| 43 | printf("%s", label); |
| 44 | for (i = 0; i < list->nr; i++) |
| 45 | printf("%s%s", i > 0 ? ", " : "", list->items[i].path); |
| 46 | putchar('\n'); |
| 47 | } |
| 48 | } |
| 49 | |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 50 | static const char *add_slash(const char *path) |
| 51 | { |
| 52 | int len = strlen(path); |
| 53 | if (path[len - 1] != '/') { |
| 54 | char *with_slash = xmalloc(len + 2); |
| 55 | memcpy(with_slash, path, len); |
Junio C Hamano | 329a304 | 2006-08-08 12:21:33 -0700 | [diff] [blame] | 56 | with_slash[len++] = '/'; |
| 57 | with_slash[len] = 0; |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 58 | return with_slash; |
| 59 | } |
| 60 | return path; |
| 61 | } |
| 62 | |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 63 | static struct lock_file lock_file; |
| 64 | |
Junio C Hamano | 7061cf0 | 2006-07-29 01:54:54 -0700 | [diff] [blame] | 65 | int cmd_mv(int argc, const char **argv, const char *prefix) |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 66 | { |
| 67 | int i, newfd, count; |
| 68 | int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 69 | const char **source, **destination, **dest_path; |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 70 | enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 71 | struct stat st; |
| 72 | struct path_list overwritten = {NULL, 0, 0, 0}; |
| 73 | struct path_list src_for_dst = {NULL, 0, 0, 0}; |
| 74 | struct path_list added = {NULL, 0, 0, 0}; |
| 75 | struct path_list deleted = {NULL, 0, 0, 0}; |
| 76 | struct path_list changed = {NULL, 0, 0, 0}; |
| 77 | |
| 78 | git_config(git_default_config); |
| 79 | |
Junio C Hamano | 40aaae8 | 2006-08-12 01:03:47 -0700 | [diff] [blame] | 80 | newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 81 | if (read_cache() < 0) |
| 82 | die("index file corrupt"); |
| 83 | |
| 84 | for (i = 1; i < argc; i++) { |
| 85 | const char *arg = argv[i]; |
| 86 | |
| 87 | if (arg[0] != '-') |
| 88 | break; |
| 89 | if (!strcmp(arg, "--")) { |
| 90 | i++; |
| 91 | break; |
| 92 | } |
| 93 | if (!strcmp(arg, "-n")) { |
| 94 | show_only = 1; |
| 95 | continue; |
| 96 | } |
| 97 | if (!strcmp(arg, "-f")) { |
| 98 | force = 1; |
| 99 | continue; |
| 100 | } |
| 101 | if (!strcmp(arg, "-k")) { |
| 102 | ignore_errors = 1; |
| 103 | continue; |
| 104 | } |
Junio C Hamano | cba05fa | 2006-08-03 21:55:41 -0700 | [diff] [blame] | 105 | usage(builtin_mv_usage); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 106 | } |
| 107 | count = argc - i - 1; |
| 108 | if (count < 1) |
| 109 | usage(builtin_mv_usage); |
| 110 | |
| 111 | source = copy_pathspec(prefix, argv + i, count, 0); |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 112 | modes = xcalloc(count, sizeof(enum update_mode)); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 113 | dest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0); |
| 114 | |
Johannes Schindelin | c5203bd | 2006-08-18 12:42:39 +0200 | [diff] [blame] | 115 | if (dest_path[0][0] == '\0') |
| 116 | /* special case: "." was normalized to "" */ |
| 117 | destination = copy_pathspec(dest_path[0], argv + i, count, 1); |
| 118 | else if (!lstat(dest_path[0], &st) && |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 119 | S_ISDIR(st.st_mode)) { |
| 120 | dest_path[0] = add_slash(dest_path[0]); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 121 | destination = copy_pathspec(dest_path[0], argv + i, count, 1); |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 122 | } else { |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 123 | if (count != 1) |
| 124 | usage(builtin_mv_usage); |
| 125 | destination = dest_path; |
| 126 | } |
| 127 | |
| 128 | /* Checking */ |
| 129 | for (i = 0; i < count; i++) { |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 130 | const char *src = source[i], *dst = destination[i]; |
| 131 | int length, src_is_dir; |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 132 | const char *bad = NULL; |
| 133 | |
| 134 | if (show_only) |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 135 | printf("Checking rename of '%s' to '%s'\n", src, dst); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 136 | |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 137 | length = strlen(src); |
| 138 | if (lstat(src, &st) < 0) |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 139 | bad = "bad source"; |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 140 | else if (!strncmp(src, dst, length) && |
| 141 | (dst[length] == 0 || dst[length] == '/')) { |
Johannes Schindelin | d78b0f3 | 2006-08-16 10:44:02 +0200 | [diff] [blame] | 142 | bad = "can not move directory into itself"; |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 143 | } else if ((src_is_dir = S_ISDIR(st.st_mode)) |
| 144 | && lstat(dst, &st) == 0) |
| 145 | bad = "cannot move directory over file"; |
| 146 | else if (src_is_dir) { |
Johannes Schindelin | aca085e | 2006-12-03 20:42:47 +0100 | [diff] [blame] | 147 | const char *src_w_slash = add_slash(src); |
| 148 | int len_w_slash = length + 1; |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 149 | int first, last; |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 150 | |
| 151 | modes[i] = WORKING_DIRECTORY; |
| 152 | |
Johannes Schindelin | aca085e | 2006-12-03 20:42:47 +0100 | [diff] [blame] | 153 | first = cache_name_pos(src_w_slash, len_w_slash); |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 154 | if (first >= 0) |
Johannes Schindelin | aca085e | 2006-12-03 20:42:47 +0100 | [diff] [blame] | 155 | die ("Huh? %.*s is in index?", |
| 156 | len_w_slash, src_w_slash); |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 157 | |
| 158 | first = -1 - first; |
| 159 | for (last = first; last < active_nr; last++) { |
| 160 | const char *path = active_cache[last]->name; |
Johannes Schindelin | aca085e | 2006-12-03 20:42:47 +0100 | [diff] [blame] | 161 | if (strncmp(path, src_w_slash, len_w_slash)) |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 162 | break; |
| 163 | } |
Johannes Schindelin | aca085e | 2006-12-03 20:42:47 +0100 | [diff] [blame] | 164 | free((char *)src_w_slash); |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 165 | |
| 166 | if (last - first < 1) |
| 167 | bad = "source directory is empty"; |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 168 | else { |
| 169 | int j, dst_len; |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 170 | |
| 171 | if (last - first > 0) { |
Jonas Fonseca | 83572c1 | 2006-08-26 16:16:18 +0200 | [diff] [blame] | 172 | source = xrealloc(source, |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 173 | (count + last - first) |
| 174 | * sizeof(char *)); |
Jonas Fonseca | 83572c1 | 2006-08-26 16:16:18 +0200 | [diff] [blame] | 175 | destination = xrealloc(destination, |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 176 | (count + last - first) |
| 177 | * sizeof(char *)); |
Jonas Fonseca | 83572c1 | 2006-08-26 16:16:18 +0200 | [diff] [blame] | 178 | modes = xrealloc(modes, |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 179 | (count + last - first) |
| 180 | * sizeof(enum update_mode)); |
| 181 | } |
| 182 | |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 183 | dst = add_slash(dst); |
| 184 | dst_len = strlen(dst) - 1; |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 185 | |
| 186 | for (j = 0; j < last - first; j++) { |
| 187 | const char *path = |
| 188 | active_cache[first + j]->name; |
| 189 | source[count + j] = path; |
| 190 | destination[count + j] = |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 191 | prefix_path(dst, dst_len, |
| 192 | path + length); |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 193 | modes[count + j] = INDEX; |
| 194 | } |
| 195 | count += last - first; |
| 196 | } |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 197 | } else if (lstat(dst, &st) == 0) { |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 198 | bad = "destination exists"; |
| 199 | if (force) { |
| 200 | /* |
| 201 | * only files can overwrite each other: |
| 202 | * check both source and destination |
| 203 | */ |
| 204 | if (S_ISREG(st.st_mode)) { |
| 205 | fprintf(stderr, "Warning: %s;" |
| 206 | " will overwrite!\n", |
| 207 | bad); |
| 208 | bad = NULL; |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 209 | path_list_insert(dst, &overwritten); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 210 | } else |
| 211 | bad = "Cannot overwrite"; |
| 212 | } |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 213 | } else if (cache_name_pos(src, length) < 0) |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 214 | bad = "not under version control"; |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 215 | else if (path_list_has_path(&src_for_dst, dst)) |
| 216 | bad = "multiple sources for the same target"; |
| 217 | else |
| 218 | path_list_insert(dst, &src_for_dst); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 219 | |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 220 | if (bad) { |
| 221 | if (ignore_errors) { |
| 222 | if (--count > 0) { |
| 223 | memmove(source + i, source + i + 1, |
| 224 | (count - i) * sizeof(char *)); |
| 225 | memmove(destination + i, |
| 226 | destination + i + 1, |
| 227 | (count - i) * sizeof(char *)); |
| 228 | } |
| 229 | } else |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 230 | die ("%s, source=%s, destination=%s", |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 231 | bad, src, dst); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 232 | } |
| 233 | } |
| 234 | |
| 235 | for (i = 0; i < count; i++) { |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 236 | const char *src = source[i], *dst = destination[i]; |
| 237 | enum update_mode mode = modes[i]; |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 238 | if (show_only || verbose) |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 239 | printf("Renaming %s to %s\n", src, dst); |
| 240 | if (!show_only && mode != INDEX && |
| 241 | rename(src, dst) < 0 && !ignore_errors) |
| 242 | die ("renaming %s failed: %s", src, strerror(errno)); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 243 | |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 244 | if (mode == WORKING_DIRECTORY) |
Johannes Schindelin | ac64a72 | 2006-07-26 19:47:54 +0200 | [diff] [blame] | 245 | continue; |
| 246 | |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 247 | if (cache_name_pos(src, strlen(src)) >= 0) { |
| 248 | path_list_insert(src, &deleted); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 249 | |
| 250 | /* destination can be a directory with 1 file inside */ |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 251 | if (path_list_has_path(&overwritten, dst)) |
| 252 | path_list_insert(dst, &changed); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 253 | else |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 254 | path_list_insert(dst, &added); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 255 | } else |
Johannes Schindelin | 60a6bf5 | 2006-08-19 16:52:21 +0200 | [diff] [blame] | 256 | path_list_insert(dst, &added); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 257 | } |
| 258 | |
| 259 | if (show_only) { |
| 260 | show_list("Changed : ", &changed); |
| 261 | show_list("Adding : ", &added); |
| 262 | show_list("Deleting : ", &deleted); |
| 263 | } else { |
| 264 | for (i = 0; i < changed.nr; i++) { |
| 265 | const char *path = changed.items[i].path; |
Pierre Habouzit | d828f6d | 2006-08-23 12:39:13 +0200 | [diff] [blame] | 266 | int j = cache_name_pos(path, strlen(path)); |
| 267 | struct cache_entry *ce = active_cache[j]; |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 268 | |
Pierre Habouzit | d828f6d | 2006-08-23 12:39:13 +0200 | [diff] [blame] | 269 | if (j < 0) |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 270 | die ("Huh? Cache entry for %s unknown?", path); |
| 271 | refresh_cache_entry(ce, 0); |
| 272 | } |
| 273 | |
| 274 | for (i = 0; i < added.nr; i++) { |
| 275 | const char *path = added.items[i].path; |
| 276 | add_file_to_index(path, verbose); |
| 277 | } |
| 278 | |
| 279 | for (i = 0; i < deleted.nr; i++) { |
| 280 | const char *path = deleted.items[i].path; |
| 281 | remove_file_from_cache(path); |
Junio C Hamano | 4fddf57 | 2006-10-01 22:22:07 -0700 | [diff] [blame] | 282 | cache_tree_invalidate_path(active_cache_tree, path); |
Johannes Schindelin | 11be42a | 2006-07-26 03:52:35 +0200 | [diff] [blame] | 283 | } |
| 284 | |
| 285 | if (active_cache_changed) { |
| 286 | if (write_cache(newfd, active_cache, active_nr) || |
| 287 | close(newfd) || |
| 288 | commit_lock_file(&lock_file)) |
| 289 | die("Unable to write new index file"); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | return 0; |
| 294 | } |