From 2e8cc345d5bdc96d0b9fd3cbf7fc8f502604f96e Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 5 Nov 2025 16:38:48 +0000 Subject: [PATCH] VC revert commands: Facilities to entirely delete revisions * lisp/vc/vc.el (vc-revision-revert, vc-revision-cherry-pick): Rename to ... (vc-revert-or-delete-revision, vc-cherry-pick): ... these (bug#79408). All uses changed. * lisp/vc/log-view.el (log-view-revision-revert) (log-view-revision-cherry-pick): Rename to ... (log-view-revert-or-delete-revisions, log-view-cherry-pick): ... these. All uses changed. * lisp/vc/log-view.el (log-view-revert-or-delete-revisions): * lisp/vc/vc.el (vc-revert-or-delete-revision): New INTERACTIVE and DELETE parameters, and prefix argument. Offer to entirely delete REV in certain circumstances (bug#79408). * lisp/vc/log-view.el (log-view--pick-or-revert): * lisp/vc/vc.el (vc--pick-or-revert): New INTERACTIVE and DELETE parameters. All uses changes. * lisp/vc/log-view.el (log-view-revert-revisions) (log-view-delete-revisions): * lisp/vc/vc.el (vc-revert-revision, vc-delete-revision): New commands (bug#79408). * doc/emacs/maintaining.texi (VC Change Log, VC Undo) (Copying Between Branches): * etc/NEWS: Document the changes. --- doc/emacs/maintaining.texi | 83 ++++++++++------ etc/NEWS | 9 +- lisp/vc/log-view.el | 147 ++++++++++++++++++++-------- lisp/vc/vc-hooks.el | 8 +- lisp/vc/vc.el | 190 +++++++++++++++++++++++++++++++++---- 5 files changed, 343 insertions(+), 94 deletions(-) diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi index 197c26410e8..147857aa836 100644 --- a/doc/emacs/maintaining.texi +++ b/doc/emacs/maintaining.texi @@ -1268,11 +1268,11 @@ Unmark all marked entries (@code{log-view-unmark-all-entries}). @item C Copy changes to a currently checked out branch; either the changes from the revision at point, or the changes from all marked revisions -(@code{log-view-revision-cherry-pick}). +(@code{log-view-cherry-pick}). @item R Undo the effects of old revisions; either the revision at point, or all -marked revisions (@code{log-view-revision-revert}). +marked revisions (@code{log-view-revert-or-delete-revisions}). @end table @vindex vc-log-show-limit @@ -1317,8 +1317,14 @@ also prompt for a specific VCS shell command to run for this purpose. Revert the work file(s) in the current VC fileset to the last revision (@code{vc-revert}). -@item M-x vc-revision-revert +@item M-x vc-revert-or-delete-revision Undo the effects of an older commit. + +@item M-x vc-revert-revision +Make a new commit which undoes the changes made by an older one. + +@item M-x vc-delete-revision +Delete an unpushed commit from the revision history. @end table @kindex C-x v u @@ -1342,20 +1348,37 @@ unlocked; you must lock again to resume editing. You can also use @kbd{C-x v u} to unlock a file if you lock it and then decide not to change it. -@findex vc-revision-revert +@findex vc-revert-or-delete-revision @cindex reverting commits To discard changes that have already been committed, by yourself or -someone else, you can use @w{@kbd{M-x vc-revision-revert}}. This is -called @dfn{reverting} a commit. The command prompts for a revision to -revert, and then the VC backend reverts it. Most backends implement -this by making a new commit which undoes the changes made by the -revision. +someone else, you can use @w{@kbd{M-x vc-revert-or-delete-revision}}. +This is called @dfn{reverting} a commit. The command prompts for a +revision to revert, and then the VC backend reverts it. - An alternative way to access this functionality is to the -@code{log-view-revision-revert} command, bound to @kbd{R} in Log View -mode buffers (@pxref{VC Change Log}). Compared to using @w{@kbd{M-x -vc-revision revert}} directly, this can make it easier to be sure you -are reverting the revision you intend. + Most backends implement this by making a new commit which undoes the +changes made by the revision. For a distributed VCS, if the commit is +one that you made and have not yet pushed, Emacs will offer to delete it +entirely, instead. With a prefix argument, this command will only try +to entirely delete the revision, failing if it has already been pushed. + + An alternative way to access this functionality is the +@code{log-view-revert-or-delete-revisions} command, bound to @kbd{R} in +Log View mode buffers (@pxref{VC Change Log}). Compared to using +@w{@kbd{M-x vc-revision revert}} directly, this can make it easier to be +sure you are reverting the revision you intend. + +@findex vc-revert-revision +@findex vc-delete-revision + More specific, specialized commands are @w{@kbd{M-x +vc-revert-revision}} and @w{@kbd{M-x vc-delete-revision}}. The first of +these prompts for a revision and then always makes a new commit which +undoes the changes made by that revision, regardless of the VCS in use +and whether or not the revision is pushed. The second command prompts +for a revision and then always deletes it, though it will stop if the +commit has been pushed. Usually @code{vc-revert-or-delete-revision} is +sufficient, but @code{vc-revert-revision} and @code{vc-delete-revision} +can sometimes be useful for constructing particular version control +histories. @node VC Ignore @subsection Ignore Version Control Files @@ -1889,7 +1912,7 @@ different revision with @kbd{C-u C-x v v}. @subsubsection Copying Changes Made By Revisions Between Branches @table @kbd -@item M-x vc-revision-cherry-pick +@item M-x vc-cherry-pick Copy a single revision to branch checked out in this working tree. @end table @@ -1910,25 +1933,25 @@ can then cherry-pick that revision onto the feature-frozen branch in order to fix the bug there, too. This is called @dfn{backporting} the revision, or backporting the fix. -@findex vc-revision-cherry-pick - You can use the command @kbd{M-x vc-revision-cherry-pick} to -cherry-pick revisions. It prompts for a revision to cherry-pick. It -then pops up a buffer for you to edit the log message for the new -revision. Normally the VC backend generates a log message including a -reference to the revision you want to copy, so that the copy can be -traced. If you wish, you can delete this reference before typing -@kbd{C-c C-c} to conclude the cherry-pick. +@findex vc-cherry-pick + You can use the command @kbd{M-x vc-cherry-pick} to cherry-pick +revisions. It prompts for a revision to cherry-pick. It then pops up a +buffer for you to edit the log message for the new revision. Normally +the VC backend generates a log message including a reference to the +revision you want to copy, so that the copy can be traced. If you wish, +you can delete this reference before typing @kbd{C-c C-c} to conclude +the cherry-pick. Alternatively you can invoke the command with a prefix argument, -i.e. @w{@kbd{C-u M-x vc-revision-cherry-pick}}. In this case the log -message from the source revision is used unmodified, and the cherry-pick -happens immediately, without popping up a buffer for log message edits. +i.e. @w{@kbd{C-u M-x vc-cherry-pick}}. In this case the log message +from the source revision is used unmodified, and the cherry-pick happens +immediately, without popping up a buffer for log message edits. An alternative way to access this functionality is the -@code{log-view-revision-cherry-pick} command, bound to @kbd{C} in Log -View mode buffers (@pxref{VC Change Log}). Compared to using -@w{@kbd{M-x vc-revision-cherry-pick}} directly, this can make it easier -to be sure you are cherry-picking the revision you intend. +@code{log-view-cherry-pick} command, bound to @kbd{C} in Log View mode +buffers (@pxref{VC Change Log}). Compared to using @w{@kbd{M-x +vc-cherry-pick}} directly, this can make it easier to be sure you are +cherry-picking the revision you intend. @ifnottex @include vc1-xtra.texi diff --git a/etc/NEWS b/etc/NEWS index 1a0589fd23d..35c536d6ee8 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2358,11 +2358,12 @@ the contents of those files. +++ *** New commands to cherry-pick and revert revisions. -The commands 'vc-revision-cherry-pick' and 'vc-revision-revert' let you -copy revisions between branches, and revert revisions. +The commands 'vc-cherry-pick', 'vc-revert-or-delete-revision', +'vc-revert-revision' and 'vc-delete-revision' let you copy revisions +between branches, revert and delete revisions. From Log View buffers, you can use 'C' to cherry-pick the revision at -point or all marked revisions, and 'R' to revert the revision at point -or all marked revisions. +point or all marked revisions, and 'R' to undo the revision at point or +all marked revisions. *** New command 'log-edit-done-strip-cvs-lines'. This command strips all lines beginning with "CVS:" from the buffer. diff --git a/lisp/vc/log-view.el b/lisp/vc/log-view.el index 0e2d506a23a..0419b2c077a 100644 --- a/lisp/vc/log-view.el +++ b/lisp/vc/log-view.el @@ -141,8 +141,8 @@ "w" #'log-view-copy-revision-as-kill "TAB" #'log-view-msg-next "" #'log-view-msg-prev - "C" #'log-view-revision-cherry-pick - "R" #'log-view-revision-revert) + "C" #'log-view-cherry-pick + "R" #'log-view-revert-or-delete-revisions) (easy-menu-define log-view-mode-menu log-view-mode-map "Log-View Display Menu." @@ -165,9 +165,9 @@ ["Toggle Details at Point" log-view-toggle-entry-display :active log-view-expanded-log-entry-function] "-----" - ["Cherry-Pick Revision(s)" log-view-revision-cherry-pick + ["Cherry-Pick Revision(s)" log-view-cherry-pick :help "Copy changes from revision(s) to a branch"] - ["Revert Revision(s)" log-view-revision-revert + ["Revert Revision(s)" log-view-revert-or-delete-revisions :help "Undo the effects of old revision(s)"] "-----" ["Next Log Entry" log-view-msg-next @@ -703,7 +703,8 @@ If called interactively, annotate the version at point." (defvar vc-log-short-style) (declare-function vc-print-log-internal "vc") -(defun log-view--pick-or-revert (directory no-comment reverse) +(defun log-view--pick-or-revert + (directory no-comment reverse interactive delete) "Copy changes from revision at point or all marked revisions. DIRECTORY is the destination, the root of the target working tree. NO-COMMENT non-nil means use the log messages of the revisions @@ -711,12 +712,15 @@ unmodified, instead of using the backend's default cherry-pick comment for that revision. NO-COMMENT non-nil with zero or one revisions marked also means don't prompt to edit the log message. -REVERSE non-nil means to make commit(s) undoing the effects of the -revisions, instead." +REVERSE non-nil means to undo the effects of the revisions, instead. +INTERACTIVE and DELETE are passed on to `vc--pick-or-revert', except +additionally if INTERACTIVE is non-nil and `vc--pick-or-revert' returns +`deleted', pass `no-confirm' to subsequent calls to that function." (let ((default-directory directory) - (marked (log-view-get-marked))) + (marked (log-view-get-marked)) + (buf (current-buffer))) (if (length> marked 1) - (progn + (let ((deleted 0)) (save-excursion (dolist (rev (if reverse (reverse marked) marked)) ;; Unmark each revision *before* copying it. @@ -724,38 +728,48 @@ revisions, instead." ;; fails, after resolving that conflict and committing the ;; cherry-pick, the right revisions will be marked to ;; resume the original multiple cherry-pick operation. + ;; FIXME: This doesn't work so long as the backend + ;; functions just give up completely if there is a + ;; conflict (which behavior is also a FIXME). Then in + ;; fact the user has to mark the revision again. (log-view-goto-rev rev) (log-view-unmark-entry 1) - (vc--pick-or-revert rev - reverse - (if no-comment - (vc-call-backend log-view-vc-backend - 'get-change-comment - nil rev) - t) - nil - log-view-vc-backend))) - (when (vc-find-backend-function log-view-vc-backend - 'modify-change-comment) - (let (vc-log-short-style) - (vc-print-log-internal log-view-vc-backend - (list default-directory) - nil nil (length marked))) - (setq-local vc-log-short-style nil ; For \\`g'. - vc-parent-buffer-name nil) - (message (substitute-command-keys "Use \ -\\[log-view-modify-change-comment] to modify any of these messages")))) + (cl-case + (vc--pick-or-revert rev reverse interactive delete + (if no-comment + (vc-call-backend log-view-vc-backend + 'get-change-comment + nil rev) + t) + nil + log-view-vc-backend) + (deleted (incf deleted) + (when interactive + (setq interactive 'no-confirm)))))) + (let ((new-commits (- (length marked) deleted))) + (when (and (plusp new-commits) + (vc-find-backend-function log-view-vc-backend + 'modify-change-comment)) + (let (vc-log-short-style) + (vc-print-log-internal log-view-vc-backend + (list default-directory) + nil nil new-commits)) + (setq-local vc-log-short-style nil ; For \\`g'. + vc-parent-buffer-name nil) + (message (substitute-command-keys "Use \ +\\[log-view-modify-change-comment] to modify any of these messages"))))) (let ((rev (or (car marked) (log-view-current-tag)))) - (vc--pick-or-revert rev - reverse + (vc--pick-or-revert rev reverse interactive delete (and no-comment (vc-call-backend log-view-vc-backend 'get-change-comment nil rev)) nil - log-view-vc-backend))))) + log-view-vc-backend))) + (when (eq (current-buffer) buf) + (revert-buffer)))) -(defun log-view-revision-cherry-pick (directory &optional no-comment) +(defun log-view-cherry-pick (directory &optional no-comment) "Copy changes from revision at point to current branch. If there are marked revisions, use those instead of the revision at point. @@ -774,31 +788,90 @@ traced. With optional argument NO-COMMENT non-nil (interactively, with a prefix argument), use the log messages from the source revisions unmodified. -See also `vc-revision-cherry-pick'." +See also `vc-cherry-pick'." (interactive (list (vc--prompt-other-working-tree log-view-vc-backend "Cherry-pick to working tree" 'allow-empty) current-prefix-arg)) - (log-view--pick-or-revert directory no-comment nil)) + (log-view--pick-or-revert directory no-comment nil nil nil)) -(defun log-view-revision-revert (directory) +(defun log-view-revert-or-delete-revisions + (directory &optional interactive delete) "Undo the effects of the revision at point. When revisions are marked, undo the effects of each of them. When called interactively, prompts for the target working tree in which to revert; the current working tree is the default choice. When called from Lisp, DIRECTORY is the root of the target working tree. +When called interactively (or with optional argument INTERACTIVE +non-nil), then if the underlying VCS is distributed, offer to entirely +delete revisions that have not been pushed. +This is instead of creating new commits undoing their effects. + +With a prefix argument (or with optional argument DELETE non-nil), +always delete revisions and never create new commits. +In this case INTERACTIVE is ignored. +This works only for unpublished commits, unless you have customized +`vc-allow-rewriting-published-history' to a non-nil value. + When reverting a single revision, prompts for editing the log message for the new commit. When reverting multiple revisions, never prompts to edit log messages. -See also `vc-revision-revert'." +See also `vc-revert-or-delete-revision'." + (interactive (list (vc--prompt-other-working-tree + (vc-responsible-backend default-directory) + (if current-prefix-arg "Delete in working tree" + "Revert in working tree") + 'allow-empty) + t current-prefix-arg)) + (log-view--pick-or-revert directory nil t interactive delete)) + +;; These are left unbound by default. A user who doesn't like the DWIM +;; behavior of `log-view-revert-or-delete-revisions' can unbind that and +;; bind these two commands instead. + +(defun log-view-revert-revisions (directory) + "Make a commit undoing the effects of the revision at point. +When revisions are marked, make such a commit for each of them. +When called interactively, prompts for the target working tree in which +to make new commits; the current working tree is the default choice. +When called from Lisp, DIRECTORY is the root of the target working tree. + +This is like `log-view-revert-or-delete-revisions' except that it only +ever makes new commits undoing the effects of revisions, instead of +considering VCS-specific alternative mechanisms to undo the effects of +revisions. + +When reverting a single revision, prompts for editing the log message +for the new commit. +When reverting multiple revisions, never prompts to edit log messages. + +See also `vc-revert-revision'." (interactive (list (vc--prompt-other-working-tree (vc-responsible-backend default-directory) "Revert in working tree" 'allow-empty))) - (log-view--pick-or-revert directory nil t)) + (log-view--pick-or-revert directory nil t nil 'never)) + +(defun log-view-delete-revisions (directory) + "Delete the revision at point from the revision history. +When revisions are marked, delete all of them. +When called interactively, prompts for the target working tree in which +to delete revisions; the current working tree is the default choice. +When called from Lisp, DIRECTORY is the root of the target working tree. + +This works only for unpublished commits, unless you have customized +`vc-allow-rewriting-published-history' to a non-nil value. + +This is the same as `log-view-revert-or-delete-revisions' invoked +interactively with a prefix argument. See also `vc-delete-revision'." + (interactive (list (vc--prompt-other-working-tree + (vc-responsible-backend default-directory) + "Delete in working tree" + 'allow-empty))) + (log-view--pick-or-revert directory nil t nil t)) ;;;; ;;;; diff diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el index d3c98311358..32b410d5c3b 100644 --- a/lisp/vc/vc-hooks.el +++ b/lisp/vc/vc-hooks.el @@ -1053,11 +1053,11 @@ other commands receive global bindings where they had none before." (defvar vc-menu-map (let ((map (make-sparse-keymap "Version Control"))) - (define-key map [vc-revision-revert] - '(menu-item "Revert Revision" vc-revision-revert + (define-key map [vc-revert-or-delete-revision] + '(menu-item "Revert Revision" vc-revert-or-delete-revision :help "Undo the effects of a revision")) - (define-key map [vc-revision-cherry-pick] - '(menu-item "Cherry-Pick Revision" vc-revision-cherry-pick + (define-key map [vc-cherry-pick] + '(menu-item "Cherry-Pick Revision" vc-cherry-pick :help "Copy the changes from a single revision to this branch")) (define-key map [separator1] menu-bar-separator) (define-key map [vc-retrieve-tag] diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 4a505b2de19..ae0f6378d33 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -2166,22 +2166,121 @@ have changed; continue with old fileset?" (current-buffer)))) (declare-function diff-buffer-file-names "diff-mode") (declare-function diff-reverse-direction "diff-mode") -(defun vc--pick-or-revert (rev reverse comment initial-contents backend) +(defun vc--pick-or-revert + (rev reverse interactive delete comment initial-contents backend) "Copy a single revision REV to branch checked out in this working tree. -REVERSE means to undo the effects of REV, instead. + +REVERSE non-nil means to undo the effects of REV, instead. +This is affected by whether the VCS is centralized or distributed and +the INTERACTIVE and DELETE arguments, as follows: +- For a centralized VCS for which Emacs knows how to do true undos, then + unless DELETE is the special value `never', do a true undo of REV. + This function supports creating new commits undoing the effects of REV + for even a centralized VCS with true undos by passing `never' as + DELETE (as `vc-revert-revision' does). + For centralized VCS, INTERACTIVE is ignored. +- For a distributed VCS, when INTERACTIVE is non-nil, DELETE is nil, and + REV has not yet been pushed, offer to delete REV entirely instead of + creating a new commit undoing its EFFECTS. + If INTERACTIVE is `no-confirm', don't prompt to confirm the deletion. +- For a distributed VCS, when DELETE is non-nil (but not `never'), only + consider deleting REV, never create a new commit, but subject to + `vc-allow-rewriting-published-history'. + In this case INTERACTIVE is ignored. +(This complex calling convention makes for simple usage of this +workhorse function from the frontend VC commands that provide access to +all this functionality.) + COMMENT is a comment string; if omitted, a buffer is popped up to accept a comment. If INITIAL-CONTENTS is non-nil, then COMMENT is used as the initial contents of the log entry buffer. If COMMENT is t then use BACKEND's default cherry-pick comment for REV without prompting. -BACKEND is the VC backend to use." - (let* ((backend (or backend (vc-responsible-backend default-directory))) - ;; `vc-*-prepare-patch' will always give us a patch with file - ;; names relative to the VC root, so switch to there now. - ;; In particular this is needed for `diff-buffer-file-names' to - ;; work properly. - (default-directory (vc-call-backend backend 'root default-directory)) - (patch (vc-call-backend backend 'prepare-patch rev)) - files whole-patch-string diff-patch-string) +BACKEND is the VC backend to use. + +Return `deleted' if we actually undid/deleted a commit. +Any other return value means we called `vc-start-logentry'." + (cond* + ((bind* (backend (or backend + (vc-responsible-backend default-directory))))) + ((and reverse (not (eq delete 'never)) + (null (vc-find-backend-function backend + 'revision-published-p)) + (vc-find-backend-function backend 'delete-revision)) + ;; Centralized VCS implementing `delete-revision'. + (vc-call-backend backend 'delete-revision rev) + 'deleted) + ((and reverse interactive (not delete) + ;; Distributed VCS for which we can do deletions. + (vc-find-backend-function backend 'revision-published-p) + (vc-find-backend-function backend 'delete-revision) + ;; REV is safe to delete. + (not (vc-call-backend backend 'revision-published-p rev))) + ;; Require confirmation, because the commit is unpublished, and so + ;; this might be the only copy of the work in REV. Don't fall back + ;; to making a new commit undoing REV's changes because we don't + ;; know the user wants that just because they said "no" to our + ;; question here, and we want to avoid two y/n prompts in a row, + ;; which is probably a less good UI than this. + (cond ((or (eq interactive 'no-confirm) + (yes-or-no-p + (format "Permanently delete %s from the revision history?" + rev))) + (vc-call-backend backend 'delete-revision rev) + 'deleted) + ((derived-mode-p 'log-view-mode) + (user-error (substitute-command-keys "\ +Use \\[log-view-revert-revisions] to create new commits \ +undoing changes made by revision(s)"))) + (t + (user-error (substitute-command-keys "\ +Use \\[vc-revert-revision] to create a new commit undoing %s's changes") + rev)))) + ((and reverse delete (not (eq delete 'never)) + ;; Distributed VCS for which we can do deletions. + (vc-find-backend-function backend 'revision-published-p) + (vc-find-backend-function backend 'delete-revision)) + ;; Even though the user has explicitly requested deletion with a + ;; prefix argument / invoking `vc-delete-revision' / invoking + ;; `log-view-delete-revisions', by default we still confirm such a + ;; destructive operation. + ;; However, we want to avoid prompting twice in the case that the + ;; user has set `vc-allow-rewriting-published-history' to `ask', and + ;; we should avoid prompting at all in the case that + ;; `vc-allow-rewriting-published-history' is another non-nil value. + ;; These requirements lead to the nested `cond*' form here. + (cond* + ((and vc-allow-rewriting-published-history + (not (eq vc-allow-rewriting-published-history 'ask))) + (vc-call-backend backend 'delete-revision rev) + 'deleted) + ((bind* (published (vc-call-backend backend 'revision-published-p rev)))) + ((and published + (eq vc-allow-rewriting-published-history 'ask) + (yes-or-no-p + (format "Revision %s appears published; allow rewriting history?" + rev))) + (vc-call-backend backend 'delete-revision rev) + 'deleted) + (published + (user-error "Will not rewrite likely-public history")) + ((yes-or-no-p + (format "Permanently delete %s from the revision history?" + rev)) + (vc-call-backend backend 'delete-revision rev) + 'deleted) + (t + (user-error "Aborted")))) + ;; If we get this far we give up on `delete-revision', i.e. we fall + ;; back to creating a commit undoing the effects of REV. + ;; + ;; `vc-*-prepare-patch' will always give us a patch with file names + ;; relative to the VC root, so switch to there now. In particular + ;; this is needed for `diff-buffer-file-names' to work properly. + ((bind* (default-directory (vc-call-backend backend 'root + default-directory)) + (patch (vc-call-backend backend 'prepare-patch rev)) + files whole-patch-string diff-patch-string)) + (t (with-current-buffer (plist-get patch :buffer) (diff-mode) (with-restriction @@ -2218,14 +2317,14 @@ BACKEND is the VC backend to use." whole-patch-string comment)) nil backend - diff-patch-string))) + diff-patch-string)))) -;; No bindings in `vc-prefix-map' for the following two commands because -;; we expect users will usually use `log-view-revision-cherry-pick' and -;; `log-view-revision-revert', which do have bindings. +;; No bindings in `vc-prefix-map' for the following three items because +;; we expect users will usually use `log-view-cherry-pick' and +;; `log-view-revert-or-delete-revisions', which do have bindings. ;;;###autoload -(defun vc-revision-cherry-pick (rev &optional comment initial-contents backend) +(defun vc-cherry-pick (rev &optional comment initial-contents backend) "Copy the changes from a single revision REV to the current branch. When called interactively, prompts for REV. Typically REV is a revision from another branch, where that branch is @@ -2256,13 +2355,55 @@ Optional argument BACKEND is the VC backend to use." nil rev)) nil backend))) - (vc--pick-or-revert rev nil comment initial-contents backend)) + (vc--pick-or-revert rev nil nil nil comment initial-contents backend)) ;;;###autoload -(defun vc-revision-revert (rev &optional comment initial-contents backend) +(defun vc-revert-or-delete-revision + (rev &optional interactive delete comment initial-contents backend) "Undo the effects of revision REV. When called interactively, prompts for REV. +When called interactively (or with optional argument INTERACTIVE +non-nil), then if the underlying VCS is distributed and REV has not been +pushed, offer to entirely delete REV. +This is instead of creating a new commit undoing the effects of REV. + +With a prefix argument (or with optional argument DELETE non-nil), +only consider deleting REV, never create a new commit. +In this case INTERACTIVE is ignored. +This works only if REV has not been pushed, unless you have customized +`vc-allow-rewriting-published-history' to a non-nil value. + +When called from Lisp, there are three calling conventions for the +COMMENT and INITIAL-CONTENTS optional arguments: +- COMMENT a string, INITIAL-CONTENTS nil means use that comment string + without prompting the user to edit it. +- COMMENT a string, INITIAL-CONTENTS non-nil means use that comment + string as the initial contents of the log entry buffer but stop for + editing. +- COMMENT t means use BACKEND's default revert comment for REV without + prompting for editing, and ignore INITIAL-CONTENTS. + +Optional argument BACKEND is the VC backend to use. + +See also `vc-revert-revision'." + (interactive (list (vc-read-revision (if current-prefix-arg + "Revision to delete: " + "Revision to revert: ")) + t current-prefix-arg)) + (vc--pick-or-revert rev t interactive delete + comment initial-contents backend)) + +;;;###autoload +(defun vc-revert-revision + (rev &optional comment initial-contents backend) + "Make a commit undoing the effects of revision REV. +When called interactively, prompts for REV. + +This is like `vc-revert-or-delete-revision' except that it only ever makes a new +commit undoing the effects of REV, instead of considering VCS-specific +alternative mechanisms to undo the effects of REV. + When called from Lisp, there are three calling conventions for the COMMENT and INITIAL-CONTENTS optional arguments: - COMMENT a string, INITIAL-CONTENTS nil means use that comment string @@ -2275,7 +2416,18 @@ COMMENT and INITIAL-CONTENTS optional arguments: Optional argument BACKEND is the VC backend to use." (interactive (list (vc-read-revision "Revision to revert: "))) - (vc--pick-or-revert rev t comment initial-contents backend)) + (vc--pick-or-revert rev t nil 'never comment initial-contents backend)) + +;;;###autoload +(defun vc-delete-revision (rev &optional backend) + "Delete revision REV from the revision history. +This works only if REV has not been pushed, unless you have customized +`vc-allow-rewriting-published-history' to a non-nil value. + +This is the same as `vc-revert-or-delete-revision' invoked interactively +with a prefix argument." + (interactive (list (vc-read-revision "Revision to delete: "))) + (vc--pick-or-revert rev t nil t nil nil backend)) (declare-function diff-bounds-of-hunk "diff-mode")