doomemacs / doomemacs/core

`doom upgrade`'s `git clean -ffd` can't remove `modules/` leftovers (gitignored) — follow-up to #8810

Open
#8,864 0 comments 0 reactions 0 assignees View on GitHub
core
Dominant language
Emacs Lisp
Stars
22.7k
Forks
3.1k
Avg merge
10h 46m
Merged PRs (30d)
4

Description

### Describe your issue

Follow-up to #8810, where leftover `$EMACSDIR/modules/` directories shadowed `sources/doom+/modules/` and produced `void-function set-popup-rule!`. That thread ended with:

> Probably means `doom upgrade` needs a `git clean -ffd` step to delete any leftovers. I'll look into that.

That step exists now (`bin/doom-upgrade:122`, added in e5699e4a), but it cannot remove the leftovers it was added for, because they are gitignored.

`.gitignore` gained `/modules/*/` in 167abe96 (2026-06-09), repurposing `$EMACSDIR/modules/` for user modules. The `git clean -ffd` step landed a month later (e5699e4a, 2026-07-09). `git clean -ffd` does not remove ignored paths — that needs `-x`. So the clean is a no-op over exactly the tree it targets.

Demonstrated on my install by temporarily restoring one leftover category:

```console
$ git clean -nffd | grep -c modules/ui # what doom upgrade runs
0
$ git clean -nffdx | grep -c modules/ui # with -x
1
$ git check-ignore -v modules/ui/popup/config.el
.gitignore:18:/modules/*/ modules/ui/popup/config.el
```

Compounding it, `doom-module-load-path` (`lisp/doom.el:206`) still ranks the leftover tree above the submodule:

```elisp
(defvar doom-module-load-path
(list (file-name-concat doom-user-dir "modules")
(file-name-concat doom-emacs-dir "modules") ; <- leftovers win
(file-name-concat doom-emacs-dir "sources/doom+/modules"))
```

#8810 involved *empty* leftover directories, which fail loudly. Mine was a complete pre-2.2 checkout of the official modules — ~1000 files frozen at 2026-03-27, while `sources/doom+` was at v26.08 (2026-08-02).

Because the stale tree contains working (just outdated) code, nothing fails loudly. `doom upgrade` and `doom sync` both report success, `git status` is clean, and the user runs four-month-old modules indefinitely. I only noticed because of a second-order effect:

**All `;;;###autodef` silently vanish.** `doom-loaddefs--scan-file` gates the autodef scan on the module source manifest (`lisp/cli/loaddefs.el:137`):

```elisp
(let* ((module? (and (doom-config-locate 'modules file) t))
...
(when module?
(doom-loaddefs--scan-autodefs file target-buffer module enabled?))
```

`sources/doom+/modules/` has `.doommodules`; the pre-2.2 tree does not. So when modules resolve from the leftovers, `module?` is nil and no autodef is ever emitted. Counts in `60-doom-module-loaddefs.init.el`, before and after removing the tree:

| autodef | leftovers win | `sources/doom+` wins |
|---|---|---|
| `set-popup-rule!` | 0 | 5 |
| `set-popup-rules!` | 0 | 4 |
| `set-lookup-handlers!` | 0 | 7 |
| `set-company-backend!` | 0 | 7 |
| `set-formatter!` | 0 | 9 |
| `set-ligatures!` | 0 | 4 |

Resulting boot failures:

```
An error occurred while booting Doom Emacs:
Error in a Doom module: modules/ui/popup/config.el, (void-function set-popup-rules!)
Error in a Doom startup hook: doom-init-ui-hook, +dashboard-init-h, (void-variable persp-auto-save-fname)
```

#### Same class, also gitignored: `lisp/` leftovers

```
lisp/doom-editor.el lisp/doom-keybinds.el lisp/doom-projects.el
lisp/doom-start.el lisp/doom-ui.el lisp/init.el
lisp/demos.org lisp/modules/
```

Dated 2026-03-27/04-15 against a Jul 22–29 tracked core. `lisp/*.el` is globbed into the CLI loaddefs, so these contribute stale definitions — `lisp/doom-ui.el` still defines `doom-init-ui-hook`, duplicating the current definition in the tracked `lisp/doom-emacs.el`.

#### Fix that worked

```sh
cd "$EMACSDIR"
rm -rf modules/{app,checkers,completion,config,editor,emacs,email,input,lang,os,term,tools,ui}
rm -f modules/{README.org,LICENSE,.gitignore}
rm -rf lisp/{doom-editor.el,doom-keybinds.el,doom-projects.el,doom-start.el,doom-ui.el,init.el,demos.org,modules}
doom sync
```

All autodefs returned, both boot errors cleared, and 59 modules now resolve from `sources/doom+` with only the tracked `modules/doom/` pair coming from `$EMACSDIR/modules/`.

#### Suggestions

1. Scope the clean so it can actually reach these — e.g. `git clean -ffdx` over `modules/` and `lisp/` specifically, rather than an unscoped `-ffd`.
2. Consider reordering `doom-module-load-path` so `sources/doom+/modules` wins over `$EMACSDIR/modules` (darrenkenny's original patch in #8810). That makes the failure mode benign even when leftovers survive.
3. A `doom doctor` check would help; this is currently invisible — `git status` clean, both CLIs reporting success, only a silent version freeze.

#### Side note

After removing the tree, `doom sync` picked up current recipes, two of which changed upstream (`dirvish` → `latiagertrutis/dirvish`). straight.el *prompts* on the remote URL mismatch, and under a non-TTY shell that prompt gets EOF:

```
signal doom-package-error ("dirvish" (end-of-file "Error reading from stdin"))
```

The sync aborts after touching packages but before regenerating the profile init, leaving loaddefs pointing at the just-removed tree. A non-interactive `doom sync` might want to fail earlier, or have a documented resolution policy for recipe URL changes.

### System information

```
doom v2.2.2 (6b855e89)
doom+ v26.08 (sources/doom+, correctly wired)
emacs 30.2
os Linux 7.1.4-arch1-1 x86_64
```

### Disclosures

- [X] This issue was written with/by AI.

Contributor guide

Open the contributing guide

Research direction

Start with bin/doom-upgrade:122, lisp/doom.el:206, and lisp/cli/loaddefs.el:137 to trace cleanup, module precedence, and autodef scanning. Reproduce the ignored modules/lisp leftovers with git clean -nffd, git clean -nffdx, and git check-ignore, then run doom sync. Done means stale trees no longer shadow the tracked sources and the affected autodefs and boot behavior are restored.

Written by the indexing model from the issue text.

Assessment

Tech stack
emacs-lisp, git
Domain
cli, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.