read-cache: fix index corruption with index v4
[gitweb.git] / builtin / rm.c
index be83c4347a9cd6fdd6a76a9575f8e39a5b2bd6c2..52826d137935ca6698006258ebdb8b207f7161df 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) Linus Torvalds 2006
  */
 #include "builtin.h"
+#include "config.h"
 #include "lockfile.h"
 #include "dir.h"
 #include "cache-tree.h"
@@ -59,27 +60,9 @@ static void print_error_files(struct string_list *files_list,
        }
 }
 
-static void error_removing_concrete_submodules(struct string_list *files, int *errs)
-{
-       print_error_files(files,
-                         Q_("the following submodule (or one of its nested "
-                            "submodules)\n"
-                            "uses a .git directory:",
-                            "the following submodules (or one of their nested "
-                            "submodules)\n"
-                            "use a .git directory:", files->nr),
-                         _("\n(use 'rm -rf' if you really want to remove "
-                           "it including all of its history)"),
-                         errs);
-       string_list_clear(files, 0);
-}
-
-static int check_submodules_use_gitfiles(void)
+static void submodules_absorb_gitdir_if_needed(const char *prefix)
 {
        int i;
-       int errs = 0;
-       struct string_list files = STRING_LIST_INIT_NODUP;
-
        for (i = 0; i < list.nr; i++) {
                const char *name = list.entry[i].name;
                int pos;
@@ -99,15 +82,12 @@ static int check_submodules_use_gitfiles(void)
                        continue;
 
                if (!submodule_uses_gitfile(name))
-                       string_list_append(&files, name);
+                       absorb_git_dir_into_superproject(prefix, name,
+                               ABSORB_GITDIR_RECURSE_SUBMODULES);
        }
-
-       error_removing_concrete_submodules(&files, &errs);
-
-       return errs;
 }
 
-static int check_local_mod(unsigned char *head, int index_only)
+static int check_local_mod(struct object_id *head, int index_only)
 {
        /*
         * Items in list are already sorted in the cache order,
@@ -120,16 +100,15 @@ static int check_local_mod(unsigned char *head, int index_only)
        int errs = 0;
        struct string_list files_staged = STRING_LIST_INIT_NODUP;
        struct string_list files_cached = STRING_LIST_INIT_NODUP;
-       struct string_list files_submodule = STRING_LIST_INIT_NODUP;
        struct string_list files_local = STRING_LIST_INIT_NODUP;
 
-       no_head = is_null_sha1(head);
+       no_head = is_null_oid(head);
        for (i = 0; i < list.nr; i++) {
                struct stat st;
                int pos;
                const struct cache_entry *ce;
                const char *name = list.entry[i].name;
-               unsigned char sha1[20];
+               struct object_id oid;
                unsigned mode;
                int local_changes = 0;
                int staged_changes = 0;
@@ -151,8 +130,8 @@ static int check_local_mod(unsigned char *head, int index_only)
                ce = active_cache[pos];
 
                if (lstat(ce->name, &st) < 0) {
-                       if (errno != ENOENT && errno != ENOTDIR)
-                               warning("'%s': %s", ce->name, strerror(errno));
+                       if (!is_missing_file_error(errno))
+                               warning_errno(_("failed to stat '%s'"), ce->name);
                        /* It already vanished from the working tree */
                        continue;
                }
