Here is V3 of Part 3 of my builtin FSMonitor series. I have addressed all of the feedback from Part 3 V2 and the mess that was Part 2.5 (now obsolete). This version builds upon the new V7 of Part 2 (which includes 2.5). This version includes: (1) fixup a few more "_()" calls in die() and error() messages. (2) refactor how fsmonitor incompatibility error messages are formatted (3) make use of new "start_daemon()" function t7527 to reduce duplicated code. (4) improve documentation around Unicode/UTF8 testing on MacOS. Here is the range-diff from V2 to V3: 1: 34619e0652 ! 1: 779a15b38e fsm-listen-win32: handle shortnames @@ compat/fsmonitor/fsm-listen-win32.c: static int normalize_path_in_utf8(FILE_NOTI @@ compat/fsmonitor/fsm-listen-win32.c: static int normalize_path_in_utf8(FILE_NOTIFY_INFORMATION *info, goto normalize; if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - error("[GLE %ld] could not convert path to UTF-8: '%.*ls'", + error(_("[GLE %ld] could not convert path to UTF-8: '%.*ls'"), - GetLastError(), - (int)(info->FileNameLength / sizeof(WCHAR)), - info->FileName); @@ t/t7527-builtin-fsmonitor.sh: test_expect_success 'implicit daemon stop (rename + + git init test_implicit_1s && + -+ start_daemon test_implicit_1s && ++ start_daemon -C test_implicit_1s && + + # renaming the .git directory will implicitly stop the daemon. + # this moves {.git, GIT~1} to {.gitxyz, GITXYZ~1}. @@ t/t7527-builtin-fsmonitor.sh: test_expect_success 'implicit daemon stop (rename + test_path_is_file test_implicit_1s2/GIT~1 && + test_path_is_dir test_implicit_1s2/GIT~2 && + -+ start_daemon test_implicit_1s2 && ++ start_daemon -C test_implicit_1s2 && + + # renaming the .git directory will implicitly stop the daemon. + # the rename-from FS Event will contain the shortname. 2: 3a0f30b849 ! 2: 11d4a17b69 t7527: test FSMonitor on repos with Unicode root paths @@ t/t7527-builtin-fsmonitor.sh: do + git -C "$u" add file1 && + git -C "$u" config core.fsmonitor true && + -+ start_daemon "$u" && ++ start_daemon -C "$u" && + git -C "$u" status >actual && + grep "new file: file1" actual + ' 3: 87d1c0b6f2 ! 3: 901fa32f6e t/helper/fsmonitor-client: create stress test @@ t/helper/test-fsmonitor-client.c: static int do_send_flush(void) + int nr_requests = 1; const char * const fsmonitor_client_usage[] = { - N_("test-helper fsmonitor-client query [<token>]"), - N_("test-helper fsmonitor-client flush"), -+ N_("test-helper fsmonitor-client hammer [<token>] [<threads>] [<requests>]"), + "test-tool fsmonitor-client query [<token>]", + "test-tool fsmonitor-client flush", ++ "test-tool fsmonitor-client hammer [<token>] [<threads>] [<requests>]", NULL, }; struct option options[] = { - OPT_STRING(0, "token", &token, N_("token"), - N_("command token to send to the server")), + OPT_STRING(0, "token", &token, "token", + "command token to send to the server"), + -+ OPT_INTEGER(0, "threads", &nr_threads, N_("number of client threads")), -+ OPT_INTEGER(0, "requests", &nr_requests, N_("number of requests per thread")), ++ OPT_INTEGER(0, "threads", &nr_threads, "number of client threads"), ++ OPT_INTEGER(0, "requests", &nr_requests, "number of requests per thread"), + OPT_END() }; 4: 8c4f90ae4f ! 4: a8f0b2a525 fsmonitor-settings: bare repos are incompatible with FSMonitor @@ builtin/fsmonitor--daemon.c: int cmd_fsmonitor__daemon(int argc, const char **ar + prepare_repo_settings(the_repository); + fsm_settings__set_ipc(the_repository); + -+ if (fsm_settings__get_mode(the_repository) == FSMONITOR_MODE_INCOMPATIBLE) { -+ const char *msg = fsm_settings__get_reason_msg(the_repository); -+ -+ return error("%s '%s'", msg ? msg : "???", xgetcwd()); -+ } ++ if (fsm_settings__error_if_incompatible(the_repository)) ++ return 1; + if (!strcmp(subcmd, "start")) return !!try_to_start_background_daemon(); @@ builtin/update-index.c: int cmd_update_index(int argc, const char **argv, const if (fsmonitor > 0) { enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r); + -+ if (fsm_mode == FSMONITOR_MODE_INCOMPATIBLE) { -+ const char *msg = fsm_settings__get_reason_msg(r); -+ -+ return error("%s '%s'", msg ? msg : "???", xgetcwd()); -+ } ++ if (fsm_settings__error_if_incompatible(the_repository)) ++ return 1; + if (fsm_mode == FSMONITOR_MODE_DISABLED) { - advise(_("core.fsmonitor is unset; " - "set it if you really want to " + warning(_("core.fsmonitor is unset; " + "set it if you really want to " ## fsmonitor-settings.c ## @@ @@ fsmonitor-settings.c static void lookup_fsmonitor_settings(struct repository *r) { struct fsmonitor_settings *s; +@@ fsmonitor-settings.c: static void lookup_fsmonitor_settings(struct repository *r) + + CALLOC_ARRAY(s, 1); + s->mode = FSMONITOR_MODE_DISABLED; ++ s->reason = FSMONITOR_REASON_OK; + + r->settings.fsmonitor = s; + @@ fsmonitor-settings.c: void fsm_settings__set_ipc(struct repository *r) lookup_fsmonitor_settings(r); @@ fsmonitor-settings.c: void fsm_settings__set_disabled(struct repository *r) + return r->settings.fsmonitor->reason; +} + -+const char *fsm_settings__get_reason_msg(struct repository *r) ++int fsm_settings__error_if_incompatible(struct repository *r) +{ + enum fsmonitor_reason reason = fsm_settings__get_reason(r); + + switch (reason) { + case FSMONITOR_REASON_OK: -+ return NULL; ++ return 0; + + case FSMONITOR_REASON_BARE: -+ return _("bare repos are incompatible with fsmonitor"); ++ error(_("bare repository '%s' is incompatible with fsmonitor"), ++ xgetcwd()); ++ return 1; + } + -+ BUG("Unhandled case in fsm_settings__get_reason_msg '%d'", ++ BUG("Unhandled case in fsm_settings__error_if_incompatible: '%d'", + reason); +} @@ fsmonitor-settings.h: void fsm_settings__set_disabled(struct repository *r); const char *fsm_settings__get_hook_path(struct repository *r); +enum fsmonitor_reason fsm_settings__get_reason(struct repository *r); -+const char *fsm_settings__get_reason_msg(struct repository *r); ++int fsm_settings__error_if_incompatible(struct repository *r); + struct fsmonitor_settings; @@ t/t7519-status-fsmonitor.sh: test_lazy_prereq UNTRACKED_CACHE ' + test_must_fail \ + git -C ./bare-clone -c core.fsmonitor=foo \ + update-index --fsmonitor 2>actual && -+ grep "bare repos are incompatible with fsmonitor" actual && ++ grep "bare repository .* is incompatible with fsmonitor" actual && + + test_must_fail \ + git -C ./bare-clone -c core.fsmonitor=true \ + update-index --fsmonitor 2>actual && -+ grep "bare repos are incompatible with fsmonitor" actual ++ grep "bare repository .* is incompatible with fsmonitor" 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 ++ grep "bare repository .* is incompatible with fsmonitor" actual +' + test_expect_success 'setup' ' 5: 6329328d18 ! 5: e32a8a7ea7 fsmonitor-settings: stub in platform-specific incompatibility checking @@ Metadata Author: Jeff Hostetler <jeffhost@xxxxxxxxxxxxx> ## Commit message ## - fsmonitor-settings: stub in platform-specific incompatibility checking + fsmonitor-settings: stub in Win32-specific incompatibility checking Extend generic incompatibility checkout with platform-specific mechanism. Stub in Win32 version. @@ fsmonitor-settings.c: static int check_for_incompatible(struct repository *r) ## fsmonitor-settings.h ## -@@ fsmonitor-settings.h: const char *fsm_settings__get_reason_msg(struct repository *r); +@@ fsmonitor-settings.h: int fsm_settings__error_if_incompatible(struct repository *r); struct fsmonitor_settings; 6: fa9e86e7de ! 6: 5546339d96 fsmonitor-settings: VFS for Git virtual repos are incompatible @@ compat/fsmonitor/fsm-settings-win32.c } ## fsmonitor-settings.c ## -@@ fsmonitor-settings.c: const char *fsm_settings__get_reason_msg(struct repository *r) - - case FSMONITOR_REASON_BARE: - return _("bare repos are incompatible with fsmonitor"); +@@ fsmonitor-settings.c: int fsm_settings__error_if_incompatible(struct repository *r) + error(_("bare repository '%s' is incompatible with fsmonitor"), + xgetcwd()); + return 1; + + case FSMONITOR_REASON_VFS4GIT: -+ return _("virtual repos are incompatible with fsmonitor"); ++ error(_("virtual repository '%s' is incompatible with fsmonitor"), ++ r->worktree); ++ return 1; } - BUG("Unhandled case in fsm_settings__get_reason_msg '%d'", + BUG("Unhandled case in fsm_settings__error_if_incompatible: '%d'", ## fsmonitor-settings.h ## @@ fsmonitor-settings.h: enum fsmonitor_mode { @@ fsmonitor-settings.h: enum fsmonitor_mode { ## t/t7519-status-fsmonitor.sh ## @@ t/t7519-status-fsmonitor.sh: test_expect_success FSMONITOR_DAEMON 'run fsmonitor-daemon in bare repo' ' - grep "bare repos are incompatible with fsmonitor" actual + grep "bare repository .* is incompatible with fsmonitor" actual ' +test_expect_success MINGW,FSMONITOR_DAEMON 'run fsmonitor-daemon in virtual repo' ' @@ t/t7519-status-fsmonitor.sh: test_expect_success FSMONITOR_DAEMON 'run fsmonitor + test_must_fail git -C ./fake-virtual-clone \ + -c core.virtualfilesystem=true \ + fsmonitor--daemon run 2>actual && -+ grep "virtual repos are incompatible with fsmonitor" actual ++ grep "virtual repository .* is incompatible with fsmonitor" actual +' + test_expect_success 'setup' ' 7: c180241041 = 7: 1d2877efda fsmonitor-settings: stub in macOS-specific incompatibility checking 8: e3bfa0bd69 ! 8: 06d7f18676 fsmonitor-settings: remote repos on macOS are incompatible @@ compat/fsmonitor/fsm-settings-darwin.c } ## fsmonitor-settings.c ## -@@ fsmonitor-settings.c: const char *fsm_settings__get_reason_msg(struct repository *r) - case FSMONITOR_REASON_BARE: - return _("bare repos are incompatible with fsmonitor"); +@@ fsmonitor-settings.c: int fsm_settings__error_if_incompatible(struct repository *r) + xgetcwd()); + return 1; + case FSMONITOR_REASON_ERROR: -+ return _("repo incompatible with fsmonitor due to errors"); ++ error(_("repository '%s' is incompatible with fsmonitor due to errors"), ++ r->worktree); ++ return 1; + + case FSMONITOR_REASON_REMOTE: -+ return _("remote repos are incompatible with fsmonitor"); ++ error(_("remote repository '%s' is incompatible with fsmonitor"), ++ r->worktree); ++ return 1; + case FSMONITOR_REASON_VFS4GIT: - return _("virtual repos are incompatible with fsmonitor"); - } + error(_("virtual repository '%s' is incompatible with fsmonitor"), + r->worktree); ## fsmonitor-settings.h ## @@ fsmonitor-settings.h: enum fsmonitor_mode { 9: e32da3118f = 9: 5ca97f482d fsmonitor-settings: remote repos on Windows are incompatible 27: e3e01677d9 ! 10: 6715143724 fsmonitor-settings: NTFS and FAT32 on MacOS are incompatible @@ compat/fsmonitor/fsm-settings-darwin.c: enum fsmonitor_reason fsm_os__incompatib ## fsmonitor-settings.c ## -@@ fsmonitor-settings.c: const char *fsm_settings__get_reason_msg(struct repository *r) - - case FSMONITOR_REASON_VFS4GIT: - return _("virtual repos are incompatible with fsmonitor"); +@@ fsmonitor-settings.c: int fsm_settings__error_if_incompatible(struct repository *r) + error(_("virtual repository '%s' is incompatible with fsmonitor"), + r->worktree); + return 1; + + case FSMONITOR_REASON_NOSOCKETS: -+ return _("repo filesystem does not support Unix sockets"); ++ error(_("repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"), ++ r->worktree); ++ return 1; } - BUG("Unhandled case in fsm_settings__get_reason_msg '%d'", + BUG("Unhandled case in fsm_settings__error_if_incompatible: '%d'", ## fsmonitor-settings.h ## @@ fsmonitor-settings.h: enum fsmonitor_reason { 10: f63de4eda3 = 11: ed1f723130 unpack-trees: initialize fsmonitor_has_run_once in o->result 11: fe305f5f28 = 12: 35c77b854b fsm-listen-darwin: ignore FSEvents caused by xattr changes on macOS 12: c8f3e251b1 ! 13: a5affb359c fsmonitor--daemon: cd out of worktree root @@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void) + */ + home = getenv("HOME"); + if (home && *home && chdir(home)) -+ die_errno("could not cd home '%s'", home); ++ die_errno(_("could not cd home '%s'"), home); + err = fsmonitor_run_daemon_1(&state); @@ compat/fsmonitor/fsm-listen-win32.c: static int recv_rdcw_watch(struct one_watch + * Shutdown if we get any error. */ - error("GetOverlappedResult failed on '%s' [GLE %ld]", + error(_("GetOverlappedResult failed on '%s' [GLE %ld]"), ## fsmonitor--daemon.h ## @@ fsmonitor--daemon.h: struct fsmonitor_daemon_state { 13: 71673be2da = 14: 087af5dfb6 fsmonitor--daemon: prepare for adding health thread 14: 5387baaf5d ! 15: e78eb20c1b fsmonitor--daemon: rename listener thread related variables @@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void) /* Prepare to (recursively) watch the <worktree-root> directory. */ ## compat/fsmonitor/fsm-listen-darwin.c ## -@@ compat/fsmonitor/fsm-listen-darwin.c: void FSEventStreamRelease(FSEventStreamRef stream); +@@ #include "fsm-listen.h" #include "fsmonitor--daemon.h" @@ compat/fsmonitor/fsm-listen-darwin.c: int fsm_listen__ctor(struct fsmonitor_daem NULL, state->path_worktree_watch.buf, kCFStringEncodingUTF8); @@ compat/fsmonitor/fsm-listen-darwin.c: int fsm_listen__ctor(struct fsmonitor_daemon_state *state) failed: - error("Unable to create FSEventStream."); + error(_("Unable to create FSEventStream.")); - FREE_AND_NULL(state->backend_data); + FREE_AND_NULL(state->listen_data); 15: f78e4ad87c = 16: 301fff5296 fsmonitor--daemon: stub in health thread 16: bb72f911a0 = 17: c6b5bdd25e fsm-health-win32: add polling framework to monitor daemon health 17: baf8c031a9 ! 18: 13d11713a8 fsm-health-win32: force shutdown daemon if worktree root moves @@ compat/fsmonitor/fsm-health-win32.c: struct fsm_health_data + return 0; + + default: -+ die("unhandled case in 'has_worktree_moved': %d", ++ die(_("unhandled case in 'has_worktree_moved': %d"), + (int)ctx); + } + 18: 796b659139 = 19: 01c1a38c46 fsm-listen-darwin: shutdown daemon if worktree root is moved/renamed 19: 2459192087 = 20: 0f0a5b5ca1 fsmonitor: optimize processing of directory events 20: 06a3241385 ! 21: d8218d197a t7527: FSMonitor tests for directory moves @@ t/t7527-builtin-fsmonitor.sh: test_expect_success 'setup' ' git -c core.fsmonitor=false add . && test_tick && git -c core.fsmonitor=false commit -m initial && -@@ t/t7527-builtin-fsmonitor.sh: verify_status () { - echo HELLO AFTER +@@ t/t7527-builtin-fsmonitor.sh: directory_to_file () { + echo 1 >dir1 } +move_directory_contents_deeper() { -+ mkdir T1/_new_ ++ mkdir T1/_new_ && + mv T1/[A-Z]* T1/_new_ +} + 21: 4b59013cad = 22: 79da369dcc t/perf/p7527: add perf test for builtin FSMonitor 22: 524d449ed6 ! 23: 4ab4306ada fsmonitor: never set CE_FSMONITOR_VALID on submodules @@ t/t7527-builtin-fsmonitor.sh: do + git -C super submodule add ../sub ./dir_1/dir_2/sub && + git -C super commit -m "add sub" && + -+ start_daemon super && ++ start_daemon -C super && + git -C super config core.fsmonitor true && + git -C super update-index --fsmonitor && + git -C super status && 23: c7264decaf ! 24: 5d0fa19929 t7527: test FSMonitor on case insensitive+preserving file system @@ t/t7527-builtin-fsmonitor.sh: test_expect_success "Submodule" ' +# test_when_finished "stop_daemon_delete_repo test_insensitive" && + + git init test_insensitive && -+ ( -+ GIT_TRACE_FSMONITOR="$(pwd)/insensitive.trace" && -+ export GIT_TRACE_FSMONITOR && + -+ start_daemon test_insensitive -+ ) && ++ start_daemon -C test_insensitive -tf "$PWD/insensitive.trace" && + + mkdir -p test_insensitive/abc/def && + echo xyz >test_insensitive/ABC/DEF/xyz && 24: 95b9d4210d = 25: 264397e8bd fsmonitor: on macOS also emit NFC spelling for NFD pathname 25: 5a0c1b7a28 ! 26: e6b621fb76 t/lib-unicode-nfc-nfd: helper prereqs for testing unicode nfc/nfd @@ t/lib-unicode-nfc-nfd.sh (new) + ls | od -t x1 | grep "64 *5f *cf *89 *cc *94 *cd *82" +' + -+if test $unicode_debug = 1 ++# The following is for debugging. I found it useful when ++# trying to understand the various (OS, FS) quirks WRT ++# Unicode and how composition/decomposition is handled. ++# For example, when trying to understand how (macOS, APFS) ++# and (macOS, HFS) and (macOS, FAT32) compare. ++# ++# It is rather noisy, so it is disabled by default. ++# ++if test "$unicode_debug" = "true" +then + if test_have_prereq UNICODE_COMPOSITION_SENSITIVE + then 26: a45c1fd300 ! 27: aa96a849ce t7527: test Unicode NFC/NFD handling on MacOS @@ t/t7527-builtin-fsmonitor.sh: test_expect_success CASE_INSENSITIVE_FS 'case inse egrep "^event: abc/def/xyz$" ./insensitive.trace ' -+unicode_debug=0 ++# The variable "unicode_debug" is defined in the following library ++# script to dump information about how the (OS, FS) handles Unicode ++# composition. Uncomment the following line if you want to enable it. ++# ++# unicode_debug=true ++ +. "$TEST_DIRECTORY/lib-unicode-nfc-nfd.sh" + +# See if the OS or filesystem does NFC/NFD aliasing/munging. @@ t/t7527-builtin-fsmonitor.sh: test_expect_success CASE_INSENSITIVE_FS 'case inse + test_when_finished "stop_daemon_delete_repo test_unicode" && + + git init test_unicode && -+ ( -+ GIT_TRACE_FSMONITOR="$(pwd)/unicode.trace" && -+ export GIT_TRACE_FSMONITOR && + -+ start_daemon test_unicode -+ ) && ++ start_daemon -C test_unicode -tf "$PWD/unicode.trace" && + + # Create a directory using an NFC spelling. + # Jeff Hostetler (27): fsm-listen-win32: handle shortnames t7527: test FSMonitor on repos with Unicode root paths t/helper/fsmonitor-client: create stress test fsmonitor-settings: bare repos are incompatible with FSMonitor fsmonitor-settings: stub in Win32-specific incompatibility checking fsmonitor-settings: VFS for Git virtual repos are incompatible fsmonitor-settings: stub in macOS-specific incompatibility checking fsmonitor-settings: remote repos on macOS are incompatible fsmonitor-settings: remote repos on Windows are incompatible fsmonitor-settings: NTFS and FAT32 on MacOS are incompatible unpack-trees: initialize fsmonitor_has_run_once in o->result fsm-listen-darwin: ignore FSEvents caused by xattr changes on macOS fsmonitor--daemon: cd out of worktree root fsmonitor--daemon: prepare for adding health thread fsmonitor--daemon: rename listener thread related variables fsmonitor--daemon: stub in health thread fsm-health-win32: add polling framework to monitor daemon health fsm-health-win32: force shutdown daemon if worktree root moves fsm-listen-darwin: shutdown daemon if worktree root is moved/renamed fsmonitor: optimize processing of directory events t7527: FSMonitor tests for directory moves t/perf/p7527: add perf test for builtin FSMonitor fsmonitor: never set CE_FSMONITOR_VALID on submodules t7527: test FSMonitor on case insensitive+preserving file system fsmonitor: on macOS also emit NFC spelling for NFD pathname t/lib-unicode-nfc-nfd: helper prereqs for testing unicode nfc/nfd t7527: test Unicode NFC/NFD handling on MacOS Makefile | 19 +- builtin/fsmonitor--daemon.c | 104 ++++++- builtin/update-index.c | 4 + compat/fsmonitor/fsm-health-darwin.c | 24 ++ compat/fsmonitor/fsm-health-win32.c | 278 +++++++++++++++++ compat/fsmonitor/fsm-health.h | 47 +++ compat/fsmonitor/fsm-listen-darwin.c | 122 ++++++-- compat/fsmonitor/fsm-listen-win32.c | 413 ++++++++++++++++++++----- compat/fsmonitor/fsm-listen.h | 2 +- compat/fsmonitor/fsm-settings-darwin.c | 89 ++++++ compat/fsmonitor/fsm-settings-win32.c | 137 ++++++++ config.mak.uname | 5 + contrib/buildsystems/CMakeLists.txt | 8 + fsmonitor--daemon.h | 11 +- fsmonitor-settings.c | 92 ++++++ fsmonitor-settings.h | 29 ++ fsmonitor.c | 73 ++++- fsmonitor.h | 11 + t/helper/test-fsmonitor-client.c | 106 +++++++ t/lib-unicode-nfc-nfd.sh | 167 ++++++++++ t/perf/p7527-builtin-fsmonitor.sh | 257 +++++++++++++++ t/t7519-status-fsmonitor.sh | 32 ++ t/t7527-builtin-fsmonitor.sh | 311 +++++++++++++++++++ unpack-trees.c | 1 + 24 files changed, 2218 insertions(+), 124 deletions(-) create mode 100644 compat/fsmonitor/fsm-health-darwin.c create mode 100644 compat/fsmonitor/fsm-health-win32.c create mode 100644 compat/fsmonitor/fsm-health.h create mode 100644 compat/fsmonitor/fsm-settings-darwin.c create mode 100644 compat/fsmonitor/fsm-settings-win32.c create mode 100755 t/lib-unicode-nfc-nfd.sh create mode 100755 t/perf/p7527-builtin-fsmonitor.sh base-commit: e6cf84dc8eb4933220187849b84e5cb96cda185f Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1143%2Fjeffhostetler%2Fbuiltin-fsmonitor-part3-v3 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1143/jeffhostetler/builtin-fsmonitor-part3-v3 Pull-Request: https://github.com/gitgitgadget/git/pull/1143 Range-diff vs v2: 1: 34619e0652b ! 1: 779a15b38e8 fsm-listen-win32: handle shortnames @@ compat/fsmonitor/fsm-listen-win32.c: static int normalize_path_in_utf8(FILE_NOTI @@ compat/fsmonitor/fsm-listen-win32.c: static int normalize_path_in_utf8(FILE_NOTIFY_INFORMATION *info, goto normalize; if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - error("[GLE %ld] could not convert path to UTF-8: '%.*ls'", + error(_("[GLE %ld] could not convert path to UTF-8: '%.*ls'"), - GetLastError(), - (int)(info->FileNameLength / sizeof(WCHAR)), - info->FileName); @@ t/t7527-builtin-fsmonitor.sh: test_expect_success 'implicit daemon stop (rename + + git init test_implicit_1s && + -+ start_daemon test_implicit_1s && ++ start_daemon -C test_implicit_1s && + + # renaming the .git directory will implicitly stop the daemon. + # this moves {.git, GIT~1} to {.gitxyz, GITXYZ~1}. @@ t/t7527-builtin-fsmonitor.sh: test_expect_success 'implicit daemon stop (rename + test_path_is_file test_implicit_1s2/GIT~1 && + test_path_is_dir test_implicit_1s2/GIT~2 && + -+ start_daemon test_implicit_1s2 && ++ start_daemon -C test_implicit_1s2 && + + # renaming the .git directory will implicitly stop the daemon. + # the rename-from FS Event will contain the shortname. 2: 3a0f30b849a ! 2: 11d4a17b692 t7527: test FSMonitor on repos with Unicode root paths @@ t/t7527-builtin-fsmonitor.sh: do + git -C "$u" add file1 && + git -C "$u" config core.fsmonitor true && + -+ start_daemon "$u" && ++ start_daemon -C "$u" && + git -C "$u" status >actual && + grep "new file: file1" actual + ' 3: 87d1c0b6f2a ! 3: 901fa32f6ea t/helper/fsmonitor-client: create stress test @@ t/helper/test-fsmonitor-client.c: static int do_send_flush(void) + int nr_requests = 1; const char * const fsmonitor_client_usage[] = { - N_("test-helper fsmonitor-client query [<token>]"), - N_("test-helper fsmonitor-client flush"), -+ N_("test-helper fsmonitor-client hammer [<token>] [<threads>] [<requests>]"), + "test-tool fsmonitor-client query [<token>]", + "test-tool fsmonitor-client flush", ++ "test-tool fsmonitor-client hammer [<token>] [<threads>] [<requests>]", NULL, }; struct option options[] = { - OPT_STRING(0, "token", &token, N_("token"), - N_("command token to send to the server")), + OPT_STRING(0, "token", &token, "token", + "command token to send to the server"), + -+ OPT_INTEGER(0, "threads", &nr_threads, N_("number of client threads")), -+ OPT_INTEGER(0, "requests", &nr_requests, N_("number of requests per thread")), ++ OPT_INTEGER(0, "threads", &nr_threads, "number of client threads"), ++ OPT_INTEGER(0, "requests", &nr_requests, "number of requests per thread"), + OPT_END() }; 4: 8c4f90ae4fd ! 4: a8f0b2a5256 fsmonitor-settings: bare repos are incompatible with FSMonitor @@ builtin/fsmonitor--daemon.c: int cmd_fsmonitor__daemon(int argc, const char **ar + prepare_repo_settings(the_repository); + fsm_settings__set_ipc(the_repository); + -+ if (fsm_settings__get_mode(the_repository) == FSMONITOR_MODE_INCOMPATIBLE) { -+ const char *msg = fsm_settings__get_reason_msg(the_repository); -+ -+ return error("%s '%s'", msg ? msg : "???", xgetcwd()); -+ } ++ if (fsm_settings__error_if_incompatible(the_repository)) ++ return 1; + if (!strcmp(subcmd, "start")) return !!try_to_start_background_daemon(); @@ builtin/update-index.c: int cmd_update_index(int argc, const char **argv, const if (fsmonitor > 0) { enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r); + -+ if (fsm_mode == FSMONITOR_MODE_INCOMPATIBLE) { -+ const char *msg = fsm_settings__get_reason_msg(r); -+ -+ return error("%s '%s'", msg ? msg : "???", xgetcwd()); -+ } ++ if (fsm_settings__error_if_incompatible(the_repository)) ++ return 1; + if (fsm_mode == FSMONITOR_MODE_DISABLED) { - advise(_("core.fsmonitor is unset; " - "set it if you really want to " + warning(_("core.fsmonitor is unset; " + "set it if you really want to " ## fsmonitor-settings.c ## @@ @@ fsmonitor-settings.c static void lookup_fsmonitor_settings(struct repository *r) { struct fsmonitor_settings *s; +@@ fsmonitor-settings.c: static void lookup_fsmonitor_settings(struct repository *r) + + CALLOC_ARRAY(s, 1); + s->mode = FSMONITOR_MODE_DISABLED; ++ s->reason = FSMONITOR_REASON_OK; + + r->settings.fsmonitor = s; + @@ fsmonitor-settings.c: void fsm_settings__set_ipc(struct repository *r) lookup_fsmonitor_settings(r); @@ fsmonitor-settings.c: void fsm_settings__set_disabled(struct repository *r) + return r->settings.fsmonitor->reason; +} + -+const char *fsm_settings__get_reason_msg(struct repository *r) ++int fsm_settings__error_if_incompatible(struct repository *r) +{ + enum fsmonitor_reason reason = fsm_settings__get_reason(r); + + switch (reason) { + case FSMONITOR_REASON_OK: -+ return NULL; ++ return 0; + + case FSMONITOR_REASON_BARE: -+ return _("bare repos are incompatible with fsmonitor"); ++ error(_("bare repository '%s' is incompatible with fsmonitor"), ++ xgetcwd()); ++ return 1; + } + -+ BUG("Unhandled case in fsm_settings__get_reason_msg '%d'", ++ BUG("Unhandled case in fsm_settings__error_if_incompatible: '%d'", + reason); +} @@ fsmonitor-settings.h: void fsm_settings__set_disabled(struct repository *r); const char *fsm_settings__get_hook_path(struct repository *r); +enum fsmonitor_reason fsm_settings__get_reason(struct repository *r); -+const char *fsm_settings__get_reason_msg(struct repository *r); ++int fsm_settings__error_if_incompatible(struct repository *r); + struct fsmonitor_settings; @@ t/t7519-status-fsmonitor.sh: test_lazy_prereq UNTRACKED_CACHE ' + test_must_fail \ + git -C ./bare-clone -c core.fsmonitor=foo \ + update-index --fsmonitor 2>actual && -+ grep "bare repos are incompatible with fsmonitor" actual && ++ grep "bare repository .* is incompatible with fsmonitor" actual && + + test_must_fail \ + git -C ./bare-clone -c core.fsmonitor=true \ + update-index --fsmonitor 2>actual && -+ grep "bare repos are incompatible with fsmonitor" actual ++ grep "bare repository .* is incompatible with fsmonitor" 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 ++ grep "bare repository .* is incompatible with fsmonitor" actual +' + test_expect_success 'setup' ' 5: 6329328d185 ! 5: e32a8a7ea7a fsmonitor-settings: stub in platform-specific incompatibility checking @@ Metadata Author: Jeff Hostetler <jeffhost@xxxxxxxxxxxxx> ## Commit message ## - fsmonitor-settings: stub in platform-specific incompatibility checking + fsmonitor-settings: stub in Win32-specific incompatibility checking Extend generic incompatibility checkout with platform-specific mechanism. Stub in Win32 version. @@ fsmonitor-settings.c: static int check_for_incompatible(struct repository *r) ## fsmonitor-settings.h ## -@@ fsmonitor-settings.h: const char *fsm_settings__get_reason_msg(struct repository *r); +@@ fsmonitor-settings.h: int fsm_settings__error_if_incompatible(struct repository *r); struct fsmonitor_settings; 6: fa9e86e7de7 ! 6: 5546339d963 fsmonitor-settings: VFS for Git virtual repos are incompatible @@ compat/fsmonitor/fsm-settings-win32.c } ## fsmonitor-settings.c ## -@@ fsmonitor-settings.c: const char *fsm_settings__get_reason_msg(struct repository *r) - - case FSMONITOR_REASON_BARE: - return _("bare repos are incompatible with fsmonitor"); +@@ fsmonitor-settings.c: int fsm_settings__error_if_incompatible(struct repository *r) + error(_("bare repository '%s' is incompatible with fsmonitor"), + xgetcwd()); + return 1; + + case FSMONITOR_REASON_VFS4GIT: -+ return _("virtual repos are incompatible with fsmonitor"); ++ error(_("virtual repository '%s' is incompatible with fsmonitor"), ++ r->worktree); ++ return 1; } - BUG("Unhandled case in fsm_settings__get_reason_msg '%d'", + BUG("Unhandled case in fsm_settings__error_if_incompatible: '%d'", ## fsmonitor-settings.h ## @@ fsmonitor-settings.h: enum fsmonitor_mode { @@ fsmonitor-settings.h: enum fsmonitor_mode { ## t/t7519-status-fsmonitor.sh ## @@ t/t7519-status-fsmonitor.sh: test_expect_success FSMONITOR_DAEMON 'run fsmonitor-daemon in bare repo' ' - grep "bare repos are incompatible with fsmonitor" actual + grep "bare repository .* is incompatible with fsmonitor" actual ' +test_expect_success MINGW,FSMONITOR_DAEMON 'run fsmonitor-daemon in virtual repo' ' @@ t/t7519-status-fsmonitor.sh: test_expect_success FSMONITOR_DAEMON 'run fsmonitor + test_must_fail git -C ./fake-virtual-clone \ + -c core.virtualfilesystem=true \ + fsmonitor--daemon run 2>actual && -+ grep "virtual repos are incompatible with fsmonitor" actual ++ grep "virtual repository .* is incompatible with fsmonitor" actual +' + test_expect_success 'setup' ' 7: c1802410410 = 7: 1d2877efda0 fsmonitor-settings: stub in macOS-specific incompatibility checking 8: e3bfa0bd69d ! 8: 06d7f18676d fsmonitor-settings: remote repos on macOS are incompatible @@ compat/fsmonitor/fsm-settings-darwin.c } ## fsmonitor-settings.c ## -@@ fsmonitor-settings.c: const char *fsm_settings__get_reason_msg(struct repository *r) - case FSMONITOR_REASON_BARE: - return _("bare repos are incompatible with fsmonitor"); +@@ fsmonitor-settings.c: int fsm_settings__error_if_incompatible(struct repository *r) + xgetcwd()); + return 1; + case FSMONITOR_REASON_ERROR: -+ return _("repo incompatible with fsmonitor due to errors"); ++ error(_("repository '%s' is incompatible with fsmonitor due to errors"), ++ r->worktree); ++ return 1; + + case FSMONITOR_REASON_REMOTE: -+ return _("remote repos are incompatible with fsmonitor"); ++ error(_("remote repository '%s' is incompatible with fsmonitor"), ++ r->worktree); ++ return 1; + case FSMONITOR_REASON_VFS4GIT: - return _("virtual repos are incompatible with fsmonitor"); - } + error(_("virtual repository '%s' is incompatible with fsmonitor"), + r->worktree); ## fsmonitor-settings.h ## @@ fsmonitor-settings.h: enum fsmonitor_mode { 9: e32da3118fb = 9: 5ca97f482d0 fsmonitor-settings: remote repos on Windows are incompatible 27: e3e01677d93 ! 10: 67151437245 fsmonitor-settings: NTFS and FAT32 on MacOS are incompatible @@ compat/fsmonitor/fsm-settings-darwin.c: enum fsmonitor_reason fsm_os__incompatib ## fsmonitor-settings.c ## -@@ fsmonitor-settings.c: const char *fsm_settings__get_reason_msg(struct repository *r) - - case FSMONITOR_REASON_VFS4GIT: - return _("virtual repos are incompatible with fsmonitor"); +@@ fsmonitor-settings.c: int fsm_settings__error_if_incompatible(struct repository *r) + error(_("virtual repository '%s' is incompatible with fsmonitor"), + r->worktree); + return 1; + + case FSMONITOR_REASON_NOSOCKETS: -+ return _("repo filesystem does not support Unix sockets"); ++ error(_("repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"), ++ r->worktree); ++ return 1; } - BUG("Unhandled case in fsm_settings__get_reason_msg '%d'", + BUG("Unhandled case in fsm_settings__error_if_incompatible: '%d'", ## fsmonitor-settings.h ## @@ fsmonitor-settings.h: enum fsmonitor_reason { 10: f63de4eda31 = 11: ed1f7231309 unpack-trees: initialize fsmonitor_has_run_once in o->result 11: fe305f5f287 = 12: 35c77b854bd fsm-listen-darwin: ignore FSEvents caused by xattr changes on macOS 12: c8f3e251b1f ! 13: a5affb359c4 fsmonitor--daemon: cd out of worktree root @@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void) + */ + home = getenv("HOME"); + if (home && *home && chdir(home)) -+ die_errno("could not cd home '%s'", home); ++ die_errno(_("could not cd home '%s'"), home); + err = fsmonitor_run_daemon_1(&state); @@ compat/fsmonitor/fsm-listen-win32.c: static int recv_rdcw_watch(struct one_watch + * Shutdown if we get any error. */ - error("GetOverlappedResult failed on '%s' [GLE %ld]", + error(_("GetOverlappedResult failed on '%s' [GLE %ld]"), ## fsmonitor--daemon.h ## @@ fsmonitor--daemon.h: struct fsmonitor_daemon_state { 13: 71673be2da5 = 14: 087af5dfb63 fsmonitor--daemon: prepare for adding health thread 14: 5387baaf5d7 ! 15: e78eb20c1bf fsmonitor--daemon: rename listener thread related variables @@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void) /* Prepare to (recursively) watch the <worktree-root> directory. */ ## compat/fsmonitor/fsm-listen-darwin.c ## -@@ compat/fsmonitor/fsm-listen-darwin.c: void FSEventStreamRelease(FSEventStreamRef stream); +@@ #include "fsm-listen.h" #include "fsmonitor--daemon.h" @@ compat/fsmonitor/fsm-listen-darwin.c: int fsm_listen__ctor(struct fsmonitor_daem NULL, state->path_worktree_watch.buf, kCFStringEncodingUTF8); @@ compat/fsmonitor/fsm-listen-darwin.c: int fsm_listen__ctor(struct fsmonitor_daemon_state *state) failed: - error("Unable to create FSEventStream."); + error(_("Unable to create FSEventStream.")); - FREE_AND_NULL(state->backend_data); + FREE_AND_NULL(state->listen_data); 15: f78e4ad87c0 = 16: 301fff5296a fsmonitor--daemon: stub in health thread 16: bb72f911a05 = 17: c6b5bdd25e4 fsm-health-win32: add polling framework to monitor daemon health 17: baf8c031a97 ! 18: 13d11713a86 fsm-health-win32: force shutdown daemon if worktree root moves @@ compat/fsmonitor/fsm-health-win32.c: struct fsm_health_data + return 0; + + default: -+ die("unhandled case in 'has_worktree_moved': %d", ++ die(_("unhandled case in 'has_worktree_moved': %d"), + (int)ctx); + } + 18: 796b6591393 = 19: 01c1a38c462 fsm-listen-darwin: shutdown daemon if worktree root is moved/renamed 19: 24591920878 = 20: 0f0a5b5ca16 fsmonitor: optimize processing of directory events 20: 06a32413854 ! 21: d8218d197ad t7527: FSMonitor tests for directory moves @@ t/t7527-builtin-fsmonitor.sh: test_expect_success 'setup' ' git -c core.fsmonitor=false add . && test_tick && git -c core.fsmonitor=false commit -m initial && -@@ t/t7527-builtin-fsmonitor.sh: verify_status () { - echo HELLO AFTER +@@ t/t7527-builtin-fsmonitor.sh: directory_to_file () { + echo 1 >dir1 } +move_directory_contents_deeper() { -+ mkdir T1/_new_ ++ mkdir T1/_new_ && + mv T1/[A-Z]* T1/_new_ +} + 21: 4b59013cadd = 22: 79da369dcce t/perf/p7527: add perf test for builtin FSMonitor 22: 524d449ed64 ! 23: 4ab4306adab fsmonitor: never set CE_FSMONITOR_VALID on submodules @@ t/t7527-builtin-fsmonitor.sh: do + git -C super submodule add ../sub ./dir_1/dir_2/sub && + git -C super commit -m "add sub" && + -+ start_daemon super && ++ start_daemon -C super && + git -C super config core.fsmonitor true && + git -C super update-index --fsmonitor && + git -C super status && 23: c7264decaf6 ! 24: 5d0fa19929d t7527: test FSMonitor on case insensitive+preserving file system @@ t/t7527-builtin-fsmonitor.sh: test_expect_success "Submodule" ' +# test_when_finished "stop_daemon_delete_repo test_insensitive" && + + git init test_insensitive && -+ ( -+ GIT_TRACE_FSMONITOR="$(pwd)/insensitive.trace" && -+ export GIT_TRACE_FSMONITOR && + -+ start_daemon test_insensitive -+ ) && ++ start_daemon -C test_insensitive -tf "$PWD/insensitive.trace" && + + mkdir -p test_insensitive/abc/def && + echo xyz >test_insensitive/ABC/DEF/xyz && 24: 95b9d4210d2 = 25: 264397e8bd4 fsmonitor: on macOS also emit NFC spelling for NFD pathname 25: 5a0c1b7a287 ! 26: e6b621fb766 t/lib-unicode-nfc-nfd: helper prereqs for testing unicode nfc/nfd @@ t/lib-unicode-nfc-nfd.sh (new) + ls | od -t x1 | grep "64 *5f *cf *89 *cc *94 *cd *82" +' + -+if test $unicode_debug = 1 ++# The following is for debugging. I found it useful when ++# trying to understand the various (OS, FS) quirks WRT ++# Unicode and how composition/decomposition is handled. ++# For example, when trying to understand how (macOS, APFS) ++# and (macOS, HFS) and (macOS, FAT32) compare. ++# ++# It is rather noisy, so it is disabled by default. ++# ++if test "$unicode_debug" = "true" +then + if test_have_prereq UNICODE_COMPOSITION_SENSITIVE + then 26: a45c1fd3000 ! 27: aa96a849ce4 t7527: test Unicode NFC/NFD handling on MacOS @@ t/t7527-builtin-fsmonitor.sh: test_expect_success CASE_INSENSITIVE_FS 'case inse egrep "^event: abc/def/xyz$" ./insensitive.trace ' -+unicode_debug=0 ++# The variable "unicode_debug" is defined in the following library ++# script to dump information about how the (OS, FS) handles Unicode ++# composition. Uncomment the following line if you want to enable it. ++# ++# unicode_debug=true ++ +. "$TEST_DIRECTORY/lib-unicode-nfc-nfd.sh" + +# See if the OS or filesystem does NFC/NFD aliasing/munging. @@ t/t7527-builtin-fsmonitor.sh: test_expect_success CASE_INSENSITIVE_FS 'case inse + test_when_finished "stop_daemon_delete_repo test_unicode" && + + git init test_unicode && -+ ( -+ GIT_TRACE_FSMONITOR="$(pwd)/unicode.trace" && -+ export GIT_TRACE_FSMONITOR && + -+ start_daemon test_unicode -+ ) && ++ start_daemon -C test_unicode -tf "$PWD/unicode.trace" && + + # Create a directory using an NFC spelling. + # -- gitgitgadget