From b64f69f6dcc08e5aeea0464f6d8b560ed7002d36 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Mon, 30 Sep 2024 21:08:38 +0800 Subject: 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. --- doc/emacs/files.texi | 5 +++++ etc/NEWS | 7 +++++++ lisp/vc/diff-mode.el | 34 ++++++++++++++++++++++++++++++++++ lisp/vc/vc.el | 11 +++++++++++ 4 files changed, 57 insertions(+) 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 -- cgit v1.2.3-70-g09d2