* vc/vc.el (vc-modify-change-comment): Change *VC-log* to *vc-log*
[bpt/emacs.git] / lisp / vc / vc.el
index ae3f6a0..e3f3c15 100644 (file)
@@ -1,8 +1,6 @@
 ;;; vc.el --- drive a version-control system from within Emacs
 
-;; Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000,
-;;   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-;;   Free Software Foundation, Inc.
+;; Copyright (C) 1992-1998, 2000-2011  Free Software Foundation, Inc.
 
 ;; Author:     FSF (see below for full credits)
 ;; Maintainer: Andre Spiegel <spiegel@gnu.org>
 ;; In the list of functions below, each identifier needs to be prepended
 ;; with `vc-sys-'.  Some of the functions are mandatory (marked with a
 ;; `*'), others are optional (`-').
-;;
+
 ;; BACKEND PROPERTIES
 ;;
 ;; * revision-granularity
 ;;   that return 'file have per-file revision numbering; backends
 ;;   that return 'repository have per-repository revision numbering,
 ;;   so a revision level implicitly identifies a changeset
-;;
+
 ;; STATE-QUERYING FUNCTIONS
 ;;
 ;; * registered (file)
 ;;
 ;; - merge (file rev1 rev2)
 ;;
-;;   Merge the changes between REV1 and REV2 into the current working file.
+;;   Merge the changes between REV1 and REV2 into the current working file
+;;   (for non-distributed VCS).
+;;
+;; - merge-branch ()
+;;
+;;   Merge another branch into the current one, prompting for a
+;;   location to merge from.
 ;;
 ;; - merge-news (file)
 ;;
 ;;   Merge recent changes from the current branch into FILE.
+;;   (for non-distributed VCS).
+;;
+;; - pull (prompt)
+;;
+;;   Pull "upstream" changes into the current branch (for distributed
+;;   VCS).  If PROMPT is non-nil, or if necessary, prompt for a
+;;   location to pull from.
 ;;
 ;; - steal-lock (file &optional revision)
 ;;
 ;;
 ;;   Mark conflicts as resolved.  Some VC systems need to run a
 ;;   command to mark conflicts as resolved.
-;;
+
 ;; HISTORY FUNCTIONS
 ;;
 ;; * print-log (files buffer &optional shortlog start-revision limit)
 ;;   If the backend supports annotating through copies and renames,
 ;;   and displays a file name and a revision, then return a cons
 ;;   (REVISION . FILENAME).
