(@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
(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]
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))