static int check_submodules_use_gitfiles(void) { int i; int errs = 0; for (i = 0; i < list.nr; i++) { const char *name = list.entry[i].name; int pos; struct cache_entry *ce; struct stat st; pos = cache_name_pos(name, strlen(name)); if (pos < 0) { pos = get_ours_cache_pos(name, pos); if (pos < 0) continue; } ce = active_cache[pos]; if (!S_ISGITLINK(ce->ce_mode) || (lstat(ce->name, &st) < 0) || is_empty_dir(name)) continue; if (!submodule_uses_gitfile(name)) errs = error(_("submodule '%s' (or one of its nested " "submodules) uses a .git directory\n" "(use 'rm -rf' if you really want to remove " "it including all of its history)"), name); } return errs; }
static int check_submodules_use_gitfiles(void) { 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; const struct cache_entry *ce; pos = cache_name_pos(name, strlen(name)); if (pos < 0) { pos = get_ours_cache_pos(name, pos); if (pos < 0) continue; } ce = active_cache[pos]; if (!S_ISGITLINK(ce->ce_mode) || !file_exists(ce->name) || is_empty_dir(name)) continue; if (!submodule_uses_gitfile(name)) string_list_append(&files, name); } error_removing_concrete_submodules(&files, &errs); return errs; }
static void submodules_absorb_gitdir_if_needed(const char *prefix) { int i; for (i = 0; i < list.nr; i++) { const char *name = list.entry[i].name; int pos; const struct cache_entry *ce; pos = cache_name_pos(name, strlen(name)); if (pos < 0) { pos = get_ours_cache_pos(name, pos); if (pos < 0) continue; } ce = active_cache[pos]; if (!S_ISGITLINK(ce->ce_mode) || !file_exists(ce->name) || is_empty_dir(name)) continue; if (!submodule_uses_gitfile(name)) absorb_git_dir_into_superproject(prefix, name, ABSORB_GITDIR_RECURSE_SUBMODULES); } }
int ok_to_remove_submodule(const char *path) { struct stat st; ssize_t len; struct child_process cp; const char *argv[] = { "status", "--porcelain", "-u", "--ignore-submodules=none", NULL, }; struct strbuf buf = STRBUF_INIT; int ok_to_remove = 1; if ((lstat(path, &st) < 0) || is_empty_dir(path)) return 1; if (!submodule_uses_gitfile(path)) return 0; memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; cp.no_stdin = 1; cp.out = -1; cp.dir = path; if (start_command(&cp)) die("Could not run 'git status --porcelain -uall --ignore-submodules=none' in submodule %s", path); len = strbuf_read(&buf, cp.out, 1024); if (len > 2) ok_to_remove = 0; close(cp.out); if (finish_command(&cp)) die("'git status --porcelain -uall --ignore-submodules=none' failed in submodule %s", path); strbuf_release(&buf); return ok_to_remove; }
int ok_to_remove_submodule(const char *path) { ssize_t len; struct child_process cp = CHILD_PROCESS_INIT; const char *argv[] = { "status", "--porcelain", "-u", "--ignore-submodules=none", NULL, }; struct strbuf buf = STRBUF_INIT; int ok_to_remove = 1; if (!file_exists(path) || is_empty_dir(path)) return 1; if (!submodule_uses_gitfile(path)) return 0; cp.argv = argv; prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; cp.no_stdin = 1; cp.out = -1; cp.dir = path; if (start_command(&cp)) die("Could not run 'git status --porcelain -uall --ignore-submodules=none' in submodule %s", path); len = strbuf_read(&buf, cp.out, 1024); if (len > 2) ok_to_remove = 0; close(cp.out); if (finish_command(&cp)) die("'git status --porcelain -uall --ignore-submodules=none' failed in submodule %s", path); strbuf_release(&buf); return ok_to_remove; }
int depopulate_submodule(const char *path) { struct strbuf dot_git = STRBUF_INIT; struct child_process cp; const char *argv[] = {"rm", "-rf", path, NULL}; /* Is it populated? */ strbuf_addf(&dot_git, "%s/.git", path); if (!resolve_gitdir(dot_git.buf)) { strbuf_release(&dot_git); return 0; } strbuf_release(&dot_git); /* Does it have a .git directory? */ if (!submodule_uses_gitfile(path)) { warning(_("cannot remove submodule '%s' because it (or one of " "its nested submodules) uses a .git directory"), path); return -1; } /* Remove the whole submodule directory */ memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 0; cp.no_stdin = 1; if (run_command(&cp)) { warning("Could not remove submodule %s", path); strbuf_release(&dot_git); return -1; } return 0; }
static int check_local_mod(unsigned char *head, int index_only) { /* * Items in list are already sorted in the cache order, * so we could do this a lot more efficiently by using * tree_desc based traversal if we wanted to, but I am * lazy, and who cares if removal of files is a tad * slower than the theoretical maximum speed? */ int i, no_head; int errs = 0; no_head = is_null_sha1(head); for (i = 0; i < list.nr; i++) { struct stat st; int pos; struct cache_entry *ce; const char *name = list.entry[i].name; unsigned char sha1[20]; unsigned mode; int local_changes = 0; int staged_changes = 0; pos = cache_name_pos(name, strlen(name)); if (pos < 0) { /* * Skip unmerged entries except for populated submodules * that could lose history when removed. */ pos = get_ours_cache_pos(name, pos); if (pos < 0) continue; if (!S_ISGITLINK(active_cache[pos]->ce_mode) || is_empty_dir(name)) continue; } ce = active_cache[pos]; if (lstat(ce->name, &st) < 0) { if (errno != ENOENT && errno != ENOTDIR) warning("'%s': %s", ce->name, strerror(errno)); /* It already vanished from the working tree */ continue; } else if (S_ISDIR(st.st_mode)) { /* if a file was removed and it is now a * directory, that is the same as ENOENT as * far as git is concerned; we do not track * directories unless they are submodules. */ if (!S_ISGITLINK(ce->ce_mode)) continue; } /* * "rm" of a path that has changes need to be treated * carefully not to allow losing local changes * accidentally. A local change could be (1) file in * work tree is different since the index; and/or (2) * the user staged a content that is different from * the current commit in the index. * * In such a case, you would need to --force the * removal. However, "rm --cached" (remove only from * the index) is safe if the index matches the file in * the work tree or the HEAD commit, as it means that * the content being removed is available elsewhere. */ /* * Is the index different from the file in the work tree? * If it's a submodule, is its work tree modified? */ if (ce_match_stat(ce, &st, 0) || (S_ISGITLINK(ce->ce_mode) && !ok_to_remove_submodule(ce->name))) local_changes = 1; /* * Is the index different from the HEAD commit? By * definition, before the very initial commit, * anything staged in the index is treated by the same * way as changed from the HEAD. */ if (no_head || get_tree_entry(head, name, sha1, &mode) || ce->ce_mode != create_ce_mode(mode) || hashcmp(ce->sha1, sha1)) staged_changes = 1; /* * If the index does not match the file in the work * tree and if it does not match the HEAD commit * either, (1) "git rm" without --cached definitely * will lose information; (2) "git rm --cached" will * lose information unless it is about removing an * "intent to add" entry. */ if (local_changes && staged_changes) { if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD)) errs = error(_("'%s' has staged content different " "from both the file and the HEAD\n" "(use -f to force removal)"), name); } else if (!index_only) { if (staged_changes) errs = error(_("'%s' has changes staged in the index\n" "(use --cached to keep the file, " "or -f to force removal)"), name); if (local_changes) { if (S_ISGITLINK(ce->ce_mode) && !submodule_uses_gitfile(name)) { errs = error(_("submodule '%s' (or one of its nested " "submodules) uses a .git directory\n" "(use 'rm -rf' if you really want to remove " "it including all of its history)"), name); } else errs = error(_("'%s' has local modifications\n" "(use --cached to keep the file, " "or -f to force removal)"), name); } } } return errs; }