Migrating from project.el

A short guide for people who’ve been using Emacs’s built-in project.el and want to try Projectile. For the bigger picture (philosophical differences, feature gaps in either direction) see Projectile versus project.el; for a side-by-side mapping of commands and config knobs see the "Concept and command concordance" section there.

Quick start

Install Projectile from MELPA (or NonGNU ELPA), enable the global mode, and bind a prefix key:

(require 'projectile)
(projectile-mode +1)
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)

project.el’s `C-x p keymap continues to work; Projectile’s keymap is independent. The prefix doesn’t have to be C-c p — many people use s-p or whatever else fits their setup.

That’s enough to get going. Most of project.el’s commands have a direct Projectile counterpart (e.g. `C-x p f becomes C-c p f, C-x p p becomes C-c p p).

Behavioral differences worth knowing

A handful of places where Projectile doesn’t behave like project.el:

  • Switching projects. project-switch-project opens a command menu; Projectile instead runs whatever’s in projectile-switch-project-action after picking a project (find-file by default). If you miss the menu UX, press C-u s-p p to get the projectile-dispatch menu after selecting a project (requires the optional transient dependency).

  • Indexing strategies. Projectile has three (native, hybrid, alien) controlled by projectile-indexing-method. The default is alien and behaves like project.el’s VC backend (delegates to `git ls-files, fd, find etc.). Pick native if you need to work somewhere external tools aren’t available.

  • .projectile. Projectile’s native config file is consulted only under native or hybrid indexing. Under alien (the default) it’s mostly ignored — the external tool’s ignore rules (.gitignore etc.) take over. So in most modern setups, .projectile doesn’t do much.

  • Project types. Projectile auto-detects ~60 project types (Cargo, Maven, Mix, npm, etc.) and pre-fills projectile-compile-project, projectile-test-project, projectile-run-project, projectile-install-project, projectile-package-project, and projectile-configure-project with sensible defaults for each. There’s no equivalent in project.elproject-compile runs whatever compile-command happens to be.

  • Buffer ownership. Projectile and project.el use slightly different rules to decide which buffers belong to a project. Don’t expect projectile-kill-buffers and project-kill-buffers to act on identical sets.

  • Persistent caches. Projectile maintains a project-file cache (controlled by projectile-enable-caching) and a known-projects file (projectile-known-projects-file). The latter is roughly analogous to project-list-file, but the format differs.

Configuration knob translation

Most knob-for-knob mappings are in the concordance table in the comparison page. A few common ones:

If you had Use

project-list-file

projectile-known-projects-file

project-vc-ignores

projectile-globally-ignored-files, projectile-globally-ignored-directories, projectile-globally-ignored-file-suffixes, plus the project-local .projectile

project-vc-extra-root-markers

projectile-project-root-files-bottom-up (and its sibling defcustoms)

project-prompter

projectile-completion-system (default, i.e. completing-read, or a custom function)

project-vc-name

projectile-project-name set via .dir-locals.el

project-list-exclude

projectile-ignored-projects (list) or projectile-ignored-project-function (predicate)

Things that don’t have a direct equivalent (yet)

A few project.el features have no real Projectile counterpart at the time of writing:

  • project-find-matching-buffer (jump to the equivalent buffer in another project)

  • project-file-history-behavior 'relativize (file-name history rewritten across projects)

  • project-other-window-command / -other-frame-command / -other-tab-command (prefix-style display redirection — we duplicate commands instead)

  • Sparse-index support for Git

  • project-vc-merge-submodules (explicit Git submodule strategy)

See the comparison page for the full list.

Coexisting with project.el

You don’t have to pick one. project.el ships with Emacs and won’t go away, and several other packages (xref, eglot, modern compilation buffers) will reach for project-current regardless of whether Projectile is loaded.

In practice this means: enabling projectile-mode doesn’t disable project.el. Both keymaps coexist, both sets of commands are available via M-x. Just keep in mind that some features (e.g. modeline integration, idle file-existence caching) come from projectile-mode itself, so if you turn the mode off you lose them.