@@ -90,7 +90,11 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data,
90
90
if (!istate -> split_index )
91
91
assert_index_minimum (istate , istate -> fsmonitor_dirty -> bit_size );
92
92
93
- trace_printf_key (& trace_fsmonitor , "read fsmonitor extension successful" );
93
+ trace2_data_string ("index" , NULL , "extension/fsmn/read/token" ,
94
+ istate -> fsmonitor_last_update );
95
+ trace_printf_key (& trace_fsmonitor ,
96
+ "read fsmonitor extension successful '%s'" ,
97
+ istate -> fsmonitor_last_update );
94
98
return 0 ;
95
99
}
96
100
@@ -134,7 +138,11 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
134
138
put_be32 (& ewah_size , sb -> len - ewah_start );
135
139
memcpy (sb -> buf + fixup , & ewah_size , sizeof (uint32_t ));
136
140
137
- trace_printf_key (& trace_fsmonitor , "write fsmonitor extension successful" );
141
+ trace2_data_string ("index" , NULL , "extension/fsmn/write/token" ,
142
+ istate -> fsmonitor_last_update );
143
+ trace_printf_key (& trace_fsmonitor ,
144
+ "write fsmonitor extension successful '%s'" ,
145
+ istate -> fsmonitor_last_update );
138
146
}
139
147
140
148
/*
@@ -143,6 +151,7 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
143
151
static int query_fsmonitor (int version , const char * last_update , struct strbuf * query_result )
144
152
{
145
153
struct child_process cp = CHILD_PROCESS_INIT ;
154
+ int result ;
146
155
147
156
if (!core_fsmonitor )
148
157
return -1 ;
@@ -153,16 +162,63 @@ static int query_fsmonitor(int version, const char *last_update, struct strbuf *
153
162
cp .use_shell = 1 ;
154
163
cp .dir = get_git_work_tree ();
155
164
156
- return capture_command (& cp , query_result , 1024 );
165
+ trace2_region_enter ("fsm_hook" , "query" , NULL );
166
+
167
+ result = capture_command (& cp , query_result , 1024 );
168
+
169
+ if (result )
170
+ trace2_data_intmax ("fsm_hook" , NULL , "query/failed" , result );
171
+ else {
172
+ trace2_data_intmax ("fsm_hook" , NULL , "query/response-length" ,
173
+ query_result -> len );
174
+
175
+ if (fsmonitor_is_trivial_response (query_result ))
176
+ trace2_data_intmax ("fsm_hook" , NULL ,
177
+ "query/trivial-response" , 1 );
178
+ }
179
+
180
+ trace2_region_leave ("fsm_hook" , "query" , NULL );
181
+
182
+ return result ;
157
183
}
158
184
159
- static void fsmonitor_refresh_callback ( struct index_state * istate , const char * name )
185
+ int fsmonitor_is_trivial_response ( const struct strbuf * query_result )
160
186
{
161
- int pos = index_name_pos (istate , name , strlen (name ));
187
+ static char trivial_response [3 ] = { '\0' , '/' , '\0' };
188
+ int is_trivial = !memcmp (trivial_response ,
189
+ & query_result -> buf [query_result -> len - 3 ], 3 );
190
+
191
+ return is_trivial ;
192
+ }
193
+
194
+ static void fsmonitor_refresh_callback (struct index_state * istate , char * name )
195
+ {
196
+ int i , len = strlen (name );
197
+ if (name [len - 1 ] == '/' ) {
198
+
199
+ /*
200
+ * TODO We should binary search to find the first path with
201
+ * TODO this directory prefix. Then linearly update entries
202
+ * TODO while the prefix matches. Taking care to search without
203
+ * TODO the trailing slash -- because '/' sorts after a few
204
+ * TODO interesting special chars, like '.' and ' '.
205
+ */
206
+
207
+ /* Mark all entries for the folder invalid */
208
+ for (i = 0 ; i < istate -> cache_nr ; i ++ ) {
209
+ if (istate -> cache [i ]-> ce_flags & CE_FSMONITOR_VALID &&
210
+ starts_with (istate -> cache [i ]-> name , name ))
211
+ istate -> cache [i ]-> ce_flags &= ~CE_FSMONITOR_VALID ;
212
+ }
213
+ /* Need to remove the / from the path for the untracked cache */
214
+ name [len - 1 ] = '\0' ;
215
+ } else {
216
+ int pos = index_name_pos (istate , name , strlen (name ));
162
217
163
- if (pos >= 0 ) {
164
- struct cache_entry * ce = istate -> cache [pos ];
165
- ce -> ce_flags &= ~CE_FSMONITOR_VALID ;
218
+ if (pos >= 0 ) {
219
+ struct cache_entry * ce = istate -> cache [pos ];
220
+ ce -> ce_flags &= ~CE_FSMONITOR_VALID ;
221
+ }
166
222
}
167
223
168
224
/*
@@ -288,16 +344,45 @@ void refresh_fsmonitor(struct index_state *istate)
288
344
istate -> fsmonitor_last_update = strbuf_detach (& last_update_token , NULL );
289
345
}
290
346
347
+ /*
348
+ * The caller wants to turn on FSMonitor. And when the caller writes
349
+ * the index to disk, a FSMonitor extension should be included. This
350
+ * requires that `istate->fsmonitor_last_update` not be NULL. But we
351
+ * have not actually talked to a FSMonitor process yet, so we don't
352
+ * have an initial value for this field.
353
+ *
354
+ * For a protocol V1 FSMonitor process, this field is a formatted
355
+ * "nanoseconds since epoch" field. However, for a protocol V2
356
+ * FSMonitor process, this field is an opaque token.
357
+ *
358
+ * Historically, `add_fsmonitor()` has initialized this field to the
359
+ * current time for protocol V1 processes. There are lots of race
360
+ * conditions here, but that code has shipped...
361
+ *
362
+ * The only true solution is to use a V2 FSMonitor and get a current
363
+ * or default token value (that it understands), but we cannot do that
364
+ * until we have actually talked to an instance of the FSMonitor process
365
+ * (but the protocol requires that we send a token first...).
366
+ *
367
+ * For simplicity, just initialize like we have a V1 process and require
368
+ * that V2 processes adapt.
369
+ */
370
+ static void initialize_fsmonitor_last_update (struct index_state * istate )
371
+ {
372
+ struct strbuf last_update = STRBUF_INIT ;
373
+
374
+ strbuf_addf (& last_update , "%" PRIu64 "" , getnanotime ());
375
+ istate -> fsmonitor_last_update = strbuf_detach (& last_update , NULL );
376
+ }
377
+
291
378
void add_fsmonitor (struct index_state * istate )
292
379
{
293
380
unsigned int i ;
294
- struct strbuf last_update = STRBUF_INIT ;
295
381
296
382
if (!istate -> fsmonitor_last_update ) {
297
383
trace_printf_key (& trace_fsmonitor , "add fsmonitor" );
298
384
istate -> cache_changed |= FSMONITOR_CHANGED ;
299
- strbuf_addf (& last_update , "%" PRIu64 "" , getnanotime ());
300
- istate -> fsmonitor_last_update = strbuf_detach (& last_update , NULL );
385
+ initialize_fsmonitor_last_update (istate );
301
386
302
387
/* reset the fsmonitor state */
303
388
for (i = 0 ; i < istate -> cache_nr ; i ++ )
0 commit comments