branch: elpa/magit
commit 7ccff374d06df833a03bfd19bbce80e29833d29b
Author: Jonas Bernoulli <[email protected]>
Commit: Jonas Bernoulli <[email protected]>

    magit-file-{untrack,rename,delete}: Support acting on directories
    
    And `magit-do-async-shell-command' too.
    
    Closes #5247.
---
 docs/CHANGELOG.4    |  3 +++
 lisp/magit-dired.el |  2 +-
 lisp/magit-files.el | 35 +++++++++++++++++++++++++----------
 3 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/docs/CHANGELOG.4 b/docs/CHANGELOG.4
index 6eddf40e88..fff3ab8972 100644
--- a/docs/CHANGELOG.4
+++ b/docs/CHANGELOG.4
@@ -28,6 +28,9 @@
 - Added Git v2.55's new ~--graph-lane-limit~ to log menus.  It is hidden
   by default and not available when using an older Git.  #5088
 
+- ~magit-file-untrack~, ~magit-file-rename~, ~magit-file-delete~ and
+  ~magit-do-async-shell-command~ can now act on directories.  #5247
+
 * v4.6.0    2026-07-01
 
 The primary focus of this release are blob-visiting buffers.  For
diff --git a/lisp/magit-dired.el b/lisp/magit-dired.el
index b00ee88a1d..85f58d1e02 100644
--- a/lisp/magit-dired.el
+++ b/lisp/magit-dired.el
@@ -102,7 +102,7 @@ for a repository."
   "Open FILE with `dired-do-async-shell-command'.
 Interactively, open the file at point."
   (interactive (list (or (magit-file-at-point)
-                         (magit-read-file "Act on file"))))
+                         (magit-read-file "Act on file" nil t))))
   (require 'dired-aux)
   (dired-do-async-shell-command
    (dired-read-shell-command "& on %s: " current-prefix-arg (list file))
diff --git a/lisp/magit-files.el b/lisp/magit-files.el
index 5087470fbe..1abc036a27 100644
--- a/lisp/magit-files.el
+++ b/lisp/magit-files.el
@@ -638,7 +638,7 @@ staged as well as unstaged changes."
                              (if (magit-file-tracked-p (car files))
                                  (magit-confirm-files 'untrack files "Untrack")
                                (user-error "Already untracked"))
-                           (list (magit-read-tracked-file "Untrack file"))))
+                           (list (magit-read-tracked-file "Untrack file" t))))
                      current-prefix-arg))
   (magit-with-toplevel
     (magit-run-git "rm" "--cached" (and force "--force") "--" files)))
@@ -649,7 +649,7 @@ staged as well as unstaged changes."
 NEWNAME may be a file or directory name.  If FILE isn't tracked in
 Git, fallback to using `rename-file'."
   (interactive
-    (let* ((file (magit-read-file "Rename file"))
+    (let* ((file (magit-read-file "Rename file" nil t))
            (path (expand-file-name file (magit-toplevel))))
       (list path (expand-file-name
                   (read-file-name (format "Move %s to destination: " file)
@@ -689,13 +689,16 @@ uncommitted changes.  When the files aren't being tracked 
in
 Git, then fallback to using `delete-file'."
   (interactive (list (if-let ((files (magit-region-values 'file t)))
                          (magit-confirm-files 'delete files "Delete")
-                       (list (magit-read-file "Delete file")))
+                       (list (magit-read-file "Delete file" nil t)))
                      current-prefix-arg))
   (if (magit-file-tracked-p (car files))
       (magit-call-git "rm" (and force "--force") "--" files)
     (let ((topdir (magit-toplevel)))
       (dolist (file files)
-        (delete-file (expand-file-name file topdir) t))))
+        (setq file (expand-file-name file topdir))
+        (if (file-directory-p file)
+            (delete-directory file t t)
+          (delete-file file t)))))
   (magit-refresh))
 
 ;;;###autoload
@@ -721,18 +724,30 @@ Git, then fallback to using `delete-file'."
      prompt files nil t nil 'magit-read-file-hist
      (car (member (or default (magit-current-file)) files)))))
 
-(defun magit-read-file (prompt &optional tracked-only)
-  (let ((choices (nconc (magit-list-files)
-                        (and (not tracked-only)
-                             (magit-untracked-files)))))
+(defun magit-read-file (prompt &optional tracked-only include-dirs)
+  (let* ((files (nconc (magit-list-files)
+                       (and (not tracked-only)
+                            (magit-untracked-files))))
+         (choices
+          (if include-dirs
+              (let (dirs)
+                (dolist (file files)
+                  (while (and file
+                              (setq file (file-name-parent-directory file))
+                              (not (equal file "./")))
+                    (if (member file dirs)
+                        (setq file nil)
+                      (push file dirs))))
+                (sort (nconc dirs files) #'string<))
+            files)))
     (magit-completing-read
      prompt choices nil t nil nil
      (car (member (or (magit-section-value-if '(file submodule))
                       (magit-file-relative-name nil tracked-only))
                   choices)))))
 
-(defun magit-read-tracked-file (prompt)
-  (magit-read-file prompt t))
+(defun magit-read-tracked-file (prompt &optional include-dirs)
+  (magit-read-file prompt t include-dirs))
 
 (defun magit-read-unmerged-file (&optional prompt)
   (let ((current  (magit-current-file))

Reply via email to