master 8ed037b9c55 3/4: Merge from origin/emacs-31

Eli Zaretskii <[email protected]> Sat, 1 Aug 2026 07:38:42 -0400 (EDT)
Newsgroups gmane.emacs.diffs
Message-ID <[email protected]>
branch: master
commit 8ed037b9c55494b2ec1a7777ef8ce949941ee5cf
Merge: 2fd87a1db4c d31d3245ba4
Author: Eli Zaretskii <[email protected]>
Commit: Eli Zaretskii <[email protected]>

    Merge from origin/emacs-31
    
    d31d3245ba4 Fix display of ambiguous-width CJK characters in GUI sess...
    cf98d9d725f Eglot: do more orthodox use of progress reporters (bug#81...
    0b8d2acdd24 ; * lisp/dired-aux.el (dired-create-destination-dirs): Do...
    660e2f9f6f3 ; * lisp/emacs-lisp/package.el (package-upgrade-all): Fix...
    26e1c1792ec Summarize changes when upgrading packages
    ed4a3e251b5 Prevent 'package-menu--mark-upgrades-1' from blocking
    a9fc13614ec Export GIT_OPTIONAL_LOCKS=0 for "git status" invocations ...
    8e70e6d687b * lisp/vc/vc.el (vc-delete-file): Don't backup backup files.
    c6e2bd3e3b8 Restore current buffer after clone-indirect-buffer-hook e...
    b21266e4cbe Assign some calendar & icalendar files to packages
    b6332ac21b1 Do not asynchronously prompt to save buffers after vc-che...
    4f36e9e8855 File name with newline: require 'ls' --dired switch
    cba18a7f9cd Fix starting local processes in the MS-Windows build
---
 lisp/calendar/diary-icalendar.el    |  1 +
 lisp/calendar/diary-lib.el          |  1 +
 lisp/calendar/icalendar-ast.el      |  1 +
 lisp/calendar/icalendar-macs.el     |  1 +
 lisp/calendar/icalendar-mode.el     |  1 +
 lisp/calendar/icalendar-parser.el   |  1 +
 lisp/calendar/icalendar-recur.el    |  1 +
 lisp/calendar/icalendar-shortdoc.el |  1 +
 lisp/calendar/icalendar-utils.el    |  1 +
 lisp/dired-aux.el                   |  2 +-
 lisp/dired.el                       | 24 ++++++++++++-----------
 lisp/emacs-lisp/package.el          | 20 ++++++++++++++-----
 lisp/international/characters.el    | 11 +++++++++--
 lisp/progmodes/eglot.el             |  8 ++++++--
 lisp/vc/vc-dispatcher.el            |  6 +++++-
 lisp/vc/vc-git.el                   | 39 ++++++++++++++++++-------------------
 lisp/vc/vc.el                       |  4 +++-
 src/buffer.c                        |  5 +++--
 src/w32proc.c                       |  4 ++--
 test/lisp/dired-tests.el            | 14 ++++++-------
 20 files changed, 91 insertions(+), 55 deletions(-)

diff --git a/lisp/calendar/diary-icalendar.el b/lisp/calendar/diary-icalendar.el
index 9604dce8e4e..2e8a981e99f 100644
--- a/lisp/calendar/diary-icalendar.el
+++ b/lisp/calendar/diary-icalendar.el
@@ -6,6 +6,7 @@
 ;; Created: January 2025
 ;; Keywords: calendar
 ;; Human-Keywords: diary, calendar, iCalendar
+;; Package: icalendar
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/calendar/diary-lib.el b/lisp/calendar/diary-lib.el
index e693a3c0d2b..c55779dde67 100644
--- a/lisp/calendar/diary-lib.el
+++ b/lisp/calendar/diary-lib.el
@@ -5,6 +5,7 @@
 ;; Author: Edward M. Reingold <[email protected]>
 ;; Maintainer: [email protected]
 ;; Keywords: calendar
+;; Package: calendar
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/calendar/icalendar-ast.el b/lisp/calendar/icalendar-ast.el
index c4faf48173f..2f136533cef 100644
--- a/lisp/calendar/icalendar-ast.el
+++ b/lisp/calendar/icalendar-ast.el
@@ -6,6 +6,7 @@
 ;; Created: October 2024
 ;; Keywords: calendar
 ;; Human-Keywords: calendar, iCalendar
+;; Package: icalendar
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/calendar/icalendar-macs.el b/lisp/calendar/icalendar-macs.el
index 43903ddd1d0..c80d09a04c8 100644
--- a/lisp/calendar/icalendar-macs.el
+++ b/lisp/calendar/icalendar-macs.el
@@ -6,6 +6,7 @@
 ;; Created: October 2024
 ;; Keywords: calendar
 ;; Human-Keywords: calendar, iCalendar
+;; Package: icalendar
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/calendar/icalendar-mode.el b/lisp/calendar/icalendar-mode.el
index c68a912d296..ebfd272a1bf 100644
--- a/lisp/calendar/icalendar-mode.el
+++ b/lisp/calendar/icalendar-mode.el
@@ -7,6 +7,7 @@
 ;; Created: October 2024
 ;; Keywords: calendar
 ;; Human-Keywords: calendar, iCalendar
+;; Package: icalendar
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/calendar/icalendar-parser.el b/lisp/calendar/icalendar-parser.el
index c6100c828e4..cee48c9d485 100644
--- a/lisp/calendar/icalendar-parser.el
+++ b/lisp/calendar/icalendar-parser.el
@@ -6,6 +6,7 @@
 ;; Created: October 2024
 ;; Keywords: calendar
 ;; Human-Keywords: calendar, iCalendar
+;; Package: icalendar
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/calendar/icalendar-recur.el b/lisp/calendar/icalendar-recur.el
index 9bf55d4ba8b..1cd376fc6ff 100644
--- a/lisp/calendar/icalendar-recur.el
+++ b/lisp/calendar/icalendar-recur.el
@@ -5,6 +5,7 @@
 ;; Author: Richard Lawrence <[email protected]>
 ;; Created: December 2024
 ;; Keywords: calendar
+;; Package: icalendar
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/calendar/icalendar-shortdoc.el b/lisp/calendar/icalendar-shortdoc.el
index cf706f54672..1d3bbcfd614 100644
--- a/lisp/calendar/icalendar-shortdoc.el
+++ b/lisp/calendar/icalendar-shortdoc.el
@@ -6,6 +6,7 @@
 ;; Created: April 2026
 ;; Keywords: calendar, help
 ;; Human-Keywords: calendar, iCalendar
+;; Package: icalendar
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/calendar/icalendar-utils.el b/lisp/calendar/icalendar-utils.el
index 565901940f0..a6f06e9e5d5 100644
--- a/lisp/calendar/icalendar-utils.el
+++ b/lisp/calendar/icalendar-utils.el
@@ -5,6 +5,7 @@
 ;; Author: Richard Lawrence <[email protected]>
 ;; Created: January 2025
 ;; Keywords: calendar
+;; Package: icalendar
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index faa0317dd32..6380b3be627 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -2276,7 +2276,7 @@ that suppresses the confirmation, and backup files are always created."
 (declare-function make-symbolic-link "fileio.c")
 
 (defcustom dired-create-destination-dirs nil
-  "Whether Dired should create destination dirs when copying/removing files.
+  "Whether Dired should create destination dirs when copying/moving files.
 If nil, don't create non-existent destination directories.
 If `ask', ask the user whether to create them.
 If `always', create them without asking.
diff --git a/lisp/dired.el b/lisp/dired.el
index c559b7be6e2..ac12bf42565 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -1506,7 +1506,10 @@ The return value is the target column for the file names."
                 (dired-switches-escape-p dired-listing-switches)
                 (dired-switches-escape-p dired-actual-switches))
       (when (and (dired--filename-with-newline-p)
-                 (dired--ls-accept-b-switch-p))
+                 ;; Can't use `dired-use-ls-dired' because users could
+                 ;; set it to `t' even though their `ls' does not
+                 ;; support "--dired".
+                 (dired--check-use-ls-dired))
         (dired--display-filename-with-newline-warning buffer)))
     (set-buffer old-buf)
     buffer))
