From: Sean Whitton Date: Fri, 4 Jul 2025 11:39:11 +0000 (+0100) Subject: VC: New incoming & outgoing diff commands (bug#62940) X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=a9cdb0ac258e68fec0be667ea1afa1da5276aed9;p=emacs.git VC: New incoming & outgoing diff commands (bug#62940) * lisp/vc/vc-hooks.el (vc-incoming-prefix-map) (vc-outgoing-prefix-map): New keymaps. (vc-use-incoming-outgoing-prefixes): New user option. * lisp/vc/vc.el (vc-root-diff-incoming, vc-root-diff-outgoing): New commands (bug#62940). * doc/emacs/maintaining.texi (VC Change Log): * etc/NEWS: Document the new commands and option. (cherry picked from commit af78b4d333e81a544ebf10d57067cca8c2fc732f) --- diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi index 4ad7ffe8c80..97b636d1c31 100644 --- a/doc/emacs/maintaining.texi +++ b/doc/emacs/maintaining.texi @@ -1039,12 +1039,33 @@ Display the change history for another branch (@code{vc-print-branch-log}). @item C-x v I -Display the changes that a ``pull'' operation will retrieve -(@code{vc-log-incoming}). +Display log entries for the changes that a ``pull'' operation will +retrieve (@code{vc-log-incoming}). + +If you customize @code{vc-use-incoming-outgoing-prefixes} to non-nil, +this command moves to @kbd{C-x v I L}. + +@item M-x vc-root-diff-incoming +Display a diff of the changes that a pull operation will retrieve. + +If you customize @code{vc-use-incoming-outgoing-prefixes} to non-nil, +this command becomes available on @kbd{C-x v I D}. @item C-x v O -Display the changes that will be sent by the next ``push'' operation -(@code{vc-log-outgoing}). +Display log entries for the changes that will be sent by the next +``push'' operation (@code{vc-log-outgoing}). + +If you customize @code{vc-use-incoming-outgoing-prefixes} to non-nil, +this command moves to @kbd{C-x v O L}. + +@item M-x vc-root-diff-outgoing +Display a diff of the changes that will be sent by the next push +operation. This command is useful as a way to preview your push and +ensure that all and only the changes you intended to include were +committed and will be pushed. + +If you customize @code{vc-use-incoming-outgoing-prefixes} to non-nil, +this command becomes available on @kbd{C-x v O D}. @item C-x v h Display the history of changes made in the region of file visited by diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el index dfda436292f..e40b983655d 100644 --- a/lisp/vc/vc-hooks.el +++ b/lisp/vc/vc-hooks.el @@ -959,6 +959,30 @@ In the latter case, VC mode is deactivated for this buffer." (fset 'vc-prefix-map vc-prefix-map) (define-key ctl-x-map "v" 'vc-prefix-map) +(defvar-keymap vc-incoming-prefix-map + "L" #'vc-log-incoming + "D" #'vc-root-diff-incoming) +(defvar-keymap vc-outgoing-prefix-map + "L" #'vc-log-outgoing + "D" #'vc-root-diff-outgoing) + +(defcustom vc-use-incoming-outgoing-prefixes nil + "Whether \\`C-x v I' and \\`C-x v O' are prefix commands. +Historically Emacs bound \\`C-x v I' and \\`C-x v O' directly to +commands. That is still the default. If this option is customized to +non-nil, these key sequences becomes prefix commands. `vc-log-incoming' +moves to \\`C-x v I L', `vc-log-outgoing' moves to \\`C-x v O L', and +other commands receive global bindings where they had none before." + :type 'boolean + :version "31.1" + :set (lambda (symbol value) + (if value + (progn (keymap-set vc-prefix-map "I" vc-incoming-prefix-map) + (keymap-set vc-prefix-map "O" vc-outgoing-prefix-map)) + (keymap-set vc-prefix-map "I" #'vc-log-incoming) + (keymap-set vc-prefix-map "O" #'vc-log-outgoing)) + (set-default symbol value))) + (defvar vc-menu-map (let ((map (make-sparse-keymap "Version Control"))) ;;(define-key map [show-files] diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 7ae54e6894c..57c7c1bfbd1 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -2447,6 +2447,69 @@ The merge base is a common ancestor between REV1 and REV2 revisions." vc-allow-async-diff (list backend (list rootdir)) rev1 rev2 (called-interactively-p 'interactive))))) +;;;###autoload +(defun vc-root-diff-incoming (&optional remote-location) + "Report diff of all changes that would be pulled from REMOTE-LOCATION. +When called interactively with a prefix argument, prompt for REMOTE-LOCATION. +In some version control systems REMOTE-LOCATION can be a remote branch name. + +See `vc-use-incoming-outgoing-prefixes' regarding giving this command a +global binding." + (interactive (vc--maybe-read-remote-location)) + (vc--with-backend-in-rootdir "VC root-diff" + (let ((default-directory rootdir) + (incoming (vc--incoming-revision backend + (or remote-location "")))) + (vc-diff-internal vc-allow-async-diff (list backend (list rootdir)) + (vc-call-backend backend 'mergebase incoming) + incoming + (called-interactively-p 'interactive))))) + +;;;###autoload +(defun vc-root-diff-outgoing (&optional remote-location) + "Report diff of all changes that would be pushed to REMOTE-LOCATION. +When called interactively with a prefix argument, prompt for REMOTE-LOCATION. +In some version control systems REMOTE-LOCATION can be a remote branch name. + +See `vc-use-incoming-outgoing-prefixes' regarding giving this command a +global binding." + ;; For this command we want to ignore uncommitted changes because + ;; those are not outgoing, and the point is to make a comparison + ;; between locally committed changes and remote committed changes. + ;; (Hence why we don't call `vc-buffer-sync-fileset'.) + (interactive (vc--maybe-read-remote-location)) + (vc--with-backend-in-rootdir "VC root-diff" + (let ((default-directory rootdir) + (incoming (vc--incoming-revision backend + (or remote-location "")))) + (vc-diff-internal vc-allow-async-diff (list backend (list rootdir)) + (vc-call-backend backend 'mergebase incoming) + ;; FIXME: In order to exclude uncommitted + ;; changes we need to pass the most recent + ;; revision as REV2. Calling `working-revision' + ;; like this works for all the backends we have + ;; in core that implement `mergebase' and so can + ;; be used with this command (Git and Hg). + ;; However, it is not clearly permitted by the + ;; current semantics of `working-revision' to + ;; call it on a directory. + ;; + ;; A possible alternative would be something + ;; like this which effectively falls back to + ;; including uncommitted changes in the case of + ;; an older VCS or where the backend rejects our + ;; attempt to call `working-revision' on a + ;; directory: + ;; (and (eq (vc-call-backend backend + ;; 'revision-granularity) + ;; 'repository) + ;; (ignore-errors + ;; (vc-call-backend backend 'working-revision + ;; rootdir))) + (vc-call-backend backend 'working-revision + rootdir) + (called-interactively-p 'interactive))))) + (declare-function ediff-load-version-control "ediff" (&optional silent)) (declare-function ediff-vc-internal "ediff-vers" (rev1 rev2 &optional startup-hooks))