aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBozhidar Batsov <bozhidar@batsov.dev>2026-02-13 12:33:18 +0200
committerBozhidar Batsov <bozhidar@batsov.dev>2026-02-13 12:33:18 +0200
commit3c287cd1b4a5289e6342361471c11685e3f168ab (patch)
treefd0c461d136ec3e9fadd14b071fe73a2c4f7faa4
parentca324b78471201dca4a7b1dc41ab7b716b1cc1c9 (diff)
Prevent directories from matching file-type project root markers
On case-insensitive filesystems (macOS default), a ~/workspace directory matches the "WORKSPACE" Bazel marker because file-exists-p returns t for both files and directories. This causes the home directory to be falsely detected as a Bazel project root, leading to severe performance issues. Add a (not (file-directory-p ...)) guard in projectile-root-top-down so that only regular files can satisfy file-type marker checks. Fixes #1961
-rw-r--r--CHANGELOG.md4
-rw-r--r--projectile.el5
-rw-r--r--test/projectile-test.el11
3 files changed, 17 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f960f2..9f0ac7a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@
* [#1837](https://github.com/bbatsov/projectile/issues/1837): Add `eat` project terminal commands with keybindings `x x` and `x 4 x`.
+### Bugs fixed
+
+* [#1961](https://github.com/bbatsov/projectile/issues/1961): Prevent directories from matching file-type project root markers (e.g., a `workspace` directory no longer matches the `WORKSPACE` Bazel marker on case-insensitive filesystems).
+
### Changes
* [#1958](https://github.com/bbatsov/projectile/issues/1958): Exclude `.projectile-cache.eld` from search results (ripgrep/ag/grep) by default.
diff --git a/projectile.el b/projectile.el
index eed377a..7f0c581 100644
--- a/projectile.el
+++ b/projectile.el
@@ -1325,7 +1325,10 @@ Return the first (topmost) matched directory or nil if not found."
(projectile-locate-dominating-file
dir
(lambda (dir)
- (cl-find-if (lambda (f) (projectile-file-exists-p (projectile-expand-file-name-wildcard f dir)))
+ (cl-find-if (lambda (f)
+ (let ((expanded (projectile-expand-file-name-wildcard f dir)))
+ (and (projectile-file-exists-p expanded)
+ (not (file-directory-p expanded)))))
(or list projectile-project-root-files)))))
(defun projectile-root-marked (dir)
diff --git a/test/projectile-test.el b/test/projectile-test.el
index 4ebe3bd..33c50e4 100644
--- a/test/projectile-test.el
+++ b/test/projectile-test.el
@@ -629,9 +629,16 @@ Just delegates OPERATION and ARGS for all operations except for`shell-command`'.
(expect (projectile-root-top-down "projectA/src/framework/lib" '(".git" "framework.conf"))
:to-equal
(expand-file-name "projectA/src/"))
- (expect (projectile-root-top-down "projectA/src/html/" '(".svn"))
+ (expect (projectile-root-top-down "projectA/src/html/" '("index.html"))
:to-equal
- (expand-file-name "projectA/src/html/"))))))
+ (expand-file-name "projectA/src/html/")))))
+ (it "does not match directories for file-type markers"
+ (projectile-test-with-sandbox
+ (projectile-test-with-files
+ ("projectA/workspace/"
+ "projectA/src/")
+ (expect (projectile-root-top-down "projectA/src/" '("workspace"))
+ :not :to-be-truthy)))))
(describe "projectile-root-top-down-recurring"
(it "identifies the root directory of a project by recurring top-down search"