emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 2d139141a6c: Support file creation and deletion in diff-apply-hun


From: Dmitry Gutov
Subject: master 2d139141a6c: Support file creation and deletion in diff-apply-hunk
Date: Mon, 7 Oct 2024 19:26:05 -0400 (EDT)

branch: master
commit 2d139141a6c249691a50ade9c8d08dc35fcdf456
Author: Dmitry Gutov <dmitry@gutov.dev>
Commit: Dmitry Gutov <dmitry@gutov.dev>

    Support file creation and deletion in diff-apply-hunk
    
    * lisp/vc/diff-mode.el (diff-find-file-name): Allow entering
    non-existing file name when the corresponding hunk is of type
    "create file" (bug#62731).  Default to file name with deleted
    prefix if diff-buffer-type is Git or Hg.  Make sure not to add
    such input to diff-remembered-files-alist, it would be hard to
    change otherwise in case of typo.
    (diff-setup-buffer-type):
    Match against the diff header common to 'hg diff' output.
    (diff-find-source-location): Look at the other source when the
    buffer is applied in reverse.
    (diff-apply-hunk): Delect file deletion and pass a different
    argument to 'diff-find-source-location' in such case.  Bind
    diff-vc-backend to nil to avoid older revision buffer being
    returned.  In the end, offer to delete the file if the hunk was of
    corresponding type and matched the existing contents.
    
    * etc/NEWS: Mention the new capability.
---
 etc/NEWS             |  2 ++
 lisp/vc/diff-mode.el | 37 +++++++++++++++++++++++++++++--------
 2 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index b5104145878..d5f48ae4391 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -424,6 +424,8 @@ useful to prepare a *vc-diff* buffer for committing a 
single hunk.
 When the region is active, it deletes all hunks that the region does not
 overlap.
 
+*** 'diff-apply-hunk' now supports creating and deleting files.
+
 ** php-ts-mode
 
 ---
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index d085c721bc8..cfa90d380ad 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -1091,13 +1091,24 @@ PREFIX is only used internally: don't use it."
              (diff-find-file-name old noprompt (match-string 1)))
          ;; if all else fails, ask the user
          (unless noprompt
-           (let ((file (expand-file-name (or (car fs) ""))))
+           (let ((file (or (car fs) ""))
+                 (creation (equal null-device
+                                  (car (diff-hunk-file-names (not old))))))
+             (when (and (memq diff-buffer-type '(git hg))
+                        (string-match "/" file))
+               ;; Strip the dst prefix (like b/) if diff is from Git/Hg.
+               (setq file (substring file (match-end 0))))
+             (setq file (expand-file-name file))
             (setq file
                   (read-file-name (format "Use file %s: " file)
-                                  (file-name-directory file) file t
+                                  (file-name-directory file) file
+                                   ;; Allow non-matching for creation.
+                                   (not creation)
                                   (file-name-nondirectory file)))
-             (setq-local diff-remembered-files-alist
-                         (cons (cons fs file) diff-remembered-files-alist))
+             (when (or (not creation) (file-exists-p file))
+               ;; Only remember files that exist. User might have mistyped.
+               (setq-local diff-remembered-files-alist
+                           (cons (cons fs file) diff-remembered-files-alist)))
              file)))))))
 
 
@@ -1647,7 +1658,9 @@ modified lines of the diff."
     (setq-local diff-buffer-type
                 (if (re-search-forward "^diff --git" nil t)
                     'git
-                  nil)))
+                  (if (re-search-forward "^diff -r.*-r" nil t)
+                      'hg
+                    nil))))
   (when (eq diff-buffer-type 'git)
     (setq diff-outline-regexp
           (concat "\\(^diff --git.*\\|" diff-hunk-header-re "\\)")))
@@ -1957,7 +1970,7 @@ SWITCHED is non-nil if the patch is already applied."
                                 diff-context-mid-hunk-header-re nil t)
                         (error "Can't find the hunk separator"))
                       (match-string 1)))))
-          (file (or (diff-find-file-name other noprompt)
+          (file (or (diff-find-file-name (xor other reverse) noprompt)
                      (error "Can't find the file")))
           (revision (and other diff-vc-backend
                           (if reverse (nth 1 diff-vc-revisions)
@@ -2020,7 +2033,11 @@ the value of this variable when given an appropriate 
prefix argument).
 With a prefix argument, REVERSE the hunk."
   (interactive "P")
   (diff-beginning-of-hunk t)
-  (pcase-let ((`(,buf ,line-offset ,pos ,old ,new ,switched)
+  (pcase-let* (;; Do not accept BUFFER.REV buffers as source location.
+               (diff-vc-backend nil)
+               ;; When we detect deletion, we will use the old file name.
+               (deletion (equal null-device (car (diff-hunk-file-names 
reverse))))
+               (`(,buf ,line-offset ,pos ,old ,new ,switched)
                ;; Sometimes we'd like to have the following behavior: if
                ;; REVERSE go to the new file, otherwise go to the old.
                ;; But that means that by default we use the old file, which is
@@ -2030,7 +2047,7 @@ With a prefix argument, REVERSE the hunk."
                ;; TODO: make it possible to ask explicitly for this behavior.
                ;;
                ;; This is duplicated in diff-test-hunk.
-               (diff-find-source-location nil reverse)))
+               (diff-find-source-location deletion reverse)))
     (cond
      ((null line-offset)
       (user-error "Can't find the text to patch"))
@@ -2056,6 +2073,10 @@ With a prefix argument, REVERSE the hunk."
                       "Hunk hasn't been applied yet; apply it now? "
                     "Hunk has already been applied; undo it? ")))))
       (message "(Nothing done)"))
+     ((and deletion (not switched))
+      (when (y-or-n-p (format-message "Delete file `%s'?" (buffer-file-name 
buf)))
+        (delete-file (buffer-file-name buf) delete-by-moving-to-trash)
+        (kill-buffer buf)))
      (t
       ;; Apply the hunk
       (with-current-buffer buf



reply via email to

[Prev in Thread] Current Thread [Next in Thread]