static int get_stat_data(struct cache_entry *ce, const unsigned char **sha1p, unsigned int *modep, int cached, int match_missing) { const unsigned char *sha1 = ce->sha1; unsigned int mode = ce->ce_mode; if (!cached && !ce_uptodate(ce)) { int changed; struct stat st; changed = check_removed(ce, &st); if (changed < 0) return -1; else if (changed) { if (match_missing) { *sha1p = sha1; *modep = mode; return 0; } return -1; } changed = ce_match_stat(ce, &st, 0); if (changed) { mode = ce_mode_from_stat(ce, st.st_mode); sha1 = null_sha1; } } *sha1p = sha1; *modep = mode; return 0; }
static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st) { int option, size; struct cache_entry *ce; /* Was the old index entry already up-to-date? */ if (old && !ce_stage(old) && !ce_match_stat(old, st, 0)) return 0; size = cache_entry_size(len); ce = xcalloc(1, size); memcpy(ce->name, path, len); ce->ce_flags = create_ce_flags(0); ce->ce_namelen = len; fill_stat_cache_info(ce, st); ce->ce_mode = ce_mode_from_stat(old, st->st_mode); if (index_path(ce->sha1, path, st, info_only ? 0 : HASH_WRITE_OBJECT)) { free(ce); return -1; } option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; if (add_cache_entry(ce, option)) return error("%s: cannot add to the index - missing --add option?", path); return 0; }
void gitmodules_config(void) { const char *work_tree = get_git_work_tree(); if (work_tree) { struct strbuf gitmodules_path = STRBUF_INIT; int pos; strbuf_addstr(&gitmodules_path, work_tree); strbuf_addstr(&gitmodules_path, "/.gitmodules"); if (read_cache() < 0) die("index file corrupt"); pos = cache_name_pos(".gitmodules", 11); if (pos < 0) { /* .gitmodules not found or isn't merged */ pos = -1 - pos; if (active_nr > pos) { /* there is a .gitmodules */ const struct cache_entry *ce = active_cache[pos]; if (ce_namelen(ce) == 11 && !memcmp(ce->name, ".gitmodules", 11)) gitmodules_is_unmerged = 1; } } else if (pos < active_nr) { struct stat st; if (lstat(".gitmodules", &st) == 0 && ce_match_stat(active_cache[pos], &st, 0) & DATA_CHANGED) gitmodules_is_modified = 1; } if (!gitmodules_is_unmerged) git_config_from_file(submodule_config, gitmodules_path.buf, NULL); strbuf_release(&gitmodules_path); } }
static int get_stat_data(struct cache_entry *ce, unsigned char ** sha1p, unsigned int *modep) { unsigned char *sha1 = ce->sha1; unsigned int mode = ce->ce_mode; if (!cached_only) { static unsigned char no_sha1[20]; int changed; struct stat st; if (lstat(ce->name, &st) < 0) { if (errno == ENOENT && match_nonexisting) { *sha1p = sha1; *modep = mode; return 0; } return -1; } changed = ce_match_stat(ce, &st); if (changed) { mode = create_ce_mode(st.st_mode); if (!trust_executable_bit && S_ISREG(mode) && S_ISREG(ce->ce_mode) && ((mode ^ ce->ce_mode) == 0111)) mode = ce->ce_mode; sha1 = no_sha1; } } *sha1p = sha1; *modep = mode; return 0; }
/* * Has a file changed or has a submodule new commits or a dirty work tree? * * Return 1 when changes are detected, 0 otherwise. If the DIRTY_SUBMODULES * option is set, the caller does not only want to know if a submodule is * modified at all but wants to know all the conditions that are met (new * commits, untracked content and/or modified content). */ static int match_stat_with_submodule(struct diff_options *diffopt, struct cache_entry *ce, struct stat *st, unsigned ce_option, unsigned *dirty_submodule) { int changed = ce_match_stat(ce, st, ce_option); if (S_ISGITLINK(ce->ce_mode) && !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES) && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) { *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES)); } return changed; }
/* * Has a file changed or has a submodule new commits or a dirty work tree? * * Return 1 when changes are detected, 0 otherwise. If the DIRTY_SUBMODULES * option is set, the caller does not only want to know if a submodule is * modified at all but wants to know all the conditions that are met (new * commits, untracked content and/or modified content). */ static int match_stat_with_submodule(struct diff_options *diffopt, struct cache_entry *ce, struct stat *st, unsigned ce_option, unsigned *dirty_submodule) { int changed = ce_match_stat(ce, st, ce_option); if (S_ISGITLINK(ce->ce_mode)) { unsigned orig_flags = diffopt->flags; if (!DIFF_OPT_TST(diffopt, OVERRIDE_SUBMODULE_CONFIG)) set_diffopt_flags_from_submodule_config(diffopt, ce->name); if (DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)) changed = 0; else if (!DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES) && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES)); diffopt->flags = orig_flags; } return changed; }
static int get_stat_data(struct cache_entry *ce, const unsigned char **sha1p, unsigned int *modep, int cached, int match_missing, unsigned *dirty_submodule, struct diff_options *diffopt) { const unsigned char *sha1 = ce->sha1; unsigned int mode = ce->ce_mode; if (!cached && !ce_uptodate(ce)) { int changed; struct stat st; changed = check_removed(ce, &st); if (changed < 0) return -1; else if (changed) { if (match_missing) { *sha1p = sha1; *modep = mode; return 0; } return -1; } changed = ce_match_stat(ce, &st, 0); if (S_ISGITLINK(ce->ce_mode) && !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES) && (!changed || (diffopt->output_format & DIFF_FORMAT_PATCH)) && is_submodule_modified(ce->name)) { changed = 1; *dirty_submodule = 1; } if (changed) { mode = ce_mode_from_stat(ce, st.st_mode); sha1 = null_sha1; } } *sha1p = sha1; *modep = mode; return 0; }
/* * Given a name and sha1 pair, if the dircache tells us the file in * the work tree has that object contents, return true, so that * prepare_temp_file() does not have to inflate and extract. */ static int work_tree_matches(const char *name, const unsigned char *sha1) { struct cache_entry *ce; struct stat st; int pos, len; /* We do not read the cache ourselves here, because the * benchmark with my previous version that always reads cache * shows that it makes things worse for diff-tree comparing * two linux-2.6 kernel trees in an already checked out work * tree. This is because most diff-tree comparisons deal with * only a small number of files, while reading the cache is * expensive for a large project, and its cost outweighs the * savings we get by not inflating the object to a temporary * file. Practically, this code only helps when we are used * by diff-cache --cached, which does read the cache before * calling us. */ if (!active_cache) return 0; len = strlen(name); pos = cache_name_pos(name, len); if (pos < 0) return 0; ce = active_cache[pos]; if ((lstat(name, &st) < 0) || !S_ISREG(st.st_mode) || /* careful! */ ce_match_stat(ce, &st) || memcmp(sha1, ce->sha1, 20)) return 0; /* we return 1 only when we can stat, it is a regular file, * stat information matches, and sha1 recorded in the cache * matches. I.e. we know the file in the work tree really is * the same as the <name, sha1> pair. */ return 1; }
int run_diff_files(struct rev_info *revs, unsigned int option) { int entries, i; int diff_unmerged_stage = revs->max_count; int silent_on_removed = option & DIFF_SILENT_ON_REMOVED; unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED) ? CE_MATCH_RACY_IS_DIRTY : 0); diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/"); if (diff_unmerged_stage < 0) diff_unmerged_stage = 2; entries = active_nr; for (i = 0; i < entries; i++) { struct stat st; unsigned int oldmode, newmode; struct cache_entry *ce = active_cache[i]; int changed; if (DIFF_OPT_TST(&revs->diffopt, QUIET) && DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES)) break; if (!ce_path_match(ce, revs->prune_data)) continue; if (ce_stage(ce)) { struct combine_diff_path *dpath; int num_compare_stages = 0; size_t path_len; path_len = ce_namelen(ce); dpath = xmalloc(combine_diff_path_size(5, path_len)); dpath->path = (char *) &(dpath->parent[5]); dpath->next = NULL; dpath->len = path_len; memcpy(dpath->path, ce->name, path_len); dpath->path[path_len] = '\0'; hashclr(dpath->sha1); memset(&(dpath->parent[0]), 0, sizeof(struct combine_diff_parent)*5); changed = check_removed(ce, &st); if (!changed) dpath->mode = ce_mode_from_stat(ce, st.st_mode); else { if (changed < 0) { perror(ce->name); continue; } if (silent_on_removed) continue; } while (i < entries) { struct cache_entry *nce = active_cache[i]; int stage; if (strcmp(ce->name, nce->name)) break; /* Stage #2 (ours) is the first parent, * stage #3 (theirs) is the second. */ stage = ce_stage(nce); if (2 <= stage) { int mode = nce->ce_mode; num_compare_stages++; hashcpy(dpath->parent[stage-2].sha1, nce->sha1); dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode); dpath->parent[stage-2].status = DIFF_STATUS_MODIFIED; } /* diff against the proper unmerged stage */ if (stage == diff_unmerged_stage) ce = nce; i++; } /* * Compensate for loop update */ i--; if (revs->combine_merges && num_compare_stages == 2) { show_combined_diff(dpath, 2, revs->dense_combined_merges, revs); free(dpath); continue; } free(dpath); dpath = NULL; /* * Show the diff for the 'ce' if we found the one * from the desired stage. */ diff_unmerge(&revs->diffopt, ce->name, 0, null_sha1); if (ce_stage(ce) != diff_unmerged_stage) continue; } if (ce_uptodate(ce)) continue; changed = check_removed(ce, &st); if (changed) { if (changed < 0) { perror(ce->name); continue; } if (silent_on_removed) continue; diff_addremove(&revs->diffopt, '-', ce->ce_mode, ce->sha1, ce->name); continue; } changed = ce_match_stat(ce, &st, ce_option); if (!changed) { ce_mark_uptodate(ce); if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER)) continue; } oldmode = ce->ce_mode; newmode = ce_mode_from_stat(ce, st.st_mode); diff_change(&revs->diffopt, oldmode, newmode, ce->sha1, (changed ? null_sha1 : ce->sha1), ce->name); } diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); 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; }
static int check_local_mod(struct object_id *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; struct string_list files_staged = STRING_LIST_INIT_NODUP; struct string_list files_cached = STRING_LIST_INIT_NODUP; struct string_list files_local = STRING_LIST_INIT_NODUP; 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; struct object_id oid; 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 (!is_missing_file_error(errno)) warning_errno(_("failed to stat '%s'"), ce->name); /* 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) && bad_to_remove_submodule(ce->name, SUBMODULE_REMOVAL_DIE_ON_ERROR | SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED))) 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->hash, name, oid.hash, &mode) || ce->ce_mode != create_ce_mode(mode) || oidcmp(&ce->oid, &oid)) 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_intent_to_add(ce)) string_list_append(&files_staged, name); } else if (!index_only) { if (staged_changes) string_list_append(&files_cached, name); if (local_changes) string_list_append(&files_local, name); } } print_error_files(&files_staged, Q_("the following file has staged content different " "from both the\nfile and the HEAD:", "the following files have staged content different" " from both the\nfile and the HEAD:", files_staged.nr), _("\n(use -f to force removal)"), &errs); string_list_clear(&files_staged, 0); print_error_files(&files_cached, Q_("the following file has changes " "staged in the index:", "the following files have changes " "staged in the index:", files_cached.nr), _("\n(use --cached to keep the file," " or -f to force removal)"), &errs); string_list_clear(&files_cached, 0); print_error_files(&files_local, Q_("the following file has local modifications:", "the following files have local modifications:", files_local.nr), _("\n(use --cached to keep the file," " or -f to force removal)"), &errs); string_list_clear(&files_local, 0); return errs; }