1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-06 06:20:55 -08:00

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.
This commit is contained in:
Sean Whitton 2025-11-05 16:38:48 +00:00
parent 5dfcba699e
commit 2e8cc345d5
5 changed files with 343 additions and 94 deletions

View file

@ -1268,11 +1268,11 @@ Unmark all marked entries (@code{log-view-unmark-all-entries}).
@item C @item C
Copy changes to a currently checked out branch; either the changes from Copy changes to a currently checked out branch; either the changes from
the revision at point, or the changes from all marked revisions the revision at point, or the changes from all marked revisions
(@code{log-view-revision-cherry-pick}). (@code{log-view-cherry-pick}).
@item R @item R
Undo the effects of old revisions; either the revision at point, or all 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 @end table
@vindex vc-log-show-limit @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 Revert the work file(s) in the current VC fileset to the last revision
(@code{vc-revert}). (@code{vc-revert}).
@item M-x vc-revision-revert @item M-x vc-revert-or-delete-revision
Undo the effects of an older commit. 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 @end table
@kindex C-x v u @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 @kbd{C-x v u} to unlock a file if you lock it and then decide not to
change it. change it.
@findex vc-revision-revert @findex vc-revert-or-delete-revision
@cindex reverting commits @cindex reverting commits
To discard changes that have already been committed, by yourself or 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 someone else, you can use @w{@kbd{M-x vc-revert-or-delete-revision}}.
called @dfn{reverting} a commit. The command prompts for a revision to This is called @dfn{reverting} a commit. The command prompts for a
revert, and then the VC backend reverts it. Most backends implement revision to revert, and then the VC backend reverts it.
this by making a new commit which undoes the changes made by the
revision.
An alternative way to access this functionality is to the Most backends implement this by making a new commit which undoes the
@code{log-view-revision-revert} command, bound to @kbd{R} in Log View changes made by the revision. For a distributed VCS, if the commit is
mode buffers (@pxref{VC Change Log}). Compared to using @w{@kbd{M-x one that you made and have not yet pushed, Emacs will offer to delete it
vc-revision revert}} directly, this can make it easier to be sure you entirely, instead. With a prefix argument, this command will only try
are reverting the revision you intend. 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 @node VC Ignore
@subsection Ignore Version Control Files @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 @subsubsection Copying Changes Made By Revisions Between Branches
@table @kbd @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. Copy a single revision to branch checked out in this working tree.
@end table @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 order to fix the bug there, too. This is called @dfn{backporting} the
revision, or backporting the fix. revision, or backporting the fix.
@findex vc-revision-cherry-pick @findex vc-cherry-pick
You can use the command @kbd{M-x vc-revision-cherry-pick} to You can use the command @kbd{M-x vc-cherry-pick} to cherry-pick
cherry-pick revisions. It prompts for a revision to cherry-pick. It revisions. It prompts for a revision to cherry-pick. It then pops up a
then pops up a buffer for you to edit the log message for the new buffer for you to edit the log message for the new revision. Normally
revision. Normally the VC backend generates a log message including a the VC backend generates a log message including a reference to the
reference to the revision you want to copy, so that the copy can be revision you want to copy, so that the copy can be traced. If you wish,
traced. If you wish, you can delete this reference before typing you can delete this reference before typing @kbd{C-c C-c} to conclude
@kbd{C-c C-c} to conclude the cherry-pick. the cherry-pick.
Alternatively you can invoke the command with a prefix argument, 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 i.e. @w{@kbd{C-u M-x vc-cherry-pick}}. In this case the log message
message from the source revision is used unmodified, and the cherry-pick from the source revision is used unmodified, and the cherry-pick happens
happens immediately, without popping up a buffer for log message edits. immediately, without popping up a buffer for log message edits.
An alternative way to access this functionality is the An alternative way to access this functionality is the
@code{log-view-revision-cherry-pick} command, bound to @kbd{C} in Log @code{log-view-cherry-pick} command, bound to @kbd{C} in Log View mode
View mode buffers (@pxref{VC Change Log}). Compared to using buffers (@pxref{VC Change Log}). Compared to using @w{@kbd{M-x
@w{@kbd{M-x vc-revision-cherry-pick}} directly, this can make it easier vc-cherry-pick}} directly, this can make it easier to be sure you are
to be sure you are cherry-picking the revision you intend. cherry-picking the revision you intend.
@ifnottex @ifnottex
@include vc1-xtra.texi @include vc1-xtra.texi

View file

