Code

Merge branch 'maint'
[git.git] / builtin-mv.c
1 /*
2  * "git mv" builtin command
3  *
4  * Copyright (C) 2006 Johannes Schindelin
5  */
6 #include "cache.h"
7 #include "builtin.h"
8 #include "dir.h"
9 #include "cache-tree.h"
10 #include "path-list.h"
11 #include "parse-options.h"
13 static const char * const builtin_mv_usage[] = {
14         "git-mv [options] <source>... <destination>",
15         NULL
16 };
18 static const char **copy_pathspec(const char *prefix, const char **pathspec,
19                                   int count, int base_name)
20 {
21         int i;
22         int len = prefix ? strlen(prefix) : 0;
23         const char **result = xmalloc((count + 1) * sizeof(const char *));
24         memcpy(result, pathspec, count * sizeof(const char *));
25         result[count] = NULL;
26         for (i = 0; i < count; i++) {
27                 int length = strlen(result[i]);
28                 if (length > 0 && result[i][length - 1] == '/') {
29                         result[i] = xmemdupz(result[i], length - 1);
30                 }
31                 if (base_name) {
32                         const char *last_slash = strrchr(result[i], '/');
33                         if (last_slash)
34                                 result[i] = last_slash + 1;
35                 }
36                 result[i] = prefix_path(prefix, len, result[i]);
37                 if (!result[i])
38                         exit(1); /* error already given */
39         }
40         return result;
41 }
43 static void show_list(const char *label, struct path_list *list)
44 {
45         if (list->nr > 0) {
46                 int i;
47                 printf("%s", label);
48                 for (i = 0; i < list->nr; i++)
49                         printf("%s%s", i > 0 ? ", " : "", list->items[i].path);
50                 putchar('\n');
51         }
52 }
54 static const char *add_slash(const char *path)
55 {
56         int len = strlen(path);
57         if (path[len - 1] != '/') {
58                 char *with_slash = xmalloc(len + 2);
59                 memcpy(with_slash, path, len);
60                 with_slash[len++] = '/';
61                 with_slash[len] = 0;
62                 return with_slash;
63         }
64         return path;
65 }
67 static struct lock_file lock_file;
69 int cmd_mv(int argc, const char **argv, const char *prefix)
70 {
71         int i, newfd;
72         int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
73         struct option builtin_mv_options[] = {
74                 OPT__DRY_RUN(&show_only),
75                 OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"),
76                 OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
77                 OPT_END(),
78         };
79         const char **source, **destination, **dest_path;
80         enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
81         struct stat st;
82         struct path_list overwritten = {NULL, 0, 0, 0};
83         struct path_list src_for_dst = {NULL, 0, 0, 0};
84         struct path_list added = {NULL, 0, 0, 0};
85         struct path_list deleted = {NULL, 0, 0, 0};
86         struct path_list changed = {NULL, 0, 0, 0};
88         git_config(git_default_config);
90         newfd = hold_locked_index(&lock_file, 1);
91         if (read_cache() < 0)
92                 die("index file corrupt");
94         argc = parse_options(argc, argv, builtin_mv_options, builtin_mv_usage, 0);
95         if (--argc < 1)
96                 usage_with_options(builtin_mv_usage, builtin_mv_options);
98         source = copy_pathspec(prefix, argv, argc, 0);
99         modes = xcalloc(argc, sizeof(enum update_mode));
100         dest_path = copy_pathspec(prefix, argv + argc, 1, 0);
102         if (dest_path[0][0] == '\0')
103                 /* special case: "." was normalized to "" */
104                 destination = copy_pathspec(dest_path[0], argv, argc, 1);
105         else if (!lstat(dest_path[0], &st) &&
106                         S_ISDIR(st.st_mode)) {
107                 dest_path[0] = add_slash(dest_path[0]);
108                 destination = copy_pathspec(dest_path[0], argv, argc, 1);
109         } else {
110                 if (argc != 1)
111                         usage_with_options(builtin_mv_usage, builtin_mv_options);
112                 destination = dest_path;
113         }
115         /* Checking */
116         for (i = 0; i < argc; i++) {
117                 const char *src = source[i], *dst = destination[i];
118                 int length, src_is_dir;
119                 const char *bad = NULL;
121                 if (show_only)
122                         printf("Checking rename of '%s' to '%s'\n", src, dst);
124                 length = strlen(src);
125                 if (lstat(src, &st) < 0)
126                         bad = "bad source";
127                 else if (!strncmp(src, dst, length) &&
128                                 (dst[length] == 0 || dst[length] == '/')) {
129                         bad = "can not move directory into itself";
130                 } else if ((src_is_dir = S_ISDIR(st.st_mode))
131                                 && lstat(dst, &st) == 0)
132                         bad = "cannot move directory over file";
133                 else if (src_is_dir) {
134                         const char *src_w_slash = add_slash(src);
135                         int len_w_slash = length + 1;
136                         int first, last;
138                         modes[i] = WORKING_DIRECTORY;
140                         first = cache_name_pos(src_w_slash, len_w_slash);
141                         if (first >= 0)
142                                 die ("Huh? %.*s is in index?",
143                                                 len_w_slash, src_w_slash);
145                         first = -1 - first;
146                         for (last = first; last < active_nr; last++) {
147                                 const char *path = active_cache[last]->name;
148                                 if (strncmp(path, src_w_slash, len_w_slash))
149                                         break;
150                         }
151                         free((char *)src_w_slash);
153                         if (last - first < 1)
154                                 bad = "source directory is empty";
155                         else {
156                                 int j, dst_len;
158                                 if (last - first > 0) {
159                                         source = xrealloc(source,
160                                                         (argc + last - first)
161                                                         * sizeof(char *));
162                                         destination = xrealloc(destination,
163                                                         (argc + last - first)
164                                                         * sizeof(char *));
165                                         modes = xrealloc(modes,
166                                                         (argc + last - first)
167                                                         * sizeof(enum update_mode));
168                                 }
170                                 dst = add_slash(dst);
171                                 dst_len = strlen(dst);
173                                 for (j = 0; j < last - first; j++) {
174                                         const char *path =
175                                                 active_cache[first + j]->name;
176                                         source[argc + j] = path;
177                                         destination[argc + j] =
178                                                 prefix_path(dst, dst_len,
179                                                         path + length + 1);
180                                         modes[argc + j] = INDEX;
181                                 }
182                                 argc += last - first;
183                         }
184                 } else if (lstat(dst, &st) == 0) {
185                         bad = "destination exists";
186                         if (force) {
187                                 /*
188                                  * only files can overwrite each other:
189                                  * check both source and destination
190                                  */
191                                 if (S_ISREG(st.st_mode)) {
192                                         fprintf(stderr, "Warning: %s;"
193                                                         " will overwrite!\n",
194                                                         bad);
195                                         bad = NULL;
196                                         path_list_insert(dst, &overwritten);
197                                 } else
198                                         bad = "Cannot overwrite";
199                         }
200                 } else if (cache_name_pos(src, length) < 0)
201                         bad = "not under version control";
202                 else if (path_list_has_path(&src_for_dst, dst))
203                         bad = "multiple sources for the same target";
204                 else
205                         path_list_insert(dst, &src_for_dst);
207                 if (bad) {
208                         if (ignore_errors) {
209                                 if (--argc > 0) {
210                                         memmove(source + i, source + i + 1,
211                                                 (argc - i) * sizeof(char *));
212                                         memmove(destination + i,
213                                                 destination + i + 1,
214                                                 (argc - i) * sizeof(char *));
215                                 }
216                         } else
217                                 die ("%s, source=%s, destination=%s",
218                                      bad, src, dst);
219                 }
220         }
222         for (i = 0; i < argc; i++) {
223                 const char *src = source[i], *dst = destination[i];
224                 enum update_mode mode = modes[i];
225                 if (show_only || verbose)
226                         printf("Renaming %s to %s\n", src, dst);
227                 if (!show_only && mode != INDEX &&
228                                 rename(src, dst) < 0 && !ignore_errors)
229                         die ("renaming %s failed: %s", src, strerror(errno));
231                 if (mode == WORKING_DIRECTORY)
232                         continue;
234                 if (cache_name_pos(src, strlen(src)) >= 0) {
235                         path_list_insert(src, &deleted);
237                         /* destination can be a directory with 1 file inside */
238                         if (path_list_has_path(&overwritten, dst))
239                                 path_list_insert(dst, &changed);
240                         else
241                                 path_list_insert(dst, &added);
242                 } else
243                         path_list_insert(dst, &added);
244         }
246         if (show_only) {
247                 show_list("Changed  : ", &changed);
248                 show_list("Adding   : ", &added);
249                 show_list("Deleting : ", &deleted);
250         } else {
251                 for (i = 0; i < changed.nr; i++) {
252                         const char *path = changed.items[i].path;
253                         int j = cache_name_pos(path, strlen(path));
254                         struct cache_entry *ce = active_cache[j];
256                         if (j < 0)
257                                 die ("Huh? Cache entry for %s unknown?", path);
258                         refresh_cache_entry(ce, 0);
259                 }
261                 for (i = 0; i < added.nr; i++) {
262                         const char *path = added.items[i].path;
263                         add_file_to_cache(path, verbose);
264                 }
266                 for (i = 0; i < deleted.nr; i++)
267                         remove_file_from_cache(deleted.items[i].path);
269                 if (active_cache_changed) {
270                         if (write_cache(newfd, active_cache, active_nr) ||
271                             commit_locked_index(&lock_file))
272                                 die("Unable to write new index file");
273                 }
274         }
276         return 0;