static void mktree_line(char *buf, size_t len, int line_termination, int allow_missing) { char *ptr, *ntr; unsigned mode; enum object_type mode_type; /* object type derived from mode */ enum object_type obj_type; /* object type derived from sha */ char *path; unsigned char sha1[20]; ptr = buf; /* * Read non-recursive ls-tree output format: * mode SP type SP sha1 TAB name */ mode = strtoul(ptr, &ntr, 8); if (ptr == ntr || !ntr || *ntr != ' ') die("input format error: %s", buf); ptr = ntr + 1; /* type */ ntr = strchr(ptr, ' '); if (!ntr || buf + len <= ntr + 40 || ntr[41] != '\t' || get_sha1_hex(ntr + 1, sha1)) die("input format error: %s", buf); /* It is perfectly normal if we do not have a commit from a submodule */ if (S_ISGITLINK(mode)) allow_missing = 1; *ntr++ = 0; /* now at the beginning of SHA1 */ path = ntr + 41; /* at the beginning of name */ if (line_termination && path[0] == '"') { struct strbuf p_uq = STRBUF_INIT; if (unquote_c_style(&p_uq, path, NULL)) die("invalid quoting"); path = strbuf_detach(&p_uq, NULL); } /* * Object type is redundantly derivable three ways. * These should all agree. */ mode_type = object_type(mode); if (mode_type != type_from_string(ptr)) { die("entry '%s' object type (%s) doesn't match mode type (%s)", path, ptr, typename(mode_type)); }
static void prepare_move_submodule(const char *src, int first, const char **submodule_gitfile) { struct strbuf submodule_dotgit = STRBUF_INIT; if (!S_ISGITLINK(active_cache[first]->ce_mode)) die(_("Directory %s is in index and no submodule?"), src); if (!is_staging_gitmodules_ok()) die(_("Please stage your changes to .gitmodules or stash them to proceed")); strbuf_addf(&submodule_dotgit, "%s/.git", src); *submodule_gitfile = read_gitfile(submodule_dotgit.buf); if (*submodule_gitfile) *submodule_gitfile = xstrdup(*submodule_gitfile); else *submodule_gitfile = SUBMODULE_WITH_GITDIR; strbuf_release(&submodule_dotgit); }
const char * mkmode(mode_t mode) { if (S_ISDIR(mode)) return "drwxr-xr-x"; else if (S_ISLNK(mode)) return "lrwxrwxrwx"; else if (S_ISGITLINK(mode)) return "m---------"; else if (S_ISREG(mode) && mode & S_IXUSR) return "-rwxr-xr-x"; else if (S_ISREG(mode)) return "-rw-r--r--"; else return "----------"; }
static void collect_submodules_from_diff(struct diff_queue_struct *q, struct diff_options *options, void *data) { int i; int *needs_pushing = data; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; if (!S_ISGITLINK(p->two->mode)) continue; if (submodule_needs_pushing(p->two->path, p->two->sha1)) { *needs_pushing = 1; break; } } }
static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options) { struct tree_desc desc; struct name_entry entry; int res = 0; const char *name; if (parse_tree(tree)) return -1; name = get_object_name(options, &tree->object); if (init_tree_desc_gently(&desc, tree->buffer, tree->size)) return -1; while (tree_entry_gently(&desc, &entry)) { struct object *obj; int result; if (S_ISGITLINK(entry.mode)) continue; if (S_ISDIR(entry.mode)) { obj = (struct object *)lookup_tree(entry.oid); if (name && obj) put_object_name(options, obj, "%s%s/", name, entry.path); result = options->walk(obj, OBJ_TREE, data, options); } else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) { obj = (struct object *)lookup_blob(entry.oid); if (name && obj) put_object_name(options, obj, "%s%s", name, entry.path); result = options->walk(obj, OBJ_BLOB, data, options); } else { result = error("in tree %s: entry %s has bad mode %.6o", describe_object(options, &tree->object), entry.path, entry.mode); } if (result < 0) return result; if (!res) res = result; } return res; }
/* * 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 void process_tree(struct rev_info *revs, struct tree *tree, struct object_array *p, struct name_path *path, const char *name) { struct object *obj = &tree->object; struct tree_desc desc; struct name_entry entry; struct name_path me; if (!revs->tree_objects) return; if (!obj) die("bad tree object"); if (obj->flags & (UNINTERESTING | SEEN)) return; if (parse_tree(tree) < 0) die("bad tree object %s", sha1_to_hex(obj->sha1)); obj->flags |= SEEN; name = xstrdup(name); add_object(obj, p, path, name); me.up = path; me.elem = name; me.elem_len = strlen(name); init_tree_desc(&desc, tree->buffer, tree->size); while (tree_entry(&desc, &entry)) { if (S_ISDIR(entry.mode)) process_tree(revs, lookup_tree(entry.sha1), p, &me, entry.path); else if (S_ISGITLINK(entry.mode)) process_gitlink(revs, entry.sha1, p, &me, entry.path); else process_blob(revs, lookup_blob(entry.sha1), p, &me, entry.path); } free(tree->buffer); tree->buffer = NULL; }
static int module_list_compute(int argc, const char **argv, const char *prefix, struct pathspec *pathspec, struct module_list *list) { int i, result = 0; char *ps_matched = NULL; parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL | PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, prefix, argv); if (pathspec->nr) ps_matched = xcalloc(pathspec->nr, 1); if (read_cache() < 0) die(_("index file corrupt")); for (i = 0; i < active_nr; i++) { const struct cache_entry *ce = active_cache[i]; if (!S_ISGITLINK(ce->ce_mode) || !match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched, 1)) continue; ALLOC_GROW(list->entries, list->nr + 1, list->alloc); list->entries[list->nr++] = ce; while (i + 1 < active_nr && !strcmp(ce->name, active_cache[i + 1]->name)) /* * Skip entries with the same name in different stages * to make sure an entry is returned only once. */ i++; } if (ps_matched && report_path_error(ps_matched, pathspec, prefix)) result = -1; free(ps_matched); return result; }
/* * Handle a path that was a directory. Four cases: * * - it's already a gitlink in the index, and we keep it that * way, and update it if we can (if we cannot find the HEAD, * we're going to keep it unchanged in the index!) * * - it's a *file* in the index, in which case it should be * removed as a file if removal is allowed, since it doesn't * exist as such any more. If removal isn't allowed, it's * an error. * * (NOTE! This is old and arguably fairly strange behaviour. * We might want to make this an error unconditionally, and * use "--force-remove" if you actually want to force removal). * * - it used to exist as a subdirectory (ie multiple files with * this particular prefix) in the index, in which case it's wrong * to try to update it as a directory. * * - it doesn't exist at all in the index, but it is a valid * git directory, and it should be *added* as a gitlink. */ static int process_directory(const char *path, int len, struct stat *st) { unsigned char sha1[20]; int pos = cache_name_pos(path, len); /* Exact match: file or existing gitlink */ if (pos >= 0) { const struct cache_entry *ce = active_cache[pos]; if (S_ISGITLINK(ce->ce_mode)) { /* Do nothing to the index if there is no HEAD! */ if (resolve_gitlink_ref(path, "HEAD", sha1) < 0) return 0; return add_one_path(ce, path, len, st); } /* Should this be an unconditional error? */ return remove_one_path(path); } /* Inexact match: is there perhaps a subdirectory match? */ pos = -pos-1; while (pos < active_nr) { const struct cache_entry *ce = active_cache[pos++]; if (strncmp(ce->name, path, len)) break; if (ce->name[len] > '/') break; if (ce->name[len] < '/') continue; /* Subdirectory match - error out */ return error("%s: is a directory - add individual files instead", path); } /* No match - should we add it as a gitlink? */ if (!resolve_gitlink_ref(path, "HEAD", sha1)) return add_one_path(NULL, path, len, st); /* Error out. */ return error("%s: is a directory - add files inside instead", path); }
static char *grab_blob(const unsigned char *sha1, unsigned int mode, unsigned long *size) { char *blob; enum object_type type; if (S_ISGITLINK(mode)) { blob = xmalloc(100); *size = snprintf(blob, 100, "Subproject commit %s\n", sha1_to_hex(sha1)); } else if (is_null_sha1(sha1)) { /* deleted blob */ *size = 0; return xcalloc(1, 1); } else { blob = read_sha1_file(sha1, &type, size); if (type != OBJ_BLOB) die("object '%s' is not a blob!", sha1_to_hex(sha1)); } return blob; }
/* * 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, const struct cache_entry *ce, struct stat *st, unsigned ce_option, unsigned *dirty_submodule) { int changed = ie_match_stat(diffopt->repo->index, ce, st, ce_option); if (S_ISGITLINK(ce->ce_mode)) { struct diff_flags orig_flags = diffopt->flags; if (!diffopt->flags.override_submodule_config) set_diffopt_flags_from_submodule_config(diffopt, ce->name); if (diffopt->flags.ignore_submodules) changed = 0; else if (!diffopt->flags.ignore_dirty_submodules && (!changed || diffopt->flags.dirty_submodules)) *dirty_submodule = is_submodule_modified(ce->name, diffopt->flags.ignore_untracked_in_submodules); diffopt->flags = orig_flags; } return changed; }
static int process_path(const char *path) { int pos, len; struct stat st; struct cache_entry *ce; len = strlen(path); if (has_symlink_leading_path(path, len)) return error("'%s' is beyond a symbolic link", path); pos = cache_name_pos(path, len); ce = pos < 0 ? NULL : active_cache[pos]; if (ce && ce_skip_worktree(ce)) { /* * working directory version is assumed "good" * so updating it does not make sense. * On the other hand, removing it from index should work */ if (allow_remove && remove_file_from_cache(path)) return error("%s: cannot remove from the index", path); return 0; } /* * First things first: get the stat information, to decide * what to do about the pathname! */ if (lstat(path, &st) < 0) return process_lstat_error(path, errno); if (S_ISDIR(st.st_mode)) return process_directory(path, len, &st); /* * Process a regular file */ if (ce && S_ISGITLINK(ce->ce_mode)) return error("%s is already a gitlink, not replacing", path); return add_one_path(ce, path, len, &st); }
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; }
static int write_archive_entry(const unsigned char *sha1, const char *base, int baselen, const char *filename, unsigned mode, int stage, void *context) { static struct strbuf path = STRBUF_INIT; struct archiver_context *c = context; struct archiver_args *args = c->args; write_archive_entry_fn_t write_entry = c->write_entry; struct git_attr_check check[2]; const char *path_without_prefix; int err; args->convert = 0; strbuf_reset(&path); strbuf_grow(&path, PATH_MAX); strbuf_add(&path, args->base, args->baselen); strbuf_add(&path, base, baselen); strbuf_addstr(&path, filename); path_without_prefix = path.buf + args->baselen; setup_archive_check(check); if (!git_check_attr(path_without_prefix, ARRAY_SIZE(check), check)) { if (ATTR_TRUE(check[0].value)) return 0; args->convert = ATTR_TRUE(check[1].value); } if (S_ISDIR(mode) || S_ISGITLINK(mode)) { strbuf_addch(&path, '/'); if (args->verbose) fprintf(stderr, "%.*s\n", (int)path.len, path.buf); err = write_entry(args, sha1, path.buf, path.len, mode); if (err) return err; return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0); } if (args->verbose) fprintf(stderr, "%.*s\n", (int)path.len, path.buf); return write_entry(args, sha1, path.buf, path.len, mode); }
static void validate_no_submodules(const struct worktree *wt) { struct index_state istate = { NULL }; int i, found_submodules = 0; if (read_index_from(&istate, worktree_git_path(wt, "index"), get_worktree_git_dir(wt)) > 0) { for (i = 0; i < istate.cache_nr; i++) { struct cache_entry *ce = istate.cache[i]; if (S_ISGITLINK(ce->ce_mode)) { found_submodules = 1; break; } } } discard_index(&istate); if (found_submodules) die(_("working trees containing submodules cannot be moved or removed")); }
/* * The index sorts alphabetically by entry name, which * means that a gitlink sorts as '\0' at the end, while * a directory (which is defined not as an entry, but as * the files it contains) will sort with the '/' at the * end. */ static enum exist_status directory_exists_in_index(const char *dirname, int len) { int pos = cache_name_pos(dirname, len); if (pos < 0) pos = -pos-1; while (pos < active_nr) { struct cache_entry *ce = active_cache[pos++]; unsigned char endchar; if (strncmp(ce->name, dirname, len)) break; endchar = ce->name[len]; if (endchar > '/') break; if (endchar == '/') return index_directory; if (!endchar && S_ISGITLINK(ce->ce_mode)) return index_gitdir; } return index_nonexistent; }
/* * When a CE gets turned into an unmerged entry, we * want it to be up-to-date */ static int verify_uptodate_1(const struct cache_entry *ce, struct unpack_trees_options *o, enum unpack_trees_error_types error_type) { struct stat st; if (o->index_only) return 0; /* * CE_VALID and CE_SKIP_WORKTREE cheat, we better check again * if this entry is truly up-to-date because this file may be * overwritten. */ if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) ; /* keep checking */ else if (o->reset || ce_uptodate(ce)) return 0; if (!lstat(ce->name, &st)) { int flags = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE; unsigned changed = ie_match_stat(o->src_index, ce, &st, flags); if (!changed) return 0; /* * NEEDSWORK: the current default policy is to allow * submodule to be out of sync wrt the superproject * index. This needs to be tightened later for * submodules that are marked to be automatically * checked out. */ if (S_ISGITLINK(ce->ce_mode)) return 0; errno = 0; } if (errno == ENOENT) return 0; return o->gently ? -1 : add_rejected_path(o, error_type, ce->name); }
static void *preload_thread(void *_data) { int nr; struct thread_data *p = _data; struct index_state *index = p->index; struct cache_entry **cep = index->cache + p->offset; struct cache_def cache = CACHE_DEF_INIT; nr = p->nr; if (nr + p->offset > index->cache_nr) nr = index->cache_nr - p->offset; do { struct cache_entry *ce = *cep++; struct stat st; if (ce_stage(ce)) continue; if (S_ISGITLINK(ce->ce_mode)) continue; if (ce_uptodate(ce)) continue; if (ce_skip_worktree(ce)) continue; if (ce->ce_flags & CE_FSMONITOR_VALID) continue; if (!ce_path_match(index, ce, &p->pathspec, NULL)) continue; if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce))) continue; if (lstat(ce->name, &st)) continue; if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY|CE_MATCH_IGNORE_FSMONITOR)) continue; ce_mark_uptodate(ce); mark_fsmonitor_valid(ce); } while (--nr > 0); cache_def_clear(&cache); return NULL; }
static void *preload_thread(void *_data) { int nr; struct thread_data *p = _data; struct index_state *index = p->index; struct cache_entry **cep = index->cache + p->offset; struct cache_def cache; struct pathspec pathspec; init_pathspec(&pathspec, p->pathspec); memset(&cache, 0, sizeof(cache)); nr = p->nr; if (nr + p->offset > index->cache_nr) nr = index->cache_nr - p->offset; do { struct cache_entry *ce = *cep++; struct stat st; if (ce_stage(ce)) continue; if (S_ISGITLINK(ce->ce_mode)) continue; if (ce_uptodate(ce)) continue; if (!ce_path_match(ce, &pathspec)) continue; if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce))) continue; if (lstat(ce->name, &st)) continue; if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY)) continue; ce_mark_uptodate(ce); } while (--nr > 0); free_pathspec(&pathspec); return NULL; }
static int get_index_dtype(const char *path, int len) { int pos; struct cache_entry *ce; ce = cache_name_exists(path, len, 0); if (ce) { if (!ce_uptodate(ce)) return DT_UNKNOWN; if (S_ISGITLINK(ce->ce_mode)) return DT_DIR; /* * Nobody actually cares about the * difference between DT_LNK and DT_REG */ return DT_REG; } /* Try to look it up as a directory */ pos = cache_name_pos(path, len); if (pos >= 0) return DT_UNKNOWN; pos = -pos-1; while (pos < active_nr) { ce = active_cache[pos++]; if (strncmp(ce->name, path, len)) break; if (ce->name[len] > '/') break; if (ce->name[len] < '/') continue; if (!ce_uptodate(ce)) break; /* continue? */ return DT_DIR; } return DT_UNKNOWN; }
static int write_tar_entry(struct archiver_args *args, const unsigned char *sha1, const char *path, size_t pathlen, unsigned int mode) { struct ustar_header header; struct strbuf ext_header = STRBUF_INIT; unsigned int old_mode = mode; unsigned long size; void *buffer; int err = 0; memset(&header, 0, sizeof(header)); if (S_ISDIR(mode) || S_ISGITLINK(mode)) { *header.typeflag = TYPEFLAG_DIR; mode = (mode | 0777) & ~tar_umask; } else if (S_ISLNK(mode)) { *header.typeflag = TYPEFLAG_LNK; mode |= 0777; } else if (S_ISREG(mode)) { *header.typeflag = TYPEFLAG_REG; mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask; } else { return error("unsupported file mode: 0%o (SHA1: %s)", mode, sha1_to_hex(sha1)); } if (pathlen > sizeof(header.name)) { size_t plen = get_path_prefix(path, pathlen, sizeof(header.prefix)); size_t rest = pathlen - plen - 1; if (plen > 0 && rest <= sizeof(header.name)) { memcpy(header.prefix, path, plen); memcpy(header.name, path + plen + 1, rest); } else { sprintf(header.name, "%s.data", sha1_to_hex(sha1)); strbuf_append_ext_header(&ext_header, "path", path, pathlen); } } else memcpy(header.name, path, pathlen); if (S_ISREG(mode) && !args->convert && sha1_object_info(sha1, &size) == OBJ_BLOB && size > big_file_threshold) buffer = NULL; else if (S_ISLNK(mode) || S_ISREG(mode)) { enum object_type type; buffer = sha1_file_to_archive(args, path, sha1, old_mode, &type, &size); if (!buffer) return error("cannot read %s", sha1_to_hex(sha1)); } else { buffer = NULL; size = 0; } if (S_ISLNK(mode)) { if (size > sizeof(header.linkname)) { sprintf(header.linkname, "see %s.paxheader", sha1_to_hex(sha1)); strbuf_append_ext_header(&ext_header, "linkpath", buffer, size); } else memcpy(header.linkname, buffer, size); } prepare_header(args, &header, mode, size); if (ext_header.len > 0) { err = write_extended_header(args, sha1, ext_header.buf, ext_header.len); if (err) { free(buffer); return err; } } strbuf_release(&ext_header); write_blocked(&header, sizeof(header)); if (S_ISREG(mode) && size > 0) { if (buffer) write_blocked(buffer, size); else err = stream_blocked(sha1); } free(buffer); return err; }
static int verify_clean_subdirectory(const struct cache_entry *ce, enum unpack_trees_error_types error_type, struct unpack_trees_options *o) { /* * we are about to extract "ce->name"; we would not want to lose * anything in the existing directory there. */ int namelen; int i; struct dir_struct d; char *pathbuf; int cnt = 0; unsigned char sha1[20]; if (S_ISGITLINK(ce->ce_mode) && resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) { /* If we are not going to update the submodule, then * we don't care. */ if (!hashcmp(sha1, ce->sha1)) return 0; return verify_clean_submodule(ce, error_type, o); } /* * First let's make sure we do not have a local modification * in that directory. */ namelen = ce_namelen(ce); for (i = locate_in_src_index(ce, o); i < o->src_index->cache_nr; i++) { struct cache_entry *ce2 = o->src_index->cache[i]; int len = ce_namelen(ce2); if (len < namelen || strncmp(ce->name, ce2->name, namelen) || ce2->name[namelen] != '/') break; /* * ce2->name is an entry in the subdirectory to be * removed. */ if (!ce_stage(ce2)) { if (verify_uptodate(ce2, o)) return -1; add_entry(o, ce2, CE_REMOVE, 0); mark_ce_used(ce2, o); } cnt++; } /* * Then we need to make sure that we do not lose a locally * present file that is not ignored. */ pathbuf = xmalloc(namelen + 2); memcpy(pathbuf, ce->name, namelen); strcpy(pathbuf+namelen, "/"); memset(&d, 0, sizeof(d)); if (o->dir) d.exclude_per_dir = o->dir->exclude_per_dir; i = read_directory(&d, pathbuf, namelen+1, NULL); if (i) return o->gently ? -1 : add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name); free(pathbuf); return cnt; }
int cmd_mv(int argc, const char **argv, const char *prefix) { int i, newfd, gitmodules_modified = 0; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT__DRY_RUN(&show_only, N_("dry run")), OPT__FORCE(&force, N_("force move/rename even if target exists")), OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, builtin_mv_usage, 0); if (--argc < 1) usage_with_options(builtin_mv_usage, builtin_mv_options); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die(_("index file corrupt")); source = internal_copy_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); /* * Keep trailing slash, needed to let * "git mv file no-such-dir/" error out. */ dest_path = internal_copy_pathspec(prefix, argv + argc, 1, KEEP_TRAILING_SLASH); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); } else { if (argc != 1) die("destination '%s' is not a directory", dest_path[0]); destination = dest_path; } /* Checking */ for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; if (show_only) printf(_("Checking rename of '%s' to '%s'\n"), src, dst); length = strlen(src); if (lstat(src, &st) < 0) bad = _("bad source"); else if (!strncmp(src, dst, length) && (dst[length] == 0 || dst[length] == '/')) { bad = _("can not move directory into itself"); } else if ((src_is_dir = S_ISDIR(st.st_mode)) && lstat(dst, &st) == 0) bad = _("cannot move directory over file"); else if (src_is_dir) { int first = cache_name_pos(src, length); if (first >= 0) { struct strbuf submodule_dotgit = STRBUF_INIT; if (!S_ISGITLINK(active_cache[first]->ce_mode)) die (_("Huh? Directory %s is in index and no submodule?"), src); if (!is_staging_gitmodules_ok()) die (_("Please, stage your changes to .gitmodules or stash them to proceed")); strbuf_addf(&submodule_dotgit, "%s/.git", src); submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf); if (submodule_gitfile[i]) submodule_gitfile[i] = xstrdup(submodule_gitfile[i]); else submodule_gitfile[i] = SUBMODULE_WITH_GITDIR; strbuf_release(&submodule_dotgit); } else { const char *src_w_slash = add_slash(src); int last, len_w_slash = length + 1; modes[i] = WORKING_DIRECTORY; first = cache_name_pos(src_w_slash, len_w_slash); if (first >= 0) die (_("Huh? %.*s is in index?"), len_w_slash, src_w_slash); first = -1 - first; for (last = first; last < active_nr; last++) { const char *path = active_cache[last]->name; if (strncmp(path, src_w_slash, len_w_slash)) break; } free((char *)src_w_slash); if (last - first < 1) bad = _("source directory is empty"); else { int j, dst_len; if (last - first > 0) { source = xrealloc(source, (argc + last - first) * sizeof(char *)); destination = xrealloc(destination, (argc + last - first) * sizeof(char *)); modes = xrealloc(modes, (argc + last - first) * sizeof(enum update_mode)); submodule_gitfile = xrealloc(submodule_gitfile, (argc + last - first) * sizeof(char *)); } dst = add_slash(dst); dst_len = strlen(dst); for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; source[argc + j] = path; destination[argc + j] = prefix_path(dst, dst_len, path + length + 1); modes[argc + j] = INDEX; submodule_gitfile[argc + j] = NULL; } argc += last - first; } } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); else if (lstat(dst, &st) == 0) { bad = _("destination exists"); if (force) { /* * only files can overwrite each other: * check both source and destination */ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { if (verbose) warning(_("overwriting '%s'"), dst); bad = NULL; } else bad = _("Cannot overwrite"); } } else if (string_list_has_string(&src_for_dst, dst)) bad = _("multiple sources for the same target"); else if (is_dir_sep(dst[strlen(dst) - 1])) bad = _("destination directory does not exist"); else string_list_insert(&src_for_dst, dst); if (bad) { if (ignore_errors) { if (--argc > 0) { memmove(source + i, source + i + 1, (argc - i) * sizeof(char *)); memmove(destination + i, destination + i + 1, (argc - i) * sizeof(char *)); memmove(modes + i, modes + i + 1, (argc - i) * sizeof(enum update_mode)); memmove(submodule_gitfile + i, submodule_gitfile + i + 1, (argc - i) * sizeof(char *)); i--; } } else die (_("%s, source=%s, destination=%s"), bad, src, dst); } } for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); if (!show_only && mode != INDEX) { if (rename(src, dst) < 0 && !ignore_errors) die_errno (_("renaming '%s' failed"), src); if (submodule_gitfile[i]) { if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); if (!update_path_in_gitmodules(src, dst)) gitmodules_modified = 1; } } if (mode == WORKING_DIRECTORY) continue; pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); if (!show_only) rename_cache_entry_at(pos, dst); } if (gitmodules_modified) stage_updated_gitmodules(); if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die(_("Unable to write new index file")); } return 0; }
static void process_tree(struct rev_info *revs, struct tree *tree, show_object_fn show, struct name_path *path, struct strbuf *base, const char *name) { struct object *obj = &tree->object; struct tree_desc desc; struct name_entry entry; struct name_path me; int all_interesting = (revs->diffopt.pathspec.nr == 0); int baselen = base->len; if (!revs->tree_objects) return; if (!obj) die("bad tree object"); if (obj->flags & (UNINTERESTING | SEEN)) return; if (parse_tree(tree) < 0) die("bad tree object %s", sha1_to_hex(obj->sha1)); obj->flags |= SEEN; show(obj, path, name); me.up = path; me.elem = name; me.elem_len = strlen(name); if (!all_interesting) { strbuf_addstr(base, name); if (base->len) strbuf_addch(base, '/'); } init_tree_desc(&desc, tree->buffer, tree->size); while (tree_entry(&desc, &entry)) { if (!all_interesting) { int showit = tree_entry_interesting(&entry, base, 0, &revs->diffopt.pathspec); if (showit < 0) break; else if (!showit) continue; else if (showit == 2) all_interesting = 1; } if (S_ISDIR(entry.mode)) process_tree(revs, lookup_tree(entry.sha1), show, &me, base, entry.path); else if (S_ISGITLINK(entry.mode)) process_gitlink(revs, entry.sha1, show, &me, entry.path); else process_blob(revs, lookup_blob(entry.sha1), show, &me, entry.path); } strbuf_setlen(base, baselen); free(tree->buffer); tree->buffer = NULL; }
static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, int argc, const char **argv) { char tmpdir[PATH_MAX]; struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT; struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT; struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT; struct strbuf wtdir = STRBUF_INIT; char *lbase_dir, *rbase_dir; size_t ldir_len, rdir_len, wtdir_len; const char *workdir, *tmp; int ret = 0, i; FILE *fp; struct hashmap working_tree_dups, submodules, symlinks2; struct hashmap_iter iter; struct pair_entry *entry; struct index_state wtindex; struct checkout lstate, rstate; int rc, flags = RUN_GIT_CMD, err = 0; struct child_process child = CHILD_PROCESS_INIT; const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL }; struct hashmap wt_modified, tmp_modified; int indices_loaded = 0; workdir = get_git_work_tree(); /* Setup temp directories */ tmp = getenv("TMPDIR"); xsnprintf(tmpdir, sizeof(tmpdir), "%s/git-difftool.XXXXXX", tmp ? tmp : "/tmp"); if (!mkdtemp(tmpdir)) return error("could not create '%s'", tmpdir); strbuf_addf(&ldir, "%s/left/", tmpdir); strbuf_addf(&rdir, "%s/right/", tmpdir); strbuf_addstr(&wtdir, workdir); if (!wtdir.len || !is_dir_sep(wtdir.buf[wtdir.len - 1])) strbuf_addch(&wtdir, '/'); mkdir(ldir.buf, 0700); mkdir(rdir.buf, 0700); memset(&wtindex, 0, sizeof(wtindex)); memset(&lstate, 0, sizeof(lstate)); lstate.base_dir = lbase_dir = xstrdup(ldir.buf); lstate.base_dir_len = ldir.len; lstate.force = 1; memset(&rstate, 0, sizeof(rstate)); rstate.base_dir = rbase_dir = xstrdup(rdir.buf); rstate.base_dir_len = rdir.len; rstate.force = 1; ldir_len = ldir.len; rdir_len = rdir.len; wtdir_len = wtdir.len; hashmap_init(&working_tree_dups, (hashmap_cmp_fn)working_tree_entry_cmp, NULL, 0); hashmap_init(&submodules, (hashmap_cmp_fn)pair_cmp, NULL, 0); hashmap_init(&symlinks2, (hashmap_cmp_fn)pair_cmp, NULL, 0); child.no_stdin = 1; child.git_cmd = 1; child.use_shell = 0; child.clean_on_exit = 1; child.dir = prefix; child.out = -1; argv_array_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z", NULL); for (i = 0; i < argc; i++) argv_array_push(&child.args, argv[i]); if (start_command(&child)) die("could not obtain raw diff"); fp = xfdopen(child.out, "r"); /* Build index info for left and right sides of the diff */ i = 0; while (!strbuf_getline_nul(&info, fp)) { int lmode, rmode; struct object_id loid, roid; char status; const char *src_path, *dst_path; if (starts_with(info.buf, "::")) die(N_("combined diff formats('-c' and '--cc') are " "not supported in\n" "directory diff mode('-d' and '--dir-diff').")); if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid, &status)) break; if (strbuf_getline_nul(&lpath, fp)) break; src_path = lpath.buf; i++; if (status != 'C' && status != 'R') { dst_path = src_path; } else { if (strbuf_getline_nul(&rpath, fp)) break; dst_path = rpath.buf; } if (S_ISGITLINK(lmode) || S_ISGITLINK(rmode)) { strbuf_reset(&buf); strbuf_addf(&buf, "Subproject commit %s", oid_to_hex(&loid)); add_left_or_right(&submodules, src_path, buf.buf, 0); strbuf_reset(&buf); strbuf_addf(&buf, "Subproject commit %s", oid_to_hex(&roid)); if (!oidcmp(&loid, &roid)) strbuf_addstr(&buf, "-dirty"); add_left_or_right(&submodules, dst_path, buf.buf, 1); continue; } if (S_ISLNK(lmode)) { char *content = get_symlink(&loid, src_path); add_left_or_right(&symlinks2, src_path, content, 0); free(content); } if (S_ISLNK(rmode)) { char *content = get_symlink(&roid, dst_path); add_left_or_right(&symlinks2, dst_path, content, 1); free(content); } if (lmode && status != 'C') { if (checkout_path(lmode, &loid, src_path, &lstate)) { ret = error("could not write '%s'", src_path); goto finish; } } if (rmode && !S_ISLNK(rmode)) { struct working_tree_entry *entry; /* Avoid duplicate working_tree entries */ FLEX_ALLOC_STR(entry, path, dst_path); hashmap_entry_init(entry, strhash(dst_path)); if (hashmap_get(&working_tree_dups, entry, NULL)) { free(entry); continue; } hashmap_add(&working_tree_dups, entry); if (!use_wt_file(workdir, dst_path, &roid)) { if (checkout_path(rmode, &roid, dst_path, &rstate)) { ret = error("could not write '%s'", dst_path); goto finish; } } else if (!is_null_oid(&roid)) { /* * Changes in the working tree need special * treatment since they are not part of the * index. */ struct cache_entry *ce2 = make_cache_entry(rmode, roid.hash, dst_path, 0, 0); add_index_entry(&wtindex, ce2, ADD_CACHE_JUST_APPEND); add_path(&rdir, rdir_len, dst_path); if (ensure_leading_directories(rdir.buf)) { ret = error("could not create " "directory for '%s'", dst_path); goto finish; } add_path(&wtdir, wtdir_len, dst_path); if (symlinks) { if (symlink(wtdir.buf, rdir.buf)) { ret = error_errno("could not symlink '%s' to '%s'", wtdir.buf, rdir.buf); goto finish; } } else { struct stat st; if (stat(wtdir.buf, &st)) st.st_mode = 0644; if (copy_file(rdir.buf, wtdir.buf, st.st_mode)) { ret = error("could not copy '%s' to '%s'", wtdir.buf, rdir.buf); goto finish; } } } } } fclose(fp); fp = NULL; if (finish_command(&child)) { ret = error("error occurred running diff --raw"); goto finish; } if (!i) goto finish; /* * Changes to submodules require special treatment.This loop writes a * temporary file to both the left and right directories to show the * change in the recorded SHA1 for the submodule. */ hashmap_iter_init(&submodules, &iter); while ((entry = hashmap_iter_next(&iter))) { if (*entry->left) { add_path(&ldir, ldir_len, entry->path); ensure_leading_directories(ldir.buf); write_file(ldir.buf, "%s", entry->left); } if (*entry->right) { add_path(&rdir, rdir_len, entry->path); ensure_leading_directories(rdir.buf); write_file(rdir.buf, "%s", entry->right); } } /* * Symbolic links require special treatment.The standard "git diff" * shows only the link itself, not the contents of the link target. * This loop replicates that behavior. */ hashmap_iter_init(&symlinks2, &iter); while ((entry = hashmap_iter_next(&iter))) { if (*entry->left) { add_path(&ldir, ldir_len, entry->path); ensure_leading_directories(ldir.buf); write_file(ldir.buf, "%s", entry->left); } if (*entry->right) { add_path(&rdir, rdir_len, entry->path); ensure_leading_directories(rdir.buf); write_file(rdir.buf, "%s", entry->right); } } strbuf_release(&buf); strbuf_setlen(&ldir, ldir_len); helper_argv[1] = ldir.buf; strbuf_setlen(&rdir, rdir_len); helper_argv[2] = rdir.buf; if (extcmd) { helper_argv[0] = extcmd; flags = 0; } else setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1); rc = run_command_v_opt(helper_argv, flags); /* * If the diff includes working copy files and those * files were modified during the diff, then the changes * should be copied back to the working tree. * Do not copy back files when symlinks are used and the * external tool did not replace the original link with a file. * * These hashes are loaded lazily since they aren't needed * in the common case of --symlinks and the difftool updating * files through the symlink. */ hashmap_init(&wt_modified, (hashmap_cmp_fn)path_entry_cmp, NULL, wtindex.cache_nr); hashmap_init(&tmp_modified, (hashmap_cmp_fn)path_entry_cmp, NULL, wtindex.cache_nr); for (i = 0; i < wtindex.cache_nr; i++) { struct hashmap_entry dummy; const char *name = wtindex.cache[i]->name; struct stat st; add_path(&rdir, rdir_len, name); if (lstat(rdir.buf, &st)) continue; if ((symlinks && S_ISLNK(st.st_mode)) || !S_ISREG(st.st_mode)) continue; if (!indices_loaded) { static struct lock_file lock; strbuf_reset(&buf); strbuf_addf(&buf, "%s/wtindex", tmpdir); if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 || write_locked_index(&wtindex, &lock, COMMIT_LOCK)) { ret = error("could not write %s", buf.buf); rollback_lock_file(&lock); goto finish; } changed_files(&wt_modified, buf.buf, workdir); strbuf_setlen(&rdir, rdir_len); changed_files(&tmp_modified, buf.buf, rdir.buf); add_path(&rdir, rdir_len, name); indices_loaded = 1; } hashmap_entry_init(&dummy, strhash(name)); if (hashmap_get(&tmp_modified, &dummy, name)) { add_path(&wtdir, wtdir_len, name); if (hashmap_get(&wt_modified, &dummy, name)) { warning(_("both files modified: '%s' and '%s'."), wtdir.buf, rdir.buf); warning(_("working tree file has been left.")); warning("%s", ""); err = 1; } else if (unlink(wtdir.buf) || copy_file(wtdir.buf, rdir.buf, st.st_mode)) warning_errno(_("could not copy '%s' to '%s'"), rdir.buf, wtdir.buf); } } if (err) { warning(_("temporary files exist in '%s'."), tmpdir); warning(_("you may want to cleanup or recover these.")); exit(1); } else exit_cleanup(tmpdir, rc); finish: if (fp) fclose(fp); free(lbase_dir); free(rbase_dir); strbuf_release(&ldir); strbuf_release(&rdir); strbuf_release(&wtdir); strbuf_release(&buf); return ret; }
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, struct tree_desc *tree, struct strbuf *base, int tn_len, int check_attr) { int hit = 0; enum interesting match = entry_not_interesting; struct name_entry entry; int old_baselen = base->len; struct strbuf name = STRBUF_INIT; int name_base_len = 0; if (super_prefix) { strbuf_addstr(&name, super_prefix); name_base_len = name.len; } while (tree_entry(tree, &entry)) { int te_len = tree_entry_len(&entry); if (match != all_entries_interesting) { strbuf_addstr(&name, base->buf + tn_len); match = tree_entry_interesting(&entry, &name, 0, pathspec); strbuf_setlen(&name, name_base_len); if (match == all_entries_not_interesting) break; if (match == entry_not_interesting) continue; } strbuf_add(base, entry.path, te_len); if (S_ISREG(entry.mode)) { hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len, check_attr ? base->buf + tn_len : NULL); } else if (S_ISDIR(entry.mode)) { enum object_type type; struct tree_desc sub; void *data; unsigned long size; data = lock_and_read_sha1_file(entry.oid->hash, &type, &size); if (!data) die(_("unable to read tree (%s)"), oid_to_hex(entry.oid)); strbuf_addch(base, '/'); init_tree_desc(&sub, data, size); hit |= grep_tree(opt, pathspec, &sub, base, tn_len, check_attr); free(data); } else if (recurse_submodules && S_ISGITLINK(entry.mode)) { hit |= grep_submodule(opt, entry.oid->hash, base->buf, base->buf + tn_len); } strbuf_setlen(base, old_baselen); if (hit && opt->status_only) break; } strbuf_release(&name); return hit; }
int fetch_populated_submodules(int num_options, const char **options, const char *prefix, int command_line_option, int quiet) { int i, result = 0, argc = 0, default_argc; struct child_process cp; const char **argv; struct string_list_item *name_for_path; const char *work_tree = get_git_work_tree(); if (!work_tree) goto out; if (!the_index.initialized) if (read_cache() < 0) die("index file corrupt"); /* 6: "fetch" (options) --recurse-submodules-default default "--submodule-prefix" prefix NULL */ argv = xcalloc(num_options + 6, sizeof(const char *)); argv[argc++] = "fetch"; for (i = 0; i < num_options; i++) argv[argc++] = options[i]; argv[argc++] = "--recurse-submodules-default"; default_argc = argc++; argv[argc++] = "--submodule-prefix"; memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; cp.no_stdin = 1; calculate_changed_submodule_paths(); for (i = 0; i < active_nr; i++) { struct strbuf submodule_path = STRBUF_INIT; struct strbuf submodule_git_dir = STRBUF_INIT; struct strbuf submodule_prefix = STRBUF_INIT; struct cache_entry *ce = active_cache[i]; const char *git_dir, *name, *default_argv; if (!S_ISGITLINK(ce->ce_mode)) continue; name = ce->name; name_for_path = unsorted_string_list_lookup(&config_name_for_path, ce->name); if (name_for_path) name = name_for_path->util; default_argv = "yes"; if (command_line_option == RECURSE_SUBMODULES_DEFAULT) { struct string_list_item *fetch_recurse_submodules_option; fetch_recurse_submodules_option = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name); if (fetch_recurse_submodules_option) { if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_OFF) continue; if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_ON_DEMAND) { if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name)) continue; default_argv = "on-demand"; } } else { if ((config_fetch_recurse_submodules == RECURSE_SUBMODULES_OFF) || gitmodules_is_unmerged) continue; if (config_fetch_recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) { if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name)) continue; default_argv = "on-demand"; } } } else if (command_line_option == RECURSE_SUBMODULES_ON_DEMAND) { if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name)) continue; default_argv = "on-demand"; } strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name); strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf); strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name); git_dir = read_gitfile(submodule_git_dir.buf); if (!git_dir) git_dir = submodule_git_dir.buf; if (is_directory(git_dir)) { if (!quiet) printf("Fetching submodule %s%s\n", prefix, ce->name); cp.dir = submodule_path.buf; argv[default_argc] = default_argv; argv[argc] = submodule_prefix.buf; if (run_command(&cp)) result = 1; } strbuf_release(&submodule_path); strbuf_release(&submodule_git_dir); strbuf_release(&submodule_prefix); } free(argv); out: string_list_clear(&changed_submodule_paths, 1); return result; }
static void process_tree(struct rev_info *revs, struct tree *tree, show_object_fn show, struct strbuf *base, const char *name, void *cb_data) { struct object *obj = &tree->object; struct tree_desc desc; struct name_entry entry; enum interesting match = revs->diffopt.pathspec.nr == 0 ? all_entries_interesting: entry_not_interesting; int baselen = base->len; if (!revs->tree_objects) return; if (!obj) die("bad tree object"); if (obj->flags & (UNINTERESTING | SEEN)) return; if (parse_tree_gently(tree, revs->ignore_missing_links) < 0) { if (revs->ignore_missing_links) return; die("bad tree object %s", oid_to_hex(&obj->oid)); } obj->flags |= SEEN; strbuf_addstr(base, name); show(obj, base->buf, cb_data); if (base->len) strbuf_addch(base, '/'); init_tree_desc(&desc, tree->buffer, tree->size); while (tree_entry(&desc, &entry)) { if (match != all_entries_interesting) { match = tree_entry_interesting(&entry, base, 0, &revs->diffopt.pathspec); if (match == all_entries_not_interesting) break; if (match == entry_not_interesting) continue; } if (S_ISDIR(entry.mode)) process_tree(revs, lookup_tree(entry.sha1), show, base, entry.path, cb_data); else if (S_ISGITLINK(entry.mode)) process_gitlink(revs, entry.sha1, show, base, entry.path, cb_data); else process_blob(revs, lookup_blob(entry.sha1), show, base, entry.path, cb_data); } strbuf_setlen(base, baselen); free_tree_buffer(tree); }
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; }
int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; const char **pathspec; char *seen; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_rm_options, builtin_rm_usage, 0); if (!argc) usage_with_options(builtin_rm_usage, builtin_rm_options); if (!index_only) setup_work_tree(); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die(_("index file corrupt")); /* * Drop trailing directory separators from directories so we'll find * submodules in the index. */ for (i = 0; i < argc; i++) { size_t pathlen = strlen(argv[i]); if (pathlen && is_dir_sep(argv[i][pathlen - 1]) && is_directory(argv[i])) { do { pathlen--; } while (pathlen && is_dir_sep(argv[i][pathlen - 1])); argv[i] = xmemdupz(argv[i], pathlen); } } pathspec = get_pathspec(prefix, argv); refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL); seen = NULL; for (i = 0; pathspec[i] ; i++) /* nothing */; seen = xcalloc(i, 1); for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); list.entry[list.nr].name = ce->name; list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode); } if (pathspec) { const char *match; int seen_any = 0; for (i = 0; (match = pathspec[i]) != NULL ; i++) { if (!seen[i]) { if (!ignore_unmatch) { die(_("pathspec '%s' did not match any files"), match); } } else { seen_any = 1; } if (!recursive && seen[i] == MATCHED_RECURSIVELY) die(_("not removing '%s' recursively without -r"), *match ? match : "."); } if (! seen_any) exit(0); } /* * If not forced, the file, the index and the HEAD (if exists) * must match; but the file can already been removed, since * this sequence is a natural "novice" way: * * rm F; git rm F * * Further, if HEAD commit exists, "diff-index --cached" must * 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()) exit(1); } /* * First remove the names from the index: we won't commit * the index unless all of them succeed. */ for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (!quiet) printf("rm '%s'\n", path); if (remove_file_from_cache(path)) die(_("git rm: unable to remove %s"), path); } if (show_only) return 0; /* * Then, unless we used "--cached", remove the filenames from * the workspace. If we fail to remove the first one, we * abort the "git rm" (but once we've successfully removed * any file at all, we'll go ahead and commit to it all: * by then we've already committed ourselves and can't fail * in the middle) */ if (!index_only) { int removed = 0; 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; continue; } } else { struct strbuf buf = STRBUF_INIT; strbuf_addstr(&buf, path); if (!remove_dir_recursively(&buf, 0)) { removed = 1; strbuf_release(&buf); continue; } strbuf_release(&buf); /* Fallthrough and let remove_path() fail. */ } } if (!remove_path(path)) { removed = 1; continue; } if (!removed) die_errno("git rm: '%s'", path); } } if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die(_("Unable to write new index file")); } return 0; }