master e0327f34543: VC Dir: don't mark directories with marked files (bug#81277)

Stephen Berman via Mailing list for Emacs changes <[email protected]>
Newsgroups gmane.emacs.diffs
Message-ID <[email protected]>
branch: master
commit e0327f345433110eac99fc9bda5ea4973c973d34
Author: Stephen Berman <[email protected]>
Commit: Stephen Berman <[email protected]>

    VC Dir: don't mark directories with marked files (bug#81277)
    
    * lisp/vc/vc-dir.el (vc-dir-mark-all-files): When called on a
    directory entry that is marked, remove the mark.
    (vc-dir-unmark-file): When called on a file, at least one of whose
    ancestors (i.e. containing directory entries) is marked, unmark
    the nearest such ancestor and mark all descendents of that
    directory that are file entries, except the file entry on which
    the command is called, and leave the descendents that are
    directory entries unmarked.
    
    * test/lisp/vc/vc-tests/vc-test-misc.el (vc-test-vc-dir-next/previous)
    (vc-test-vc-dir-mark/unmark-all-dir-entry): Add bug number reference.
    (vc-test-vc-dir-mark-all-with-marked-directory): New test.
    (vc-test--vc-dir-unmark-file): New function.
    (vc-test-vc-dir-unmark-file-with-marked-directory): Use it in this
    new test.
---
 lisp/vc/vc-dir.el                     |  14 ++++-
 test/lisp/vc/vc-tests/vc-test-misc.el | 101 +++++++++++++++++++++++++++++++++-
 2 files changed, 110 insertions(+), 5 deletions(-)

diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el
index fc9f34e0bf8..8f351f65c7f 100644
--- a/lisp/vc/vc-dir.el
+++ b/lisp/vc/vc-dir.el
@@ -928,6 +928,11 @@ share the same state."
       (if (vc-dir-fileinfo->directory data)
 	  ;; It's a directory, mark child files.
 	  (let (crt-data)
+            ;; First, if the directory itself is marked, unmark it,
+            ;; since we don't allow both a directory and its children to
+            ;; be marked.
+            (setf (vc-dir-fileinfo->marked data) nil)
+	    (ewoc-invalidate vc-ewoc crt)
 	    (while (and (setq crt (ewoc-next vc-ewoc crt))
 			(setq crt-data (ewoc-data crt))
 			(not (vc-dir-fileinfo->directory crt-data)))
@@ -1027,9 +1032,12 @@ Replace mark on `%s' with marks on all subitems but this one?"
               (setf (vc-dir-fileinfo->marked (ewoc-data parent)) nil)
               (push parent to-inval)
               (dolist (child all-children)
-                (setf (vc-dir-fileinfo->marked (ewoc-data child))
-                      (not (memq child subtree)))
-                (push child to-inval))))
+                (let ((data (ewoc-data child)))
+                  ;; Mark only file children, not directory children.
+                  (unless (vc-dir-fileinfo->directory data)
+                    (setf (vc-dir-fileinfo->marked data)
+                          (not (memq child subtree)))
+                    (push child to-inval))))))
         ;; The current item is a directory that's not marked, implicitly
         ;; or explicitly, but it has marked items below it.
         ;; Offer to unmark those.
diff --git a/test/lisp/vc/vc-tests/vc-test-misc.el b/test/lisp/vc/vc-tests/vc-test-misc.el
index 049cef3c253..32c6b4d765d 100644
--- a/test/lisp/vc/vc-tests/vc-test-misc.el
+++ b/test/lisp/vc/vc-tests/vc-test-misc.el
@@ -298,7 +298,7 @@ See bug#80803 and bug#80967."
                 (should (equal (vc-dir-fileinfo->state data)
                                'edited))))))))))
 
-(ert-deftest vc-test-vc-dir-next/previous ()
+(ert-deftest vc-test-vc-dir-next/previous () ; bug#81248
   "Test navigating with `vc-dir-{next,previous}-{line,directory}'."
   (skip-unless (executable-find vc-git-program))
   (vc-test--with-author-identity 'Git
@@ -358,7 +358,7 @@ See bug#80803 and bug#80967."
           (should (and (looking-at "\\./$") (looking-back "^ +")))
           (kill-buffer vc-dir-buf))))))
 
-(ert-deftest vc-test-vc-dir-mark/unmark-all-dir-entry ()
+(ert-deftest vc-test-vc-dir-mark/unmark-all-dir-entry () ; bug#81249
   "Test `vc-dir-{un}mark-all' called on directory entry."
   (skip-unless (executable-find vc-git-program))
   (vc-test--with-author-identity 'Git
@@ -426,5 +426,102 @@ See bug#80803 and bug#80967."
               (should (seq-set-equal-p (vc-dir-marked-files) dir-children))))
           (kill-buffer vc-dir-buf))))))
 