@@ -187,7 +166,9 @@ static int check_local_mod(unsigned char *head, int index_only)
                 */
                if (ce_match_stat(ce, &st, 0) ||
                    (S_ISGITLINK(ce->ce_mode) &&
-                    !ok_to_remove_submodule(ce->name)))
+                    bad_to_remove_submodule(ce->name,
+                               SUBMODULE_REMOVAL_DIE_ON_ERROR |
+                               SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED)))
                        local_changes = 1;
 
                /*
@@ -197,9 +178,9 @@ static int check_local_mod(unsigned char *head, int index_only)
                 * way as changed from the HEAD.
                 */
                if (no_head
-                    || get_tree_entry(head, name, sha1, &mode)
+                    || get_tree_entry(head->hash, name, oid.hash, &mode)
                     || ce->ce_mode != create_ce_mode(mode)
-                    || hashcmp(ce->sha1, sha1))
+                    || oidcmp(&ce->oid, &oid))
                        staged_changes = 1;
 
                /*
@@ -217,13 +198,8 @@ static int check_local_mod(unsigned char *head, int index_only)
                else if (!index_only) {
                        if (staged_changes)
                                string_list_append(&files_cached, name);
-                       if (local_changes) {
-                               if (S_ISGITLINK(ce->ce_mode) &&
-                                   !submodule_uses_gitfile(name))
-                                       string_list_append(&files_submodule, name);
-                               else
-                                       string_list_append(&files_local, name);
-                       }
+                       if (local_changes)
+                               string_list_append(&files_local, name);
                }
        }
        print_error_files(&files_staged,
@@ -245,8 +221,6 @@ static int check_local_mod(unsigned char *head, int index_only)
                          &errs);
        string_list_clear(&files_cached, 0);
 
-       error_removing_concrete_submodules(&files_submodule, &errs);
-
        print_error_files(&files_local,
                          Q_("the following file has local modifications:",
                             "the following files have local modifications:",
@@ -292,14 +266,13 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        if (!index_only)
                setup_work_tree();
 
-       hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
        if (read_cache() < 0)
                die(_("index file corrupt"));
 
        parse_pathspec(&pathspec, 0,
-                      PATHSPEC_PREFER_CWD |
-                      PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
+                      PATHSPEC_PREFER_CWD,
                       prefix, argv);
        refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);
 
@@ -340,6 +313,9 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                        exit(0);
        }
 
+       if (!index_only)
+               submodules_absorb_gitdir_if_needed(prefix);
+
        /*
         * If not forced, the file, the index and the HEAD (if exists)
         * must match; but the file can already been removed, since
@@ -351,13 +327,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
         * report no changes unless forced.
         */
        if (!force) {
-               unsigned char sha1[20];
-               if (get_sha1("HEAD", sha1))
-                       hashclr(sha1);
-               if (check_local_mod(sha1, index_only))
-                       exit(1);
-       } else if (!index_only) {
-               if (check_submodules_use_gitfiles())
+               struct object_id oid;
+               if (get_oid("HEAD", &oid))
+                       oidclr(&oid);
+               if (check_local_mod(&oid, index_only))
                        exit(1);
        }
 
@@ -387,32 +360,19 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
         */
        if (!index_only) {
                int removed = 0, gitmodules_modified = 0;
+               struct strbuf buf = STRBUF_INIT;
                for (i = 0; i < list.nr; i++) {
                        const char *path = list.entry[i].name;
                        if (list.entry[i].is_submodule) {
-                               if (is_empty_dir(path)) {
-                                       if (!rmdir(path)) {
-                                               removed = 1;
-                                               if (!remove_path_from_gitmodules(path))
-                                                       gitmodules_modified = 1;
-                                               continue;
-                                       }
-                               } else {
-                                       struct strbuf buf = STRBUF_INIT;
-                                       strbuf_addstr(&buf, path);
-                                       if (!remove_dir_recursively(&buf, 0)) {
-                                               removed = 1;
-                                               if (!remove_path_from_gitmodules(path))
-                                                       gitmodules_modified = 1;
-                                               strbuf_release(&buf);
-                                               continue;
-                                       } else if (!file_exists(path))
-                                               /* Submodule was removed by user */
-                                               if (!remove_path_from_gitmodules(path))
-                                                       gitmodules_modified = 1;
-                                       strbuf_release(&buf);
-                                       /* Fallthrough and let remove_path() fail. */
-                               }
+                               strbuf_reset(&buf);
+                               strbuf_addstr(&buf, path);
+                               if (remove_dir_recursively(&buf, 0))
+                                       die(_("could not remove '%s'"), path);
+
+                               removed = 1;
+                               if (!remove_path_from_gitmodules(path))
+                                       gitmodules_modified = 1;
+                               continue;
                        }
                        if (!remove_path(path)) {
                                removed = 1;
@@ -421,6 +381,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                        if (!removed)
                                die_errno("git rm: '%s'", path);
                }
+               strbuf_release(&buf);
                if (gitmodules_modified)
                        stage_updated_gitmodules();
        }