mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
Merge pull request #2639 from H4M4CHi-ttr/fix/windows-utf8-watcher-paths
Thank you for the Windows path fix and the careful pre-fix investigation. This uses the existing UTF-8 filesystem helpers so valid repository roots and cached graph databases are not mistaken for missing paths. Contributor history and DCO are preserved.
This commit is contained in:
3 files changed
+67
-15
No files matched your search
@@ -449,8 +449,8 @@ static bool application_regular_db_exists(const char *project) {
|
||||
if (written <= 0 || (size_t)written >= sizeof(path)) {
|
||||
return false;
|
||||
}
|
||||
struct stat status;
|
||||
return stat(path, &status) == 0 && S_ISREG(status.st_mode);
|
||||
cbm_path_info_t status;
|
||||
return cbm_path_info_utf8(path, &status) == CBM_PATH_INFO_OK && status.is_regular;
|
||||
}
|
||||
|
||||
static bool application_canonical_directory_exists(const char *path) {
|
||||
|
||||
+27
-9
@@ -522,14 +522,28 @@ static watcher_git_status_t git_repo_status(cbm_watcher_t *w, project_state_t *s
|
||||
* a `.git` file (the gitlink form used by linked worktrees and initialized
|
||||
* submodules). Deliberately a filesystem check, not a git invocation — the whole
|
||||
* point is to learn something `rev-parse` cannot tell us, because it walks up. */
|
||||
/* Preserve stat's link-following behavior while inspecting UTF-8 paths. */
|
||||
static int watcher_path_info(const char *path, cbm_path_info_t *info) {
|
||||
int status = cbm_path_info_utf8(path, info);
|
||||
if (status == CBM_PATH_INFO_OK && info->is_symlink) {
|
||||
char canonical[CBM_SZ_4K];
|
||||
if (!cbm_canonical_path(path, canonical, sizeof(canonical))) {
|
||||
return CBM_PATH_INFO_UNAVAILABLE;
|
||||
}
|
||||
status = cbm_path_info_utf8(canonical, info);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool git_has_own_dot_git(const char *root_path) {
|
||||
char path[CBM_SZ_4K];
|
||||
int written = snprintf(path, sizeof(path), "%s/.git", root_path);
|
||||
if (written <= 0 || (size_t)written >= sizeof(path)) {
|
||||
return false;
|
||||
}
|
||||
struct stat st;
|
||||
return stat(path, &st) == 0 && (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode));
|
||||
cbm_path_info_t info;
|
||||
return watcher_path_info(path, &info) == CBM_PATH_INFO_OK &&
|
||||
(info.is_directory || info.is_regular);
|
||||
}
|
||||
|
||||
/* Does the ancestor repository actually track anything inside this directory?
|
||||
@@ -990,13 +1004,17 @@ static root_status_t root_status(const char *root_path, int *out_errno) {
|
||||
if (!root_path) {
|
||||
return ROOT_UNCERTAIN;
|
||||
}
|
||||
struct stat st;
|
||||
if (stat(root_path, &st) == 0) {
|
||||
/* Exists but is no longer a directory → the root directory is gone. */
|
||||
return S_ISDIR(st.st_mode) ? ROOT_PRESENT : ROOT_MISSING;
|
||||
cbm_path_info_t info;
|
||||
int status = watcher_path_info(root_path, &info);
|
||||
if (status == CBM_PATH_INFO_OK) {
|
||||
return info.is_directory ? ROOT_PRESENT : ROOT_MISSING;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
*out_errno = status == CBM_PATH_INFO_ABSENT ? ENOENT : EIO;
|
||||
#else
|
||||
*out_errno = errno;
|
||||
return cbm_watcher_root_missing_errno(errno) ? ROOT_MISSING : ROOT_UNCERTAIN;
|
||||
#endif
|
||||
return status == CBM_PATH_INFO_ABSENT ? ROOT_MISSING : ROOT_UNCERTAIN;
|
||||
}
|
||||
|
||||
/* Sustained-absence window (seconds) before a missing root may be pruned.
|
||||
@@ -1338,8 +1356,8 @@ static void publish_status(project_state_t *s, bool scanned) {
|
||||
|
||||
/* Init baseline for a project: check if git, get HEAD, count files */
|
||||
static bool init_baseline(cbm_watcher_t *w, project_state_t *s) {
|
||||
struct stat st;
|
||||
if (stat(s->root_path, &st) != 0) {
|
||||
cbm_path_info_t info;
|
||||
if (watcher_path_info(s->root_path, &info) != CBM_PATH_INFO_OK || !info.is_directory) {
|
||||
cbm_log_warn("watcher.root_gone", "project", s->project_name, "path", s->root_path);
|
||||
s->baseline_done = true;
|
||||
s->is_git = false;
|
||||
|
||||
+38
-4
@@ -1103,8 +1103,8 @@ TEST(watcher_stop_and_unwatch_cancel_blocked_git_without_backstop) {
|
||||
watcher_windows_image_identity_t expected_git;
|
||||
ASSERT_TRUE(watcher_windows_capture_image_identity(fake_git, &expected_git));
|
||||
|
||||
const char *old_path_raw = getenv("PATH");
|
||||
const char *old_marker_raw = getenv(WATCHER_TEST_BLOCKING_GIT_MARKER_ENV);
|
||||
const char *old_path_raw = cbm_runtime_getenv("PATH");
|
||||
const char *old_marker_raw = cbm_runtime_getenv(WATCHER_TEST_BLOCKING_GIT_MARKER_ENV);
|
||||
char *old_path = old_path_raw ? cbm_strdup(old_path_raw) : NULL;
|
||||
char *old_marker = old_marker_raw ? cbm_strdup(old_marker_raw) : NULL;
|
||||
size_t path_size = strlen(bin) + 2 + (old_path ? strlen(old_path) : 0);
|
||||
@@ -1238,8 +1238,8 @@ TEST(watcher_stop_and_unwatch_cancel_blocked_git_without_backstop) {
|
||||
ASSERT_TRUE(watcher_test_self_image(self));
|
||||
ASSERT_EQ(symlink(self, fake_git), 0);
|
||||
|
||||
const char *old_path_raw = getenv("PATH");
|
||||
const char *old_marker_raw = getenv(WATCHER_TEST_BLOCKING_GIT_MARKER_ENV);
|
||||
const char *old_path_raw = cbm_runtime_getenv("PATH");
|
||||
const char *old_marker_raw = cbm_runtime_getenv(WATCHER_TEST_BLOCKING_GIT_MARKER_ENV);
|
||||
char *old_path = old_path_raw ? strdup(old_path_raw) : NULL;
|
||||
char *old_marker = old_marker_raw ? strdup(old_marker_raw) : NULL;
|
||||
size_t path_size = strlen(bin) + 2 + (old_path ? strlen(old_path) : 0);
|
||||
@@ -3995,7 +3995,41 @@ TEST(watcher_null_watch_count) {
|
||||
* SUITE
|
||||
* ══════════════════════════════════════════════════════════════════ */
|
||||
|
||||
TEST(watcher_unicode_root_is_not_pruned_as_missing) {
|
||||
char *parent = th_mktempdir("cbm_watch_utf8");
|
||||
ASSERT_NOT_NULL(parent);
|
||||
char root[CBM_SZ_4K];
|
||||
snprintf(root, sizeof(root), "%s/\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E", parent);
|
||||
ASSERT_EQ(th_mkdir_p(root), 0);
|
||||
const char *saved = cbm_runtime_getenv("CBM_WATCHER_PRUNE_GRACE_S");
|
||||
char *saved_copy = saved ? strdup(saved) : NULL;
|
||||
cbm_setenv("CBM_WATCHER_PRUNE_GRACE_S", "0", 1);
|
||||
cbm_store_t *store = cbm_store_open_memory();
|
||||
cbm_store_upsert_project(store, "unicode-root", root);
|
||||
cbm_watcher_t *watcher = cbm_watcher_new(store, NULL, NULL);
|
||||
cbm_watcher_watch(watcher, "unicode-root", root);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
cbm_watcher_poll_once(watcher);
|
||||
}
|
||||
cbm_project_t info = {0};
|
||||
bool kept = cbm_watcher_watch_count(watcher) == 1 &&
|
||||
cbm_store_get_project(store, "unicode-root", &info) == CBM_STORE_OK;
|
||||
cbm_project_free_fields(&info);
|
||||
cbm_watcher_free(watcher);
|
||||
cbm_store_close(store);
|
||||
if (saved_copy) {
|
||||
cbm_setenv("CBM_WATCHER_PRUNE_GRACE_S", saved_copy, 1);
|
||||
} else {
|
||||
cbm_unsetenv("CBM_WATCHER_PRUNE_GRACE_S");
|
||||
}
|
||||
free(saved_copy);
|
||||
ASSERT_EQ(th_rmtree(parent), 0);
|
||||
ASSERT_TRUE(kept);
|
||||
PASS();
|
||||
}
|
||||
|
||||
SUITE(watcher) {
|
||||
RUN_TEST(watcher_unicode_root_is_not_pruned_as_missing);
|
||||
/* Adaptive interval */
|
||||
RUN_TEST(poll_interval_base);
|
||||
RUN_TEST(poll_interval_scaling);
|
||||
|
||||
Reference in new issue
Block a user