fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.

When the index is read from disk, the fsmonitor index extension is used to flag the last known potentially dirty index entries. The registered core.fsmonitor command is called with the time the index was last updated and returns the list of files changed since that time. This list is used to flag any additional dirty cache entries and untracked cache directories. We can then use this valid state to speed up preload_index(), ie_match_stat(), and refresh_cache_ent() as they do not need to lstat() files to detect potential changes for those entries marked CE_FSMONITOR_VALID. In addition, if the untracked cache is turned on valid_cached_dir() can skip checking directories for new or changed files as fsmonitor will invalidate the cache only for those directories that have been identified as having potential changes. To keep the CE_FSMONITOR_VALID state accurate during git operations; when git updates a cache entry to match the current state on disk, it will now set the CE_FSMONITOR_VALID bit. Inversely, anytime git changes a cache entry, the CE_FSMONITOR_VALID bit is cleared and the corresponding untracked cache directory is marked invalid. Signed-off-by: Ben Peart <benpeart@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ben Peart committed Sep 22, 2017 at 12:35 UTC 883e248b8a0fd88773cb902ab8e91273eb147d07
16 files changed +417 -19
Makefile
+1
@@ -786,6 +786,7 @@ LIB_OBJS += ewah/ewah_rlw.o
786 LIB_OBJS += exec_cmd.o
787 LIB_OBJS += fetch-pack.o
788 LIB_OBJS += fsck.o
789 +LIB_OBJS += fsmonitor.o
790 LIB_OBJS += gettext.o
791 LIB_OBJS += gpg-interface.o
792 LIB_OBJS += graph.o
builtin/update-index.c
+2
@@ -16,6 +16,7 @@
16 #include "pathspec.h"
17 #include "dir.h"
18 #include "split-index.h"
19 +#include "fsmonitor.h"
20
21 /*
22 * 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)
234 else
235 active_cache[pos]->ce_flags &= ~flag;
236 active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
237 + mark_fsmonitor_invalid(&the_index, active_cache[pos]);
238 cache_tree_invalidate_path(&the_index, path);
239 active_cache_changed |= CE_ENTRY_CHANGED;
240 return 0;
cache.h
+8 -2
@@ -203,6 +203,7 @@ struct cache_entry {
203 #define CE_ADDED (1 << 19)
204
205 #define CE_HASHED (1 << 20)
206 +#define CE_FSMONITOR_VALID (1 << 21)
207 #define CE_WT_REMOVE (1 << 22) /* remove in work directory */
208 #define CE_CONFLICTED (1 << 23)
209
@@ -326,6 +327,7 @@ static inline unsigned int canon_mode(unsigned int mode)
327 #define CACHE_TREE_CHANGED (1 << 5)
328 #define SPLIT_INDEX_ORDERED (1 << 6)
329 #define UNTRACKED_CHANGED (1 << 7)
330 +#define FSMONITOR_CHANGED (1 << 8)
331
332 struct split_index;
333 struct untracked_cache;
@@ -344,6 +346,7 @@ struct index_state {
346 struct hashmap dir_hash;
347 unsigned char sha1[20];
348 struct untracked_cache *untracked;
349 + uint64_t fsmonitor_last_update;
350 };
351
352 extern struct index_state the_index;
@@ -679,8 +682,10 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,
682 #define CE_MATCH_IGNORE_MISSING 0x08
683 /* enable stat refresh */
684 #define CE_MATCH_REFRESH 0x10
682 -extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
683 -extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
685 +/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
686 +#define CE_MATCH_IGNORE_FSMONITOR 0X20
687 +extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
688 +extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
689
690 #define HASH_WRITE_OBJECT 1
691 #define HASH_FORMAT_CHECK 2
@@ -773,6 +778,7 @@ extern int core_apply_sparse_checkout;
778 extern int precomposed_unicode;
779 extern int protect_hfs;
780 extern int protect_ntfs;
781 +extern const char *core_fsmonitor;
782
783 /*
784 * Include broken refs in all ref iterations, which will
config.c
+14
@@ -2165,6 +2165,20 @@ int git_config_get_max_percent_split_change(void)
2165 return -1; /* default value */
2166 }
2167
2168 +int git_config_get_fsmonitor(void)
2169 +{
2170 + if (git_config_get_pathname("core.fsmonitor", &core_fsmonitor))
2171 + core_fsmonitor = getenv("GIT_FSMONITOR_TEST");
2172 +
2173 + if (core_fsmonitor && !*core_fsmonitor)
2174 + core_fsmonitor = NULL;
2175 +
2176 + if (core_fsmonitor)
2177 + return 1;
2178 +
2179 + return 0;
2180 +}
2181 +
2182 NORETURN
2183 void git_die_config_linenr(const char *key, const char *filename, int linenr)
2184 {
config.h
+1
@@ -211,6 +211,7 @@ extern int git_config_get_pathname(const char *key, const char **dest);
211 extern int git_config_get_untracked_cache(void);
212 extern int git_config_get_split_index(void);
213 extern int git_config_get_max_percent_split_change(void);
214 +extern int git_config_get_fsmonitor(void);
215
216 /* This dies if the configured or default date is in the future */
217 extern int git_config_get_expiry(const char *key, const char **output);
diff-lib.c
+2
@@ -12,6 +12,7 @@
12 #include "refs.h"
13 #include "submodule.h"
14 #include "dir.h"
15 +#include "fsmonitor.h"
16
17 /*
18 * diff-files
@@ -228,6 +229,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
229
230 if (!changed && !dirty_submodule) {
231 ce_mark_uptodate(ce);
232 + mark_fsmonitor_valid(ce);
233 if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
234 continue;
235 }
dir.c
+17 -10
@@ -18,6 +18,7 @@
18 #include "utf8.h"
19 #include "varint.h"
20 #include "ewah/ewok.h"
21 +#include "fsmonitor.h"
22
23 /*
24 * 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,
1689 if (!untracked)
1690 return 0;
1691
1691 - if (stat(path->len ? path->buf : ".", &st)) {
1692 - invalidate_directory(dir->untracked, untracked);
1693 - memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
1694 - return 0;
1695 - }
1696 - if (!untracked->valid ||
1697 - match_stat_data_racy(istate, &untracked->stat_data, &st)) {
1698 - if (untracked->valid)
1692 + /*
1693 + * With fsmonitor, we can trust the untracked cache's valid field.
1694 + */
1695 + refresh_fsmonitor(istate);
1696 + if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
1697 + if (stat(path->len ? path->buf : ".", &st)) {
1698 invalidate_directory(dir->untracked, untracked);
1700 - fill_stat_data(&untracked->stat_data, &st);
1701 - return 0;
1699 + memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
1700 + return 0;
1701 + }
1702 + if (!untracked->valid ||
1703 + match_stat_data_racy(istate, &untracked->stat_data, &st)) {
1704 + if (untracked->valid)
1705 + invalidate_directory(dir->untracked, untracked);
1706 + fill_stat_data(&untracked->stat_data, &st);
1707 + return 0;
1708 + }
1709 }
1710
1711 if (untracked->check_only != !!check_only) {
dir.h
+2
@@ -139,6 +139,8 @@ struct untracked_cache {
139 int gitignore_invalidated;
140 int dir_invalidated;
141 int dir_opened;
142 + /* fsmonitor invalidation data */
143 + unsigned int use_fsmonitor : 1;
144 };
145
146 struct dir_struct {
entry.c
+2
@@ -4,6 +4,7 @@
4 #include "streaming.h"
5 #include "submodule.h"
6 #include "progress.h"
7 +#include "fsmonitor.h"
8
9 static void create_directories(const char *path, int path_len,
10 const struct checkout *state)
@@ -357,6 +358,7 @@ finish:
358 lstat(ce->name, &st);
359 fill_stat_cache_info(ce, &st);
360 ce->ce_flags |= CE_UPDATE_IN_BASE;
361 + mark_fsmonitor_invalid(state->istate, ce);
362 state->istate->cache_changed |= CE_ENTRY_CHANGED;
363 }
364 return 0;
environment.c
+1
@@ -76,6 +76,7 @@ int protect_hfs = PROTECT_HFS_DEFAULT;
76 #define PROTECT_NTFS_DEFAULT 0
77 #endif
78 int protect_ntfs = PROTECT_NTFS_DEFAULT;
79 +const char *core_fsmonitor;
80
81 /*
82 * The character that begins a commented line in user-editable file
fsmonitor.c new
+253
@@ -0,0 +1,253 @@
1 +#include "cache.h"
2 +#include "config.h"
3 +#include "dir.h"
4 +#include "ewah/ewok.h"
5 +#include "fsmonitor.h"
6 +#include "run-command.h"
7 +#include "strbuf.h"
8 +
9 +#define INDEX_EXTENSION_VERSION (1)
10 +#define HOOK_INTERFACE_VERSION (1)
11 +
12 +struct trace_key trace_fsmonitor = TRACE_KEY_INIT(FSMONITOR);
13 +
14 +static void fsmonitor_ewah_callback(size_t pos, void *is)
15 +{
16 + struct index_state *istate = (struct index_state *)is;
17 + struct cache_entry *ce = istate->cache[pos];
18 +
19 + ce->ce_flags &= ~CE_FSMONITOR_VALID;
20 +}
21 +
22 +int read_fsmonitor_extension(struct index_state *istate, const void *data,
23 + unsigned long sz)
24 +{
25 + const char *index = data;
26 + uint32_t hdr_version;
27 + uint32_t ewah_size;
28 + struct ewah_bitmap *fsmonitor_dirty;
29 + int i;
30 + int ret;
31 +
32 + if (sz < sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint32_t))
33 + return error("corrupt fsmonitor extension (too short)");
34 +
35 + hdr_version = get_be32(index);
36 + index += sizeof(uint32_t);
37 + if (hdr_version != INDEX_EXTENSION_VERSION)
38 + return error("bad fsmonitor version %d", hdr_version);
39 +
40 + istate->fsmonitor_last_update = get_be64(index);
41 + index += sizeof(uint64_t);
42 +
43 + ewah_size = get_be32(index);
44 + index += sizeof(uint32_t);
45 +
46 + fsmonitor_dirty = ewah_new();
47 + ret = ewah_read_mmap(fsmonitor_dirty, index, ewah_size);
48 + if (ret != ewah_size) {
49 + ewah_free(fsmonitor_dirty);
50 + return error("failed to parse ewah bitmap reading fsmonitor index extension");
51 + }
52 +
53 + if (git_config_get_fsmonitor()) {
54 + /* Mark all entries valid */
55 + for (i = 0; i < istate->cache_nr; i++)
56 + istate->cache[i]->ce_flags |= CE_FSMONITOR_VALID;
57 +
58 + /* Mark all previously saved entries as dirty */
59 + ewah_each_bit(fsmonitor_dirty, fsmonitor_ewah_callback, istate);
60 +
61 + /* Now mark the untracked cache for fsmonitor usage */
62 + if (istate->untracked)
63 + istate->untracked->use_fsmonitor = 1;
64 + }
65 + ewah_free(fsmonitor_dirty);
66 +
67 + trace_printf_key(&trace_fsmonitor, "read fsmonitor extension successful");
68 + return 0;
69 +}
70 +
71 +void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
72 +{
73 + uint32_t hdr_version;
74 + uint64_t tm;
75 + struct ewah_bitmap *bitmap;
76 + int i;
77 + uint32_t ewah_start;
78 + uint32_t ewah_size = 0;
79 + int fixup = 0;
80 +
81 + put_be32(&hdr_version, INDEX_EXTENSION_VERSION);
82 + strbuf_add(sb, &hdr_version, sizeof(uint32_t));
83 +
84 + put_be64(&tm, istate->fsmonitor_last_update);
85 + strbuf_add(sb, &tm, sizeof(uint64_t));
86 + fixup = sb->len;
87 + strbuf_add(sb, &ewah_size, sizeof(uint32_t)); /* we'll fix this up later */
88 +
89 + ewah_start = sb->len;
90 + bitmap = ewah_new();
91 + for (i = 0; i < istate->cache_nr; i++)
92 + if (!(istate->cache[i]->ce_flags & CE_FSMONITOR_VALID))
93 + ewah_set(bitmap, i);
94 + ewah_serialize_strbuf(bitmap, sb);
95 + ewah_free(bitmap);
96 +
97 + /* fix up size field */
98 + put_be32(&ewah_size, sb->len - ewah_start);
99 + memcpy(sb->buf + fixup, &ewah_size, sizeof(uint32_t));
100 +
101 + trace_printf_key(&trace_fsmonitor, "write fsmonitor extension successful");
102 +}
103 +
104 +/*
105 + * Call the query-fsmonitor hook passing the time of the last saved results.
106 + */
107 +static int query_fsmonitor(int version, uint64_t last_update, struct strbuf *query_result)
108 +{
109 + struct child_process cp = CHILD_PROCESS_INIT;
110 + char ver[64];
111 + char date[64];
112 + const char *argv[4];
113 +
114 + if (!(argv[0] = core_fsmonitor))
115 + return -1;
116 +
117 + snprintf(ver, sizeof(version), "%d", version);
118 + snprintf(date, sizeof(date), "%" PRIuMAX, (uintmax_t)last_update);
119 + argv[1] = ver;
120 + argv[2] = date;
121 + argv[3] = NULL;
122 + cp.argv = argv;
123 + cp.use_shell = 1;
124 +
125 + return capture_command(&cp, query_result, 1024);
126 +}
127 +
128 +static void fsmonitor_refresh_callback(struct index_state *istate, const char *name)
129 +{
130 + int pos = index_name_pos(istate, name, strlen(name));
131 +
132 + if (pos >= 0) {
133 + struct cache_entry *ce = istate->cache[pos];
134 + ce->ce_flags &= ~CE_FSMONITOR_VALID;
135 + }
136 +
137 + /*
138 + * Mark the untracked cache dirty even if it wasn't found in the index
139 + * as it could be a new untracked file.
140 + */
141 + trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
142 + untracked_cache_invalidate_path(istate, name);
143 +}
144 +
145 +void refresh_fsmonitor(struct index_state *istate)
146 +{
147 + static int has_run_once = 0;
148 + struct strbuf query_result = STRBUF_INIT;
149 + int query_success = 0;
150 + size_t bol; /* beginning of line */
151 + uint64_t last_update;
152 + char *buf;
153 + int i;
154 +
155 + if (!core_fsmonitor || has_run_once)
156 + return;
157 + has_run_once = 1;
158 +
159 + trace_printf_key(&trace_fsmonitor, "refresh fsmonitor");
160 + /*
161 + * This could be racy so save the date/time now and query_fsmonitor
162 + * should be inclusive to ensure we don't miss potential changes.
163 + */
164 + last_update = getnanotime();
165 +
166 + /*
167 + * If we have a last update time, call query_fsmonitor for the set of
168 + * changes since that time, else assume everything is possibly dirty
169 + * and check it all.
170 + */
171 + if (istate->fsmonitor_last_update) {
172 + query_success = !query_fsmonitor(HOOK_INTERFACE_VERSION,
173 + istate->fsmonitor_last_update, &query_result);
174 + trace_performance_since(last_update, "fsmonitor process '%s'", core_fsmonitor);
175 + trace_printf_key(&trace_fsmonitor, "fsmonitor process '%s' returned %s",
176 + core_fsmonitor, query_success ? "success" : "failure");
177 + }
178 +
179 + /* a fsmonitor process can return '/' to indicate all entries are invalid */
180 + if (query_success && query_result.buf[0] != '/') {
181 + /* Mark all entries returned by the monitor as dirty */
182 + buf = query_result.buf;
183 + bol = 0;
184 + for (i = 0; i < query_result.len; i++) {
185 + if (buf[i] != '\0')
186 + continue;
187 + fsmonitor_refresh_callback(istate, buf + bol);
188 + bol = i + 1;
189 + }
190 + if (bol < query_result.len)
191 + fsmonitor_refresh_callback(istate, buf + bol);
192 + } else {
193 + /* Mark all entries invalid */
194 + for (i = 0; i < istate->cache_nr; i++)
195 + istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
196 +
197 + if (istate->untracked)
198 + istate->untracked->use_fsmonitor = 0;
199 + }
200 + strbuf_release(&query_result);
201 +
202 + /* Now that we've updated istate, save the last_update time */
203 + istate->fsmonitor_last_update = last_update;
204 +}
205 +
206 +void add_fsmonitor(struct index_state *istate)
207 +{
208 + int i;
209 +
210 + if (!istate->fsmonitor_last_update) {
211 + trace_printf_key(&trace_fsmonitor, "add fsmonitor");
212 + istate->cache_changed |= FSMONITOR_CHANGED;
213 + istate->fsmonitor_last_update = getnanotime();
214 +
215 + /* reset the fsmonitor state */
216 + for (i = 0; i < istate->cache_nr; i++)
217 + istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
218 +
219 + /* reset the untracked cache */
220 + if (istate->untracked) {
221 + add_untracked_cache(istate);
222 + istate->untracked->use_fsmonitor = 1;
223 + }
224 +
225 + /* Update the fsmonitor state */
226 + refresh_fsmonitor(istate);
227 + }
228 +}
229 +
230 +void remove_fsmonitor(struct index_state *istate)
231 +{
232 + if (istate->fsmonitor_last_update) {
233 + trace_printf_key(&trace_fsmonitor, "remove fsmonitor");
234 + istate->cache_changed |= FSMONITOR_CHANGED;
235 + istate->fsmonitor_last_update = 0;
236 + }
237 +}
238 +
239 +void tweak_fsmonitor(struct index_state *istate)
240 +{
241 + switch (git_config_get_fsmonitor()) {
242 + case -1: /* keep: do nothing */
243 + break;
244 + case 0: /* false */
245 + remove_fsmonitor(istate);
246 + break;
247 + case 1: /* true */
248 + add_fsmonitor(istate);
249 + break;
250 + default: /* unknown value: do nothing */
251 + break;
252 + }
253 +}
fsmonitor.h new
+66
@@ -0,0 +1,66 @@
1 +#ifndef FSMONITOR_H
2 +#define FSMONITOR_H
3 +
4 +extern struct trace_key trace_fsmonitor;
5 +
6 +/*
7 + * Read the fsmonitor index extension and (if configured) restore the
8 + * CE_FSMONITOR_VALID state.
9 + */
10 +extern int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
11 +
12 +/*
13 + * Write the CE_FSMONITOR_VALID state into the fsmonitor index extension.
14 + */
15 +extern void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
16 +
17 +/*
18 + * Add/remove the fsmonitor index extension
19 + */
20 +extern void add_fsmonitor(struct index_state *istate);
21 +extern void remove_fsmonitor(struct index_state *istate);
22 +
23 +/*
24 + * Add/remove the fsmonitor index extension as necessary based on the current
25 + * core.fsmonitor setting.
26 + */
27 +extern void tweak_fsmonitor(struct index_state *istate);
28 +
29 +/*
30 + * Run the configured fsmonitor integration script and clear the
31 + * CE_FSMONITOR_VALID bit for any files returned as dirty. Also invalidate
32 + * any corresponding untracked cache directory structures. Optimized to only
33 + * run the first time it is called.
34 + */
35 +extern void refresh_fsmonitor(struct index_state *istate);
36 +
37 +/*
38 + * Set the given cache entries CE_FSMONITOR_VALID bit. This should be
39 + * called any time the cache entry has been updated to reflect the
40 + * current state of the file on disk.
41 + */
42 +static inline void mark_fsmonitor_valid(struct cache_entry *ce)
43 +{
44 + if (core_fsmonitor) {
45 + ce->ce_flags |= CE_FSMONITOR_VALID;
46 + trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_clean '%s'", ce->name);
47 + }
48 +}
49 +
50 +/*
51 + * Clear the given cache entry's CE_FSMONITOR_VALID bit and invalidate
52 + * any corresponding untracked cache directory structures. This should
53 + * be called any time git creates or modifies a file that should
54 + * trigger an lstat() or invalidate the untracked cache for the
55 + * corresponding directory
56 + */
57 +static inline void mark_fsmonitor_invalid(struct index_state *istate, struct cache_entry *ce)
58 +{
59 + if (core_fsmonitor) {
60 + ce->ce_flags &= ~CE_FSMONITOR_VALID;
61 + untracked_cache_invalidate_path(istate, ce->name);
62 + trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_invalid '%s'", ce->name);
63 + }
64 +}
65 +
66 +#endif
preload-index.c
+5 -1
@@ -4,6 +4,7 @@
4 #include "cache.h"
5 #include "pathspec.h"
6 #include "dir.h"
7 +#include "fsmonitor.h"
8
9 #ifdef NO_PTHREADS
10 static void preload_index(struct index_state *index,
@@ -55,15 +56,18 @@ static void *preload_thread(void *_data)
56 continue;
57 if (ce_skip_worktree(ce))
58 continue;
59 + if (ce->ce_flags & CE_FSMONITOR_VALID)
60 + continue;
61 if (!ce_path_match(ce, &p->pathspec, NULL))
62 continue;
63 if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
64 continue;
65 if (lstat(ce->name, &st))
66 continue;
64 - if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY))
67 + if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY|CE_MATCH_IGNORE_FSMONITOR))
68 continue;
69 ce_mark_uptodate(ce);
70 + mark_fsmonitor_valid(ce);
71 } while (--nr > 0);
72 cache_def_clear(&cache);
73 return NULL;
read-cache.c
+40 -5
@@ -19,6 +19,7 @@
19 #include "varint.h"
20 #include "split-index.h"
21 #include "utf8.h"
22 +#include "fsmonitor.h"
23
24 /* Mask for the name length in ce_flags in the on-disk index */
25
@@ -38,11 +39,12 @@
39 #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
40 #define CACHE_EXT_LINK 0x6c696e6b /* "link" */
41 #define CACHE_EXT_UNTRACKED 0x554E5452 /* "UNTR" */
42 +#define CACHE_EXT_FSMONITOR 0x46534D4E /* "FSMN" */
43
44 /* changes that can be kept in $GIT_DIR/index (basically all extensions) */
45 #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
46 CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \
45 - SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED)
47 + SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED | FSMONITOR_CHANGED)
48
49 struct index_state the_index;
50 static const char *alternate_index_output;
@@ -62,6 +64,7 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache
64 free(old);
65 set_index_entry(istate, nr, ce);
66 ce->ce_flags |= CE_UPDATE_IN_BASE;
67 + mark_fsmonitor_invalid(istate, ce);
68 istate->cache_changed |= CE_ENTRY_CHANGED;
69 }
70
@@ -150,8 +153,10 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
153 if (assume_unchanged)
154 ce->ce_flags |= CE_VALID;
155
153 - if (S_ISREG(st->st_mode))
156 + if (S_ISREG(st->st_mode)) {
157 ce_mark_uptodate(ce);
158 + mark_fsmonitor_valid(ce);
159 + }
160 }
161
162 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,
305 return match_stat_data(sd, st);
306 }
307
303 -int ie_match_stat(const struct index_state *istate,
308 +int ie_match_stat(struct index_state *istate,
309 const struct cache_entry *ce, struct stat *st,
310 unsigned int options)
311 {
@@ -308,7 +313,10 @@ int ie_match_stat(const struct index_state *istate,
313 int ignore_valid = options & CE_MATCH_IGNORE_VALID;
314 int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
315 int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
316 + int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;
317
318 + if (!ignore_fsmonitor)
319 + refresh_fsmonitor(istate);
320 /*
321 * If it's marked as always valid in the index, it's
322 * valid whatever the checked-out copy says.
@@ -319,6 +327,8 @@ int ie_match_stat(const struct index_state *istate,
327 return 0;
328 if (!ignore_valid && (ce->ce_flags & CE_VALID))
329 return 0;
330 + if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID))
331 + return 0;
332
333 /*
334 * 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,
366 return changed;
367 }
368
359 -int ie_modified(const struct index_state *istate,
369 +int ie_modified(struct index_state *istate,
370 const struct cache_entry *ce,
371 struct stat *st, unsigned int options)
372 {
@@ -777,6 +787,7 @@ int chmod_index_entry(struct index_state *istate, struct cache_entry *ce,
787 }
788 cache_tree_invalidate_path(istate, ce->name);
789 ce->ce_flags |= CE_UPDATE_IN_BASE;
790 + mark_fsmonitor_invalid(istate, ce);
791 istate->cache_changed |= CE_ENTRY_CHANGED;
792
793 return 0;
@@ -1228,10 +1239,13 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
1239 int ignore_valid = options & CE_MATCH_IGNORE_VALID;
1240 int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
1241 int ignore_missing = options & CE_MATCH_IGNORE_MISSING;
1242 + int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;
1243
1244 if (!refresh || ce_uptodate(ce))
1245 return ce;
1246
1247 + if (!ignore_fsmonitor)
1248 + refresh_fsmonitor(istate);
1249 /*
1250 * CE_VALID or CE_SKIP_WORKTREE means the user promised us
1251 * 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,
1259 ce_mark_uptodate(ce);
1260 return ce;
1261 }
1262 + if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID)) {
1263 + ce_mark_uptodate(ce);
1264 + return ce;
1265 + }
1266
1267 if (has_symlink_leading_path(ce->name, ce_namelen(ce))) {
1268 if (ignore_missing)
@@ -1282,8 +1300,10 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
1300 * because CE_UPTODATE flag is in-core only;
1301 * we are not going to write this change out.
1302 */
1285 - if (!S_ISGITLINK(ce->ce_mode))
1303 + if (!S_ISGITLINK(ce->ce_mode)) {
1304 ce_mark_uptodate(ce);
1305 + mark_fsmonitor_valid(ce);
1306 + }
1307 return ce;
1308 }
1309 }
@@ -1391,6 +1411,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
1411 */
1412 ce->ce_flags &= ~CE_VALID;
1413 ce->ce_flags |= CE_UPDATE_IN_BASE;
1414 + mark_fsmonitor_invalid(istate, ce);
1415 istate->cache_changed |= CE_ENTRY_CHANGED;
1416 }
1417 if (quiet)
@@ -1550,6 +1571,9 @@ static int read_index_extension(struct index_state *istate,
1571 case CACHE_EXT_UNTRACKED:
1572 istate->untracked = read_untracked_extension(data, sz);
1573 break;
1574 + case CACHE_EXT_FSMONITOR:
1575 + read_fsmonitor_extension(istate, data, sz);
1576 + break;
1577 default:
1578 if (*ext < 'A' || 'Z' < *ext)
1579 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)
1746 check_ce_order(istate);
1747 tweak_untracked_cache(istate);
1748 tweak_split_index(istate);
1749 + tweak_fsmonitor(istate);
1750 }
1751
1752 /* 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,
2331 if (err)
2332 return -1;
2333 }
2334 + if (!strip_extensions && istate->fsmonitor_last_update) {
2335 + struct strbuf sb = STRBUF_INIT;
2336 +
2337 + write_fsmonitor_extension(&sb, istate);
2338 + err = write_index_ext_header(&c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
2339 + || ce_write(&c, newfd, sb.buf, sb.len) < 0;
2340 + strbuf_release(&sb);
2341 + if (err)
2342 + return -1;
2343 + }
2344
2345 if (ce_flush(&c, newfd, istate->sha1))
2346 return -1;
submodule.c
+1 -1
@@ -62,7 +62,7 @@ int is_staging_gitmodules_ok(const struct index_state *istate)
62 if ((pos >= 0) && (pos < istate->cache_nr)) {
63 struct stat st;
64 if (lstat(GITMODULES_FILE, &st) == 0 &&
65 - ce_match_stat(istate->cache[pos], &st, 0) & DATA_CHANGED)
65 + ce_match_stat(istate->cache[pos], &st, CE_MATCH_IGNORE_FSMONITOR) & DATA_CHANGED)
66 return 0;
67 }
68
unpack-trees.c
+2
@@ -14,6 +14,7 @@
14 #include "dir.h"
15 #include "submodule.h"
16 #include "submodule-config.h"
17 +#include "fsmonitor.h"
18
19 /*
20 * Error messages expected by scripts out of plumbing commands such as
@@ -408,6 +409,7 @@ static int apply_sparse_checkout(struct index_state *istate,
409 ce->ce_flags &= ~CE_SKIP_WORKTREE;
410 if (was_skip_worktree != ce_skip_worktree(ce)) {
411 ce->ce_flags |= CE_UPDATE_IN_BASE;
412 + mark_fsmonitor_invalid(istate, ce);
413 istate->cache_changed |= CE_ENTRY_CHANGED;
414 }
415