From a039e44622b644deb90893f7b27c0940c486bbb7 Mon Sep 17 00:00:00 2001
From: Bozhidar Batsov <bozhidar@toptal.com>
Date: Sun, 26 Apr 2026 12:30:10 +0100
Subject: Document remaining indexing semantics

Three doc gaps surfaced during the indexing review:

* The `*' prefix on `projectile-globally-ignored-directories' entries
  is not a glob - it is what promotes a basename match from
  "anchored at the project root" to "anywhere in the tree", and it
  only takes effect under hybrid indexing.
* The default `projectile-generic-command' picks `fd' when it's on
  PATH and falls back to a `find ... | tr ...' pipeline. The
  fallback does not exclude common build directories, which is a
  trap for non-VCS projects under alien on hosts without `fd'.
* `projectile-git-use-fd' controls how Projectile handles
  deleted-but-unstaged files: with `fd' they disappear immediately,
  with `git ls-files' Projectile post-filters via `git ls-files -zd'.
---
 CHANGELOG.md                              |  3 +++
 doc/modules/ROOT/pages/configuration.adoc | 22 ++++++++++++++++++----
 doc/modules/ROOT/pages/projects.adoc      | 19 +++++++++++++++++++
 3 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 196958e..bed2f14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@
 
 ### Changes
 
+* `projectile-get-immediate-sub-projects` skips the `git submodule foreach` shell-out for git projects with no `.gitmodules` file anywhere up the parent chain. Hot path for monorepos that index the project root often.
+* `projectile-discover-projects-in-directory` now uses `directory-files-no-dot-files-regexp` to skip `.` and `..` at the C level instead of doing the post-filter in Elisp - matches the indexing walker.
+* Document the anchored vs `*`-prefixed semantics of `projectile-globally-ignored-directories`, the `find` fallback's lack of common directory exclusions when `fd` isn't available, and how `fd`/`git ls-files` handle deleted-but-unstaged files differently.
 * Speed up native indexing on large trees: `projectile-index-directory` now hashes the ignored-files / ignored-directories / globally-ignored-directory-names lists once per indexing call (the per-file `member' scans were O(N*M)), expands dirconfig glob patterns once per directory level instead of once per (file, pattern) pair, and accumulates results into a shared cell so we no longer pay for an `apply append' at each recursion level.
 * `projectile-remove-ignored` (hybrid post-processing) now hashes the ignored-files basenames and pre-splits ignored-dirs into prefix-match and any-segment groups, so the per-file inner loops drop from O(M) `seq-some` walks to O(1) hash lookups (or O(segments) for `*`-prefixed entries).
 * Hybrid indexing now batches the external command into a single invocation when the project's `.projectile` declares multiple `+` keep entries, instead of shelling out once per kept subdirectory. The kept paths are passed to the indexing tool (e.g. `git ls-files`, `fd`, `find`) as positional pathspecs and submodule files outside those subdirectories are filtered out. Resolves the long-standing TODO in `projectile-project-files`.
diff --git a/doc/modules/ROOT/pages/configuration.adoc b/doc/modules/ROOT/pages/configuration.adoc
index 1492633..2eada23 100644
--- a/doc/modules/ROOT/pages/configuration.adoc
+++ b/doc/modules/ROOT/pages/configuration.adoc
@@ -116,18 +116,32 @@ WARNING: If you ever decide to tweak those keep in mind that the command should
  the list of files **relative** to the project root and the resulting file list should be 0-delimited
  (as opposed to newline delimited).
 
-For non-VCS projects Projectile will invoke whatever is in `projectile-generic-command`. By default that's:
+For non-VCS projects Projectile will invoke whatever is in `projectile-generic-command`. The default chooses `fd` when it's installed and falls back to `find`:
 
+[source,elisp]
 ----
-find . -type f -print0
+;; Effective default value of projectile-generic-command, picked at load time:
+;;   when fd is on PATH:
+"fd . -0 --type f --color=never --strip-cwd-prefix"
+;;   otherwise:
+"find . -type f | cut -c3- | tr '\\n' '\\0'"
 ----
 
 TIP: It's a great idea to install https://github.com/sharkdp/fd[fd] which is much faster than `find`.
- If `fd` is found, projectile will use it as a replacement for `find`.
+ If `fd` is found, projectile will use it as a replacement for `find` for non-VCS projects.
+
+WARNING: The `find` fallback does *not* exclude common build/cache directories
+ (`.git`, `node_modules`, `target`, `build`, …); a non-VCS project under `alien`
+ indexing on a host without `fd` will list everything.  Either install `fd`,
+ switch to `hybrid` indexing so `projectile-globally-ignored-directories`
+ applies, or override `projectile-generic-command` with a tighter recipe.
 
 By default, `fd` is also used inside Git repositories (instead of `git ls-files`),
 because `git ls-files` has the limitation that it lists deleted files until the
-deletions are staged. You can control this with `projectile-git-use-fd`:
+deletions are staged. With `fd`, deleted files disappear from the listing
+immediately; with `git ls-files`, Projectile post-filters the listing against
+`git ls-files -zd` to hide deletions until they're staged. You can control this
+with `projectile-git-use-fd`:
 
 [source,elisp]
 ----
diff --git a/doc/modules/ROOT/pages/projects.adoc b/doc/modules/ROOT/pages/projects.adoc
index d08bae8..d14092b 100644
--- a/doc/modules/ROOT/pages/projects.adoc
+++ b/doc/modules/ROOT/pages/projects.adoc
@@ -976,6 +976,25 @@ globally ignoring files and directories.  These take effect with `native` and
 (setq projectile-global-ignore-file-patterns '("\\.min\\.js$" "\\.map$"))
 ----
 
+==== Anchored vs anywhere directory ignores
+
+`projectile-globally-ignored-directories` distinguishes between *anchored*
+entries (matched as a path prefix relative to the project root) and *anywhere*
+entries (matched at any depth in the tree). The leading `*` is **not** a glob
+character — it is the marker that promotes an entry from anchored to anywhere.
+
+[source,elisp]
+----
+(setq projectile-globally-ignored-directories
+      '("tmp"           ; only ignores ./tmp at the project root
+        "*node_modules" ; ignores any directory named node_modules at any depth
+        ))
+----
+
+The `*` prefix only matters for the `hybrid` post-processor; in `native`
+indexing every entry is matched by directory basename at every traversal step
+(so `tmp` and `*tmp` behave the same). `alien` ignores both forms entirely.
+
 You can also _unignore_ specific files or directories that would otherwise be
 excluded.  This is useful when your VCS ignores files that you still want
 Projectile to show:
-- 
cgit v1.0

