Skip to content

Commit 8da2c57

Browse files
kewillfordgitster
authored andcommitted
fsmonitor: handle version 2 of the hooks that will use opaque token
Some file monitors like watchman will use something other than a timestamp to keep better track of what changes happen in between calls to query the fsmonitor. The clockid in watchman is a string. Now that the index is storing an opaque token for the last update the code needs to be updated to pass that opaque token to a verion 2 of the fsmonitor hook. Because there are repos that already have version 1 of the hook and we want them to continue to work when git is updated, we need to handle both version 1 and version 2 of the hook. In order to do that a config value is being added core.fsmonitorHookVersion to force what version of the hook should be used. When this is not set it will default to -1 and then the code will attempt to call version 2 of the hook first. If that fails it will fallback to trying version 1. Signed-off-by: Kevin Willford <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 56c6910 commit 8da2c57

File tree

4 files changed

+71
-15
lines changed

4 files changed

+71
-15
lines changed

fsmonitor.c

+64-11
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
#define INDEX_EXTENSION_VERSION1 (1)
1010
#define INDEX_EXTENSION_VERSION2 (2)
11-
#define HOOK_INTERFACE_VERSION (1)
11+
#define HOOK_INTERFACE_VERSION1 (1)
12+
#define HOOK_INTERFACE_VERSION2 (2)
1213

1314
struct trace_key trace_fsmonitor = TRACE_KEY_INIT(FSMONITOR);
1415

@@ -25,6 +26,22 @@ static void fsmonitor_ewah_callback(size_t pos, void *is)
2526
ce->ce_flags &= ~CE_FSMONITOR_VALID;
2627
}
2728

