diff --git a/CHANGELOG.md b/CHANGELOG.md index 91d0eeae1..85542af7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ * [#871](https://github.com/bbatsov/projectile/issues/871): Stop advice for `compilation-find-file` to override other advices. * [#557](https://github.com/bbatsov/projectile/issues/557): stack overflow in `projectile-find-tag'. +* [#952](https://github.com/bbatsov/projectile/issues/952): VCS submodules brought in even thought not descendent of project root. ## 0.13.0 (10/21/2015) diff --git a/projectile.el b/projectile.el index ecb6eaed1..e78aa9414 100644 --- a/projectile.el +++ b/projectile.el @@ -912,15 +912,10 @@ Files are returned as relative paths to the project root." (t "")))) (defun projectile-get-all-sub-projects (project) - "Get all sub-projects for a given projects. -PROJECT is base directory to start search recursively." - (let* ((default-directory project) - ;; search for sub-projects under current project `project' - (submodules (mapcar - (lambda (s) - (file-name-as-directory (expand-file-name s default-directory))) - (projectile-files-via-ext-command (projectile-get-sub-projects-command))))) + "Get all sub-projects for a given project. +PROJECT is base directory to start search recursively." + (let ((submodules (projectile-get-immediate-sub-projects project))) (cond ((null submodules) nil) @@ -930,6 +925,33 @@ PROJECT is base directory to start search recursively." (mapcar (lambda (s) (projectile-get-all-sub-projects s)) submodules))))))) +(defun projectile-get-immediate-sub-projects (path) + "Get immediate sub-projects for a given project without recursing. + +PATH is the vcs root or project root from which to start +searching, and should end with an appropriate path delimiter, such as +'/' or a '\\'. + +If the vcs get-sub-projects query returns results outside of path, +they are excluded from the results of this function." + (let* ((default-directory path) + ;; search for sub-projects under current project `project' + (submodules (mapcar + (lambda (s) + (file-name-as-directory (expand-file-name s default-directory))) + (projectile-files-via-ext-command (projectile-get-sub-projects-command)))) + (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. + (-filter (lambda (submodule) + (string-match-p project-child-folder-regex + submodule)) + submodules))) + (defun projectile-get-sub-projects-files () "Get files from sub-projects recursively." (-flatten diff --git a/test/projectile-test.el b/test/projectile-test.el index b385bae1d..452406c13 100644 --- a/test/projectile-test.el +++ b/test/projectile-test.el @@ -636,6 +636,31 @@ (projectile-find-matching-file "spec/models/food/sea_spec.rb")))))))) +(ert-deftest projectile-test-exclude-out-of-project-submodules () + (projectile-test-with-files + (;; VSC root is here + "project/" + "project/.git/" + "project/.gitmodules" + ;; Current project root is here: + "project/web-ui/" + "project/web-ui/.projectile" + ;; VCS git submodule will return the following submodules, + ;; relative to current project root, 'project/web-ui/': + "project/web-ui/vendor/client-submodule/" + "project/server/vendor/server-submodule/") + (let ((project (file-truename (expand-file-name "project/web-ui")))) + (noflet ((projectile-files-via-ext-command + (arg) (when (string= default-directory project) + '("vendor/client-submodule" + "../server/vendor/server-submodule"))) + (projectile-project-root + () project)) + + ;; assert that it only returns the submodule 'project/web-ui/vendor/client-submodule/' + (should (equal (list (expand-file-name "vendor/client-submodule/" project)) + (projectile-get-all-sub-projects project))))))) + ;; Local Variables: ;; indent-tabs-mode: nil ;; End: