37
INDEX = (1 << 2),
38
SPARSE = (1 << 3),
39
SKIP_WORKTREE_DIR = (1 << 4),
40
+ /*
41
+ * A file gets moved implicitly via a move of one of its parent
42
+ * directories. This flag causes us to skip the check that we don't try
43
+ * to move a file and any of its parent directories at the same point
44
+ * in time.
45
+ */
46
+ MOVE_VIA_PARENT_DIR = (1 << 5),
47
};
48
49
#define DUP_BASENAME 1
188
strbuf_release(&a_src_dir);
189
}
190
191
+struct pathmap_entry {
192
+ struct hashmap_entry ent;
193
+ const char *path;
194
+};
195
+
196
+static int pathmap_cmp(const void *cmp_data UNUSED,
197
+ const struct hashmap_entry *a,
198
+ const struct hashmap_entry *b,
199
+ const void *key UNUSED)
200
+{
201
+ const struct pathmap_entry *e1 = container_of(a, struct pathmap_entry, ent);
202
+ const struct pathmap_entry *e2 = container_of(b, struct pathmap_entry, ent);
203
+ return fspathcmp(e1->path, e2->path);
204
+}
205
+
206
int cmd_mv(int argc,
207
const char **argv,
208
const char *prefix,
233
struct cache_entry *ce;
234
struct string_list only_match_skip_worktree = STRING_LIST_INIT_DUP;
235
struct string_list dirty_paths = STRING_LIST_INIT_DUP;
236
+ struct hashmap moved_dirs = HASHMAP_INIT(pathmap_cmp, NULL);
237
+ struct strbuf pathbuf = STRBUF_INIT;
238
int ret;
239
240
git_config(git_default_config, NULL);
353
354
dir_check:
355
if (S_ISDIR(st.st_mode)) {
356
+ struct pathmap_entry *entry;
357
char *dst_with_slash;
358
size_t dst_with_slash_len;
359
int j, n;
371
goto act_on_entry;
372
}
373
374
+ entry = xmalloc(sizeof(*entry));
375
+ entry->path = src;
376
+ hashmap_entry_init(&entry->ent, fspathhash(src));
377
+ hashmap_add(&moved_dirs, &entry->ent);
378
+
379
/* last - first >= 1 */
380
modes[i] |= WORKING_DIRECTORY;
381
396
strvec_push(&sources, path);
397
strvec_push(&destinations, prefixed_path);
398
369
- memset(modes + argc + j, 0, sizeof(enum update_mode));
370
- modes[argc + j] |= ce_skip_worktree(ce) ? SPARSE : INDEX;
399
+ modes[argc + j] = MOVE_VIA_PARENT_DIR | (ce_skip_worktree(ce) ? SPARSE : INDEX);
400
submodule_gitfiles[argc + j] = NULL;
401
402
free(prefixed_path);
492
}
493
}
494
495
+ for (i = 0; i < argc; i++) {
496
+ const char *slash_pos;
497
+
498
+ if (modes[i] & MOVE_VIA_PARENT_DIR)
499
+ continue;
500
+
501
+ strbuf_reset(&pathbuf);
502
+ strbuf_addstr(&pathbuf, sources.v[i]);
503
+
504
+ slash_pos = strrchr(pathbuf.buf, '/');
505
+ while (slash_pos > pathbuf.buf) {
506
+ struct pathmap_entry needle;
507
+
508
+ strbuf_setlen(&pathbuf, slash_pos - pathbuf.buf);
509
+
510
+ needle.path = pathbuf.buf;
511
+ hashmap_entry_init(&needle.ent, fspathhash(pathbuf.buf));
512
+
513
+ if (hashmap_get_entry(&moved_dirs, &needle, ent, NULL))
514
+ die(_("cannot move both '%s' and its parent directory '%s'"),
515
+ sources.v[i], pathbuf.buf);
516
+
517
+ slash_pos = strrchr(pathbuf.buf, '/');
518
+ }
519
+ }
520
+
521
if (only_match_skip_worktree.nr) {
522
advise_on_updating_sparse_paths(&only_match_skip_worktree);
523
if (!ignore_errors) {
642
strvec_clear(&dest_paths);
643
strvec_clear(&destinations);
644
strvec_clear(&submodule_gitfiles_to_free);
645
+ hashmap_clear_and_free(&moved_dirs, struct pathmap_entry, ent);
646
+ strbuf_release(&pathbuf);
647
free(submodule_gitfiles);
648
free(modes);
649
return ret;