+(ert-deftest vc-test-vc-dir-mark-all-with-marked-directory () ; bug#81277
+  "Test `vc-dir-mark-all' called on a marked directory entry."
+  (skip-unless (executable-find vc-git-program))
+  (vc-test--with-author-identity 'Git
+    (let ((vc-handled-backends '(Git)))
+      (ert-with-temp-directory tempdir
+        (let ((default-directory tempdir)
+              (files '("dir1/file11" "dir1/file12"))
+              vc-dir-buf
+              dir-children)
+          (vc-test--create-repo-function 'Git)
+          (dolist (file files)
+            (make-empty-file file t))
+          (vc-dir default-directory 'Git)
+          (while (vc-dir-busy) (sit-for 0.05))
+          (setq vc-dir-buf (current-buffer))
+          ;; Move point to "dir1/" entry line.
+          (goto-char (ewoc-location (ewoc-nth vc-ewoc 1)))
+          ;; Mark "dir1/".
+          (vc-dir-mark-file)
+          ;; Move back to "dir1/" entry.
+          (vc-dir-previous-line 1)
+          ;; Test that it's marked.
+          (should (vc-dir-fileinfo->marked (ewoc-data (ewoc-locate vc-ewoc))))
+          (vc-dir-mark-all-files nil)
+          ;; Now it should have been unmarked.
+          (should-not
+           (vc-dir-fileinfo->marked (ewoc-data (ewoc-locate vc-ewoc))))
+          ;; All its children should be marked.
+          (let ((dir-children (vc-dir-find-child-files
+                               (expand-file-name
+                                (vc-dir-fileinfo->name
+                                 (ewoc-data (ewoc-nth vc-ewoc 1)))))))
+            (should (seq-set-equal-p (vc-dir-marked-files) dir-children)))
+          (kill-buffer vc-dir-buf))))))
+
+(defun vc-test--vc-dir-unmark-file ()
+  "Execute `vc-dir-unmark-file' assuming \"y\" at `y-or-n-p' prompt."
+  (cl-letf (((symbol-function 'y-or-n-p)
+             (lambda (_prompt) t)))
+    (vc-dir-unmark-file)))
+
+(ert-deftest vc-test-vc-dir-unmark-file-with-marked-directory () ; bug#81277
+  "Test `vc-dir-unmark-file' with a marked ancestor directory."
+  (skip-unless (executable-find vc-git-program))
+  (vc-test--with-author-identity 'Git
+    (let ((vc-handled-backends '(Git)))
+      (ert-with-temp-directory tempdir
+        (let ((default-directory tempdir)
+              (files '("dir1/file11" "dir1/file12"
+                       "dir1/dir2/file21" "dir1/dir2/file22"
+                       "dir1/dir3/file31" "dir1/dir3/file32"))
+              vc-dir-buf unmarked directories)
+          (vc-test--create-repo-function 'Git)
+          (dolist (file files)
+            (make-empty-file file t))
+          (vc-dir default-directory 'Git)
+          (while (vc-dir-busy) (sit-for 0.05))
+          (setq vc-dir-buf (current-buffer))
+          ;; Move point to "dir1/" entry line.
+          (goto-char (ewoc-location (ewoc-nth vc-ewoc 1)))
+          ;; Mark "dir1/".
+          (vc-dir-mark-file)
+          ;; On next entry simulate invoking `vc-dir-unmark-file' and
+          ;; answering "y" to `y-or-n-p' prompt.
+          (vc-test--vc-dir-unmark-file)
+          ;; Move back to that entry and test that it's unmarked.
+          (vc-dir-previous-line 1)
+          (should-not
+           (vc-dir-fileinfo->marked (ewoc-data (ewoc-locate vc-ewoc))))
+          (push (expand-file-name
+                 (vc-dir-fileinfo->name
+                  (ewoc-data (ewoc-locate vc-ewoc))))
+                unmarked)
+          ;; All other non-directory descendents of "dir1/" should be
+          ;; marked.
+          (let* ((dir-children (vc-dir-find-child-files
+                                (expand-file-name
+                                 (vc-dir-fileinfo->name
+                                  (ewoc-data (ewoc-nth vc-ewoc 1))))))
+                 (rest-children (seq-difference dir-children unmarked)))
+            (should (seq-set-equal-p (vc-dir-marked-files) rest-children)))
+          ;; All directory entries should be unmarked.
+          (goto-char (point-max))
+          (while (not (bobp))
+            (vc-dir-previous-directory)
+            (push (expand-file-name
+                   (vc-dir-fileinfo->name
+                    (ewoc-data (ewoc-locate vc-ewoc))))
+                  directories)
+            (when (equal
+                   (vc-dir-fileinfo->name (ewoc-data (ewoc-locate vc-ewoc)))
+                   (vc-dir-fileinfo->name (ewoc-data (ewoc-nth vc-ewoc 1))))
+              (goto-char (point-min))))
+          (should-not (seq-intersection directories (vc-dir-marked-files)))
+          (kill-buffer vc-dir-buf))))))
+
 (provide 'vc-test-misc)
 ;;; vc-test-misc.el ends here
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.