-;;
+
 ;; TAG SYSTEM
 ;;
 ;; - create-tag (dir name branchp)
 ;;   does a sanity check whether there aren't any uncommitted changes at
 ;;   or below DIR, and then performs a tree walk, using the `checkout'
 ;;   function to retrieve the corresponding revisions.
-;;
+
 ;; MISCELLANEOUS
 ;;
 ;; - make-version-backups-p (file)
 ;;   buffer, if one is present, instead of adding to the ChangeLog.
 ;;
 ;; - When vc-next-action calls vc-checkin it could pre-fill the
-;;   *VC-log* buffer with some obvious items: the list of files that
+;;   *vc-log* buffer with some obvious items: the list of files that
 ;;   were added, the list of files that were removed.  If the diff is
 ;;   available, maybe it could even call something like
 ;;   `diff-add-change-log-entries-other-window' to create a detailed
 
 (require 'vc-hooks)
 (require 'vc-dispatcher)
+(require 'ediff)
 
 (eval-when-compile
   (require 'cl)
@@ -763,6 +775,12 @@ See `run-hooks'."
   :type 'hook
   :group 'vc)
 
+(defcustom vc-revert-show-diff t
+  "If non-nil, `vc-revert' shows a `vc-diff' buffer before querying."
+  :type 'boolean
+  :group 'vc
+  :version "24.1")
+
 ;; Header-insertion hair
 
 (defcustom vc-static-header-alist
@@ -770,7 +788,7 @@ See `run-hooks'."
      "\n#ifndef lint\nstatic char vcid[] = \"\%s\";\n#endif /* lint */\n"))
   "Associate static header string templates with file types.
 A \%s in the template is replaced with the first string associated with
-the file's version control type in `vc-header-alist'."
+the file's version control type in `vc-BACKEND-header'."
   :type '(repeat (cons :format "%v"
                       (regexp :tag "File Type")
                       (string :tag "Header String")))
@@ -913,6 +931,17 @@ Within directories, only files already under version control are noticed."
     (nreverse flattened)))
 
 (defvar vc-dir-backend)
+(defvar log-view-vc-backend)
+(defvar diff-vc-backend)
+
+(defun vc-deduce-backend ()
+  (cond ((derived-mode-p 'vc-dir-mode)   vc-dir-backend)
+       ((derived-mode-p 'log-view-mode) log-view-vc-backend)
+       ((derived-mode-p 'diff-mode)     diff-vc-backend)
+        ;; Maybe we could even use comint-mode rather than shell-mode?
+       ((derived-mode-p 'dired-mode 'shell-mode 'compilation-mode)
+        (vc-responsible-backend default-directory))
+       (vc-mode (vc-backend buffer-file-name))))
 
 (declare-function vc-dir-current-file "vc-dir" ())
 (declare-function vc-dir-deduce-fileset "vc-dir" (&optional state-model-only-files))
@@ -976,7 +1005,7 @@ current buffer."
   (let ((backend (vc-responsible-backend default-directory)))
     (unless backend (error "Directory not under VC"))
     (list backend
-       (dired-map-over-marks (dired-get-filename nil t) nil))))
+          (dired-map-over-marks (dired-get-filename nil t) nil))))
 
 (defun vc-ensure-vc-buffer ()
   "Make sure that the current buffer visits a version-controlled file."
@@ -1093,9 +1122,12 @@ merge in the changes into your working copy."
        (dolist (file files)
          (unless (file-writable-p file)
            ;; Make the file+buffer read-write.
-           (unless (y-or-n-p (format "%s is edited but read-only; make it writable and continue?" file))
+           (unless (y-or-n-p (format "%s is edited but read-only; make it writable and continue? " file))
              (error "Aborted"))
-           (set-file-modes file (logior (file-modes file) 128))
+            ;; Maybe we somehow lost permissions on the directory.
+            (condition-case nil
+                (set-file-modes file (logior (file-modes file) 128))
+              (error (error "Unable to make file writable")))
            (let ((visited (get-file-buffer file)))
              (when visited
                (with-current-buffer visited
@@ -1382,7 +1414,7 @@ Runs the normal hooks `vc-before-checkin-hook' and `vc-checkin-hook'."
    (vc-start-logentry
     files comment initial-contents
     "Enter a change comment."
-    "*VC-log*"
+    "*vc-log*"
     (lambda ()
       (vc-call-backend backend 'log-edit-mode))
     (lexical-let ((rev rev))
@@ -1427,6 +1459,16 @@ Runs the normal hooks `vc-before-checkin-hook' and `vc-checkin-hook'."
 ;;          (vc-call-backend ',(vc-backend f)
 ;;                           'diff (list ',f) ',rev1 ',rev2))))))
 
+(defvar vc-coding-system-inherit-eol t
+  "When non-nil, inherit the EOL format for reading Diff output from the file.
+
+Used in `vc-coding-system-for-diff' to determine the EOL format to use
+for reading Diff output for a file.  If non-nil, the EOL format is
+inherited from the file itself.
+Set this variable to nil if your Diff tool might use a different
+EOL.  Then Emacs will auto-detect the EOL format in Diff output, which
+gives better results.") ;; Cf. bug#4451.
+
 (defun vc-coding-system-for-diff (file)
   "Return the coding system for reading diff output for FILE."
   (or coding-system-for-read
@@ -1434,7 +1476,12 @@ Runs the normal hooks `vc-before-checkin-hook' and `vc-checkin-hook'."
       ;; use the buffer's coding system
       (let ((buf (find-buffer-visiting file)))
         (when buf (with-current-buffer buf
-                   buffer-file-coding-system)))
+                   (if vc-coding-system-inherit-eol
+                       buffer-file-coding-system
+                     ;; Don't inherit the EOL part of the coding-system,
+                     ;; because some Diff tools may choose to use
+                     ;; a different one.  bug#4451.
+                     (coding-system-base buffer-file-coding-system)))))
       ;; otherwise, try to find one based on the file name
       (car (find-operation-coding-system 'insert-file-contents file))
       ;; and a final fallback
@@ -1493,10 +1540,13 @@ to override the value of `vc-diff-switches' and `diff-switches'."
 (defvar vc-diff-added-files nil
   "If non-nil, diff added files by comparing them to /dev/null.")
 
-(defun vc-diff-internal (async vc-fileset rev1 rev2 &optional verbose)
+(defun vc-diff-internal (async vc-fileset rev1 rev2 &optional verbose buffer)
   "Report diffs between two revisions of a fileset.
-Diff output goes to the *vc-diff* buffer.  The function
-returns t if the buffer had changes, nil otherwise."
+Output goes to the buffer BUFFER, which defaults to *vc-diff*.
+BUFFER, if non-nil, should be a buffer or a buffer name.
+Return t if the buffer had changes, nil otherwise."
+  (unless buffer
+    (setq buffer "*vc-diff*"))
   (let* ((files (cadr vc-fileset))
         (messages (cons (format "Finding changes in %s..."
                                  (vc-delistify files))
@@ -1508,7 +1558,7 @@ returns t if the buffer had changes, nil otherwise."
         ;; be to call the back end separately for each file.
         (coding-system-for-read
          (if files (vc-coding-system-for-diff (car files)) 'undecided)))
-    (vc-setup-buffer "*vc-diff*")
+    (vc-setup-buffer buffer)
     (message "%s" (car messages))
     ;; Many backends don't handle well the case of a file that has been
     ;; added but not yet committed to the repo (notably CVS and Subversion).
@@ -1533,13 +1583,13 @@ returns t if the buffer had changes, nil otherwise."
                 (error "No revisions of %s exist" file)
               ;; We regard this as "changed".
               ;; Diff it against /dev/null.
-              (apply 'vc-do-command "*vc-diff*"
+              (apply 'vc-do-command buffer
                      1 "diff" file
                      (append (vc-switches nil 'diff) '("/dev/null"))))))
         (setq files (nreverse filtered))))
     (let ((vc-disable-async-diff (not async)))
-      (vc-call-backend (car vc-fileset) 'diff files rev1 rev2 "*vc-diff*"))
-    (set-buffer "*vc-diff*")
+      (vc-call-backend (car vc-fileset) 'diff files rev1 rev2 buffer))
+    (set-buffer buffer)
     (if (and (zerop (buffer-size))
              (not (get-buffer-process (current-buffer))))
         ;; Treat this case specially so as not to pop the buffer.
@@ -1547,6 +1597,7 @@ returns t if the buffer had changes, nil otherwise."
           (message "%s" (cdr messages))
           nil)
       (diff-mode)
+      (set (make-local-variable 'diff-vc-backend) (car vc-fileset))
       (set (make-local-variable 'revert-buffer-function)
           `(lambda (ignore-auto noconfirm)
              (vc-diff-internal ,async ',vc-fileset ,rev1 ,rev2 ,verbose)))
@@ -1576,45 +1627,48 @@ returns t if the buffer had changes, nil otherwise."
                          nil nil initial-input nil default)
       (read-string prompt initial-input nil default))))
 
+(defun vc-diff-build-argument-list-internal ()
+  "Build argument list for calling internal diff functions."
+  (let* ((vc-fileset (vc-deduce-fileset t)) ;FIXME: why t?  --Stef
+         (files (cadr vc-fileset))
+         (backend (car vc-fileset))
+         (first (car files))
+         (rev1-default nil)
+         (rev2-default nil))
+    (cond
+     ;; someday we may be able to do revision completion on non-singleton
+     ;; filesets, but not yet.
+     ((/= (length files) 1)
+      nil)
+     ;; if it's a directory, don't supply any revision default
+     ((file-directory-p first)
+      nil)
+     ;; if the file is not up-to-date, use working revision as older revision
+     ((not (vc-up-to-date-p first))
+      (setq rev1-default (vc-working-revision first)))
+     ;; if the file is not locked, use last and previous revisions as defaults
+     (t
+      (setq rev1-default (vc-call-backend backend 'previous-revision first
+                                          (vc-working-revision first)))
+      (when (string= rev1-default "") (setq rev1-default nil))
+      (setq rev2-default (vc-working-revision first))))
+    ;; construct argument list
+    (let* ((rev1-prompt (if rev1-default
+                            (concat "Older revision (default "
+                                    rev1-default "): ")
+                          "Older revision: "))
+           (rev2-prompt (concat "Newer revision (default "
+                                (or rev2-default "current source") "): "))
+           (rev1 (vc-read-revision rev1-prompt files backend rev1-default))
+           (rev2 (vc-read-revision rev2-prompt files backend rev2-default)))
+      (when (string= rev1 "") (setq rev1 nil))
+      (when (string= rev2 "") (setq rev2 nil))
+      (list files rev1 rev2))))
+
 ;;;###autoload
 (defun vc-version-diff (files rev1 rev2)
   "Report diffs between revisions of the fileset in the repository history."
-  (interactive
-   (let* ((vc-fileset (vc-deduce-fileset t)) ;FIXME: why t?  --Stef
-         (files (cadr vc-fileset))
-          (backend (car vc-fileset))
-         (first (car files))
-         (rev1-default nil)
-         (rev2-default nil))
-     (cond
-      ;; someday we may be able to do revision completion on non-singleton
-      ;; filesets, but not yet.
-      ((/= (length files) 1)
-       nil)
-      ;; if it's a directory, don't supply any revision default
-      ((file-directory-p first)
-       nil)
-      ;; if the file is not up-to-date, use working revision as older revision
-      ((not (vc-up-to-date-p first))
-       (setq rev1-default (vc-working-revision first)))
-      ;; if the file is not locked, use last and previous revisions as defaults
-      (t
-       (setq rev1-default (vc-call-backend backend 'previous-revision first
-                                           (vc-working-revision first)))
-       (when (string= rev1-default "") (setq rev1-default nil))
-       (setq rev2-default (vc-working-revision first))))
-     ;; construct argument list
-     (let* ((rev1-prompt (if rev1-default
-                            (concat "Older revision (default "
-                                    rev1-default "): ")
-                          "Older revision: "))
-           (rev2-prompt (concat "Newer revision (default "
-                                (or rev2-default "current source") "): "))
-           (rev1 (vc-read-revision rev1-prompt files backend rev1-default))
-           (rev2 (vc-read-revision rev2-prompt files backend rev2-default)))
-       (when (string= rev1 "") (setq rev1 nil))
-       (when (string= rev2 "") (setq rev2 nil))
-       (list files rev1 rev2))))
+  (interactive (vc-diff-build-argument-list-internal))
   ;; All that was just so we could do argument completion!
   (when (and (not rev1) rev2)
     (error "Not a valid revision range"))
@@ -1639,6 +1693,48 @@ saving the buffer."
     (vc-diff-internal t (vc-deduce-fileset t) nil nil
                      (called-interactively-p 'interactive))))
 
+(declare-function ediff-vc-internal (rev1 rev2 &optional startup-hooks))
+
+;;;###autoload
+(defun vc-version-ediff (files rev1 rev2)
+  "Show differences between revisions of the fileset in the
+repository history using ediff."
+  (interactive (vc-diff-build-argument-list-internal))
+  ;; All that was just so we could do argument completion!
+  (when (and (not rev1) rev2)
+    (error "Not a valid revision range"))
+
+  (message "%s" (format "Finding changes in %s..." (vc-delistify files)))
+
+  ;; Functions ediff-(vc|rcs)-internal use "" instead of nil.
+  (when (null rev1) (setq rev1 ""))
+  (when (null rev2) (setq rev2 ""))
+
+  (cond
+   ;; FIXME We only support running ediff on one file for now.
+   ;; We could spin off an ediff session per file in the file set.
+   ((= (length files) 1)
+    (ediff-load-version-control)
+    (find-file (car files))             ;FIXME: find-file from Elisp is bad.
+    (ediff-vc-internal rev1 rev2 nil))
+   (t
+    (error "More than one file is not supported"))))
+
+;;;###autoload
+(defun vc-ediff (historic &optional not-urgent)
+  "Display diffs between file revisions using ediff.
+Normally this compares the currently selected fileset with their
+working revisions.  With a prefix argument HISTORIC, it reads two revision
+designators specifying which revisions to compare.
+
+The optional argument NOT-URGENT non-nil means it is ok to say no to
+saving the buffer."
+  (interactive (list current-prefix-arg t))
+  (if historic
+      (call-interactively 'vc-version-ediff)
+    (when buffer-file-name (vc-buffer-sync not-urgent))
+    (vc-version-ediff (cadr (vc-deduce-fileset t)) nil nil)))
+
 ;;;###autoload
 (defun vc-root-diff (historic &optional not-urgent)
   "Display diffs between VC-controlled whole tree revisions.
@@ -1656,10 +1752,7 @@ saving the buffer."
       ;; that's not what we want here, we want the diff for the VC root dir.
       (call-interactively 'vc-version-diff)
     (when buffer-file-name (vc-buffer-sync not-urgent))
-    (let ((backend
-          (cond ((derived-mode-p 'vc-dir-mode)  vc-dir-backend)
-                ((derived-mode-p 'dired-mode) (vc-responsible-backend default-directory))
-                (vc-mode (vc-backend buffer-file-name))))
+    (let ((backend (vc-deduce-backend))
          rootdir working-revision)
       (unless backend
        (error "Buffer is not version controlled"))
@@ -1692,8 +1785,9 @@ If `F.~REV~' already exists, use it instead of checking it out again."
                    rev)))
     (switch-to-buffer-other-window (vc-find-revision file revision))))
 
-(defun vc-find-revision (file revision)
-  "Read REVISION of FILE into a buffer and return the buffer."
+(defun vc-find-revision (file revision &optional backend)
+  "Read REVISION of FILE into a buffer and return the buffer.
+Use BACKEND as the VC backend if specified."
   (let ((automatic-backup (vc-version-backup-file-name file revision))
        (filebuf (or (get-file-buffer file) (current-buffer)))
         (filename (vc-version-backup-file-name file revision 'manual)))
@@ -1711,7 +1805,9 @@ If `F.~REV~' already exists, use it instead of checking it out again."
                      ;; Change buffer to get local value of
                      ;; vc-checkout-switches.
                      (with-current-buffer filebuf
-                       (vc-call find-revision file revision outbuf))))
+                       (if backend
+                           (vc-call-backend backend 'find-revision file revision outbuf)
+                         (vc-call find-revision file revision outbuf)))))
                  (setq failed nil))
              (when (and failed (file-exists-p filename))
                (delete-file filename))))
@@ -1780,7 +1876,7 @@ The headers are reset to their non-expanded form."
     (vc-start-logentry
      files oldcomment t
      "Enter a replacement change comment."
-     "*VC-log*"
+     "*vc-log*"
      (lambda () (vc-call-backend backend 'log-edit-mode))
      (lexical-let ((rev rev))
        (lambda (files comment)
@@ -1789,53 +1885,65 @@ The headers are reset to their non-expanded form."
 
 ;;;###autoload
 (defun vc-merge ()
-  "Merge changes between two revisions into the current buffer's file.
-This asks for two revisions to merge from in the minibuffer.  If the
-first revision is a branch number, then merge all changes from that
-branch.  If the first revision is empty, merge news, i.e. recent changes
-from the current branch.
-
-See Info node `Merging'."
+  "Perform a version control merge operation.
+On a distributed version control system, this runs a \"merge\"
+operation to incorporate changes from another branch onto the
+current branch, prompting for an argument list.
+
+On a non-distributed version control system, this merges changes
+between two revisions into the current fileset.  This asks for
+two revisions to merge from in the minibuffer.  If the first
+revision is a branch number, then merge all changes from that
+branch.  If the first revision is empty, merge the most recent
+changes from the current branch."
   (interactive)
-  (vc-ensure-vc-buffer)
-  (vc-buffer-sync)
-  (let* ((file buffer-file-name)
-        (backend (vc-backend file))
-        (state (vc-state file))
-        first-revision second-revision status)
+  (let* ((vc-fileset (vc-deduce-fileset t))
+        (backend (car vc-fileset))
+        (files (cadr vc-fileset)))
     (cond
-     ((stringp state)  ;; Locking VCses only
-      (error "File is locked by %s" state))
-     ((not (vc-editable-p file))
-      (if (y-or-n-p
-          "File must be checked out for merging.  Check out now? ")
-         (vc-checkout file t)
-       (error "Merge aborted"))))
-    (setq first-revision
-         (vc-read-revision
-           (concat "Branch or revision to merge from "
-                   "(default news on current branch): ")
-           (list file)
-           backend))
-    (if (string= first-revision "")
-        (setq status (vc-call-backend backend 'merge-news file))
-      (if (not (vc-find-backend-function backend 'merge))
-         (error "Sorry, merging is not implemented for %s" backend)
-       (if (not (vc-branch-p first-revision))
-           (setq second-revision
-                 (vc-read-revision
-                   "Second revision: "
-                   (list file) backend nil
-                   ;; FIXME: This is CVS/RCS/SCCS specific.
-                   (concat (vc-branch-part first-revision) ".")))
-         ;; We want to merge an entire branch.  Set revisions
-         ;; accordingly, so that vc-BACKEND-merge understands us.
-         (setq second-revision first-revision)
-         ;; first-revision must be the starting point of the branch
-         (setq first-revision (vc-branch-part first-revision)))
-       (setq status (vc-call-backend backend 'merge file
-                                      first-revision second-revision))))
-    (vc-maybe-resolve-conflicts file status "WORKFILE" "MERGE SOURCE")))
+     ;; If a branch-merge operation is defined, use it.
+     ((vc-find-backend-function backend 'merge-branch)
+      (vc-call-backend backend 'merge-branch))
+     ;; Otherwise, do a per-file merge.
+     ((vc-find-backend-function backend 'merge)
+      (vc-buffer-sync)
+      (dolist (file files)
+       (let* ((state (vc-state file))
+              first-revision second-revision status)
+         (cond
+          ((stringp state)     ;; Locking VCses only
+           (error "File %s is locked by %s" file state))
+          ((not (vc-editable-p file))
+           (vc-checkout file t)))
+         (setq first-revision
+               (vc-read-revision
+                (concat "Merge " file
+                        "from branch or revision "
+                        "(default news on current branch): ")
+                (list file)
+                backend))
+         (cond
+          ((string= first-revision "")
+           (setq status (vc-call-backend backend 'merge-news file)))
+          (t
+           (if (not (vc-branch-p first-revision))
+               (setq second-revision
+                     (vc-read-revision
+                      "Second revision: "
+                      (list file) backend nil
+                      ;; FIXME: This is CVS/RCS/SCCS specific.
+                      (concat (vc-branch-part first-revision) ".")))
+             ;; We want to merge an entire branch.  Set revisions
+             ;; accordingly, so that vc-BACKEND-merge understands us.
+             (setq second-revision first-revision)
+             ;; first-revision must be the starting point of the branch
+             (setq first-revision (vc-branch-part first-revision)))
+           (setq status (vc-call-backend backend 'merge file
+                                         first-revision second-revision))))
+         (vc-maybe-resolve-conflicts file status "WORKFILE" "MERGE SOURCE"))))
+     (t
+      (error "Sorry, merging is not implemented for %s" backend)))))
+
 
 (defun vc-maybe-resolve-conflicts (file status &optional name-A name-B)
   (vc-resynch-buffer file t (not (buffer-modified-p)))
@@ -1904,7 +2012,7 @@ checked out in that new branch."
          ;; For VC's that do not work at file level, it's pointless
          ;; to ask for a directory, branches are created at repository level.
          default-directory
-       (read-file-name "Directory: " default-directory default-directory t))
+       (read-directory-name "Directory: " default-directory default-directory t))
       (read-string (if current-prefix-arg "New branch name: " "New tag name: "))
       current-prefix-arg)))
   (message "Making %s... " (if branchp "branch" "tag"))
@@ -1930,7 +2038,7 @@ allowed and simply skipped)."
          ;; For VC's that do not work at file level, it's pointless
          ;; to ask for a directory, branches are created at repository level.
          default-directory
-       (read-file-name "Directory: " default-directory default-directory t))
+       (read-directory-name "Directory: " default-directory default-directory t))
       (read-string "Tag name to retrieve (default latest revisions): "))))
   (let ((update (yes-or-no-p "Update any affected buffers? "))
        (msg (if (or (not name) (string= name ""))
@@ -1956,7 +2064,6 @@ If it contains `directory' then if the fileset contains a directory show a short
 If it contains `file' then show short logs for files.
 Not all VC backends support short logs!")
 
-(defvar log-view-vc-backend)
 (defvar log-view-vc-fileset)
 
 (defun vc-print-log-setup-buttons (working-revision is-start-revision limit pl-return)
@@ -1965,22 +2072,20 @@ Not all VC backends support short logs!")
     (goto-char (point-max))
     (lexical-let ((working-revision working-revision)
                  (limit limit))
-      (widget-create 'push-button
-                    :notify (lambda (&rest ignore)
-                              (vc-print-log-internal
-                               log-view-vc-backend log-view-vc-fileset
-                               working-revision nil (* 2 limit)))
-                    :help-echo "Show the log again, and double the number of log entries shown"
-                    "Show 2X entries")
-      (widget-insert "    ")
-      (widget-create 'push-button
-                    :notify (lambda (&rest ignore)
-                              (vc-print-log-internal
-                               log-view-vc-backend log-view-vc-fileset
-                               working-revision nil nil))
-                    :help-echo "Show the log again, showing all entries"
-                    "Show unlimited entries"))
-    (widget-setup)))
+      (insert "\n")
+      (insert-text-button "Show 2X entries"
+                         'action (lambda (&rest ignore)
+                                   (vc-print-log-internal
+                                    log-view-vc-backend log-view-vc-fileset
+                                    working-revision nil (* 2 limit)))
+                         'help-echo "Show the log again, and double the number of log entries shown")
+      (insert "    ")
+      (insert-text-button "Show unlimited entries"
+                         'action (lambda (&rest ignore)
+                                   (vc-print-log-internal
+                                    log-view-vc-backend log-view-vc-fileset
+                                    working-revision nil nil))
+                         'help-echo "Show the log again, including all entries"))))
 
 (defun vc-print-log-internal (backend files working-revision
                                       &optional is-start-revision limit)
@@ -2002,8 +2107,10 @@ Not all VC backends support short logs!")
     (setq type (if vc-short-log 'short 'long))
     (lexical-let
        ((working-revision working-revision)
+        (backend backend)
         (limit limit)
         (shortlog vc-short-log)
+        (files files)
         (is-start-revision is-start-revision))
       (vc-log-internal-common
        backend buffer-name files type
@@ -2014,7 +2121,9 @@ Not all VC backends support short logs!")
         (vc-print-log-setup-buttons working-revision
                                     is-start-revision limit ret))
        (lambda (bk)
-        (vc-call-backend bk 'show-log-entry working-revision))))))
+        (vc-call-backend bk 'show-log-entry working-revision))
+       (lambda (ignore-auto noconfirm)
+        (vc-print-log-internal backend files working-revision is-start-revision limit))))))
 
 (defvar vc-log-view-type nil
   "Set this to differentiate the different types of logs.")
@@ -2026,7 +2135,8 @@ Not all VC backends support short logs!")
                               type
                               backend-func
                               setup-buttons-func
-                              goto-location-func)
+                              goto-location-func
+                              rev-buff-func)
   (let (retval)
     (with-current-buffer (get-buffer-create buffer-name)
       (set (make-local-variable 'vc-log-view-type) type))
@@ -2037,7 +2147,9 @@ Not all VC backends support short logs!")
       ;; to t, so let's keep doing it, just in case.
       (vc-call-backend backend 'log-view-mode)
       (set (make-local-variable 'log-view-vc-backend) backend)
-      (set (make-local-variable 'log-view-vc-fileset) files))
+      (set (make-local-variable 'log-view-vc-fileset) files)
+      (set (make-local-variable 'revert-buffer-function)
+          rev-buff-func))
     (vc-exec-after
      `(let ((inhibit-read-only t))
        (funcall ',setup-buttons-func ',backend ',files ',retval)
@@ -2055,7 +2167,14 @@ Not all VC backends support short logs!")
        (vc-call-backend bk type-arg buf remote-location)))
    (lambda (bk files-arg ret))
    (lambda (bk)
-     (goto-char (point-min)))))
+     (goto-char (point-min)))
+   (lexical-let
+    ((backend backend)
+     (remote-location remote-location)
+     (buffer-name buffer-name)
+     (type type))
+    (lambda (ignore-auto noconfirm)
+      (vc-incoming-outgoing-internal backend remote-location buffer-name type)))))
 
 ;;;###autoload
 (defun vc-print-log (&optional working-revision limit)
@@ -2105,10 +2224,7 @@ When called interactively with a prefix argument, prompt for LIMIT."
        (list lim)))
     (t
      (list (when (> vc-log-show-limit 0) vc-log-show-limit)))))
-  (let ((backend
-        (cond ((derived-mode-p 'vc-dir-mode)  vc-dir-backend)
-              ((derived-mode-p 'dired-mode) (vc-responsible-backend default-directory))
-              (vc-mode (vc-backend buffer-file-name))))
+  (let ((backend (vc-deduce-backend))
        rootdir working-revision)
     (unless backend
       (error "Buffer is not version controlled"))
@@ -2118,12 +2234,12 @@ When called interactively with a prefix argument, prompt for LIMIT."
 
 ;;;###autoload
 (defun vc-log-incoming (&optional remote-location)
-  "Show a log of changes that will be received with a pull operation from REMOTE-LOCATION."
-  (interactive "sRemote location (empty for default): ")
-  (let ((backend
-        (cond ((derived-mode-p 'vc-dir-mode)  vc-dir-backend)
-              ((derived-mode-p 'dired-mode) (vc-responsible-backend default-directory))
-              (vc-mode (vc-backend buffer-file-name))))
+  "Show a log of changes that will be received with a pull operation from REMOTE-LOCATION.
+When called interactively with a prefix argument, prompt for REMOTE-LOCATION.."
+  (interactive
+   (when current-prefix-arg
+     (list (read-string "Remote location (empty for default): "))))
+  (let ((backend (vc-deduce-backend))
        rootdir working-revision)
     (unless backend
       (error "Buffer is not version controlled"))
@@ -2131,12 +2247,12 @@ When called interactively with a prefix argument, prompt for LIMIT."
 
 ;;;###autoload
 (defun vc-log-outgoing (&optional remote-location)
-  "Show a log of changes that will be sent with a push operation to REMOTE-LOCATION."
-  (interactive "sRemote location (empty for default): ")
-  (let ((backend
-        (cond ((derived-mode-p 'vc-dir-mode)  vc-dir-backend)
-              ((derived-mode-p 'dired-mode) (vc-responsible-backend default-directory))
-              (vc-mode (vc-backend buffer-file-name))))
+  "Show a log of changes that will be sent with a push operation to REMOTE-LOCATION.
+When called interactively with a prefix argument, prompt for REMOTE-LOCATION."
+  (interactive
+   (when current-prefix-arg
+     (list (read-string "Remote location (empty for default): "))))
+  (let ((backend (vc-deduce-backend))
        rootdir working-revision)
     (unless backend
       (error "Buffer is not version controlled"))
@@ -2149,11 +2265,12 @@ This asks for confirmation if the buffer contents are not identical
 to the working revision (except for keyword expansion)."
   (interactive)
   (let* ((vc-fileset (vc-deduce-fileset))
-        (files (cadr vc-fileset)))
-    ;; If any of the files is visited by the current buffer, make
-    ;; sure buffer is saved.  If the user says `no', abort since
-    ;; we cannot show the changes and ask for confirmation to
-    ;; discard them.
+        (files (cadr vc-fileset))
+        (queried nil)
+        diff-buffer)
+    ;; If any of the files is visited by the current buffer, make sure
+    ;; buffer is saved.  If the user says `no', abort since we cannot
+    ;; show the changes and ask for confirmation to discard them.
     (when (or (not files) (memq (buffer-file-name) files))
       (vc-buffer-sync nil))
     (dolist (file files)
@@ -2161,20 +2278,29 @@ to the working revision (except for keyword expansion)."
        (when (and buf (buffer-modified-p buf))
          (error "Please kill or save all modified buffers before reverting")))
       (when (vc-up-to-date-p file)
-       (unless (yes-or-no-p (format "%s seems up-to-date.  Revert anyway? " file))
+       (if (yes-or-no-p (format "%s seems up-to-date.  Revert anyway? " file))
+           (setq queried t)
          (error "Revert canceled"))))
-    (when (vc-diff-internal vc-allow-async-revert vc-fileset nil nil)
-      (unless (yes-or-no-p
-              (format "Discard changes in %s? "
-                      (let ((str (vc-delistify files))
-                            (nfiles (length files)))
-                        (if (< (length str) 50)
-                            str
-                          (format "%d file%s" nfiles
-                                  (if (= nfiles 1) "" "s"))))))
-       (error "Revert canceled"))
-      (delete-windows-on "*vc-diff*")
-      (kill-buffer "*vc-diff*"))
+    (unwind-protect
+       (when (if vc-revert-show-diff
+                 (progn
+                   (setq diff-buffer (generate-new-buffer-name "*vc-diff*"))
+                   (vc-diff-internal vc-allow-async-revert vc-fileset
+                                     nil nil nil diff-buffer))
+               ;; Avoid querying the user again.
+               (null queried))
+         (unless (yes-or-no-p
+                  (format "Discard changes in %s? "
+                          (let ((str (vc-delistify files))
+                                (nfiles (length files)))
+                            (if (< (length str) 50)
+                                str
+                              (format "%d file%s" nfiles
+                                      (if (= nfiles 1) "" "s"))))))
+           (error "Revert canceled")))
+      (when diff-buffer
+       (delete-windows-on diff-buffer)
+       (kill-buffer diff-buffer)))
     (dolist (file files)
       (message "Reverting %s..." (vc-delistify files))
       (vc-revert-file file)
@@ -2237,35 +2363,47 @@ depending on the underlying version-control system."
 (define-obsolete-function-alias 'vc-revert-buffer 'vc-revert "23.1")
 
 ;;;###autoload
-(defun vc-update ()
-  "Update the current fileset's files to their tip revisions.
-For each one that contains no changes, and is not locked, then this simply
-replaces the work file with the latest revision on its branch.  If the file
-contains changes, and the backend supports merging news, then any recent
-changes from the current branch are merged into the working file."
-  (interactive)
-  (let* ((vc-fileset (vc-deduce-fileset))
+(defun vc-pull (&optional arg)
+  "Update the current fileset or branch.
+On a distributed version control system, this runs a \"pull\"
+operation to update the current branch, prompting for an argument
+list if required.  Optional prefix ARG forces a prompt.
+
+On a non-distributed version control system, update the current
+fileset to the tip revisions.  For each unchanged and unlocked
+file, this simply replaces the work file with the latest revision
+on its branch.  If the file contains changes, any changes in the
+tip revision are merged into the working file."
+  (interactive "P")
+  (let* ((vc-fileset (vc-deduce-fileset t))
         (backend (car vc-fileset))
         (files (cadr vc-fileset)))
-    (save-some-buffers          ; save buffers visiting files
-     nil (lambda ()
-           (and (buffer-modified-p)
-                (let ((file (buffer-file-name)))
-                  (and file (member file files))))))
-    (dolist (file files)
-      (if (vc-up-to-date-p file)
-         (vc-checkout file nil t)
-       (if (eq (vc-checkout-model backend (list file)) 'locking)
-           (if (eq (vc-state file) 'edited)
-               (error "%s"
-                      (substitute-command-keys
-                       "File is locked--type \\[vc-revert] to discard changes"))
-             (error "Unexpected file state (%s) -- type %s"
-                    (vc-state file)
-                    (substitute-command-keys
-                     "\\[vc-next-action] to correct")))
-          (vc-maybe-resolve-conflicts
-           file (vc-call-backend backend 'merge-news file)))))))
+    (cond
+     ;; If a pull operation is defined, use it.
+     ((vc-find-backend-function backend 'pull)
+      (vc-call-backend backend 'pull arg))
+     ;; If VCS has `merge-news' functionality (CVS and SVN), use it.
+     ((vc-find-backend-function backend 'merge-news)
+      (save-some-buffers ; save buffers visiting files
+       nil (lambda ()
+            (and (buffer-modified-p)
+                 (let ((file (buffer-file-name)))
+                   (and file (member file files))))))
+      (dolist (file files)
+       (if (vc-up-to-date-p file)
+           (vc-checkout file nil t)
+         (vc-maybe-resolve-conflicts
+          file (vc-call-backend backend 'merge-news file)))))
+     ;; For a locking VCS, check out each file.
+     ((eq (vc-checkout-model backend files) 'locking)
+      (dolist (file files)
+       (if (vc-up-to-date-p file)
+           (vc-checkout file nil t))))
+     (t
+      (error "VC update is unsupported for `%s'" backend)))))
+
+;;;###autoload
+(defalias 'vc-update 'vc-pull)
 
 (defun vc-version-backup-file (file &optional rev)
   "Return name of backup file for revision REV of FILE.
@@ -2544,9 +2682,6 @@ log entries should be gathered."
     (when index
       (substring rev 0 index))))
 
-(define-obsolete-function-alias
-  'vc-default-previous-version 'vc-default-previous-revision "23.1")
-
 (defun vc-default-responsible-p (backend file)
   "Indicate whether BACKEND is reponsible for FILE.
 The default is to return nil always."
@@ -2701,5 +2836,4 @@ Invoke FUNC f ARGS on each VC-managed file f underneath it."
 
 (provide 'vc)
 
-;; arch-tag: ca82c1de-3091-4e26-af92-460abc6213a6
 ;;; vc.el ends here