Subject: bug#54838: 28.1; Buffers are not updated when a directory is renamed in dired

From: Eli Zaretskii <[email-masked]>
Date: Sun, 10 Apr 2022 20:14:33 +0300

> From: Nicolas Martyanoff <[email-masked]>
> Date: Sun, 10 Apr 2022 17:25:40 +0200
> 
> - C-x C-f /tmp/dir1/file
> 
> - C-x C-s
>   Answer 'y' to the prompt to create the directory. At this point the
>   buffer 'file' visits /tmp/dir1/file, and the file exists on the disk.
> 
> - Run dired in /tmp.
> 
> - Rename /tmp/dir1 into /tmp/dir2, e.g. using 'R'.
> 
> - Switch to the 'file' buffer. It still visits /tmp/dir1/file even
>   though the file is now located in /tmp/dir2.

Please try the patch below and see if it solves the problem, including
in other similar use cases.

Tassilo, part of this bug is yet another fallout from replacing
dired-in-this-tree-p with file-in-directory-p: the latter returns nil
if its second arg doesn't exist, and in this scenario we are dealing
with a directory that was already renamed to the new name.  Do you
agree with my proposed solution for that below?  I intend to install
it on the release branch, as this is a regression from Emacs 27.

Thanks.

diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 15f95eb..fc50cce 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -1838,22 +1838,23 @@ dired-rename-file
   "Rename FILE to NEWNAME.
 Signal a `file-already-exists' error if a file NEWNAME already exists
 unless OK-IF-ALREADY-EXISTS is non-nil."
-  (dired-handle-overwrite newname)
-  (dired-maybe-create-dirs (file-name-directory newname))
-  (if (and dired-vc-rename-file
-           (vc-backend file)
-           (ignore-errors (vc-responsible-backend newname)))
-      (vc-rename-file file newname)
-    ;; error is caught in -create-files
-    (rename-file file newname ok-if-already-exists))
-  ;; Silently rename the visited file of any buffer visiting this file.
-  (and (get-file-buffer file)
-       (with-current-buffer (get-file-buffer file)
-	 (set-visited-file-name newname nil t)))
-  (dired-remove-file file)
-  ;; See if it's an inserted subdir, and rename that, too.
-  (when (file-directory-p file)
-    (dired-rename-subdir file newname)))
+  (let ((file-is-dir-p (file-directory-p file)))
+    (dired-handle-overwrite newname)
+    (dired-maybe-create-dirs (file-name-directory newname))
+    (if (and dired-vc-rename-file
+             (vc-backend file)
+             (ignore-errors (vc-responsible-backend newname)))
+        (vc-rename-file file newname)
+      ;; error is caught in -create-files
+      (rename-file file newname ok-if-already-exists))
+    ;; Silently rename the visited file of any buffer visiting this file.
+    (and (get-file-buffer file)
+         (with-current-buffer (get-file-buffer file)
+	   (set-visited-file-name newname nil t)))
+    (dired-remove-file file)
+    ;; See if it's an inserted subdir, and rename that, too.
+    (when file-is-dir-p
+      (dired-rename-subdir file newname))))
 
 (defun dired-rename-subdir (from-dir to-dir)
   (setq from-dir (file-name-as-directory from-dir)
@@ -1866,7 +1867,13 @@ dired-rename-subdir
     (while blist
       (with-current-buffer (car blist)
 	(if (and buffer-file-name
-		 (file-in-directory-p buffer-file-name expanded-from-dir))
+                 ;; If FROM-DIR was renamed/removed, fall back to
+                 ;; 'dired-in-this-tree-p', since
+                 ;; 'file-in-directory-p' will punt.
+                 (or (and (file-exists-p expanded-from-dir)
+		          (file-in-directory-p buffer-file-name
+                                               expanded-from-dir))
+                     (dired-in-this-tree-p buffer-file-name expanded-from-dir)))
 	    (let ((modflag (buffer-modified-p))
 		  (to-file (replace-regexp-in-string
 			    (concat "^" (regexp-quote from-dir))
@@ -1885,7 +1892,8 @@ dired-rename-subdir-1
     (while alist
       (setq elt (car alist)
 	    alist (cdr alist))
-      (if (file-in-directory-p (car elt) expanded-dir)
+      (if (or (file-in-directory-p (car elt) expanded-dir)
+              (dired-in-this-tree-p (car elt) expanded-dir))
 	  ;; ELT's subdir is affected by the rename
 	  (dired-rename-subdir-2 elt dir to)))
     (if (equal dir default-directory)