fsmonitor-settings: bare repos are incompatible with FSMonitor

Bare repos do not have a worktree, so there is nothing for the daemon watch. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff Hostetler committed May 26, 2022 at 21:46 UTC 62a62a28308453ee830bb7e02cf732474a6e01a5
5 files changed +186 -20
builtin/fsmonitor--daemon.c
+18
@@ -1423,6 +1423,7 @@ static int try_to_start_background_daemon(void)
1423 int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
1424 {
1425 const char *subcmd;
1426 + enum fsmonitor_reason reason;
1427 int detach_console = 0;
1428
1429 struct option options[] = {
@@ -1449,6 +1450,23 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
1450 die(_("invalid 'ipc-threads' value (%d)"),
1451 fsmonitor__ipc_threads);
1452
1453 + prepare_repo_settings(the_repository);
1454 + /*
1455 + * If the repo is fsmonitor-compatible, explicitly set IPC-mode
1456 + * (without bothering to load the `core.fsmonitor` config settings).
1457 + *
1458 + * If the repo is not compatible, the repo-settings will be set to
1459 + * incompatible rather than IPC, so we can use one of the __get
1460 + * routines to detect the discrepancy.
1461 + */
1462 + fsm_settings__set_ipc(the_repository);
1463 +
1464 + reason = fsm_settings__get_reason(the_repository);
1465 + if (reason > FSMONITOR_REASON_OK)
1466 + die("%s",
1467 + fsm_settings__get_incompatible_msg(the_repository,
1468 + reason));
1469 +
1470 if (!strcmp(subcmd, "start"))
1471 return !!try_to_start_background_daemon();
1472
builtin/update-index.c
+16
@@ -1237,6 +1237,22 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
1237
1238 if (fsmonitor > 0) {
1239 enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
1240 + enum fsmonitor_reason reason = fsm_settings__get_reason(r);
1241 +
1242 + /*
1243 + * The user wants to turn on FSMonitor using the command
1244 + * line argument. (We don't know (or care) whether that
1245 + * is the IPC or HOOK version.)
1246 + *
1247 + * Use one of the __get routines to force load the FSMonitor
1248 + * config settings into the repo-settings. That will detect
1249 + * whether the file system is compatible so that we can stop
1250 + * here with a nice error message.
1251 + */
1252 + if (reason > FSMONITOR_REASON_OK)
1253 + die("%s",
1254 + fsm_settings__get_incompatible_msg(r, reason));
1255 +
1256 if (fsm_mode == FSMONITOR_MODE_DISABLED) {
1257 warning(_("core.fsmonitor is unset; "
1258 "set it if you really want to "
fsmonitor-settings.c
+113 -20
@@ -9,23 +9,42 @@
9 */
10 struct fsmonitor_settings {
11 enum fsmonitor_mode mode;
12 + enum fsmonitor_reason reason;
13 char *hook_path;
14 };
15
15 -static void lookup_fsmonitor_settings(struct repository *r)
16 +static enum fsmonitor_reason check_for_incompatible(struct repository *r)
17 +{
18 + if (!r->worktree) {
19 + /*
20 + * Bare repositories don't have a working directory and
21 + * therefore have nothing to watch.
22 + */
23 + return FSMONITOR_REASON_BARE;
24 + }
25 +
26 + return FSMONITOR_REASON_OK;
27 +}
28 +
29 +static struct fsmonitor_settings *alloc_settings(void)
30 {
31 struct fsmonitor_settings *s;
32 +
33 + CALLOC_ARRAY(s, 1);
34 + s->mode = FSMONITOR_MODE_DISABLED;
35 + s->reason = FSMONITOR_REASON_UNTESTED;
36 +
37 + return s;
38 +}
39 +
40 +static void lookup_fsmonitor_settings(struct repository *r)
41 +{
42 const char *const_str;
43 int bool_value;
44
45 if (r->settings.fsmonitor)
46 return;
47
24 - CALLOC_ARRAY(s, 1);
25 - s->mode = FSMONITOR_MODE_DISABLED;
26 -
27 - r->settings.fsmonitor = s;
28 -
48 /*
49 * Overload the existing "core.fsmonitor" config setting (which
50 * has historically been either unset or a hook pathname) to
@@ -38,6 +57,8 @@ static void lookup_fsmonitor_settings(struct repository *r)
57 case 0: /* config value was set to <bool> */
58 if (bool_value)
59 fsm_settings__set_ipc(r);
60 + else
61 + fsm_settings__set_disabled(r);
62 return;
63
64 case 1: /* config value was unset */
@@ -53,18 +74,18 @@ static void lookup_fsmonitor_settings(struct repository *r)
74 return;
75 }
76
56 - if (!const_str || !*const_str)
57 - return;
58 -
59 - fsm_settings__set_hook(r, const_str);
77 + if (const_str && *const_str)
78 + fsm_settings__set_hook(r, const_str);
79 + else
80 + fsm_settings__set_disabled(r);
81 }
82
83 enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
84 {
85 if (!r)
86 r = the_repository;
66 -
67 - lookup_fsmonitor_settings(r);
87 + if (!r->settings.fsmonitor)
88 + lookup_fsmonitor_settings(r);
89
90 return r->settings.fsmonitor->mode;
91 }
@@ -73,31 +94,55 @@ const char *fsm_settings__get_hook_path(struct repository *r)
94 {
95 if (!r)
96 r = the_repository;
76 -
77 - lookup_fsmonitor_settings(r);
97 + if (!r->settings.fsmonitor)
98 + lookup_fsmonitor_settings(r);
99
100 return r->settings.fsmonitor->hook_path;
101 }
102
103 void fsm_settings__set_ipc(struct repository *r)
104 {
105 + enum fsmonitor_reason reason = check_for_incompatible(r);
106 +
107 + if (reason != FSMONITOR_REASON_OK) {
108 + fsm_settings__set_incompatible(r, reason);
109 + return;
110 + }
111 +
112 + /*
113 + * Caller requested IPC explicitly, so avoid (possibly
114 + * recursive) config lookup.
115 + */
116 if (!r)
117 r = the_repository;
86 -
87 - lookup_fsmonitor_settings(r);
118 + if (!r->settings.fsmonitor)
119 + r->settings.fsmonitor = alloc_settings();
120
121 r->settings.fsmonitor->mode = FSMONITOR_MODE_IPC;
122 + r->settings.fsmonitor->reason = reason;
123 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
124 }
125
126 void fsm_settings__set_hook(struct repository *r, const char *path)
127 {
128 + enum fsmonitor_reason reason = check_for_incompatible(r);
129 +
130 + if (reason != FSMONITOR_REASON_OK) {
131 + fsm_settings__set_incompatible(r, reason);
132 + return;
133 + }
134 +
135 + /*
136 + * Caller requested hook explicitly, so avoid (possibly
137 + * recursive) config lookup.
138 + */
139 if (!r)
140 r = the_repository;
97 -
98 - lookup_fsmonitor_settings(r);
141 + if (!r->settings.fsmonitor)
142 + r->settings.fsmonitor = alloc_settings();
143
144 r->settings.fsmonitor->mode = FSMONITOR_MODE_HOOK;
145 + r->settings.fsmonitor->reason = reason;
146 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
147 r->settings.fsmonitor->hook_path = strdup(path);
148 }
@@ -106,9 +151,57 @@ void fsm_settings__set_disabled(struct repository *r)
151 {
152 if (!r)
153 r = the_repository;
109 -
110 - lookup_fsmonitor_settings(r);
154 + if (!r->settings.fsmonitor)
155 + r->settings.fsmonitor = alloc_settings();
156
157 r->settings.fsmonitor->mode = FSMONITOR_MODE_DISABLED;
158 + r->settings.fsmonitor->reason = FSMONITOR_REASON_OK;
159 + FREE_AND_NULL(r->settings.fsmonitor->hook_path);
160 +}
161 +
162 +void fsm_settings__set_incompatible(struct repository *r,
163 + enum fsmonitor_reason reason)
164 +{
165 + if (!r)
166 + r = the_repository;
167 + if (!r->settings.fsmonitor)
168 + r->settings.fsmonitor = alloc_settings();
169 +
170 + r->settings.fsmonitor->mode = FSMONITOR_MODE_INCOMPATIBLE;
171 + r->settings.fsmonitor->reason = reason;
172 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
173 }
174 +
175 +enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)
176 +{
177 + if (!r)
178 + r = the_repository;
179 + if (!r->settings.fsmonitor)
180 + lookup_fsmonitor_settings(r);
181 +
182 + return r->settings.fsmonitor->reason;
183 +}
184 +
185 +char *fsm_settings__get_incompatible_msg(const struct repository *r,
186 + enum fsmonitor_reason reason)
187 +{
188 + struct strbuf msg = STRBUF_INIT;
189 +
190 + switch (reason) {
191 + case FSMONITOR_REASON_UNTESTED:
192 + case FSMONITOR_REASON_OK:
193 + goto done;
194 +
195 + case FSMONITOR_REASON_BARE:
196 + strbuf_addf(&msg,
197 + _("bare repository '%s' is incompatible with fsmonitor"),
198 + xgetcwd());
199 + goto done;
200 + }
201 +
202 + BUG("Unhandled case in fsm_settings__get_incompatible_msg: '%d'",
203 + reason);
204 +
205 +done:
206 + return strbuf_detach(&msg, NULL);
207 +}
fsmonitor-settings.h
+16
@@ -4,18 +4,34 @@
4 struct repository;
5
6 enum fsmonitor_mode {
7 + FSMONITOR_MODE_INCOMPATIBLE = -1, /* see _reason */
8 FSMONITOR_MODE_DISABLED = 0,
9 FSMONITOR_MODE_HOOK = 1, /* core.fsmonitor=<hook_path> */
10 FSMONITOR_MODE_IPC = 2, /* core.fsmonitor=<true> */
11 };
12
13 +/*
14 + * Incompatibility reasons.
15 + */
16 +enum fsmonitor_reason {
17 + FSMONITOR_REASON_UNTESTED = 0,
18 + FSMONITOR_REASON_OK, /* no incompatibility or when disabled */
19 + FSMONITOR_REASON_BARE,
20 +};
21 +
22 void fsm_settings__set_ipc(struct repository *r);
23 void fsm_settings__set_hook(struct repository *r, const char *path);
24 void fsm_settings__set_disabled(struct repository *r);
25 +void fsm_settings__set_incompatible(struct repository *r,
26 + enum fsmonitor_reason reason);
27
28 enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);
29 const char *fsm_settings__get_hook_path(struct repository *r);
30
31 +enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);
32 +char *fsm_settings__get_incompatible_msg(const struct repository *r,
33 + enum fsmonitor_reason reason);
34 +
35 struct fsmonitor_settings;
36
37 #endif /* FSMONITOR_SETTINGS_H */
t/t7519-status-fsmonitor.sh
+23
@@ -55,6 +55,29 @@ test_lazy_prereq UNTRACKED_CACHE '
55 test $ret -ne 1
56 '
57
58 +# Test that we detect and disallow repos that are incompatible with FSMonitor.
59 +test_expect_success 'incompatible bare repo' '
60 + test_when_finished "rm -rf ./bare-clone actual expect" &&
61 + git init --bare bare-clone &&
62 +
63 + test_must_fail \
64 + git -C ./bare-clone -c core.fsmonitor=foo \
65 + update-index --fsmonitor 2>actual &&
66 + grep "bare repository .* is incompatible with fsmonitor" actual &&
67 +
68 + test_must_fail \
69 + git -C ./bare-clone -c core.fsmonitor=true \
70 + update-index --fsmonitor 2>actual &&
71 + grep "bare repository .* is incompatible with fsmonitor" actual
72 +'
73 +
74 +test_expect_success FSMONITOR_DAEMON 'run fsmonitor-daemon in bare repo' '
75 + test_when_finished "rm -rf ./bare-clone actual" &&
76 + git init --bare bare-clone &&
77 + test_must_fail git -C ./bare-clone fsmonitor--daemon run 2>actual &&
78 + grep "bare repository .* is incompatible with fsmonitor" actual
79 +'
80 +
81 test_expect_success 'setup' '
82 mkdir -p .git/hooks &&
83 : >tracked &&