diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c index 6011fe42ee..899355c55a 100644 --- a/builtin/fsmonitor--daemon.c +++ b/builtin/fsmonitor--daemon.c @@ -1424,6 +1424,17 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix) die(_("invalid 'ipc-threads' value (%d)"), fsmonitor__ipc_threads); + prepare_repo_settings(the_repository); + fsm_settings__set_ipc(the_repository); + + if (fsm_settings__get_mode(the_repository) == FSMONITOR_MODE_INCOMPATIBLE) { + struct strbuf buf_reason = STRBUF_INIT; + fsm_settings__get_reason(the_repository, &buf_reason); + error("%s '%s'", buf_reason.buf, xgetcwd()); + strbuf_release(&buf_reason); + return -1; + } + if (!strcmp(subcmd, "start")) return !!try_to_start_background_daemon(); diff --git a/builtin/update-index.c b/builtin/update-index.c index 79db3ff37e..3f1ed95549 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -1216,6 +1216,14 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) if (fsmonitor > 0) { enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r); + if (fsm_mode == FSMONITOR_MODE_INCOMPATIBLE) { + struct strbuf buf_reason = STRBUF_INIT; + fsm_settings__get_reason(r, &buf_reason); + error("%s", buf_reason.buf); + strbuf_release(&buf_reason); + return -1; + } + if (fsm_mode == FSMONITOR_MODE_DISABLED) { warning(_("core.useBuiltinFSMonitor is unset; " "set it if you really want to enable the " diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c index 2770266f5e..5a803a41d5 100644 --- a/fsmonitor-settings.c +++ b/fsmonitor-settings.c @@ -9,19 +9,57 @@ */ struct fsmonitor_settings { enum fsmonitor_mode mode; + enum fsmonitor_reason reason; char *hook_path; }; -void fsm_settings__set_ipc(struct repository *r) +static void set_incompatible(struct repository *r, + enum fsmonitor_reason reason) { struct fsmonitor_settings *s = r->settings.fsmonitor; + s->mode = FSMONITOR_MODE_INCOMPATIBLE; + s->reason = reason; +} + +static int check_for_incompatible(struct repository *r) +{ + if (!r->worktree) { + /* + * Bare repositories don't have a working directory and + * therefore have nothing to watch. + */ + set_incompatible(r, FSMONITOR_REASON_BARE); + return 1; + } + + return 0; +} + +static struct fsmonitor_settings *s_init(struct repository *r) +{ + if (!r->settings.fsmonitor) + CALLOC_ARRAY(r->settings.fsmonitor, 1); + + return r->settings.fsmonitor; +} + +void fsm_settings__set_ipc(struct repository *r) +{ + struct fsmonitor_settings *s = s_init(r); + + if (check_for_incompatible(r)) + return; + s->mode = FSMONITOR_MODE_IPC; } void fsm_settings__set_hook(struct repository *r, const char *path) { - struct fsmonitor_settings *s = r->settings.fsmonitor; + struct fsmonitor_settings *s = s_init(r); + + if (check_for_incompatible(r)) + return; s->mode = FSMONITOR_MODE_HOOK; s->hook_path = strdup(path); @@ -29,9 +67,10 @@ void fsm_settings__set_hook(struct repository *r, const char *path) void fsm_settings__set_disabled(struct repository *r) { - struct fsmonitor_settings *s = r->settings.fsmonitor; + struct fsmonitor_settings *s = s_init(r); s->mode = FSMONITOR_MODE_DISABLED; + s->reason = FSMONITOR_REASON_ZERO; FREE_AND_NULL(s->hook_path); } @@ -65,12 +104,6 @@ static int check_for_hook(struct repository *r) static void lookup_fsmonitor_settings(struct repository *r) { - struct fsmonitor_settings *s; - - CALLOC_ARRAY(s, 1); - - r->settings.fsmonitor = s; - if (check_for_ipc(r)) return; @@ -95,3 +128,35 @@ const char *fsm_settings__get_hook_path(struct repository *r) return r->settings.fsmonitor->hook_path; } + +static void create_reason_message(struct repository *r, + struct strbuf *buf_reason) +{ + struct fsmonitor_settings *s = r->settings.fsmonitor; + + switch (s->reason) { + case FSMONITOR_REASON_ZERO: + return; + + case FSMONITOR_REASON_BARE: + strbuf_addstr(buf_reason, + _("bare repos are incompatible with fsmonitor")); + return; + + default: + BUG("Unhandled case in create_reason_message '%d'", s->reason); + } +} +enum fsmonitor_reason fsm_settings__get_reason(struct repository *r, + struct strbuf *buf_reason) +{ + strbuf_reset(buf_reason); + + if (!r->settings.fsmonitor) + lookup_fsmonitor_settings(r); + + if (r->settings.fsmonitor->mode == FSMONITOR_MODE_INCOMPATIBLE) + create_reason_message(r, buf_reason); + + return r->settings.fsmonitor->reason; +} diff --git a/fsmonitor-settings.h b/fsmonitor-settings.h index 50b2923461..eb45524b1f 100644 --- a/fsmonitor-settings.h +++ b/fsmonitor-settings.h @@ -4,17 +4,28 @@ struct repository; enum fsmonitor_mode { + FSMONITOR_MODE_INCOMPATIBLE = -1, /* see _reason */ FSMONITOR_MODE_DISABLED = 0, FSMONITOR_MODE_HOOK = 1, /* core.fsmonitor */ FSMONITOR_MODE_IPC = 2, /* core.useBuiltinFSMonitor */ }; +/* + * Incompatibility reasons. + */ +enum fsmonitor_reason { + FSMONITOR_REASON_ZERO = 0, + FSMONITOR_REASON_BARE = 1, +}; + void fsm_settings__set_ipc(struct repository *r); void fsm_settings__set_hook(struct repository *r, const char *path); void fsm_settings__set_disabled(struct repository *r); enum fsmonitor_mode fsm_settings__get_mode(struct repository *r); const char *fsm_settings__get_hook_path(struct repository *r); +enum fsmonitor_reason fsm_settings__get_reason(struct repository *r, + struct strbuf *buf_reason); struct fsmonitor_settings; diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh index a6308acf00..51b2218601 100755 --- a/t/t7519-status-fsmonitor.sh +++ b/t/t7519-status-fsmonitor.sh @@ -55,6 +55,32 @@ test_lazy_prereq UNTRACKED_CACHE ' test $ret -ne 1 ' +# Test that we detect and disallow repos that are incompatible with FSMonitor. +test_expect_success 'incompatible bare repo' ' + test_when_finished "rm -rf ./bare-clone actual expect" && + git init --bare bare-clone && + cat >expect <<-\EOF && + error: bare repos are incompatible with fsmonitor + EOF + + test_must_fail \ + git -C ./bare-clone -c core.fsmonitor=foo \ + update-index --fsmonitor 2>actual && + test_cmp expect actual && + + test_must_fail \ + git -C ./bare-clone -c core.usebuiltinfsmonitor=true \ + update-index --fsmonitor 2>actual && + test_cmp expect actual +' + +test_expect_success FSMONITOR_DAEMON 'run fsmonitor-daemon in bare repo' ' + test_when_finished "rm -rf ./bare-clone actual" && + git init --bare bare-clone && + test_must_fail git -C ./bare-clone fsmonitor--daemon run 2>actual && + grep "bare repos are incompatible with fsmonitor" actual +' + test_expect_success 'setup' ' mkdir -p .git/hooks && : >tracked &&