]> git.eshelyaron.com Git - emacs.git/commitdiff
VC: New incoming & outgoing diff commands (bug#62940)
authorSean Whitton <spwhitton@spwhitton.name>
Fri, 4 Jul 2025 11:39:11 +0000 (12:39 +0100)
committerEshel Yaron <me@eshelyaron.com>
Wed, 23 Jul 2025 20:16:39 +0000 (22:16 +0200)
* 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)

doc/emacs/maintaining.texi
lisp/vc/vc-hooks.el
lisp/vc/vc.el

index 4ad7ffe8c800fae4dd325ed7e7e87853ab2a3d1d..97b636d1c315e500169e2fe6a411e12ae996268f 100644 (file)
@@ -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
index dfda436292f70c9d19f2c239b59e915389c4b462..e40b983655d897ef52cb88674e3b388aa9241d78 100644 (file)
@@ -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]
index 7ae54e6894cc92da193a39dd19e31bc21a16ec64..57c7c1bfbd1a25c03ad4a6564693459f827ccd17 100644 (file)
@@ -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))