/* * Has the work tree entity been removed? * * Return 1 if it was removed from the work tree, 0 if an entity to be * compared with the cache entry ce still exists (the latter includes * the case where a directory that is not a submodule repository * exists for ce that is a submodule -- it is a submodule that is not * checked out). Return negative for an error. */ static int check_removed(const struct cache_entry *ce, struct stat *st) { if (lstat(ce->name, st) < 0) { if (errno != ENOENT && errno != ENOTDIR) return -1; return 1; } if (has_symlink_leading_path(ce->name, ce_namelen(ce))) return 1; if (S_ISDIR(st->st_mode)) { unsigned char sub[20]; /* * If ce is already a gitlink, we can have a plain * directory (i.e. the submodule is not checked out), * or a checked out submodule. Either case this is not * a case where something was removed from the work tree, * so we will return 0. * * Otherwise, if the directory is not a submodule * repository, that means ce which was a blob turned into * a directory --- the blob was removed! */ if (!S_ISGITLINK(ce->ce_mode) && resolve_gitlink_ref(ce->name, "HEAD", sub)) return 1; } return 0; }
static enum directory_treatment treat_directory(struct dir_struct *dir, const char *dirname, int len, const struct path_simplify *simplify) { /* The "len-1" is to strip the final '/' */ switch (directory_exists_in_index(dirname, len-1)) { case index_directory: return recurse_into_directory; case index_gitdir: if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) return ignore_directory; return show_directory; case index_nonexistent: if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) break; if (!(dir->flags & DIR_NO_GITLINKS)) { unsigned char sha1[20]; if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0) return show_directory; } return recurse_into_directory; } /* This is the "show_other_directories" case */ if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) return show_directory; if (!read_directory_recursive(dir, dirname, len, 1, simplify)) return ignore_directory; return show_directory; }
/* * 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); }
int remove_dir_recursively(struct strbuf *path, int flag) { DIR *dir; struct dirent *e; int ret = 0, original_len = path->len, len; int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY); int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL); unsigned char submodule_head[20]; if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) && !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) /* Do not descend and nuke a nested git work tree. */ return 0; flag &= ~(REMOVE_DIR_KEEP_TOPLEVEL|REMOVE_DIR_KEEP_NESTED_GIT); dir = opendir(path->buf); if (!dir) { if (!keep_toplevel) return rmdir(path->buf); else return -1; } if (path->buf[original_len - 1] != '/') strbuf_addch(path, '/'); len = path->len; while ((e = readdir(dir)) != NULL) { struct stat st; if (is_dot_or_dotdot(e->d_name)) continue; strbuf_setlen(path, len); strbuf_addstr(path, e->d_name); if (lstat(path->buf, &st)) ; /* fall thru */ else if (S_ISDIR(st.st_mode)) { if (!remove_dir_recursively(path, flag)) continue; /* happy */ } else if (!only_empty && !unlink(path->buf)) continue; /* happy, too */ /* path too long, stat fails, or non-directory still exists */ ret = -1; break; } closedir(dir); strbuf_setlen(path, original_len); if (!ret && !keep_toplevel) ret = rmdir(path->buf); return ret; }
static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data) { unsigned char sha1[20]; int flag; if (submodule) { if (resolve_gitlink_ref(submodule, "HEAD", sha1) == 0) return fn("HEAD", sha1, 0, cb_data); return 0; } if (resolve_ref("HEAD", sha1, 1, &flag)) return fn("HEAD", sha1, flag, cb_data); return 0; }
int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) { struct object_id oid; int flag; if (submodule) { if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0) return fn("HEAD", &oid, 0, cb_data); return 0; } if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag)) return fn("HEAD", &oid, flag, cb_data); return 0; }
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; }
static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up) { DIR *dir; struct dirent *e; int ret = 0, original_len = path->len, len, kept_down = 0; int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY); int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL); unsigned char submodule_head[20]; if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) && !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) { /* Do not descend and nuke a nested git work tree. */ if (kept_up) *kept_up = 1; return 0; } flag &= ~REMOVE_DIR_KEEP_TOPLEVEL; dir = opendir(path->buf); if (!dir) { /* an empty dir could be removed even if it is unreadble */ if (!keep_toplevel) return rmdir(path->buf); else return -1; } if (path->buf[original_len - 1] != '/') strbuf_addch(path, '/'); len = path->len; while ((e = readdir(dir)) != NULL) { struct stat st; if (is_dot_or_dotdot(e->d_name)) continue; strbuf_setlen(path, len); strbuf_addstr(path, e->d_name); if (lstat(path->buf, &st)) ; /* fall thru */ else if (S_ISDIR(st.st_mode)) { if (!remove_dir_recurse(path, flag, &kept_down)) continue; /* happy */ } else if (!only_empty && !unlink(path->buf)) continue; /* happy, too */ /* path too long, stat fails, or non-directory still exists */ ret = -1; break; } closedir(dir); strbuf_setlen(path, original_len); if (!ret && !keep_toplevel && !kept_down) ret = rmdir(path->buf); else if (kept_up) /* * report the uplevel that it is not an error that we * did not rmdir() our directory. */ *kept_up = !ret; return ret; }
static struct ref_list *get_ref_dir(const char *submodule, const char *base, struct ref_list *list) { DIR *dir; const char *path; if (submodule) path = git_path_submodule(submodule, "%s", base); else path = git_path("%s", base); dir = opendir(path); if (dir) { struct dirent *de; int baselen = strlen(base); char *ref = xmalloc(baselen + 257); memcpy(ref, base, baselen); if (baselen && base[baselen-1] != '/') ref[baselen++] = '/'; while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; struct stat st; int flag; int namelen; const char *refdir; if (de->d_name[0] == '.') continue; namelen = strlen(de->d_name); if (namelen > 255) continue; if (has_extension(de->d_name, ".lock")) continue; memcpy(ref + baselen, de->d_name, namelen+1); refdir = submodule ? git_path_submodule(submodule, "%s", ref) : git_path("%s", ref); if (stat(refdir, &st) < 0) continue; if (S_ISDIR(st.st_mode)) { list = get_ref_dir(submodule, ref, list); continue; } if (submodule) { hashclr(sha1); flag = 0; if (resolve_gitlink_ref(submodule, ref, sha1) < 0) { hashclr(sha1); flag |= REF_BROKEN; } } else if (!resolve_ref(ref, sha1, 1, &flag)) { hashclr(sha1); flag |= REF_BROKEN; } list = add_ref(ref, sha1, flag, list, NULL); } free(ref); closedir(dir); } return sort_ref_list(list); }
static void show_patch_diff(struct combine_diff_path *elem, int num_parent, int dense, struct rev_info *rev) { struct diff_options *opt = &rev->diffopt; unsigned long result_size, cnt, lno; char *result, *cp; struct sline *sline; /* survived lines */ int mode_differs = 0; int i, show_hunks; int working_tree_file = is_null_sha1(elem->sha1); int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV; const char *a_prefix, *b_prefix; mmfile_t result_file; context = opt->context; a_prefix = opt->a_prefix ? opt->a_prefix : "a/"; b_prefix = opt->b_prefix ? opt->b_prefix : "b/"; /* Read the result of merge first */ if (!working_tree_file) result = grab_blob(elem->sha1, elem->mode, &result_size); else { /* Used by diff-tree to read from the working tree */ struct stat st; int fd = -1; if (lstat(elem->path, &st) < 0) goto deleted_file; if (S_ISLNK(st.st_mode)) { struct strbuf buf = STRBUF_INIT; if (strbuf_readlink(&buf, elem->path, st.st_size) < 0) { error("readlink(%s): %s", elem->path, strerror(errno)); return; } result_size = buf.len; result = strbuf_detach(&buf, NULL); elem->mode = canon_mode(st.st_mode); } else if (S_ISDIR(st.st_mode)) { unsigned char sha1[20]; if (resolve_gitlink_ref(elem->path, "HEAD", sha1) < 0) result = grab_blob(elem->sha1, elem->mode, &result_size); else result = grab_blob(sha1, elem->mode, &result_size); } else if (0 <= (fd = open(elem->path, O_RDONLY))) { size_t len = xsize_t(st.st_size); ssize_t done; int is_file, i; elem->mode = canon_mode(st.st_mode); /* if symlinks don't work, assume symlink if all parents * are symlinks */ is_file = has_symlinks; for (i = 0; !is_file && i < num_parent; i++) is_file = !S_ISLNK(elem->parent[i].mode); if (!is_file) elem->mode = canon_mode(S_IFLNK); result_size = len; result = xmalloc(len + 1); done = read_in_full(fd, result, len); if (done < 0) die_errno("read error '%s'", elem->path); else if (done < len) die("early EOF '%s'", elem->path); result[len] = 0; /* If not a fake symlink, apply filters, e.g. autocrlf */ if (is_file) { struct strbuf buf = STRBUF_INIT; if (convert_to_git(elem->path, result, len, &buf, safe_crlf)) { free(result); result = strbuf_detach(&buf, &len); result_size = len; } } } else { deleted_file: result_size = 0; elem->mode = 0; result = xcalloc(1, 1); } if (0 <= fd) close(fd); } for (cnt = 0, cp = result; cp < result + result_size; cp++) { if (*cp == '\n') cnt++; } if (result_size && result[result_size-1] != '\n') cnt++; /* incomplete line */ sline = xcalloc(cnt+2, sizeof(*sline)); sline[0].bol = result; for (lno = 0; lno <= cnt + 1; lno++) { sline[lno].lost_tail = &sline[lno].lost_head; sline[lno].flag = 0; } for (lno = 0, cp = result; cp < result + result_size; cp++) { if (*cp == '\n') { sline[lno].len = cp - sline[lno].bol; lno++; if (lno < cnt) sline[lno].bol = cp + 1; } } if (result_size && result[result_size-1] != '\n') sline[cnt-1].len = result_size - (sline[cnt-1].bol - result); result_file.ptr = result; result_file.size = result_size; /* Even p_lno[cnt+1] is valid -- that is for the end line number * for deletion hunk at the end. */ sline[0].p_lno = xcalloc((cnt+2) * num_parent, sizeof(unsigned long)); for (lno = 0; lno <= cnt; lno++) sline[lno+1].p_lno = sline[lno].p_lno + num_parent; for (i = 0; i < num_parent; i++) { int j; for (j = 0; j < i; j++) { if (!hashcmp(elem->parent[i].sha1, elem->parent[j].sha1)) { reuse_combine_diff(sline, cnt, i, j); break; } } if (i <= j) combine_diff(elem->parent[i].sha1, elem->parent[i].mode, &result_file, sline, cnt, i, num_parent); if (elem->parent[i].mode != elem->mode) mode_differs = 1; } show_hunks = make_hunks(sline, cnt, num_parent, dense); if (show_hunks || mode_differs || working_tree_file) { const char *abb; int use_color = DIFF_OPT_TST(opt, COLOR_DIFF); const char *c_meta = diff_get_color(use_color, DIFF_METAINFO); const char *c_reset = diff_get_color(use_color, DIFF_RESET); int added = 0; int deleted = 0; if (rev->loginfo && !rev->no_commit_id) show_log(rev); dump_quoted_path(dense ? "diff --cc " : "diff --combined ", "", elem->path, c_meta, c_reset); printf("%sindex ", c_meta); for (i = 0; i < num_parent; i++) { abb = find_unique_abbrev(elem->parent[i].sha1, abbrev); printf("%s%s", i ? "," : "", abb); } abb = find_unique_abbrev(elem->sha1, abbrev); printf("..%s%s\n", abb, c_reset); if (mode_differs) { deleted = !elem->mode; /* We say it was added if nobody had it */ added = !deleted; for (i = 0; added && i < num_parent; i++) if (elem->parent[i].status != DIFF_STATUS_ADDED) added = 0; if (added) printf("%snew file mode %06o", c_meta, elem->mode); else { if (deleted) printf("%sdeleted file ", c_meta); printf("mode "); for (i = 0; i < num_parent; i++) { printf("%s%06o", i ? "," : "", elem->parent[i].mode); } if (elem->mode) printf("..%06o", elem->mode); } printf("%s\n", c_reset); } if (added) dump_quoted_path("--- ", "", "/dev/null", c_meta, c_reset); else dump_quoted_path("--- ", a_prefix, elem->path, c_meta, c_reset); if (deleted) dump_quoted_path("+++ ", "", "/dev/null", c_meta, c_reset); else dump_quoted_path("+++ ", b_prefix, elem->path, c_meta, c_reset); dump_sline(sline, cnt, num_parent, DIFF_OPT_TST(opt, COLOR_DIFF)); } free(result); for (lno = 0; lno < cnt; lno++) { if (sline[lno].lost_head) { struct lline *ll = sline[lno].lost_head; while (ll) { struct lline *tmp = ll; ll = ll->next; free(tmp); } } } free(sline[0].p_lno); free(sline); }