rm: do not complain about d/f conflicts during deletion

If we used to have an index entry "d/f", but "d" has been replaced by a non-directory entry, the user may still want to run "git rm" to delete the stale index entry. They could use "git rm --cached" to just touch the index, but "git rm" should also work: we explicitly try to handle the case that the file has already been removed from the working tree. However, because unlinking "d/f" in this case will not yield ENOENT, but rather ENOTDIR, we do not notice that the file is already gone. Instead, we report it as an error. The simple solution is to treat ENOTDIR in this case exactly like ENOENT; all we want to know is whether the file is already gone, and if a leading path is no longer a directory, then by definition the sub-path is gone. Reported-by: jpinheiro <7jpinheiro@gmail.com> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Apr 4, 2013 at 15:03 UTC 9a6728d4d14a08791861a63b7567ee0b50e59f70
3 files changed +27 -2
builtin/rm.c
+1 -1
@@ -110,7 +110,7 @@ static int check_local_mod(unsigned char *head, int index_only)
110 ce = active_cache[pos];
111
112 if (lstat(ce->name, &st) < 0) {
113 - if (errno != ENOENT)
113 + if (errno != ENOENT && errno != ENOTDIR)
114 warning("'%s': %s", ce->name, strerror(errno));
115 /* It already vanished from the working tree */
116 continue;
dir.c
+1 -1
@@ -1603,7 +1603,7 @@ int remove_path(const char *name)
1603 {
1604 char *slash;
1605
1606 - if (unlink(name) && errno != ENOENT)
1606 + if (unlink(name) && errno != ENOENT && errno != ENOTDIR)
1607 return -1;
1608
1609 slash = strrchr(name, '/');
t/t3600-rm.sh
+25
@@ -622,4 +622,29 @@ test_expect_success 'rm of a populated nested submodule with a nested .git direc
622 rm -rf submod
623 '
624
625 +test_expect_success 'rm of d/f when d has become a non-directory' '
626 + rm -rf d &&
627 + mkdir d &&
628 + >d/f &&
629 + git add d &&
630 + rm -rf d &&
631 + >d &&
632 + git rm d/f &&
633 + test_must_fail git rev-parse --verify :d/f &&
634 + test_path_is_file d
635 +'
636 +
637 +test_expect_success SYMLINKS 'rm of d/f when d has become a dangling symlink' '
638 + rm -rf d &&
639 + mkdir d &&
640 + >d/f &&
641 + git add d &&
642 + rm -rf d &&
643 + ln -s nonexistent d &&
644 + git rm d/f &&
645 + test_must_fail git rev-parse --verify :d/f &&
646 + test -h d &&
647 + test_path_is_missing d
648 +'
649 +
650 test_done