@@ -1796,6 +1799,10 @@ BEG..END is the line where the file info is located."
   "Return non-nil if the string SWITCHES contains -R or --recursive."
   (dired-check-switches switches "R" "recursive"))
 
+(defun dired--check-use-ls-dired ()
+  "Return non-nil if `ls' supports the \"--dired\" switch."
+  (eq 0 (call-process insert-directory-program nil nil nil "--dired" "-N")))
+
 (defun dired-insert-directory (dir switches &optional file-list wildcard hdr)
   "Insert a directory listing of DIR, Dired style.
 Use SWITCHES to make the listings.
@@ -1815,11 +1822,7 @@ If HDR is non-nil, insert a header line with the directory name."
          (not (bound-and-true-p eshell-ls-use-in-dired))
 	 (or remotep
              (if (eq dired-use-ls-dired 'unspecified)
-		 ;; Check whether "ls --dired" gives exit code 0, and
-		 ;; save the answer in `dired-use-ls-dired'.
-		 (or (setq dired-use-ls-dired
-			   (eq 0 (call-process insert-directory-program
-                                               nil nil nil "--dired" "-N")))
+		 (or (setq dired-use-ls-dired (dired--check-use-ls-dired))
 		     (progn
 		       (message "ls does not support --dired -N; \
 see `dired-use-ls-dired' for more details.")
@@ -4087,10 +4090,6 @@ or as \"\\n\")."
             (and res (throw 'found res)))))
     (directory-files default-directory nil "\n")))
 
-(defun dired--ls-accept-b-switch-p ()
-  "Return non-nil if the `ls' used by Dired accepts the `b' switch."
-  (eq 0 (call-process insert-directory-program nil nil nil "-b")))
-
 (defun dired--remove-b-switch ()
   "Remove all variants of the `b' switch from `dired-actual-switches'.
 This removes not only all occurrences of the short form `-b' but also
@@ -4125,7 +4124,10 @@ otherwise remove the `b' switch unless it is in
 (defun dired--set-auto-toggle-b-switch (symbol value)
   "The :set function for user option `dired-auto-toggle-b-switch'."
   (custom-set-default symbol value)
-  (when (dired--ls-accept-b-switch-p)
+  (when
+      ;; Can't use `dired-use-ls-dired' because users could set it to
+      ;; `t' even though their `ls' does not support "--dired".
+      (dired--check-use-ls-dired)
     (if value
         (add-hook 'post-command-hook #'dired--toggle-b-switch nil t)
       (remove-hook 'post-command-hook #'dired--toggle-b-switch t))
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index 062659fc912..bf567cb7b7d 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -2202,7 +2202,8 @@ from ELPA by either using `\\[package-upgrade]' or
 `\\<package-menu-mode-map>\\[package-menu-mark-install]' after `\\[list-packages]'."
   (interactive (list (not noninteractive)))
   (package-refresh-contents)
-  (let ((upgradeable (package--upgradeable-packages package-install-upgrade-built-in)))
+  (let ((upgradeable (package--upgradeable-packages package-install-upgrade-built-in))
+        (upgraded '()))
     (if (not upgradeable)
         (message "No packages to upgrade")
       (when (and query
@@ -2214,7 +2215,15 @@ from ELPA by either using `\\[package-upgrade]' or
         (user-error "Upgrade aborted"))
       (dolist (pkg upgradeable)
         (with-demoted-errors "Error while upgrading: %S"
-          (package-upgrade pkg))))))
+          (package-upgrade pkg)
+          (push pkg upgraded)))
+      (let ((rejected (cl-set-difference upgradeable upgraded)))
+        (message
+         "Upgraded: %s%s"
+         (mapconcat #'symbol-name upgraded ", ")
+         (if rejected
+             (concat "; Rejected: " (mapconcat #'symbol-name rejected ", "))
+           ""))))))
 
 (defun package--dependencies (pkg)
   "Return a list of all transitive dependencies of PKG.
@@ -3978,9 +3987,10 @@ Implementation of `package-menu-mark-upgrades'."
                   ((equal pkg-desc upgrade)
                    (package-menu-mark-install))
                   (t
-                   (unless (package-matches-selector-p
-                            package-retention-policy
-                            pkg-desc)
+                   (if (package-matches-selector-p
+                        package-retention-policy
+                        pkg-desc)
+                       (forward-line)
                      (package-menu-mark-delete)))))))
       (message "Packages marked for upgrading: %d"
                (length upgrades)))))
diff --git a/lisp/international/characters.el b/lisp/international/characters.el
index b29b0f8d094..a880577064b 100644
--- a/lisp/international/characters.el
+++ b/lisp/international/characters.el
@@ -1715,8 +1715,15 @@ the value of the variable with `setq'."
               (ambiguous-is-wide
                (and cjk-ambiguous-chars-are-wide
                     ;; MS-Windows Terminal forces all ambiguous
-                    ;; characters to be narrow, even in CJK locales.
-                    (not (and (boundp 'w32--terminal-is-conhost)
+                    ;; characters to be narrow, even in CJK locales,
+                    ;; assuming the fonts it uses were left at their
+                    ;; defaults.
+                    (not (and (eq system-type 'windows-nt)
+                              ;; 'window-system' might be nil when this
+                              ;; code is called from startup.el, as part
+                              ;; of 'set-locale-environment', so we must
+                              ;; test 'initial-window-system' instead.
+                              (null initial-window-system)
                               (null w32--terminal-is-conhost))))))
           (map-char-table
            (lambda (range _val)
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index f701a38ab8b..0f0791d37ab 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -2872,7 +2872,8 @@ return it back to the server.  :null is returned if the list was empty."
                 (if (eq eglot-report-progress 'messages)
                     (make-progress-reporter
                      (format "[eglot] %s %s: %s"
-                             (eglot-project-nickname server) token title))
+                             (eglot-project-nickname server) token title)
+                     0 100)
                   (list 'eglot--mode-line-reporter token title)))
               (upd (pcnt msg &optional
                          (pr (gethash token (eglot--progress-reporters server))))
@@ -2880,7 +2881,10 @@ return it back to the server.  :null is returned if the list was empty."
                   ((eq (car pr) 'eglot--mode-line-reporter)
                    (setcdr (cddr pr) (list msg pcnt))
                    (force-mode-line-update t))
-                  (pr (eglot--reporter-update pr pcnt msg)))))
+                  (pr
+                   (if (eql pcnt 100)
+                       (progress-reporter-done pr)
+                     (eglot--reporter-update pr pcnt msg))))))
       (eglot--dbind ((WorkDoneProgress) kind title percentage message) value
         (pcase kind
           ("begin"
diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el
index 2111b8f2900..53ab1da1c60 100644
--- a/lisp/vc/vc-dispatcher.el
+++ b/lisp/vc/vc-dispatcher.el
@@ -696,7 +696,11 @@ If the current buffer visits a file, call `vc-refresh-state'."
                            (with-current-buffer buf
                              ,@body))))))
       (cond ((derived-mode-p 'vc-dir-mode)
-             (run-delayed (vc-dir-refresh)))
+             (run-delayed
+              ;; Avoid interrupting the user with prompts to save
+              ;; buffers.
+              (let ((non-essential t))
+                (vc-dir-refresh))))
             ((derived-mode-p 'dired-mode)
              (run-delayed
               (when (= (buffer-modified-tick buf) tick)
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index 638edd6c703..cb4943f15e6 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -2896,6 +2896,21 @@ page for the meanings of these attributes."
 
 ;;; Internal commands
 
+(defun vc-git--env-vars (subcommand)
+  "Return env vars for the `process-environment' of Git processes."
+  `("GIT_DIR"
+    ,@(and vc-git-use-literal-pathspecs
+           '("GIT_LITERAL_PATHSPECS=1"))
+    ;; Avoid optional repository locking during background operations
+    ;; (bug#21559, bug#80903).  Skipping these locks is always safe and
+    ;; can only lead to subsequent commands running more slowly.
+    ;; The "git status" case covers how `vc-checkin' uses
+    ;; `vc-dir-resynch-file' to update the display state of files
+    ;; undergoing an asynchronous check-in.
+    ,@(and (or revert-buffer-in-progress
+               (equal subcommand "status"))
+           '("GIT_OPTIONAL_LOCKS=0"))))
+
 (defun vc-git-command (buffer okstatus file-or-list &rest flags)
   "A wrapper around `vc-do-command' for use in vc-git.el.
 The difference to `vc-do-command' is that this function always invokes
@@ -2915,16 +2930,8 @@ The difference to `vc-do-command' is that this function always invokes
          ;; want to do it only for commands which really require it.
 	 (coding-system-for-write
           (or coding-system-for-write vc-git-commits-coding-system))
-         (process-environment
-          (append
-           `("GIT_DIR"
-             ,@(and vc-git-use-literal-pathspecs
-                    '("GIT_LITERAL_PATHSPECS=1"))
-             ;; Avoid repository locking during background operations
-             ;; (bug#21559).
-             ,@(and revert-buffer-in-progress
-                    '("GIT_OPTIONAL_LOCKS=0")))
-           process-environment))
+         (process-environment (append (vc-git--env-vars (car flags))
+                                      process-environment))
          (file1 (and (not (cdr-safe file-or-list))
                      (or (car-safe file-or-list) file-or-list)))
          (file-list-is-rootdir (and file1
@@ -2966,16 +2973,8 @@ The difference to `vc-do-command' is that this function always invokes
          (or coding-system-for-read vc-git-log-output-coding-system))
 	(coding-system-for-write
          (or coding-system-for-write vc-git-commits-coding-system))
-	(process-environment
-	 (append
-	  `("GIT_DIR"
-            ,@(when vc-git-use-literal-pathspecs
-                '("GIT_LITERAL_PATHSPECS=1"))
-	    ;; Avoid repository locking during background operations
-	    ;; (bug#21559).
-	    ,@(when revert-buffer-in-progress
-		'("GIT_OPTIONAL_LOCKS=0")))
-	  process-environment)))
+	(process-environment (append (vc-git--env-vars command)
+                                     process-environment)))
     (apply #'process-file vc-git-program infile buffer nil
            "--no-pager" command args)))
 
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index e2588fc3765..8fa0c661f93 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -5210,7 +5210,9 @@ These are recursively deleted."
             (backend (if (file-directory-p file)
                          (vc-responsible-backend file)
                        (vc-backend file))))
-        (unless (or (file-directory-p file) (null make-backup-files)
+        (unless (or (file-directory-p file)
+                    (null make-backup-files)
+                    (backup-file-name-p file)
                     (not (file-exists-p file)))
           (with-current-buffer (or buf (find-file-noselect file))
             (let ((backup-inhibited nil))
diff --git a/src/buffer.c b/src/buffer.c
index 0a2bff27c11..a02ff0b318d 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -937,7 +937,7 @@ Interactively, CLONE and INHIBIT-BUFFER-HOOKS are nil.  */)
     }
   else
     {
-      struct buffer *old_b = current_buffer;
+      specpdl_ref count = SPECPDL_INDEX ();
 
       clone_per_buffer_values (b->base_buffer, b);
       bset_filename (b, Qnil);
@@ -946,6 +946,7 @@ Interactively, CLONE and INHIBIT-BUFFER-HOOKS are nil.  */)
       bset_backed_up (b, Qnil);
       bset_local_minor_modes (b, Qnil);
       bset_auto_save_file_name (b, Qnil);
+      record_unwind_current_buffer ();
       set_buffer_internal_1 (b);
       Fset (Qbuffer_save_without_query, Qnil);
       Fset (Qbuffer_file_number, Qnil);
@@ -955,7 +956,7 @@ Interactively, CLONE and INHIBIT-BUFFER-HOOKS are nil.  */)
 	 variable copies for list variables that might be mangled due
 	 to destructive operations in the indirect buffer. */
       run_hook (Qclone_indirect_buffer_hook);
-      set_buffer_internal_1 (old_b);
+      unbind_to (count, Qnil);
     }
 
   run_buffer_list_update_hook (b);
diff --git a/src/w32proc.c b/src/w32proc.c
index 52d7ef9b5a7..ce75f5b27ce 100644
--- a/src/w32proc.c
+++ b/src/w32proc.c
@@ -2762,8 +2762,8 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
     {
       program = build_string (cmdname);
       full = Qnil;
-      openp (Vexec_path, program, Vexec_suffixes, &full, make_fixnum (X_OK),
-	     0, 0, NULL);
+      openp (Vexec_path, program, Fdefault_value (Qexec_suffixes),
+	     &full, make_fixnum (X_OK), 0, 0, NULL);
       if (NILP (full))
 	{
 	  errno = EINVAL;
diff --git a/test/lisp/dired-tests.el b/test/lisp/dired-tests.el
index 65ca58cc705..47ead6a30b3 100644
--- a/test/lisp/dired-tests.el
+++ b/test/lisp/dired-tests.el
@@ -660,10 +660,9 @@ The current directory at call time should not affect the result (Bug#50630)."
 
 (ert-deftest dired-test-filename-with-newline-1 () ; bug#79528, bug#80499
   "Test handling of file name with literal embedded newline."
-  ;; File names with embedded newlines are not allowed on MS-Windows and
-  ;; MS-DOS.
-  (skip-when (memq system-type '(windows-nt ms-dos)))
-  (skip-unless (dired--ls-accept-b-switch-p))
+  ;; The handling of file names with embedded newlines requires an `ls'
+  ;; that supports the "--dired" switch.
+  (skip-unless (dired--check-use-ls-dired))
   (with-current-buffer "*Messages*"
     (let ((inhibit-read-only t))
       (erase-buffer)))
@@ -696,10 +695,9 @@ The current directory at call time should not affect the result (Bug#50630)."
 
 (ert-deftest dired-test-filename-with-newline-2 () ; bug#79528, bug#80499
   "Test handling of file name with embedded newline using `b' switch."
-  ;; File names with embedded newlines are not allowed on MS-Windows and
-  ;; MS-DOS.
-  (skip-when (memq system-type '(windows-nt ms-dos)))
-  (skip-unless (dired--ls-accept-b-switch-p))
+  ;; The handling of file names with embedded newlines requires an `ls'
+  ;; that supports the "--dired" switch.
+  (skip-unless (dired--check-use-ls-dired))
   (with-current-buffer "*Messages*"
     (let ((inhibit-read-only t))
       (erase-buffer)))