From 02ce466798c30447e30c45fbd6620e3a4aaf4538 Mon Sep 17 00:00:00 2001 From: Andrew Barnes Date: Fri, 19 Jun 2026 09:24:00 -0400 Subject: [PATCH] fix(discover): honor subfolder root .gitignore Signed-off-by: Andrew Barnes --- src/discover/discover.c | 11 ++++------- tests/test_discover.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/discover/discover.c b/src/discover/discover.c index 314c00c53..cf1343145 100644 --- a/src/discover/discover.c +++ b/src/discover/discover.c @@ -541,15 +541,12 @@ int cbm_discover_ex(const char *repo_path, const cbm_discover_opts_t *opts, cbm_ return CBM_NOT_FOUND; } - /* Load gitignore if .git directory exists */ + /* Respect a .gitignore at the indexed path even when the caller points us + * at a subdirectory instead of the repository root. */ cbm_gitignore_t *gitignore = NULL; char gi_path[CBM_SZ_4K]; - snprintf(gi_path, sizeof(gi_path), "%s/.git", repo_path); - struct stat gi_stat; - if (wide_stat(gi_path, &gi_stat) == 0 && S_ISDIR(gi_stat.st_mode)) { - snprintf(gi_path, sizeof(gi_path), "%s/.gitignore", repo_path); - gitignore = cbm_gitignore_load(gi_path); - } + snprintf(gi_path, sizeof(gi_path), "%s/.gitignore", repo_path); + gitignore = cbm_gitignore_load(gi_path); /* Load cbmignore if specified or exists at repo root */ cbm_gitignore_t *cbmignore = NULL; diff --git a/tests/test_discover.c b/tests/test_discover.c index af51a9286..b923a3fbd 100644 --- a/tests/test_discover.c +++ b/tests/test_discover.c @@ -703,6 +703,41 @@ TEST(discover_nested_gitignore_stacks_with_root) { PASS(); } +TEST(discover_root_gitignore_respected_without_git_dir) { + char *repo = th_mktempdir("cbm_disc_subpkg_repo"); + ASSERT(repo != NULL); + + th_mkdir_p(TH_PATH(repo, ".git")); + th_write_file(TH_PATH(repo, "pkg/.gitignore"), "secret.py\n"); + th_write_file(TH_PATH(repo, "pkg/secret.py"), "def secret(): return 1\n"); + th_write_file(TH_PATH(repo, "pkg/keep.py"), "def keep(): return 1\n"); + + char pkg[512]; + snprintf(pkg, sizeof(pkg), "%s/pkg", repo); + + cbm_discover_opts_t opts = {0}; + cbm_file_info_t *files = NULL; + int count = 0; + int rc = cbm_discover(pkg, &opts, &files, &count); + ASSERT_EQ(rc, 0); + + bool found_secret = false; + bool found_keep = false; + for (int i = 0; i < count; i++) { + if (strstr(files[i].rel_path, "secret.py")) + found_secret = true; + if (strstr(files[i].rel_path, "keep.py")) + found_keep = true; + } + ASSERT_FALSE(found_secret); + ASSERT_TRUE(found_keep); + ASSERT_EQ(count, 1); + + cbm_discover_free(files, count); + th_cleanup(repo); + PASS(); +} + /* ── Suite ─────────────────────────────────────────────────────── */ SUITE(discover) { @@ -797,4 +832,5 @@ SUITE(discover) { /* Nested .gitignore tests (issue #178) */ RUN_TEST(discover_nested_gitignore); RUN_TEST(discover_nested_gitignore_stacks_with_root); + RUN_TEST(discover_root_gitignore_respected_without_git_dir); }