From ea3d78898a2be7aafbfda5973fad5453f50d10fe Mon Sep 17 00:00:00 2001 From: Philip Kaludercic Date: Fri, 5 Jun 2026 18:06:10 +0200 Subject: [PATCH] Allow uninstalling dependency with multiple installations * lisp/emacs-lisp/package.el (package--dependency-p): Add new function to check if a package is a dependency. (package-delete): Prompt user if it is OK to uninstall a package that serves as a dependency, but where the package has multiple installations. This also means that we don't de-select a selected dependency if there are multiple installations. (Bug#81082) --- lisp/emacs-lisp/package.el | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el index 068b94360d4..cd3c850f7a4 100644 --- a/lisp/emacs-lisp/package.el +++ b/lisp/emacs-lisp/package.el @@ -2379,6 +2379,15 @@ installed), maybe you need to \\[package-refresh-contents]") (equal (cadr (assq (package-desc-name pkg) package-alist)) pkg)) +(defun package--dependency-p (pkg) + "Return non-nil if PKG is a dependency." + (let ((name (package-desc-name pkg))) + (catch 'is-dependency + (dolist (ent (package--alist) nil) + (dolist (desc (cdr ent)) + (when (assq name (package-desc-reqs desc)) + (throw 'is-dependency t))))))) + (declare-function comp-el-to-eln-filename "comp.c") (defvar package-vc-repository-store) (defun package--delete-directory (dir) @@ -2433,15 +2442,20 @@ If NOSAVE is non-nil, the package is not removed from (if (null (remove pkg-desc (cdr pkgs))) (remq pkgs package-alist) package-alist))) - pkg-used-elsewhere-by) + pkg-used-elsewhere-by) ;; If the user is trying to delete this package, they definitely ;; don't want it marked as selected, so we remove it from ;; `package-selected-packages' even if it can't be deleted. (when (and (null nosave) (package--user-selected-p name) - ;; Don't deselect if this is an older version of an - ;; upgraded package. - (package--newest-p pkg-desc)) + (or + ;; Don't deselect if this is an older version of an + ;; upgraded package. + (package--newest-p pkg-desc) + ;; Don't deselect if the package is a dependency and + ;; there are multiple installations (bug#81082). + (and (length< (alist-get name package-alist) 1) + (package--dependency-p pkg-desc)))) (package--save-selected-packages (remove name package-selected-packages))) (cond ((not (string-prefix-p (file-name-as-directory (expand-file-name package-user-dir)) @@ -2452,7 +2466,16 @@ If NOSAVE is non-nil, the package is not removed from ((and (null force) (setq pkg-used-elsewhere-by (let ((package-alist new-package-alist)) - (package--used-elsewhere-p pkg-desc)))) ;See bug#65475 + (package--used-elsewhere-p pkg-desc))) ;See bug#65475 + (and-let* ((other (package-get-descriptor + name 'installed + (lambda (pkg-other) + (not (equal pkg-desc pkg-other)))))) + (not (yes-or-no-p + (format "Package `%s' is used by `%s' as dependency, but `%s' is also available. Use that instead?" + (package-desc-full-name pkg-desc) + (package-desc-name pkg-used-elsewhere-by) + (package-desc-full-name other)))))) ;; Don't delete packages used as dependency elsewhere. (error "Package `%s' is used by `%s' as dependency, not deleting" (package-desc-full-name pkg-desc)