aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--builtin/update-index.c2
-rw-r--r--cache.h10
-rw-r--r--config.c14
-rw-r--r--config.h1
-rw-r--r--diff-lib.c2
-rw-r--r--dir.c27
-rw-r--r--dir.h2
-rw-r--r--entry.c2
-rw-r--r--environment.c1
-rw-r--r--fsmonitor.c253
-rw-r--r--fsmonitor.h66
-rw-r--r--preload-index.c6
-rw-r--r--read-cache.c45
-rw-r--r--submodule.c2
-rw-r--r--unpack-trees.c2
16 files changed, 417 insertions, 19 deletions
diff --git a/Makefile b/Makefile
index f2bb7f2f6..9d6ec9c1e 100644
--- a/Makefile
+++ b/Makefile
@@ -786,6 +786,7 @@ LIB_OBJS += ewah/ewah_rlw.o
LIB_OBJS += exec_cmd.o
LIB_OBJS += fetch-pack.o
LIB_OBJS += fsck.o
+LIB_OBJS += fsmonitor.o
LIB_OBJS += gettext.o
LIB_OBJS += gpg-interface.o
LIB_OBJS += graph.o
diff --git a/builtin/update-index.c b/builtin/update-index.c
index e1ca0759d..6f39ee927 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -16,6 +16,7 @@
#include "pathspec.h"
#include "dir.h"
#include "split-index.h"
+#include "fsmonitor.h"
/*
* Default to not allowing changes to the list of files. The
@@ -233,6 +234,7 @@ static int mark_ce_flags(const char *path, int flag, int mark)
else
active_cache[pos]->ce_flags &= ~flag;
active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+ mark_fsmonitor_invalid(&the_index, active_cache[pos]);
cache_tree_invalidate_path(&the_index, path);
active_cache_changed |= CE_ENTRY_CHANGED;
return 0;
diff --git a/cache.h b/cache.h
index a916bc79e..f1c903e1b 100644
--- a/cache.h
+++ b/cache.h
@@ -203,6 +203,7 @@ struct cache_entry {
#define CE_ADDED (1 << 19)
#define CE_HASHED (1 << 20)
+#define CE_FSMONITOR_VALID (1 << 21)
#define CE_WT_REMOVE (1 << 22) /* remove in work directory */
#define CE_CONFLICTED (1 << 23)
@@ -326,6 +327,7 @@ static inline unsigned int canon_mode(unsigned int mode)
#define CACHE_TREE_CHANGED (1 << 5)
#define SPLIT_INDEX_ORDERED (1 << 6)
#define UNTRACKED_CHANGED (1 << 7)
+#define FSMONITOR_CHANGED (1 << 8)
struct split_index;
struct untracked_cache;
@@ -344,6 +346,7 @@ struct index_state {
struct hashmap dir_hash;
unsigned char sha1[20];
struct untracked_cache *untracked;
+ uint64_t fsmonitor_last_update;
};
extern struct index_state the_index;
@@ -679,8 +682,10 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,
#define CE_MATCH_IGNORE_MISSING 0x08
/* enable stat refresh */
#define CE_MATCH_REFRESH 0x10
-extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
-extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
+#define CE_MATCH_IGNORE_FSMONITOR 0X20
+extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
#define HASH_WRITE_OBJECT 1
#define HASH_FORMAT_CHECK 2
@@ -773,6 +778,7 @@ extern int core_apply_sparse_checkout;
extern int precomposed_unicode;
extern int protect_hfs;
extern int protect_ntfs;
+extern const char *core_fsmonitor;
/*
* Include broken refs in all ref iterations, which will
diff --git a/config.c b/config.c
index d0d8ce823..ddda96e58 100644
--- a/config.c
+++ b/config.c
@@ -2165,6 +2165,20 @@ int git_config_get_max_percent_split_change(void)
return -1; /* default value */
}
+int git_config_get_fsmonitor(void)
+{
+ if (git_config_get_pathname("core.fsmonitor", &core_fsmonitor))
+ core_fsmonitor = getenv("GIT_FSMONITOR_TEST");
+
+ if (core_fsmonitor && !*core_fsmonitor)
+ core_fsmonitor = NULL;
+
+ if (core_fsmonitor)
+ return 1;
+
+ return 0;
+}
+
NORETURN
void git_die_config_linenr(const char *key, const char *filename, int linenr)
{
diff --git a/config.h b/config.h
index 97471b887..c9fcf691b 100644
--- a/config.h
+++ b/config.h
@@ -211,6 +211,7 @@ extern int git_config_get_pathname(const char *key, const char **dest);
extern int git_config_get_untracked_cache(void);
extern int git_config_get_split_index(void);
extern int git_config_get_max_percent_split_change(void);
+extern int git_config_get_fsmonitor(void);
/* This dies if the configured or default date is in the future */
extern int git_config_get_expiry(const char *key, const char **output);
diff --git a/diff-lib.c b/diff-lib.c
index 2a52b0795..23c6d03ca 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -12,6 +12,7 @@
#include "refs.h"
#include "submodule.h"
#include "dir.h"
+#include "fsmonitor.h"
/*
* diff-files
@@ -228,6 +229,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
if (!changed && !dirty_submodule) {
ce_mark_uptodate(ce);
+ mark_fsmonitor_valid(ce);
if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
continue;
}
diff --git a/dir.c b/dir.c
index 1c55dc3e3..ac9833dae 100644
--- a/dir.c
+++ b/dir.c
@@ -18,6 +18,7 @@
#include "utf8.h"
#include "varint.h"
#include "ewah/ewok.h"
+#include "fsmonitor.h"
/*
* Tells read_directory_recursive how a file or directory should be treated.
@@ -1688,17 +1689,23 @@ static int valid_cached_dir(struct dir_struct *dir,
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)
+ /*
+ * With fsmonitor, we can trust the untracked cache's valid field.
+ */
+ refresh_fsmonitor(istate);
+ if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
+ if (stat(path->len ? path->buf : ".", &st)) {
invalidate_directory(dir->untracked, untracked);
- fill_stat_data(&untracked->stat_data, &st);
- return 0;
+ 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;
+ }
}
if (untracked->check_only != !!check_only) {
diff --git a/dir.h b/dir.h
index e3717055d..fab8fc156 100644
--- a/dir.h
+++ b/dir.h
@@ -139,6 +139,8 @@ struct untracked_cache {
int gitignore_invalidated;
int dir_invalidated;
int dir_opened;
+ /* fsmonitor invalidation data */
+ unsigned int use_fsmonitor : 1;
};
struct dir_struct {
diff --git a/entry.c b/entry.c
index cb291aa88..3a7b66737 100644
--- a/entry.c
+++ b/entry.c
@@ -4,6 +4,7 @@
#include "streaming.h"
#include "submodule.h"
#include "progress.h"
+#include "fsmonitor.h"
static void create_directories(const char *path, int path_len,
const struct checkout *state)
@@ -357,6 +358,7 @@ finish:
lstat(ce->name, &st);
fill_stat_cache_info(ce, &st);
ce->ce_flags |= CE_UPDATE_IN_BASE;
+ mark_fsmonitor_invalid(state->istate, ce);
state->istate->cache_changed |= CE_ENTRY_CHANGED;
}
return 0;
diff --git a/environment.c b/environment.c
index 3fd4b1084..d0b9fc64d 100644
--- a/environment.c
+++ b/environment.c
@@ -76,6 +76,7 @@ int protect_hfs = PROTECT_HFS_DEFAULT;
#define PROTECT_NTFS_DEFAULT 0
#endif
int protect_ntfs = PROTECT_NTFS_DEFAULT;
+const char *core_fsmonitor;
/*
* The character that begins a commented line in user-editable file
diff --git a/fsmonitor.c b/fsmonitor.c
new file mode 100644
index 000000000..7c1540c05
--- /dev/null
+++ b/fsmonitor.c
@@ -0,0 +1,253 @@
+#include "cache.h"
+#include "config.h"
+#include "dir.h"
+#include "ewah/ewok.h"
+#include "fsmonitor.h"
+#include "run-command.h"
+#include "strbuf.h"
+
+#define INDEX_EXTENSION_VERSION (1)
+#define HOOK_INTERFACE_VERSION (1)
+
+struct trace_key trace_fsmonitor = TRACE_KEY_INIT(FSMONITOR);
+
+static void fsmonitor_ewah_callback(size_t pos, void *is)
+{
+ struct index_state *istate = (struct index_state *)is;
+ struct cache_entry *ce = istate->cache[pos];
+
+ ce->ce_flags &= ~CE_FSMONITOR_VALID;
+}
+
+int read_fsmonitor_extension(struct index_state *istate, const void *data,
+ unsigned long sz)
+{
+ const char *index = data;
+ uint32_t hdr_version;
+ uint32_t ewah_size;
+ struct ewah_bitmap *fsmonitor_dirty;
+ int i;
+ int ret;
+
+ if (sz < sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint32_t))
+ return error("corrupt fsmonitor extension (too short)");
+
+ hdr_version = get_be32(index);
+ index += sizeof(uint32_t);
+ if (hdr_version != INDEX_EXTENSION_VERSION)
+ return error("bad fsmonitor version %d", hdr_version);
+
+ istate->fsmonitor_last_update = get_be64(index);
+ index += sizeof(uint64_t);
+
+ ewah_size = get_be32(index);
+ index += sizeof(uint32_t);
+
+ fsmonitor_dirty = ewah_new();
+ ret = ewah_read_mmap(fsmonitor_dirty, index, ewah_size);
+ if (ret != ewah_size) {
+ ewah_free(fsmonitor_dirty);
+ return error("failed to parse ewah bitmap reading fsmonitor index extension");
+ }
+
+ if (git_config_get_fsmonitor()) {
+ /* Mark all entries valid */
+ for (i = 0; i < istate->cache_nr; i++)
+ istate->cache[i]->ce_flags |= CE_FSMONITOR_VALID;
+
+ /* Mark all previously saved entries as dirty */
+ ewah_each_bit(fsmonitor_dirty, fsmonitor_ewah_callback, istate);
+
+ /* Now mark the untracked cache for fsmonitor usage */
+ if (istate->untracked)
+ istate->untracked->use_fsmonitor = 1;
+ }
+ ewah_free(fsmonitor_dirty);
+
+ trace_printf_key(&trace_fsmonitor, "read fsmonitor extension successful");
+ return 0;
+}
+
+void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
+{
+ uint32_t hdr_version;
+ uint64_t tm;
+ struct ewah_bitmap *bitmap;
+ int i;
+ uint32_t ewah_start;
+ uint32_t ewah_size = 0;
+ int fixup = 0;
+
+ put_be32(&hdr_version, INDEX_EXTENSION_VERSION);
+ strbuf_add(sb, &hdr_version, sizeof(uint32_t));
+
+ put_be64(&tm, istate->fsmonitor_last_update);
+ strbuf_add(sb, &tm, sizeof(uint64_t));
+ fixup = sb->len;
+ strbuf_add(sb, &ewah_size, sizeof(uint32_t)); /* we'll fix this up later */
+
+ ewah_start = sb->len;
+ bitmap = ewah_new();
+ for (i = 0; i < istate->cache_nr; i++)
+ if (!(istate->cache[i]->ce_flags & CE_FSMONITOR_VALID))
+ ewah_set(bitmap, i);
+ ewah_serialize_strbuf(bitmap, sb);
+ ewah_free(bitmap);
+
+ /* fix up size field */
+ put_be32(&ewah_size, sb->len - ewah_start);
+ memcpy(sb->buf + fixup, &ewah_size, sizeof(uint32_t));
+
+ trace_printf_key(&trace_fsmonitor, "write fsmonitor extension successful");
+}
+
+/*
+ * Call the query-fsmonitor hook passing the time of the last saved results.
+ */
+static int query_fsmonitor(int version, uint64_t last_update, struct strbuf *query_result)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ char ver[64];
+ char date[64];
+ const char *argv[4];
+
+ if (!(argv[0] = core_fsmonitor))
+ return -1;
+
+ snprintf(ver, sizeof(version), "%d", version);
+ snprintf(date, sizeof(date), "%" PRIuMAX, (uintmax_t)last_update);
+ argv[1] = ver;
+ argv[2] = date;
+ argv[3] = NULL;
+ cp.argv = argv;
+ cp.use_shell = 1;
+
+ return capture_command(&cp, query_result, 1024);
+}
+
+static void fsmonitor_refresh_callback(struct index_state *istate, const char *name)
+{
+ int pos = index_name_pos(istate, name, strlen(name));
+
+ if (pos >= 0) {
+ struct cache_entry *ce = istate->cache[pos];
+ ce->ce_flags &= ~CE_FSMONITOR_VALID;
+ }
+
+ /*
+ * Mark the untracked cache dirty even if it wasn't found in the index
+ * as it could be a new untracked file.
+ */
+ trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
+ untracked_cache_invalidate_path(istate, name);
+}
+
+void refresh_fsmonitor(struct index_state *istate)
+{
+ static int has_run_once = 0;
+ struct strbuf query_result = STRBUF_INIT;
+ int query_success = 0;
+ size_t bol; /* beginning of line */
+ uint64_t last_update;
+ char *buf;
+ int i;
+
+ if (!core_fsmonitor || has_run_once)
+ return;
+ has_run_once = 1;
+
+ trace_printf_key(&trace_fsmonitor, "refresh fsmonitor");
+ /*
+ * This could be racy so save the date/time now and query_fsmonitor
+ * should be inclusive to ensure we don't miss potential changes.
+ */
+ last_update = getnanotime();
+
+ /*
+ * If we have a last update time, call query_fsmonitor for the set of
+ * changes since that time, else assume everything is possibly dirty
+ * and check it all.
+ */
+ if (istate->fsmonitor_last_update) {
+ query_success = !query_fsmonitor(HOOK_INTERFACE_VERSION,
+ istate->fsmonitor_last_update, &query_result);
+ trace_performance_since(last_update, "fsmonitor process '%s'", core_fsmonitor);
+ trace_printf_key(&trace_fsmonitor, "fsmonitor process '%s' returned %s",
+ core_fsmonitor, query_success ? "success" : "failure");
+ }
+
+ /* a fsmonitor process can return '/' to indicate all entries are invalid */
+ if (query_success && query_result.buf[0] != '/') {
+ /* Mark all entries returned by the monitor as dirty */
+ buf = query_result.buf;
+ bol = 0;
+ for (i = 0; i < query_result.len; i++) {
+ if (buf[i] != '\0')
+ continue;
+ fsmonitor_refresh_callback(istate, buf + bol);
+ bol = i + 1;
+ }
+ if (bol < query_result.len)
+ fsmonitor_refresh_callback(istate, buf + bol);
+ } else {
+ /* Mark all entries invalid */
+ for (i = 0; i < istate->cache_nr; i++)
+ istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
+
+ if (istate->untracked)
+ istate->untracked->use_fsmonitor = 0;
+ }
+ strbuf_release(&query_result);
+
+ /* Now that we've updated istate, save the last_update time */
+ istate->fsmonitor_last_update = last_update;
+}
+
+void add_fsmonitor(struct index_state *istate)
+{
+ int i;
+
+ if (!istate->fsmonitor_last_update) {
+ trace_printf_key(&trace_fsmonitor, "add fsmonitor");
+ istate->cache_changed |= FSMONITOR_CHANGED;
+ istate->fsmonitor_last_update = getnanotime();
+
+ /* reset the fsmonitor state */
+ for (i = 0; i < istate->cache_nr; i++)
+ istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
+
+ /* reset the untracked cache */
+ if (istate->untracked) {
+ add_untracked_cache(istate);
+ istate->untracked->use_fsmonitor = 1;
+ }
+
+ /* Update the fsmonitor state */
+ refresh_fsmonitor(istate);
+ }
+}
+
+void remove_fsmonitor(struct index_state *istate)
+{
+ if (istate->fsmonitor_last_update) {
+ trace_printf_key(&trace_fsmonitor, "remove fsmonitor");
+ istate->cache_changed |= FSMONITOR_CHANGED;
+ istate->fsmonitor_last_update = 0;
+ }
+}
+
+void tweak_fsmonitor(struct index_state *istate)
+{
+ switch (git_config_get_fsmonitor()) {
+ case -1: /* keep: do nothing */
+ break;
+ case 0: /* false */
+ remove_fsmonitor(istate);
+ break;
+ case 1: /* true */
+ add_fsmonitor(istate);
+ break;
+ default: /* unknown value: do nothing */
+ break;
+ }
+}
diff --git a/fsmonitor.h b/fsmonitor.h
new file mode 100644
index 000000000..0de644e01
--- /dev/null
+++ b/fsmonitor.h
@@ -0,0 +1,66 @@
+#ifndef FSMONITOR_H
+#define FSMONITOR_H
+
+extern struct trace_key trace_fsmonitor;
+
+/*
+ * Read the fsmonitor index extension and (if configured) restore the
+ * CE_FSMONITOR_VALID state.
+ */
+extern int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
+
+/*
+ * Write the CE_FSMONITOR_VALID state into the fsmonitor index extension.
+ */
+extern void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
+
+/*
+ * Add/remove the fsmonitor index extension
+ */
+extern void add_fsmonitor(struct index_state *istate);
+extern void remove_fsmonitor(struct index_state *istate);
+
+/*
+ * Add/remove the fsmonitor index extension as necessary based on the current
+ * core.fsmonitor setting.
+ */
+extern void tweak_fsmonitor(struct index_state *istate);
+
+/*
+ * Run the configured fsmonitor integration script and clear the
+ * CE_FSMONITOR_VALID bit for any files returned as dirty. Also invalidate
+ * any corresponding untracked cache directory structures. Optimized to only
+ * run the first time it is called.
+ */
+extern void refresh_fsmonitor(struct index_state *istate);
+
+/*
+ * Set the given cache entries CE_FSMONITOR_VALID bit. This should be
+ * called any time the cache entry has been updated to reflect the
+ * current state of the file on disk.
+ */
+static inline void mark_fsmonitor_valid(struct cache_entry *ce)
+{
+ if (core_fsmonitor) {
+ ce->ce_flags |= CE_FSMONITOR_VALID;
+ trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_clean '%s'", ce->name);
+ }
+}
+
+/*
+ * Clear the given cache entry's CE_FSMONITOR_VALID bit and invalidate
+ * any corresponding untracked cache directory structures. This should
+ * be called any time git creates or modifies a file that should
+ * trigger an lstat() or invalidate the untracked cache for the
+ * corresponding directory
+ */
+static inline void mark_fsmonitor_invalid(struct index_state *istate, struct cache_entry *ce)
+{
+ if (core_fsmonitor) {
+ ce->ce_flags &= ~CE_FSMONITOR_VALID;
+ untracked_cache_invalidate_path(istate, ce->name);
+ trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_invalid '%s'", ce->name);
+ }
+}
+
+#endif
diff --git a/preload-index.c b/preload-index.c
index 75564c497..2a83255e4 100644
--- a/preload-index.c
+++ b/preload-index.c
@@ -4,6 +4,7 @@
#include "cache.h"
#include "pathspec.h"
#include "dir.h"
+#include "fsmonitor.h"
#ifdef NO_PTHREADS
static void preload_index(struct index_state *index,
@@ -55,15 +56,18 @@ static void *preload_thread(void *_data)
continue;
if (ce_skip_worktree(ce))
continue;
+ if (ce->ce_flags & CE_FSMONITOR_VALID)
+ continue;
if (!ce_path_match(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))
+ 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;
diff --git a/read-cache.c b/read-cache.c
index 40da87ea7..05c0a33fd 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -19,6 +19,7 @@
#include "varint.h"
#include "split-index.h"
#include "utf8.h"
+#include "fsmonitor.h"
/* Mask for the name length in ce_flags in the on-disk index */
@@ -38,11 +39,12 @@
#define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
#define CACHE_EXT_LINK 0x6c696e6b /* "link" */
#define CACHE_EXT_UNTRACKED 0x554E5452 /* "UNTR" */
+#define CACHE_EXT_FSMONITOR 0x46534D4E /* "FSMN" */
/* changes that can be kept in $GIT_DIR/index (basically all extensions) */
#define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \
- SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED)
+ SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED | FSMONITOR_CHANGED)
struct index_state the_index;
static const char *alternate_index_output;
@@ -62,6 +64,7 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache
free(old);
set_index_entry(istate, nr, ce);
ce->ce_flags |= CE_UPDATE_IN_BASE;
+ mark_fsmonitor_invalid(istate, ce);
istate->cache_changed |= CE_ENTRY_CHANGED;
}
@@ -150,8 +153,10 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
if (assume_unchanged)
ce->ce_flags |= CE_VALID;
- if (S_ISREG(st->st_mode))
+ if (S_ISREG(st->st_mode)) {
ce_mark_uptodate(ce);
+ mark_fsmonitor_valid(ce);
+ }
}
static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
@@ -300,7 +305,7 @@ int match_stat_data_racy(const struct index_state *istate,
return match_stat_data(sd, st);
}
-int ie_match_stat(const struct index_state *istate,
+int ie_match_stat(struct index_state *istate,
const struct cache_entry *ce, struct stat *st,
unsigned int options)
{
@@ -308,7 +313,10 @@ int ie_match_stat(const struct index_state *istate,
int ignore_valid = options & CE_MATCH_IGNORE_VALID;
int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
+ int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;
+ if (!ignore_fsmonitor)
+ refresh_fsmonitor(istate);
/*
* If it's marked as always valid in the index, it's
* valid whatever the checked-out copy says.
@@ -319,6 +327,8 @@ int ie_match_stat(const struct index_state *istate,
return 0;
if (!ignore_valid && (ce->ce_flags & CE_VALID))
return 0;
+ if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID))
+ return 0;
/*
* Intent-to-add entries have not been added, so the index entry
@@ -356,7 +366,7 @@ int ie_match_stat(const struct index_state *istate,
return changed;
}
-int ie_modified(const struct index_state *istate,
+int ie_modified(struct index_state *istate,
const struct cache_entry *ce,
struct stat *st, unsigned int options)
{
@@ -777,6 +787,7 @@ int chmod_index_entry(struct index_state *istate, struct cache_entry *ce,
}
cache_tree_invalidate_path(istate, ce->name);
ce->ce_flags |= CE_UPDATE_IN_BASE;
+ mark_fsmonitor_invalid(istate, ce);
istate->cache_changed |= CE_ENTRY_CHANGED;
return 0;
@@ -1228,10 +1239,13 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
int ignore_valid = options & CE_MATCH_IGNORE_VALID;
int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
int ignore_missing = options & CE_MATCH_IGNORE_MISSING;
+ int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;
if (!refresh || ce_uptodate(ce))
return ce;
+ if (!ignore_fsmonitor)
+ refresh_fsmonitor(istate);
/*
* CE_VALID or CE_SKIP_WORKTREE means the user promised us
* that the change to the work tree does not matter and told
@@ -1245,6 +1259,10 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
ce_mark_uptodate(ce);
return ce;
}
+ if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID)) {
+ ce_mark_uptodate(ce);
+ return ce;
+ }
if (has_symlink_leading_path(ce->name, ce_namelen(ce))) {
if (ignore_missing)
@@ -1282,8 +1300,10 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
* because CE_UPTODATE flag is in-core only;
* we are not going to write this change out.
*/
- if (!S_ISGITLINK(ce->ce_mode))
+ if (!S_ISGITLINK(ce->ce_mode)) {
ce_mark_uptodate(ce);
+ mark_fsmonitor_valid(ce);
+ }
return ce;
}
}
@@ -1391,6 +1411,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
*/
ce->ce_flags &= ~CE_VALID;
ce->ce_flags |= CE_UPDATE_IN_BASE;
+ mark_fsmonitor_invalid(istate, ce);
istate->cache_changed |= CE_ENTRY_CHANGED;
}
if (quiet)
@@ -1550,6 +1571,9 @@ static int read_index_extension(struct index_state *istate,
case CACHE_EXT_UNTRACKED:
istate->untracked = read_untracked_extension(data, sz);
break;
+ case CACHE_EXT_FSMONITOR:
+ read_fsmonitor_extension(istate, data, sz);
+ break;
default:
if (*ext < 'A' || 'Z' < *ext)
return error("index uses %.4s extension, which we do not understand",
@@ -1722,6 +1746,7 @@ static void post_read_index_from(struct index_state *istate)
check_ce_order(istate);
tweak_untracked_cache(istate);
tweak_split_index(istate);
+ tweak_fsmonitor(istate);
}
/* remember to discard_cache() before reading a different cache! */
@@ -2306,6 +2331,16 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (err)
return -1;
}
+ if (!strip_extensions && istate->fsmonitor_last_update) {
+ struct strbuf sb = STRBUF_INIT;
+
+ write_fsmonitor_extension(&sb, istate);
+ err = write_index_ext_header(&c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
+ || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+ strbuf_release(&sb);
+ if (err)
+ return -1;
+ }
if (ce_flush(&c, newfd, istate->sha1))
return -1;
diff --git a/submodule.c b/submodule.c
index 3cea8221e..8a931a1aa 100644
--- a/submodule.c
+++ b/submodule.c
@@ -62,7 +62,7 @@ int is_staging_gitmodules_ok(const struct index_state *istate)
if ((pos >= 0) && (pos < istate->cache_nr)) {
struct stat st;
if (lstat(GITMODULES_FILE, &st) == 0 &&
- ce_match_stat(istate->cache[pos], &st, 0) & DATA_CHANGED)
+ ce_match_stat(istate->cache[pos], &st, CE_MATCH_IGNORE_FSMONITOR) & DATA_CHANGED)
return 0;
}
diff --git a/unpack-trees.c b/unpack-trees.c
index 71b70ccb1..1f5d37163 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -14,6 +14,7 @@
#include "dir.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "fsmonitor.h"
/*
* Error messages expected by scripts out of plumbing commands such as
@@ -408,6 +409,7 @@ static int apply_sparse_checkout(struct index_state *istate,
ce->ce_flags &= ~CE_SKIP_WORKTREE;
if (was_skip_worktree != ce_skip_worktree(ce)) {
ce->ce_flags |= CE_UPDATE_IN_BASE;
+ mark_fsmonitor_invalid(istate, ce);
istate->cache_changed |= CE_ENTRY_CHANGED;
}