29+
static int fsmonitor_hook_version(void)
30+
{
31+
int hook_version;
32+
33+
if (git_config_get_int("core.fsmonitorhookversion", &hook_version))
34+
return -1;
35+
36+
if (hook_version == HOOK_INTERFACE_VERSION1 ||
37+
hook_version == HOOK_INTERFACE_VERSION2)
38+
return hook_version;
39+
40+
warning("Invalid hook version '%i' in core.fsmonitorhookversion. "
41+
"Must be 1 or 2.", hook_version);
42+
return -1;
43+
}
44+
2845
int read_fsmonitor_extension(struct index_state *istate, const void *data,
2946
unsigned long sz)
3047
{
@@ -158,15 +175,18 @@ static void fsmonitor_refresh_callback(struct index_state *istate, const char *n
158175
void refresh_fsmonitor(struct index_state *istate)
159176
{
160177
struct strbuf query_result = STRBUF_INIT;
161-
int query_success = 0;
162-
size_t bol; /* beginning of line */
178+
int query_success = 0, hook_version = -1;
179+
size_t bol = 0; /* beginning of line */
163180
uint64_t last_update;
164181
struct strbuf last_update_token = STRBUF_INIT;
165182
char *buf;
166183
unsigned int i;
167184

168185
if (!core_fsmonitor || istate->fsmonitor_has_run_once)
169186
return;
187+
188+
hook_version = fsmonitor_hook_version();
189+
170190
istate->fsmonitor_has_run_once = 1;
171191

172192
trace_printf_key(&trace_fsmonitor, "refresh fsmonitor");
@@ -175,27 +195,60 @@ void refresh_fsmonitor(struct index_state *istate)
175195
* should be inclusive to ensure we don't miss potential changes.
176196
*/
177197
last_update = getnanotime();
178-
strbuf_addf(&last_update_token, "%"PRIu64"", last_update);
198+
if (hook_version == HOOK_INTERFACE_VERSION1)
199+
strbuf_addf(&last_update_token, "%"PRIu64"", last_update);
179200

180201
/*
181-
* If we have a last update time, call query_fsmonitor for the set of
182-
* changes since that time, else assume everything is possibly dirty
202+
* If we have a last update token, call query_fsmonitor for the set of
203+
* changes since that token, else assume everything is possibly dirty
183204
* and check it all.
184205
*/
185206
if (istate->fsmonitor_last_update) {
186-
query_success = !query_fsmonitor(HOOK_INTERFACE_VERSION,
187-
istate->fsmonitor_last_update, &query_result);
207+
if (hook_version == -1 || hook_version == HOOK_INTERFACE_VERSION2) {
208+
query_success = !query_fsmonitor(HOOK_INTERFACE_VERSION2,
209+
istate->fsmonitor_last_update, &query_result);
210+
211+
if (query_success) {
212+
if (hook_version < 0)
213+
hook_version = HOOK_INTERFACE_VERSION2;
214+
215+
/*
216+
* First entry will be the last update token
217+
* Need to use a char * variable because static
218+
* analysis was suggesting to use strbuf_addbuf
219+
* but we don't want to copy the entire strbuf
220+
* only the the chars up to the first NUL
221+
*/
222+
buf = query_result.buf;
223+
strbuf_addstr(&last_update_token, buf);
224+
if (!last_update_token.len) {
225+
warning("Empty last update token.");
226+
query_success = 0;
227+
} else {
228+
bol = last_update_token.len + 1;
229+
}
230+
} else if (hook_version < 0) {
231+
hook_version = HOOK_INTERFACE_VERSION1;
232+
if (!last_update_token.len)
233+
strbuf_addf(&last_update_token, "%"PRIu64"", last_update);
234+
}
235+
}
236+
237+
if (hook_version == HOOK_INTERFACE_VERSION1) {
238+
query_success = !query_fsmonitor(HOOK_INTERFACE_VERSION1,
239+
istate->fsmonitor_last_update, &query_result);
240+
}
241+
188242
trace_performance_since(last_update, "fsmonitor process '%s'", core_fsmonitor);
189243
trace_printf_key(&trace_fsmonitor, "fsmonitor process '%s' returned %s",
190244
core_fsmonitor, query_success ? "success" : "failure");
191245
}
192246

193247
/* a fsmonitor process can return '/' to indicate all entries are invalid */
194-
if (query_success && query_result.buf[0] != '/') {
248+
if (query_success && query_result.buf[bol] != '/') {
195249
/* Mark all entries returned by the monitor as dirty */
196250
buf = query_result.buf;
197-
bol = 0;
198-
for (i = 0; i < query_result.len; i++) {
251+
for (i = bol; i < query_result.len; i++) {
199252
if (buf[i] != '\0')
200253
continue;
201254
fsmonitor_refresh_callback(istate, buf + bol);

t/t7519-status-fsmonitor.sh

+6-1
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ write_integration_script () {
3232
echo "$0: exactly 2 arguments expected"
3333
exit 2
3434
fi
35-
if test "$1" != 1
35+
if test "$1" != 2
3636
then
3737
echo "Unsupported core.fsmonitor hook version." >&2
3838
exit 1
3939
fi
40+
printf "last_update_token\0"
4041
printf "untracked\0"
4142
printf "dir1/untracked\0"
4243
printf "dir2/untracked\0"
@@ -107,6 +108,7 @@ EOF
107108
# test that "update-index --fsmonitor-valid" sets the fsmonitor valid bit
108109
test_expect_success 'update-index --fsmonitor-valid" sets the fsmonitor valid bit' '
109110
write_script .git/hooks/fsmonitor-test<<-\EOF &&
111+
printf "last_update_token\0"
110112
EOF
111113
git update-index --fsmonitor &&
112114
git update-index --fsmonitor-valid dir1/modified &&
@@ -167,6 +169,7 @@ EOF
167169
# test that newly added files are marked valid
168170
test_expect_success 'newly added files are marked valid' '
169171
write_script .git/hooks/fsmonitor-test<<-\EOF &&
172+
printf "last_update_token\0"
170173
EOF
171174
git add new &&
172175
git add dir1/new &&
@@ -207,6 +210,7 @@ EOF
207210
# test that *only* files returned by the integration script get flagged as invalid
208211
test_expect_success '*only* files returned by the integration script get flagged as invalid' '
209212
write_script .git/hooks/fsmonitor-test<<-\EOF &&
213+
printf "last_update_token\0"
210214
printf "dir1/modified\0"
211215
EOF
212216
clean_repo &&
@@ -276,6 +280,7 @@ do
276280
# (if enabled) files unless it is told about them.
277281
test_expect_success "status doesn't detect unreported modifications" '
278282
write_script .git/hooks/fsmonitor-test<<-\EOF &&
283+
printf "last_update_token\0"
279284
:>marker
280285
EOF
281286
clean_repo &&

t/t7519/fsmonitor-all

-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ fi
1717

1818
if test "$1" != 1
1919
then
20-
echo "Unsupported core.fsmonitor hook version." >&2
2120
exit 1
2221
fi
2322

t/t7519/fsmonitor-watchman

+1-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ if ($version == 1) {
2626
# subtract one second to make sure watchman will return all changes
2727
$time = int ($time / 1000000000) - 1;
2828
} else {
29-
die "Unsupported query-fsmonitor hook version '$version'.\n" .
30-
"Falling back to scanning...\n";
29+
exit 1;
3130
}
3231

3332
my $git_work_tree;

0 commit comments

Comments
 (0)