]> git.eshelyaron.com Git - emacs.git/commitdiff
Support file creation and deletion in diff-apply-hunk
authorDmitry Gutov <dmitry@gutov.dev>
Mon, 7 Oct 2024 23:25:14 +0000 (02:25 +0300)
committerEshel Yaron <me@eshelyaron.com>
Mon, 14 Oct 2024 17:34:47 +0000 (19:34 +0200)
* 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.

(cherry picked from commit 2d139141a6c249691a50ade9c8d08dc35fcdf456)

etc/NEWS
lisp/vc/diff-mode.el

index b55db508078640bec7d52cba1857fcb4665d701c..b6ec3a09e2389e59dc56de5cbc8e8c121cc405f3 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -378,6 +378,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
 
 ---
index cca8ce2be4efe4d77fc152e064603662748f5ce1..71754aa1ca9cfb74d36a4f4cf92cb27d2ab811f4 100644 (file)
@@ -1090,13 +1090,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)))))))
 
 
@@ -1646,7 +1657,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 "\\)")))
@@ -1956,7 +1969,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)
@@ -2026,7 +2039,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
@@ -2036,7 +2053,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"))
@@ -2062,6 +2079,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
       (run-hook-with-args 'diff-apply-hunk-functions