Skip to content

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

Description

@voodoologic

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 e5699e4), but it cannot remove the leftovers it was added for, because they are gitignored.

.gitignore gained /modules/*/ in 167abe9 (2026-06-09), repurposing $EMACSDIR/modules/ for user modules. The git clean -ffd step landed a month later (e5699e4, 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:

$ 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:

(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):

(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

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 doom doctor: void-function set-popup-rule! error on commit bcfc0db7e71f #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 (dirvishlatiagertrutis/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

  • This issue was written with/by AI.

Metadata

Metadata

Assignees

No one assigned

    Labels

    coreRelevant to Doom core

    Type

    Fields

    Open to PRs

    No PRs

    Priority

    None yet

    Projects

    Status
    Investigating

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions