aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBozhidar Batsov <bozhidar@toptal.com>2026-04-26 12:29:52 +0100
committerBozhidar Batsov <bozhidar@toptal.com>2026-04-26 12:29:52 +0100
commitccd8052beb84a889565ffd08a58cf643e2e439f3 (patch)
tree7b45e149914261c4c3591a7fdd97cf1207382a72
parentfa890caa2b21af3895b302e22e1d74df8ee55453 (diff)
Skip git submodule scan when there is no .gitmodules
`projectile-get-immediate-sub-projects' was unconditionally shelling out to `git submodule --quiet foreach ...' on every indexing call for git projects, even when the project had no submodules at all. For monorepos that re-index the project root often this is pure overhead. Use `locate-dominating-file' to look for `.gitmodules' along the parent chain (PATH may be inside a git repo without being its toplevel) and skip the shell-out when none is found. Also tighten `projectile-discover-projects-in-directory' to filter `.' / `..' via `directory-files-no-dot-files-regexp' instead of a post-filter `member' check, matching the indexing walker's style.
-rw-r--r--projectile.el50
1 files changed, 29 insertions, 21 deletions
diff --git a/projectile.el b/projectile.el
index 0f8c480..c4c0154 100644
--- a/projectile.el
+++ b/projectile.el
@@ -1330,9 +1330,10 @@ discover projects there."
(format "Projectile is discovering projects in %s..."
(propertize directory 'face 'font-lock-keyword-face)))))
(progress-reporter-update progress-reporter)
- (dolist (dir (ignore-errors (directory-files directory t)))
- (when (and (file-directory-p dir)
- (not (member (file-name-nondirectory dir) '(".." "."))))
+ (dolist (dir (ignore-errors
+ (directory-files directory t
+ directory-files-no-dot-files-regexp)))
+ (when (file-directory-p dir)
(projectile-discover-projects-in-directory dir (1- depth))))
(progress-reporter-done progress-reporter))
(when (projectile-project-p directory)
@@ -1856,24 +1857,31 @@ searching, and should end with an appropriate path delimiter, such as
If the vcs get-sub-projects query returns results outside of path,
they are excluded from the results of this function."
- (let* ((vcs (projectile-project-vcs path))
- ;; search for sub-projects under current project `project'
- (submodules (mapcar
- (lambda (s)
- (file-name-as-directory (expand-file-name s path)))
- (projectile-files-via-ext-command path (projectile-get-sub-projects-command vcs))))
- (project-child-folder-regex
- (concat "\\`"
- (regexp-quote path))))
-
- ;; If project root is inside of an VCS folder, but not actually an
- ;; VCS root itself, submodules external to the project will be
- ;; included in the VCS get sub-projects result. Let's remove them.
- (seq-filter
- (lambda (submodule)
- (string-match-p project-child-folder-regex
- submodule))
- submodules)))
+ (let ((vcs (projectile-project-vcs path)))
+ ;; For Git projects without a `.gitmodules' file there is nothing
+ ;; for `git submodule foreach' to find, so we can skip the
+ ;; shell-out altogether. PATH may be inside a Git repo without
+ ;; being its toplevel (e.g. a subproject of an outer repo) so look
+ ;; for `.gitmodules' along the parent chain rather than just at
+ ;; PATH itself. This is hot for monorepos that index the project
+ ;; root often.
+ (unless (and (eq vcs 'git)
+ (not (locate-dominating-file path ".gitmodules")))
+ (let* ((submodules (mapcar
+ (lambda (s)
+ (file-name-as-directory (expand-file-name s path)))
+ (projectile-files-via-ext-command
+ path (projectile-get-sub-projects-command vcs))))
+ (project-child-folder-regex
+ (concat "\\`" (regexp-quote path))))
+ ;; If project root is inside of an VCS folder, but not
+ ;; actually an VCS root itself, submodules external to the
+ ;; project will be included in the VCS get sub-projects
+ ;; result. Let's remove them.
+ (seq-filter
+ (lambda (submodule)
+ (string-match-p project-child-folder-regex submodule))
+ submodules)))))
(defun projectile-get-sub-projects-files (project-root vcs)
"Get files from sub-projects for PROJECT-ROOT recursively.