summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2024-09-30 21:08:38 +0800
committerSean Whitton <spwhitton@spwhitton.name>2024-10-01 08:56:59 +0800
commitb64f69f6dcc08e5aeea0464f6d8b560ed7002d36 (patch)
treea76e63f2abbc3790af79d090ebf3b3c703165b03
parentf95d4208b6bcfbbf95c956d8a66faa92aa5e4965 (diff)
New command diff-delete-other-hunks
* lisp/vc/diff-mode.el (diff-delete-other-hunks): New command (bug#73387). (diff-mode-map): Bind the new command to C-c RET n. (diff-mode-menu): New entry for the new command. (vc-next-action): Stop, and warn, if the user attempts to commit a patch from a narrowed buffer (bug#73387). * doc/emacs/files.texi (Diff Mode): * etc/NEWS: Document the new command.
-rw-r--r--doc/emacs/files.texi5
-rw-r--r--etc/NEWS7
-rw-r--r--lisp/vc/diff-mode.el34
-rw-r--r--lisp/vc/vc.el11
4 files changed, 57 insertions, 0 deletions
diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi
index a3a8c854aa6..67a1a3be3ff 100644
--- a/doc/emacs/files.texi
+++ b/doc/emacs/files.texi
@@ -1703,6 +1703,11 @@ confirm you really want to revert and kill the hunk. You can customize
Apply all the hunks in the buffer (@code{diff-apply-buffer}). If the
diffs were applied successfully, save the changed buffers.
+@findex diff-delete-other-hunks
+@item C-c @key{RET} n
+Delete all hunks other than the current hunk. If the region is active,
+then delete all hunks that the region does not overlap.
+
@findex diff-refine-hunk
@item C-c C-b
Highlight the changes of the hunk at point with a finer granularity
diff --git a/etc/NEWS b/etc/NEWS
index 37568ffdbea..bc1d0cf52c8 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -382,6 +382,13 @@ Previously, 'diff-file-prev' and 'diff-hunk-prev' would move when point
is after the corresponding file or hunk header, but not when inside it.
Now they will always move to the start of the current header.
++++
+*** New command 'diff-delete-other-hunks' bound to C-c RET n.
+This command deletes all hunks other than the current hunk. It is
+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.
+
** php-ts-mode
---
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index 25c6238765d..948d89c579e 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -220,6 +220,7 @@ The default \"-b\" means to ignore whitespace-only changes,
"C-c C-a" #'diff-apply-hunk
"C-c M-r" #'diff-revert-and-kill-hunk
"C-c C-m a" #'diff-apply-buffer
+ "C-c C-m n" #'diff-delete-other-hunks
"C-c C-e" #'diff-ediff-patch
"C-c C-n" #'diff-restrict-view
"C-c C-s" #'diff-split-hunk
@@ -278,6 +279,8 @@ The default \"-b\" means to ignore whitespace-only changes,
:help "Kill current hunk"]
["Kill current file's hunks" diff-file-kill
:help "Kill all current file's hunks"]
+ ["Delete other hunks" diff-delete-other-hunks
+ :help "Delete hunks other than the current hunk"]
"-----"
["Previous Hunk" diff-hunk-prev
:help "Go to the previous count'th hunk"]
@@ -814,6 +817,37 @@ If the prefix ARG is given, restrict the view to the current file instead."
(goto-char (car bounds))
(ignore-errors (diff-beginning-of-hunk t)))))
+;; This is not `diff-kill-other-hunks' because we might need to make
+;; copies of file headers in order to ensure the new kill ring entry
+;; would be a patch with the same meaning. That is not implemented
+;; because it does not seem like it would be useful.
+(defun diff-delete-other-hunks (&optional beg end)
+ "Delete all hunks other than the current hunk.
+Interactively, if the region is active, then delete all hunks that the
+region does not overlap. When called from Lisp, the optional arguments
+BEG and END specify the region of hunks not to delete."
+ (interactive (list (use-region-beginning) (use-region-end)))
+ (when (buffer-narrowed-p)
+ (user-error "Command is not safe in a narrowed buffer"))
+ (let ((inhibit-read-only t))
+ (save-excursion
+ (cond ((xor beg end)
+ (error "Require exactly zero or two arguments"))
+ (beg
+ (goto-char beg)
+ (setq beg (car (diff-bounds-of-hunk)))
+ (goto-char end)
+ (setq end (cadr (diff-bounds-of-hunk))))
+ (t
+ (pcase-setq `(,beg ,end) (diff-bounds-of-hunk))))
+ (delete-region end (point-max))
+ (goto-char beg)
+ (diff-beginning-of-file)
+ (diff-hunk-next)
+ (delete-region (point) beg)
+ (diff-beginning-of-file-and-junk)
+ (delete-region (point-min) (point)))))
+
(defun diff-beginning-of-file-and-junk ()
"Go to the beginning of file-related diff-info.
This is like `diff-beginning-of-file' except it tries to skip back over leading
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 597a1622f5a..fce98d091bf 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -1302,6 +1302,17 @@ from which to check out the file(s)."
;; Fileset comes from a diff-mode buffer, see
;; 'diff-vc-deduce-fileset', and the buffer is the patch to apply.
((eq model 'patch)
+ (when (buffer-narrowed-p)
+ ;; If user used `diff-restrict-view' then we may not have the
+ ;; file header, and the commit will not succeed (bug#73387).
+ (user-error "Cannot commit patch when narrowed; consider %s"
+ (mapconcat (lambda (c)
+ (key-description
+ (where-is-internal c nil t)))
+ '(widen
+ diff-delete-other-hunks
+ vc-next-action)
+ " ")))
(vc-checkin files backend nil nil nil (buffer-string)))
((or (null state) (eq state 'unregistered))
(cond (verbose