@ -90,7 +90,11 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data,
@@ -90,7 +90,11 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data,
if (!istate->split_index)
assert_index_minimum(istate, istate->fsmonitor_dirty->bit_size);
trace_printf_key(&trace_fsmonitor, "read fsmonitor extension successful");
trace2_data_string("index", NULL, "extension/fsmn/read/token",
istate->fsmonitor_last_update);
trace_printf_key(&trace_fsmonitor,
"read fsmonitor extension successful '%s'",
istate->fsmonitor_last_update);
return 0;
}
@ -134,7 +138,11 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
@@ -134,7 +138,11 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
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");
trace2_data_string("index", NULL, "extension/fsmn/write/token",
istate->fsmonitor_last_update);
trace_printf_key(&trace_fsmonitor,
"write fsmonitor extension successful '%s'",
istate->fsmonitor_last_update);
}
/*
@ -143,6 +151,7 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
@@ -143,6 +151,7 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
static int query_fsmonitor(int version, const char *last_update, struct strbuf *query_result)
{
struct child_process cp = CHILD_PROCESS_INIT;
int result;
if (!core_fsmonitor)
return -1;
@ -153,16 +162,63 @@ static int query_fsmonitor(int version, const char *last_update, struct strbuf *
@@ -153,16 +162,63 @@ static int query_fsmonitor(int version, const char *last_update, struct strbuf *
cp.use_shell = 1;
cp.dir = get_git_work_tree();
return capture_command(&cp, query_result, 1024);
trace2_region_enter("fsm_hook", "query", NULL);
result = capture_command(&cp, query_result, 1024);
if (result)
trace2_data_intmax("fsm_hook", NULL, "query/failed", result);
else {
trace2_data_intmax("fsm_hook", NULL, "query/response-length",
query_result->len);
if (fsmonitor_is_trivial_response(query_result))
trace2_data_intmax("fsm_hook", NULL,
"query/trivial-response", 1);
}
trace2_region_leave("fsm_hook", "query", NULL);
return result;
}
static void fsmonitor_refresh_callback(struct index_state *istate, const char *name)
int fsmonitor_is_trivial_response(const struct strbuf *query_result)
{
int pos = index_name_pos(istate, name, strlen(name));
static char trivial_response[3] = { '\0', '/', '\0' };
int is_trivial = !memcmp(trivial_response,
&query_result->buf[query_result->len - 3], 3);
return is_trivial;
}
static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
{
int i, len = strlen(name);
if (name[len - 1] == '/') {
/*
* TODO We should binary search to find the first path with
* TODO this directory prefix. Then linearly update entries
* TODO while the prefix matches. Taking care to search without
* TODO the trailing slash -- because '/' sorts after a few
* TODO interesting special chars, like '.' and ' '.
*/
/* Mark all entries for the folder invalid */
for (i = 0; i < istate->cache_nr; i++) {
if (istate->cache[i]->ce_flags & CE_FSMONITOR_VALID &&
starts_with(istate->cache[i]->name, name))
istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
}
/* Need to remove the / from the path for the untracked cache */
name[len - 1] = '\0';
} else {
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;
if (pos >= 0) {
struct cache_entry *ce = istate->cache[pos];
ce->ce_flags &= ~CE_FSMONITOR_VALID;
}
}
/*
@ -288,16 +344,45 @@ void refresh_fsmonitor(struct index_state *istate)
@@ -288,16 +344,45 @@ void refresh_fsmonitor(struct index_state *istate)
istate->fsmonitor_last_update = strbuf_detach(&last_update_token, NULL);
}
/*
* The caller wants to turn on FSMonitor. And when the caller writes
* the index to disk, a FSMonitor extension should be included. This
* requires that `istate->fsmonitor_last_update` not be NULL. But we
* have not actually talked to a FSMonitor process yet, so we don't
* have an initial value for this field.
*
* For a protocol V1 FSMonitor process, this field is a formatted
* "nanoseconds since epoch" field. However, for a protocol V2
* FSMonitor process, this field is an opaque token.
*
* Historically, `add_fsmonitor()` has initialized this field to the
* current time for protocol V1 processes. There are lots of race
* conditions here, but that code has shipped...
*
* The only true solution is to use a V2 FSMonitor and get a current
* or default token value (that it understands), but we cannot do that
* until we have actually talked to an instance of the FSMonitor process
* (but the protocol requires that we send a token first...).
*
* For simplicity, just initialize like we have a V1 process and require
* that V2 processes adapt.
*/
static void initialize_fsmonitor_last_update(struct index_state *istate)
{
struct strbuf last_update = STRBUF_INIT;
strbuf_addf(&last_update, "%"PRIu64"", getnanotime());
istate->fsmonitor_last_update = strbuf_detach(&last_update, NULL);
}
void add_fsmonitor(struct index_state *istate)
{
unsigned int i;
struct strbuf last_update = STRBUF_INIT;
if (!istate->fsmonitor_last_update) {
trace_printf_key(&trace_fsmonitor, "add fsmonitor");
istate->cache_changed |= FSMONITOR_CHANGED;
strbuf_addf(&last_update, "%"PRIu64"", getnanotime());
istate->fsmonitor_last_update = strbuf_detach(&last_update, NULL);
initialize_fsmonitor_last_update(istate);
/* reset the fsmonitor state */
for (i = 0; i < istate->cache_nr; i++)