@ -2358,11 +2358,12 @@ the contents of those files.
+++ +++
*** New commands to cherry-pick and revert revisions. *** New commands to cherry-pick and revert revisions.
The commands 'vc-revision-cherry-pick' and 'vc-revision-revert' let you The commands 'vc-cherry-pick', 'vc-revert-or-delete-revision',
copy revisions between branches, and revert revisions. '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 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 point or all marked revisions, and 'R' to undo the revision at point or
or all marked revisions. all marked revisions.
*** New command 'log-edit-done-strip-cvs-lines'. *** New command 'log-edit-done-strip-cvs-lines'.
This command strips all lines beginning with "CVS:" from the buffer. This command strips all lines beginning with "CVS:" from the buffer.

View file

@ -141,8 +141,8 @@
"w" #'log-view-copy-revision-as-kill "w" #'log-view-copy-revision-as-kill
"TAB" #'log-view-msg-next "TAB" #'log-view-msg-next
"<backtab>" #'log-view-msg-prev "<backtab>" #'log-view-msg-prev
"C" #'log-view-revision-cherry-pick "C" #'log-view-cherry-pick
"R" #'log-view-revision-revert) "R" #'log-view-revert-or-delete-revisions)
(easy-menu-define log-view-mode-menu log-view-mode-map (easy-menu-define log-view-mode-menu log-view-mode-map
"Log-View Display Menu." "Log-View Display Menu."
@ -165,9 +165,9 @@
["Toggle Details at Point" log-view-toggle-entry-display ["Toggle Details at Point" log-view-toggle-entry-display
:active log-view-expanded-log-entry-function] :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"] :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)"] :help "Undo the effects of old revision(s)"]
"-----" "-----"
["Next Log Entry" log-view-msg-next ["Next Log Entry" log-view-msg-next
@ -703,7 +703,8 @@ If called interactively, annotate the version at point."
(defvar vc-log-short-style) (defvar vc-log-short-style)
(declare-function vc-print-log-internal "vc") (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. "Copy changes from revision at point or all marked revisions.
DIRECTORY is the destination, the root of the target working tree. DIRECTORY is the destination, the root of the target working tree.
NO-COMMENT non-nil means use the log messages of the revisions 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. for that revision.
NO-COMMENT non-nil with zero or one revisions marked also means don't NO-COMMENT non-nil with zero or one revisions marked also means don't
prompt to edit the log message. prompt to edit the log message.
REVERSE non-nil means to make commit(s) undoing the effects of the REVERSE non-nil means to undo the effects of the revisions, instead.
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) (let ((default-directory directory)
(marked (log-view-get-marked))) (marked (log-view-get-marked))
(buf (current-buffer)))
(if (length> marked 1) (if (length> marked 1)
(progn (let ((deleted 0))
(save-excursion (save-excursion
(dolist (rev (if reverse (reverse marked) marked)) (dolist (rev (if reverse (reverse marked) marked))
;; Unmark each revision *before* copying it. ;; Unmark each revision *before* copying it.
@ -724,38 +728,48 @@ revisions, instead."
;; fails, after resolving that conflict and committing the ;; fails, after resolving that conflict and committing the
;; cherry-pick, the right revisions will be marked to ;; cherry-pick, the right revisions will be marked to
;; resume the original multiple cherry-pick operation. ;; 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-goto-rev rev)
(log-view-unmark-entry 1) (log-view-unmark-entry 1)
(vc--pick-or-revert rev (cl-case
reverse (vc--pick-or-revert rev reverse interactive delete
(if no-comment (if no-comment
(vc-call-backend log-view-vc-backend (vc-call-backend log-view-vc-backend
'get-change-comment 'get-change-comment
nil rev) nil rev)
t) t)
nil nil
log-view-vc-backend))) log-view-vc-backend)
(when (vc-find-backend-function log-view-vc-backend (deleted (incf deleted)
'modify-change-comment) (when interactive
(let (vc-log-short-style) (setq interactive 'no-confirm))))))
(vc-print-log-internal log-view-vc-backend (let ((new-commits (- (length marked) deleted)))
(list default-directory) (when (and (plusp new-commits)
nil nil (length marked))) (vc-find-backend-function log-view-vc-backend
(setq-local vc-log-short-style nil ; For \\`g'. 'modify-change-comment))
vc-parent-buffer-name nil) (let (vc-log-short-style)
(message (substitute-command-keys "Use \ (vc-print-log-internal log-view-vc-backend
\\[log-view-modify-change-comment] to modify any of these messages")))) (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)))) (let ((rev (or (car marked) (log-view-current-tag))))
(vc--pick-or-revert rev (vc--pick-or-revert rev reverse interactive delete
reverse
(and no-comment (and no-comment
(vc-call-backend log-view-vc-backend (vc-call-backend log-view-vc-backend
'get-change-comment 'get-change-comment
nil rev)) nil rev))
nil 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. "Copy changes from revision at point to current branch.
If there are marked revisions, use those instead of the revision at point. 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 a prefix argument), use the log messages from the source revisions
unmodified. unmodified.
See also `vc-revision-cherry-pick'." See also `vc-cherry-pick'."
(interactive (interactive
(list (vc--prompt-other-working-tree log-view-vc-backend (list (vc--prompt-other-working-tree log-view-vc-backend
"Cherry-pick to working tree" "Cherry-pick to working tree"
'allow-empty) 'allow-empty)
current-prefix-arg)) 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. "Undo the effects of the revision at point.
When revisions are marked, undo the effects of each of them. When revisions are marked, undo the effects of each of them.
When called interactively, prompts for the target working tree in which When called interactively, prompts for the target working tree in which
to revert; the current working tree is the default choice. 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 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 When reverting a single revision, prompts for editing the log message
for the new commit. for the new commit.
When reverting multiple revisions, never prompts to edit log messages. 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 (interactive (list (vc--prompt-other-working-tree
(vc-responsible-backend default-directory) (vc-responsible-backend default-directory)
"Revert in working tree" "Revert in working tree"
'allow-empty))) '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

View file

@ -1053,11 +1053,11 @@ other commands receive global bindings where they had none before."
(defvar vc-menu-map (defvar vc-menu-map
(let ((map (make-sparse-keymap "Version Control"))) (let ((map (make-sparse-keymap "Version Control")))
(define-key map [vc-revision-revert] (define-key map [vc-revert-or-delete-revision]
'(menu-item "Revert Revision" vc-revision-revert '(menu-item "Revert Revision" vc-revert-or-delete-revision
:help "Undo the effects of a revision")) :help "Undo the effects of a revision"))
(define-key map [vc-revision-cherry-pick] (define-key map [vc-cherry-pick]
'(menu-item "Cherry-Pick Revision" vc-revision-cherry-pick '(menu-item "Cherry-Pick Revision" vc-cherry-pick
:help "Copy the changes from a single revision to this branch")) :help "Copy the changes from a single revision to this branch"))
(define-key map [separator1] menu-bar-separator) (define-key map [separator1] menu-bar-separator)
(define-key map [vc-retrieve-tag] (define-key map [vc-retrieve-tag]

View file

@ -2166,22 +2166,121 @@ have changed; continue with old fileset?" (current-buffer))))
(declare-function diff-buffer-file-names "diff-mode") (declare-function diff-buffer-file-names "diff-mode")
(declare-function diff-reverse-direction "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. "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 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 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 initial contents of the log entry buffer. If COMMENT is t then use
BACKEND's default cherry-pick comment for REV without prompting. BACKEND's default cherry-pick comment for REV without prompting.
BACKEND is the VC backend to use." 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 Return `deleted' if we actually undid/deleted a commit.
;; names relative to the VC root, so switch to there now. Any other return value means we called `vc-start-logentry'."
;; In particular this is needed for `diff-buffer-file-names' to (cond*
;; work properly. ((bind* (backend (or backend
(default-directory (vc-call-backend backend 'root default-directory)) (vc-responsible-backend default-directory)))))
(patch (vc-call-backend backend 'prepare-patch rev)) ((and reverse (not (eq delete 'never))
files whole-patch-string diff-patch-string) (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) (with-current-buffer (plist-get patch :buffer)
(diff-mode) (diff-mode)
(with-restriction (with-restriction
@ -2218,14 +2317,14 @@ BACKEND is the VC backend to use."
whole-patch-string comment)) whole-patch-string comment))
nil nil
backend backend
diff-patch-string))) diff-patch-string))))
;; No bindings in `vc-prefix-map' for the following two commands because ;; No bindings in `vc-prefix-map' for the following three items because
;; we expect users will usually use `log-view-revision-cherry-pick' and ;; we expect users will usually use `log-view-cherry-pick' and
;; `log-view-revision-revert', which do have bindings. ;; `log-view-revert-or-delete-revisions', which do have bindings.
;;;###autoload ;;;###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. "Copy the changes from a single revision REV to the current branch.
When called interactively, prompts for REV. When called interactively, prompts for REV.
Typically REV is a revision from another branch, where that branch is 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 rev))
nil nil
backend))) backend)))
(vc--pick-or-revert rev nil comment initial-contents backend)) (vc--pick-or-revert rev nil nil nil comment initial-contents backend))
;;;###autoload ;;;###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. "Undo the effects of revision REV.
When called interactively, prompts for 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 When called from Lisp, there are three calling conventions for the
COMMENT and INITIAL-CONTENTS optional arguments: COMMENT and INITIAL-CONTENTS optional arguments:
- COMMENT a string, INITIAL-CONTENTS nil means use that comment string - 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." Optional argument BACKEND is the VC backend to use."
(interactive (list (vc-read-revision "Revision to revert: "))) (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") (declare-function diff-bounds-of-hunk "diff-mode")