#include "cache.h"
#include "config.h"
#include "dir.h"
+#include "object-store.h"
#include "attr.h"
#include "refs.h"
#include "wildmatch.h"
#include "utf8.h"
#include "varint.h"
#include "ewah/ewok.h"
+#include "fsmonitor.h"
+#include "submodule-config.h"
/*
* Tells read_directory_recursive how a file or directory should be treated.
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
struct index_state *istate, const char *path, int len,
struct untracked_cache_dir *untracked,
- int check_only, const struct pathspec *pathspec);
+ int check_only, int stop_at_first_file, const struct pathspec *pathspec);
static int get_dtype(struct dirent *de, struct index_state *istate,
const char *path, int len);
return 1;
}
+/*
+ * Read the contents of the blob with the given OID into a buffer.
+ * Append a trailing LF to the end if the last line doesn't have one.
+ *
+ * Returns:
+ * -1 when the OID is invalid or unknown or does not refer to a blob.
+ * 0 when the blob is empty.
+ * 1 along with { data, size } of the (possibly augmented) buffer
+ * when successful.
+ *
+ * Optionally updates the given oid_stat with the given OID (when valid).
+ */
+static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
+ size_t *size_out, char **data_out)
+{
+ enum object_type type;
+ unsigned long sz;
+ char *data;
+
+ *size_out = 0;
+ *data_out = NULL;
+
+ data = read_object_file(oid, &type, &sz);
+ if (!data || type != OBJ_BLOB) {
+ free(data);
+ return -1;
+ }
+
+ if (oid_stat) {
+ memset(&oid_stat->stat, 0, sizeof(oid_stat->stat));
+ oidcpy(&oid_stat->oid, oid);
+ }
+
+ if (sz == 0) {
+ free(data);
+ return 0;
+ }
+
+ if (data[sz - 1] != '\n') {
+ data = xrealloc(data, st_add(sz, 1));
+ data[sz++] = '\n';
+ }
+
+ *size_out = xsize_t(sz);
+ *data_out = data;
+
+ return 1;
+}
+
#define DO_MATCH_EXCLUDE (1<<0)
#define DO_MATCH_DIRECTORY (1<<1)
#define DO_MATCH_SUBMODULE (1<<2)
if (found_dup)
continue;
- error("pathspec '%s' did not match any file(s) known to git.",
+ error(_("pathspec '%s' did not match any file(s) known to git"),
pathspec->items[num].original);
errors++;
}
x->el = el;
}
-static void *read_skip_worktree_file_from_index(const struct index_state *istate,
- const char *path, size_t *size,
- struct sha1_stat *sha1_stat)
+static int read_skip_worktree_file_from_index(const struct index_state *istate,
+ const char *path,
+ size_t *size_out, char **data_out,
+ struct oid_stat *oid_stat)
{
int pos, len;
- unsigned long sz;
- enum object_type type;
- void *data;
len = strlen(path);
pos = index_name_pos(istate, path, len);
if (pos < 0)
- return NULL;
+ return -1;
if (!ce_skip_worktree(istate->cache[pos]))
- return NULL;
- data = read_sha1_file(istate->cache[pos]->oid.hash, &type, &sz);
- if (!data || type != OBJ_BLOB) {
- free(data);
- return NULL;
- }
- *size = xsize_t(sz);
- if (sha1_stat) {
- memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat));
- hashcpy(sha1_stat->sha1, istate->cache[pos]->oid.hash);
- }
- return data;
+ return -1;
+
+ return do_read_blob(&istate->cache[pos]->oid, oid_stat, size_out, data_out);
}
/*
FLEX_ALLOC_MEM(d, name, name, len);
ALLOC_GROW(dir->dirs, dir->dirs_nr + 1, dir->dirs_alloc);
- memmove(dir->dirs + first + 1, dir->dirs + first,
- (dir->dirs_nr - first) * sizeof(*dir->dirs));
+ MOVE_ARRAY(dir->dirs + first + 1, dir->dirs + first,
+ dir->dirs_nr - first);
dir->dirs_nr++;
dir->dirs[first] = d;
return d;
struct untracked_cache_dir *dir)
{
int i;
- uc->dir_invalidated++;
+
+ /*
+ * Invalidation increment here is just roughly correct. If
+ * untracked_nr or any of dirs[].recurse is non-zero, we
+ * should increment dir_invalidated too. But that's more
+ * expensive to do.
+ */
+ if (dir->valid)
+ uc->dir_invalidated++;
+
dir->valid = 0;
dir->untracked_nr = 0;
for (i = 0; i < dir->dirs_nr; i++)
dir->dirs[i]->recurse = 0;
}
+static int add_excludes_from_buffer(char *buf, size_t size,
+ const char *base, int baselen,
+ struct exclude_list *el);
+
/*
* Given a file with name "fname", read it (either from disk, or from
* an index if 'istate' is non-null), parse it and store the
* ss_valid is non-zero, "ss" must contain good value as input.
*/
static int add_excludes(const char *fname, const char *base, int baselen,
- struct exclude_list *el,
- struct index_state *istate,
- struct sha1_stat *sha1_stat)
+ struct exclude_list *el, struct index_state *istate,
+ struct oid_stat *oid_stat)
{
struct stat st;
- int fd, i, lineno = 1;
+ int r;
+ int fd;
size_t size = 0;
- char *buf, *entry;
+ char *buf;
fd = open(fname, O_RDONLY);
if (fd < 0 || fstat(fd, &st) < 0) {
warn_on_fopen_errors(fname);
else
close(fd);
- if (!istate ||
- (buf = read_skip_worktree_file_from_index(istate, fname, &size, sha1_stat)) == NULL)
+ if (!istate)
return -1;
- if (size == 0) {
- free(buf);
- return 0;
- }
- if (buf[size-1] != '\n') {
- buf = xrealloc(buf, st_add(size, 1));
- buf[size++] = '\n';
- }
+ r = read_skip_worktree_file_from_index(istate, fname,
+ &size, &buf,
+ oid_stat);
+ if (r != 1)
+ return r;
} else {
size = xsize_t(st.st_size);
if (size == 0) {
- if (sha1_stat) {
- fill_stat_data(&sha1_stat->stat, &st);
- hashcpy(sha1_stat->sha1, EMPTY_BLOB_SHA1_BIN);
- sha1_stat->valid = 1;
+ if (oid_stat) {
+ fill_stat_data(&oid_stat->stat, &st);
+ oidcpy(&oid_stat->oid, the_hash_algo->empty_blob);
+ oid_stat->valid = 1;
}
close(fd);
return 0;
}
buf[size++] = '\n';
close(fd);
- if (sha1_stat) {
+ if (oid_stat) {
int pos;
- if (sha1_stat->valid &&
- !match_stat_data_racy(istate, &sha1_stat->stat, &st))
+ if (oid_stat->valid &&
+ !match_stat_data_racy(istate, &oid_stat->stat, &st))
; /* no content change, ss->sha1 still good */
else if (istate &&
(pos = index_name_pos(istate, fname, strlen(fname))) >= 0 &&
!ce_stage(istate->cache[pos]) &&
ce_uptodate(istate->cache[pos]) &&
!would_convert_to_git(istate, fname))
- hashcpy(sha1_stat->sha1,
- istate->cache[pos]->oid.hash);
+ oidcpy(&oid_stat->oid,
+ &istate->cache[pos]->oid);
else
- hash_sha1_file(buf, size, "blob", sha1_stat->sha1);
- fill_stat_data(&sha1_stat->stat, &st);
- sha1_stat->valid = 1;
+ hash_object_file(buf, size, "blob",
+ &oid_stat->oid);
+ fill_stat_data(&oid_stat->stat, &st);
+ oid_stat->valid = 1;
}
}
+ add_excludes_from_buffer(buf, size, base, baselen, el);
+ return 0;
+}
+
+static int add_excludes_from_buffer(char *buf, size_t size,
+ const char *base, int baselen,
+ struct exclude_list *el)
+{
+ int i, lineno = 1;
+ char *entry;
+
el->filebuf = buf;
if (skip_utf8_bom(&buf, size))
return add_excludes(fname, base, baselen, el, istate, NULL);
}
+int add_excludes_from_blob_to_list(
+ struct object_id *oid,
+ const char *base, int baselen,
+ struct exclude_list *el)
+{
+ char *buf;
+ size_t size;
+ int r;
+
+ r = do_read_blob(oid, NULL, &size, &buf);
+ if (r != 1)
+ return r;
+
+ add_excludes_from_buffer(buf, size, base, baselen, el);
+ return 0;
+}
+
struct exclude_list *add_exclude_list(struct dir_struct *dir,
int group_type, const char *src)
{
* Used to set up core.excludesfile and .git/info/exclude lists.
*/
static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
- struct sha1_stat *sha1_stat)
+ struct oid_stat *oid_stat)
{
struct exclude_list *el;
/*
if (!dir->untracked)
dir->unmanaged_exclude_files++;
el = add_exclude_list(dir, EXC_FILE, fname);
- if (add_excludes(fname, "", 0, el, NULL, sha1_stat) < 0)
- die("cannot use %s as an exclude file", fname);
+ if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0)
+ die(_("cannot use %s as an exclude file"), fname);
}
void add_excludes_from_file(struct dir_struct *dir, const char *fname)
while (current < baselen) {
const char *cp;
- struct sha1_stat sha1_stat;
+ struct oid_stat oid_stat;
stk = xcalloc(1, sizeof(*stk));
if (current < 0) {
}
/* Try to read per-directory file */
- hashclr(sha1_stat.sha1);
- sha1_stat.valid = 0;
+ oidclr(&oid_stat.oid);
+ oid_stat.valid = 0;
if (dir->exclude_per_dir &&
/*
* If we know that no files have been added in
(!untracked || !untracked->valid ||
/*
* .. and .gitignore does not exist before
- * (i.e. null exclude_sha1). Then we can skip
+ * (i.e. null exclude_oid). Then we can skip
* loading .gitignore, which would result in
* ENOENT anyway.
*/
- !is_null_sha1(untracked->exclude_sha1))) {
+ !is_null_oid(&untracked->exclude_oid))) {
/*
* dir->basebuf gets reused by the traversal, but we
* need fname to remain unchanged to ensure the src
strbuf_addstr(&sb, dir->exclude_per_dir);
el->src = strbuf_detach(&sb, NULL);
add_excludes(el->src, el->src, stk->baselen, el, istate,
- untracked ? &sha1_stat : NULL);
+ untracked ? &oid_stat : NULL);
}
/*
* NEEDSWORK: when untracked cache is enabled, prep_exclude()
* order, though, if you do that.
*/
if (untracked &&
- hashcmp(sha1_stat.sha1, untracked->exclude_sha1)) {
+ oidcmp(&oid_stat.oid, &untracked->exclude_oid)) {
invalidate_gitignore(dir->untracked, untracked);
- hashcpy(untracked->exclude_sha1, sha1_stat.sha1);
+ oidcpy(&untracked->exclude_oid, &oid_stat.oid);
}
dir->exclude_stack = stk;
current = stk->baselen;
case index_nonexistent:
if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
break;
+ if (exclude &&
+ (dir->flags & DIR_SHOW_IGNORED_TOO) &&
+ (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {
+
+ /*
+ * This is an excluded directory and we are
+ * showing ignored paths that match an exclude
+ * pattern. (e.g. show directory as ignored
+ * only if it matches an exclude pattern).
+ * This path will either be 'path_excluded`
+ * (if we are showing empty directories or if
+ * the directory is not empty), or will be
+ * 'path_none' (empty directory, and we are
+ * not showing empty directories).
+ */
+ if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
+ return path_excluded;
+
+ if (read_directory_recursive(dir, istate, dirname, len,
+ untracked, 1, 1, pathspec) == path_excluded)
+ return path_excluded;
+
+ return path_none;
+ }
if (!(dir->flags & DIR_NO_GITLINKS)) {
- unsigned char sha1[20];
- if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
- return path_untracked;
+ struct object_id oid;
+ if (resolve_gitlink_ref(dirname, "HEAD", &oid) == 0)
+ return exclude ? path_excluded : path_untracked;
}
return path_recurse;
}
untracked = lookup_untracked(dir->untracked, untracked,
dirname + baselen, len - baselen);
+
+ /*
+ * If this is an excluded directory, then we only need to check if
+ * the directory contains any files.
+ */
return read_directory_recursive(dir, istate, dirname, len,
- untracked, 1, pathspec);
+ untracked, 1, exclude, pathspec);
}
/*
{
int exclude;
int has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);
+ enum path_treatment path_treatment;
if (dtype == DT_UNKNOWN)
dtype = get_dtype(de, istate, path->buf, path->len);
return path_none;
case DT_DIR:
strbuf_addch(path, '/');
- return treat_directory(dir, istate, untracked, path->buf, path->len,
- baselen, exclude, pathspec);
+ path_treatment = treat_directory(dir, istate, untracked,
+ path->buf, path->len,
+ baselen, exclude, pathspec);
+ /*
+ * If 1) we only want to return directories that
+ * match an exclude pattern and 2) this directory does
+ * not match an exclude pattern but all of its
+ * contents are excluded, then indicate that we should
+ * recurse into this directory (instead of marking the
+ * directory itself as an ignored path).
+ */
+ if (!exclude &&
+ path_treatment == path_excluded &&
+ (dir->flags & DIR_SHOW_IGNORED_TOO) &&
+ (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))
+ return path_recurse;
+ return path_treatment;
case DT_REG:
case DT_LNK:
return exclude ? path_excluded : path_untracked;
* with check_only set.
*/
return read_directory_recursive(dir, istate, path->buf, path->len,
- cdir->ucd, 1, pathspec);
+ cdir->ucd, 1, 0, pathspec);
/*
* We get path_recurse in the first run when
* directory_exists_in_index() returns index_nonexistent. We
if (!de)
return treat_path_fast(dir, untracked, cdir, istate, path,
baselen, pathspec);
- if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
+ if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git"))
return path_none;
strbuf_setlen(path, baselen);
strbuf_addstr(path, de->d_name);
if (!untracked)
return 0;
- if (stat(path->len ? path->buf : ".", &st)) {
- invalidate_directory(dir->untracked, untracked);
- memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
- return 0;
- }
- if (!untracked->valid ||
- match_stat_data_racy(istate, &untracked->stat_data, &st)) {
- if (untracked->valid)
- invalidate_directory(dir->untracked, untracked);
- fill_stat_data(&untracked->stat_data, &st);
- return 0;
+ /*
+ * With fsmonitor, we can trust the untracked cache's valid field.
+ */
+ refresh_fsmonitor(istate);
+ if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
+ if (lstat(path->len ? path->buf : ".", &st)) {
+ memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
+ return 0;
+ }
+ if (!untracked->valid ||
+ match_stat_data_racy(istate, &untracked->stat_data, &st)) {
+ fill_stat_data(&untracked->stat_data, &st);
+ return 0;
+ }
}
- if (untracked->check_only != !!check_only) {
- invalidate_directory(dir->untracked, untracked);
+ if (untracked->check_only != !!check_only)
return 0;
- }
/*
* prep_exclude will be called eventually on this directory,
struct strbuf *path,
int check_only)
{
+ const char *c_path;
+
memset(cdir, 0, sizeof(*cdir));
cdir->untracked = untracked;
if (valid_cached_dir(dir, untracked, istate, path, check_only))
return 0;
- cdir->fdir = opendir(path->len ? path->buf : ".");
- if (dir->untracked)
+ c_path = path->len ? path->buf : ".";
+ cdir->fdir = opendir(c_path);
+ if (!cdir->fdir)
+ warning_errno(_("could not open directory '%s'"), c_path);
+ if (dir->untracked) {
+ invalidate_directory(dir->untracked, untracked);
dir->untracked->dir_opened++;
+ }
if (!cdir->fdir)
return -1;
return 0;
* Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change.
*
+ * If 'stop_at_first_file' is specified, 'path_excluded' is returned
+ * to signal that a file was found. This is the least significant value that
+ * indicates that a file was encountered that does not depend on the order of
+ * whether an untracked or exluded path was encountered first.
+ *
* Returns the most significant path_treatment value encountered in the scan.
+ * If 'stop_at_first_file' is specified, `path_excluded` is the most
+ * significant path_treatment value that will be returned.
*/
+
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
struct index_state *istate, const char *base, int baselen,
struct untracked_cache_dir *untracked, int check_only,
- const struct pathspec *pathspec)
+ int stop_at_first_file, const struct pathspec *pathspec)
{
struct cached_dir cdir;
enum path_treatment state, subdir_state, dir_state = path_none;
subdir_state =
read_directory_recursive(dir, istate, path.buf,
path.len, ud,
- check_only, pathspec);
+ check_only, stop_at_first_file, pathspec);
if (subdir_state > dir_state)
dir_state = subdir_state;
}
if (check_only) {
+ if (stop_at_first_file) {
+ /*
+ * If stopping at first file, then
+ * signal that a file was found by
+ * returning `path_excluded`. This is
+ * to return a consistent value
+ * regardless of whether an ignored or
+ * excluded file happened to be
+ * encountered 1st.
+ *
+ * In current usage, the
+ * `stop_at_first_file` is passed when
+ * an ancestor directory has matched
+ * an exclude pattern, so any found
+ * files will be excluded.
+ */
+ if (dir_state >= path_excluded) {
+ dir_state = path_excluded;
+ break;
+ }
+ }
+
/* abort early if maximum state has been reached */
if (dir_state == path_untracked) {
if (cdir.fdir)
const struct pathspec *pathspec)
{
struct untracked_cache_dir *root;
+ static int untracked_cache_disabled = -1;
- if (!dir->untracked || getenv("GIT_DISABLE_UNTRACKED_CACHE"))
+ if (!dir->untracked)
+ return NULL;
+ if (untracked_cache_disabled < 0)
+ untracked_cache_disabled = git_env_bool("GIT_DISABLE_UNTRACKED_CACHE", 0);
+ if (untracked_cache_disabled)
return NULL;
/*
return NULL;
if (!ident_in_untracked(dir->untracked)) {
- warning(_("Untracked cache is disabled on this system or location."));
+ warning(_("untracked cache is disabled on this system or location"));
return NULL;
}
/* Validate $GIT_DIR/info/exclude and core.excludesfile */
root = dir->untracked->root;
- if (hashcmp(dir->ss_info_exclude.sha1,
- dir->untracked->ss_info_exclude.sha1)) {
+ if (oidcmp(&dir->ss_info_exclude.oid,
+ &dir->untracked->ss_info_exclude.oid)) {
invalidate_gitignore(dir->untracked, root);
dir->untracked->ss_info_exclude = dir->ss_info_exclude;
}
- if (hashcmp(dir->ss_excludes_file.sha1,
- dir->untracked->ss_excludes_file.sha1)) {
+ if (oidcmp(&dir->ss_excludes_file.oid,
+ &dir->untracked->ss_excludes_file.oid)) {
invalidate_gitignore(dir->untracked, root);
dir->untracked->ss_excludes_file = dir->ss_excludes_file;
}
const char *path, int len, const struct pathspec *pathspec)
{
struct untracked_cache_dir *untracked;
+ uint64_t start = getnanotime();
if (has_symlink_leading_path(path, len))
return dir->nr;
*/
dir->untracked = NULL;
if (!len || treat_leading_path(dir, istate, path, len, pathspec))
- read_directory_recursive(dir, istate, path, len, untracked, 0, pathspec);
+ read_directory_recursive(dir, istate, path, len, untracked, 0, 0, pathspec);
QSORT(dir->entries, dir->nr, cmp_dir_entry);
QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry);
dir->nr = i;
}
+ trace_performance_since(start, "read directory %.*s", len, path);
if (dir->untracked) {
+ static int force_untracked_cache = -1;
static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
+
+ if (force_untracked_cache < 0)
+ force_untracked_cache =
+ git_env_bool("GIT_FORCE_UNTRACKED_CACHE", 0);
trace_printf_key(&trace_untracked_stats,
"node creation: %u\n"
"gitignore invalidation: %u\n"
dir->untracked->gitignore_invalidated,
dir->untracked->dir_invalidated,
dir->untracked->dir_opened);
- if (dir->untracked == istate->untracked &&
+ if (force_untracked_cache &&
+ dir->untracked == istate->untracked &&
(dir->untracked->dir_opened ||
dir->untracked->gitignore_invalidated ||
dir->untracked->dir_invalidated))
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];
+ struct object_id submodule_head;
if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
- !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
+ !resolve_gitlink_ref(path->buf, "HEAD", &submodule_head)) {
/* Do not descend and nuke a nested git work tree. */
if (kept_up)
*kept_up = 1;
{
dir->exclude_per_dir = ".gitignore";
- /* core.excludefile defaulting to $XDG_HOME/git/ignore */
+ /* core.excludesfile defaulting to $XDG_CONFIG_HOME/git/ignore */
if (!excludes_file)
excludes_file = xdg_config_home("ignore");
if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
stat_data_to_disk(&stat_data, &untracked->stat_data);
strbuf_add(&wd->sb_stat, &stat_data, sizeof(stat_data));
}
- if (!is_null_sha1(untracked->exclude_sha1)) {
+ if (!is_null_oid(&untracked->exclude_oid)) {
ewah_set(wd->sha1_valid, i);
- strbuf_add(&wd->sb_sha1, untracked->exclude_sha1, 20);
+ strbuf_add(&wd->sb_sha1, untracked->exclude_oid.hash,
+ the_hash_algo->rawsz);
}
intlen = encode_varint(untracked->untracked_nr, intbuf);
FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len);
stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
- hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1);
- hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
+ hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.oid.hash);
+ hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.oid.hash);
ouc->dir_flags = htonl(untracked->dir_flags);
varint_len = encode_varint(untracked->ident.len, varbuf);
ud->valid = 1;
}
-static void read_sha1(size_t pos, void *cb)
+static void read_oid(size_t pos, void *cb)
{
struct read_data *rd = cb;
struct untracked_cache_dir *ud = rd->ucd[pos];
- if (rd->data + 20 > rd->end) {
+ if (rd->data + the_hash_algo->rawsz > rd->end) {
rd->data = rd->end + 1;
return;
}
- hashcpy(ud->exclude_sha1, rd->data);
- rd->data += 20;
+ hashcpy(ud->exclude_oid.hash, rd->data);
+ rd->data += the_hash_algo->rawsz;
}
-static void load_sha1_stat(struct sha1_stat *sha1_stat,
- const unsigned char *data,
- const unsigned char *sha1)
+static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data,
+ const unsigned char *sha1)
{
- stat_data_from_disk(&sha1_stat->stat, data);
- hashcpy(sha1_stat->sha1, sha1);
- sha1_stat->valid = 1;
+ stat_data_from_disk(&oid_stat->stat, data);
+ hashcpy(oid_stat->oid.hash, sha1);
+ oid_stat->valid = 1;
}
struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
struct read_data rd;
const unsigned char *next = data, *end = (const unsigned char *)data + sz;
const char *ident;
- int ident_len, len;
+ int ident_len;
+ ssize_t len;
const char *exclude_per_dir;
if (sz <= 1 || end[-1] != '\0')
uc = xcalloc(1, sizeof(*uc));
strbuf_init(&uc->ident, ident_len);
strbuf_add(&uc->ident, ident, ident_len);
- load_sha1_stat(&uc->ss_info_exclude,
- next + ouc_offset(info_exclude_stat),
- next + ouc_offset(info_exclude_sha1));
- load_sha1_stat(&uc->ss_excludes_file,
- next + ouc_offset(excludes_file_stat),
- next + ouc_offset(excludes_file_sha1));
+ load_oid_stat(&uc->ss_info_exclude,
+ next + ouc_offset(info_exclude_stat),
+ next + ouc_offset(info_exclude_sha1));
+ load_oid_stat(&uc->ss_excludes_file,
+ next + ouc_offset(excludes_file_stat),
+ next + ouc_offset(excludes_file_sha1));
uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
uc->exclude_per_dir = xstrdup(exclude_per_dir);
ewah_each_bit(rd.check_only, set_check_only, &rd);
rd.data = next + len;
ewah_each_bit(rd.valid, read_stat, &rd);
- ewah_each_bit(rd.sha1_valid, read_sha1, &rd);
+ ewah_each_bit(rd.sha1_valid, read_oid, &rd);
next = rd.data;
done:
}
void untracked_cache_invalidate_path(struct index_state *istate,
- const char *path)
+ const char *path, int safe_path)
{
if (!istate->untracked || !istate->untracked->root)
return;
+ if (!safe_path && !verify_path(path, 0))
+ return;
invalidate_one_component(istate->untracked, istate->untracked->root,
path, strlen(path));
}
void untracked_cache_remove_from_index(struct index_state *istate,
const char *path)
{
- untracked_cache_invalidate_path(istate, path);
+ untracked_cache_invalidate_path(istate, path, 1);
}
void untracked_cache_add_to_index(struct index_state *istate,
const char *path)
{
- untracked_cache_invalidate_path(istate, path);
+ untracked_cache_invalidate_path(istate, path, 1);
+}
+
+static void connect_wt_gitdir_in_nested(const char *sub_worktree,
+ const char *sub_gitdir)
+{
+ int i;
+ struct repository subrepo;
+ struct strbuf sub_wt = STRBUF_INIT;
+ struct strbuf sub_gd = STRBUF_INIT;
+
+ const struct submodule *sub;
+
+ /* If the submodule has no working tree, we can ignore it. */
+ if (repo_init(&subrepo, sub_gitdir, sub_worktree))
+ return;
+
+ if (repo_read_index(&subrepo) < 0)
+ die(_("index file corrupt in repo %s"), subrepo.gitdir);
+
+ for (i = 0; i < subrepo.index->cache_nr; i++) {
+ const struct cache_entry *ce = subrepo.index->cache[i];
+
+ if (!S_ISGITLINK(ce->ce_mode))
+ continue;
+
+ while (i + 1 < subrepo.index->cache_nr &&
+ !strcmp(ce->name, subrepo.index->cache[i + 1]->name))
+ /*
+ * Skip entries with the same name in different stages
+ * to make sure an entry is returned only once.
+ */
+ i++;
+
+ sub = submodule_from_path(&subrepo, &null_oid, ce->name);
+ if (!sub || !is_submodule_active(&subrepo, ce->name))
+ /* .gitmodules broken or inactive sub */
+ continue;
+
+ strbuf_reset(&sub_wt);
+ strbuf_reset(&sub_gd);
+ strbuf_addf(&sub_wt, "%s/%s", sub_worktree, sub->path);
+ strbuf_addf(&sub_gd, "%s/modules/%s", sub_gitdir, sub->name);
+
+ connect_work_tree_and_git_dir(sub_wt.buf, sub_gd.buf, 1);
+ }
+ strbuf_release(&sub_wt);
+ strbuf_release(&sub_gd);
+ repo_clear(&subrepo);
}
-/* Update gitfile and core.worktree setting to connect work tree and git dir */
-void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
+void connect_work_tree_and_git_dir(const char *work_tree_,
+ const char *git_dir_,
+ int recurse_into_nested)
{
struct strbuf gitfile_sb = STRBUF_INIT;
struct strbuf cfg_sb = STRBUF_INIT;
strbuf_release(&gitfile_sb);
strbuf_release(&cfg_sb);
strbuf_release(&rel_path);
+
+ if (recurse_into_nested)
+ connect_wt_gitdir_in_nested(work_tree, git_dir);
+
free(work_tree);
free(git_dir);
}
die_errno(_("could not migrate git directory from '%s' to '%s'"),
old_git_dir, new_git_dir);
- connect_work_tree_and_git_dir(path, new_git_dir);
+ connect_work_tree_and_git_dir(path, new_git_dir, 0);
}