]> code.delx.au - gnu-emacs/blobdiff - lisp/vc.el
(Fbuffer_substring): Doc fix.
[gnu-emacs] / lisp / vc.el
index 836495f589913bd7bcda8801c0fa5f5854786b03..11675a724d3ca6b852ebfaf236cbca0f23c9cd6c 100644 (file)
@@ -5,6 +5,8 @@
 ;; Author:     FSF (see below for full credits)
 ;; Maintainer: Andre Spiegel <spiegel@gnu.org>
 
+;; $Id: vc.el,v 1.293 2001/01/08 16:23:33 spiegel Exp $
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
@@ -31,7 +33,7 @@
 ;;   Paul Eggert <eggert@twinsun.com>
 ;;   Sebastian Kremer <sk@thp.uni-koeln.de>
 ;;   Martin Lorentzson <martinl@gnu.org>
-;;   Dave Love <d.love@gnu.org>
+;;   Dave Love <fx@gnu.org>
 ;;   Stefan Monnier <monnier@cs.yale.edu>
 ;;   Andre Spiegel <spiegel@gnu.org>
 ;;   Richard Stallman <rms@gnu.org>
 ;;
 ;; Developer's notes on some concurrency issues are included at the end of
 ;; the file.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;; Backend-specific functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
-;; for each operation FUN, the backend should provide a function vc-BACKEND-FUN.
-;; Operations marked with a `-' instead of a `*' have a sensible default
-;; behavior.
-
+;; ADDING SUPPORT FOR OTHER BACKENDS
+;;
+;; VC can use arbitrary version control systems as a backend.  To add
+;; support for a new backend named SYS, write a library vc-sys.el that
+;; contains functions of the form `vc-sys-...' (note that SYS is in lower
+;; case for the function and library names).  VC will use that library if
+;; you put the symbol SYS somewhere into the list of
+;; `vc-handled-backends'.  Then, for example, if `vc-sys-registered'
+;; returns non-nil for a file, all SYS-specific versions of VC commands
+;; will be available for that file.
+;;
+;; VC keeps some per-file information in the form of properties (see
+;; vc-file-set/getprop in vc-hooks.el).  The backend-specific functions
+;; do not generally need to be aware of these properties.  For example,
+;; `vc-sys-workfile-version' should compute the workfile version and
+;; return it; it should not look it up in the property, and it needn't
+;; store it there either.  However, if a backend-specific function does
+;; store a value in a property, that value takes precedence over any
+;; value that the generic code might want to set (check for uses of 
+;; the macro `with-vc-properties' in vc.el).
+;;
+;; 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 (`-').
+;;
+;; STATE-QUERYING FUNCTIONS
+;;
 ;; * registered (file)
-;; * state (file)
+;;
+;;   Return non-nil if FILE is registered in this backend.
+;;
+;; * state (file) 
+;;
+;;   Return the current version control state of FILE.  For a list of
+;;   possible values, see `vc-state'.  This function should do a full and
+;;   reliable state computation; it is usually called immediately after
+;;   C-x v v.  If you want to use a faster heuristic when visiting a
+;;   file, put that into `state-heuristic' below.
+;;
 ;; - state-heuristic (file)
-;;     The default behavior delegates to `state'.
+;;
+;;   If provided, this function is used to estimate the version control
+;;   state of FILE at visiting time.  It should be considerably faster
+;;   than the implementation of `state'.  For a list of possible values,
+;;   see the doc string of `vc-state'.
+;;
 ;; - dir-state (dir)
+;;
+;;   If provided, this function is used to find the version control state
+;;   of all files in DIR in a fast way.  The function should not return
+;;   anything, but rather store the files' states into the corresponding
+;;   `vc-state' properties.
+;;
+;; * workfile-version (file)
+;;
+;;   Return the current workfile version of FILE.
+;;
+;; - latest-on-branch-p (file)
+;;
+;;   Return non-nil if the current workfile version of FILE is the latest
+;;   on its branch.  The default implementation always returns t, which
+;;   means that working with non-current versions is not supported by
+;;   default.
+;;
 ;; * checkout-model (file)
+;;
+;;   Indicate whether FILE needs to be "checked out" before it can be
+;;   edited.  See `vc-checkout-model' for a list of possible values.
+;;
+;; - workfile-unchanged-p (file)
+;;
+;;   Return non-nil if FILE is unchanged from its current workfile
+;;   version.  This function should do a brief comparison of FILE's
+;;   contents with those of the master version.  If the backend does not
+;;   have such a brief-comparison feature, the default implementation of
+;;   this function can be used, which delegates to a full
+;;   vc-BACKEND-diff.
+;;
 ;; - mode-line-string (file)
-;; * workfile-version (file)
-;; * revert (file)
-;; - merge-news (file)
-;;     Only needed if state `needs-merge' is possible.
-;; - merge (file rev1 rev2)
-;; - steal-lock (file &optional version)
-;;     Only required if files can be locked by somebody else.
-;; * register (file rev comment)
+;;
+;;   If provided, this function should return the VC-specific mode line
+;;   string for FILE.  The default implementation deals well with all
+;;   states that `vc-state' can return.
+;;
+;; - dired-state-info (file)
+;;
+;;   Translate the `vc-state' property of FILE into a string that can be
+;;   used in a vc-dired buffer.  The default implementation deals well
+;;   with all states that `vc-state' can return.
+;;
+;; STATE-CHANGING FUNCTIONS
+;;
+;; * register (file &optional rev comment)
+;;
+;;   Register FILE in this backend.  Optionally, an initial revision REV
+;;   and an initial description of the file, COMMENT, may be specified.
+;;
 ;; - responsible-p (file)
-;;     Should also work if FILE is a directory (ends with a slash).
+;;
+;;   Return non-nil if this backend considers itself "responsible" for
+;;   FILE, which can also be a directory.  This function is used to find
+;;   out what backend to use for registration of new files and for things
+;;   like change log generation.  The default implementation always
+;;   returns nil.
+;;
 ;; - could-register (file)
-;; * checkout (file writable &optional rev destfile)
-;;     Checkout revision REV of FILE into DESTFILE.
-;;     DESTFILE defaults to FILE.
-;;     The file should be made writable if WRITABLE is non-nil.
-;;     REV can be nil (BASE) or "" (HEAD) or any other revision.
+;;
+;;   Return non-nil if FILE could be registered under this backend.  The
+;;   default implementation always returns t.
+;;
+;; - receive-file (file rev)
+;;
+;;   Let this backend "receive" a file that is already registered under
+;;   another backend.  The default implementation simply calls `register'
+;;   for FILE, but it can be overridden to do something more specific,
+;;   e.g. keep revision numbers consistent or choose editing modes for
+;;   FILE that resemble those of the other backend.
+;;
+;; - unregister (file)
+;;
+;;   Unregister FILE from this backend.  This is only needed if this
+;;   backend may be used as a "more local" backend for temporary editing.
+;;
 ;; * checkin (file rev comment)
+;;
+;;   Commit changes in FILE to this backend.  If REV is non-nil, that
+;;   should become the new revision number.  COMMENT is used as a
+;;   check-in comment.
+;;
+;; * checkout (file &optional editable rev destfile)
+;;
+;;   Check out revision REV of FILE into the working area.  If EDITABLE
+;;   is non-nil, FILE should be writable by the user and if locking is
+;;   used for FILE, a lock should also be set.  If REV is non-nil, that
+;;   is the revision to check out (default is current workfile version);
+;;   if REV is the empty string, that means to check out the head of the
+;;   trunk.  If optional arg DESTFILE is given, it is an alternate
+;;   filename to write the contents to.
+;;
+;; * revert (file)
+;;
+;;   Revert FILE back to the current workfile version.
+;;
+;; - cancel-version (file editable)
+;;
+;;   Cancel the current workfile version of FILE, i.e. remove it from the
+;;   master.  EDITABLE non-nil means that FILE should be writable
+;;   afterwards, and if locking is used for FILE, then a lock should also
+;;   be set.  If this function is not provided, trying to cancel a
+;;   version is caught as an error.
+;;
+;; - merge (file rev1 rev2)
+;;
+;;   Merge the changes between REV1 and REV2 into the current working file.
+;;
+;; - merge-news (file)
+;;
+;;   Merge recent changes from the current branch into FILE.
+;;
+;; - steal-lock (file &optional version)
+;;
+;;   Steal any lock on the current workfile version of FILE, or on
+;;   VERSION if that is provided.  This function is only needed if
+;;   locking is used for files under this backend, and if files can
+;;   indeed be locked by other users.
+;;
+;; HISTORY FUNCTIONS
+;;
+;; * print-log (file)
+;;
+;;   Insert the revision log of FILE into the current buffer.
+;;
+;; - show-log-entry (version)
+;;
+;;   If provided, search the log entry for VERSION in the current buffer,
+;;   and make sure it is displayed in the buffer's window.  The default
+;;   implementation of this function works for RCS-style logs.
+;;
+;; - wash-log (file)
+;;
+;;   Remove all non-comment information from the output of print-log.  The
+;;   default implementation of this function works for RCS-style logs.
+;;
 ;; - logentry-check ()
+;;
+;;   If defined, this function is run to find out whether the user
+;;   entered a valid log entry for check-in.  The log entry is in the
+;;   current buffer, and if it is not a valid one, the function should
+;;   throw an error.
+;;
+;; - comment-history (file)
+;;
+;;   Return a string containing all log entries that were made for FILE.
+;;   This is used for transferring a file from one backend to another,
+;;   retaining comment information.  The default implementation of this
+;;   function does this by calling print-log and then wash-log, and
+;;   returning the resulting buffer contents as a string.
+;;
+;; - update-changelog (files)
+;;
+;;   Using recent log entries, create ChangeLog entries for FILES, or for
+;;   all files at or below the default-directory if FILES is nil.  The
+;;   default implementation runs rcs2log, which handles RCS- and
+;;   CVS-style logs.
+;;
 ;; * diff (file &optional rev1 rev2)
-;;     Insert the diff for FILE into the current buffer.
-;;     REV1 should default to workfile-version.
-;;     REV2 should default to the current workfile
-;;     Return a status of either 0 (i.e. no diff) or 1 (i.e. either non-empty
-;;     diff or the diff is run asynchronously).
-;; - workfile-unchanged-p (file)
-;;     Return non-nil if FILE is unchanged from its current workfile version.
-;;     This function should do a brief comparison of FILE's contents
-;;     with those of the master version.  If the backend does not have
-;;     such a brief-comparison feature, the default implementation of this
-;;     function can be used, which delegates to a full vc-BACKEND-diff.
-;; - clear-headers ()
-;; * check-headers ()
-;; - dired-state-info (file)
+;;
+;;   Insert the diff for FILE into the current buffer.  If REV1 and REV2
+;;   are non-nil, report differences from REV1 to REV2.  If REV1 is nil,
+;;   use the current workfile version (as found in the repository) as the
+;;   older version; if REV2 is nil, use the current workfile contents as
+;;   the newer version.  This function should return a status of either 0
+;;   (no differences found), or 1 (either non-empty diff or the diff is
+;;   run asynchronously).
+;;
+;; - annotate-command (file buf rev)
+;;
+;;   If this function is provided, it should produce an annotated version
+;;   of FILE in BUF, relative to version REV.  This is currently only
+;;   implemented for CVS, using the `cvs annotate' command.
+;;
+;; - annotate-difference (point)
+;;
+;;   Only required if `annotate-command' is defined for the backend.
+;;   Return the difference between the age of the line at point and the
+;;   current time.  Return NIL if there is no more comparison to be made
+;;   in the buffer.  Return value as defined for `current-time'.  You can
+;;   safely assume that point is placed at the beginning of each line,
+;;   starting at `point-min'.  The buffer that point is placed in is the
+;;   Annotate output, as defined by the relevant backend.
+;;
+;; SNAPSHOT SYSTEM
+;;
 ;; - create-snapshot (dir name branchp)
-;;     Take a snapshot of the current state of files under DIR and name it NAME.
-;;     This should make sure that files are up-to-date before proceeding
-;;     with the action.
-;;     DIR can also be a file and if BRANCHP is specified, NAME
-;;     should be created as a branch and DIR should be checked out under
-;;     this new branch.  The default behavior does not support branches
-;;     but does a sanity check, a tree traversal and for each file calls
-;;     `assign-name'.
-;; * assign-name (file name)
-;;     Give name NAME to the current version of FILE, assuming it is
-;;     up-to-date.  Only used by the default version of `create-snapshot'.
+;;
+;;   Take a snapshot of the current state of files under DIR and name it
+;;   NAME.  This should make sure that files are up-to-date before
+;;   proceeding with the action.  DIR can also be a file and if BRANCHP
+;;   is specified, NAME should be created as a branch and DIR should be
+;;   checked out under this new branch.  The default implementation does
+;;   not support branches but does a sanity check, a tree traversal and
+;;   for each file calls `assign-name'.
+;;
+;; - assign-name (file name)
+;;
+;;   Give name NAME to the current version of FILE, assuming it is
+;;   up-to-date.  Only used by the default version of `create-snapshot'.
+;;
 ;; - retrieve-snapshot (dir name update)
-;;     Retrieve a named snapshot of all registered files at or below DIR.
-;;     If UPDATE is non-nil, then update buffers of any files in the snapshot
-;;     that are currently visited.
-;; * print-log (file)
-;;     Insert the revision log of FILE into the current buffer.
-;; - show-log-entry (version)
-;; - update-changelog (files)
-;;     Find changelog entries for FILES, or for all files at or below
-;;     the default-directory if FILES is nil.
-;; * latest-on-branch-p (file)
-;; - cancel-version (file writable)
+;;
+;;   Retrieve a named snapshot of all registered files at or below DIR.
+;;   If UPDATE is non-nil, then update buffers of any files in the
+;;   snapshot that are currently visited.  The default implementation
+;;   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 versions.
+;;
+;; MISCELLANEOUS
+;;
+;; - make-version-backups-p (file)
+;;
+;;   Return non-nil if unmodified repository versions of FILE should be
+;;   backed up locally.  If this is done, VC can perform `diff' and
+;;   `revert' operations itself, without calling the backend system.  The
+;;   default implementation always returns nil.
+;;
+;; - check-headers ()
+;;
+;;   Return non-nil if the current buffer contains any version headers.
+;;
+;; - clear-headers ()
+;;
+;;   In the current buffer, reset all version headers to their unexpanded
+;;   form.  This function should be provided if the state-querying code
+;;   for this backend uses the version headers to determine the state of
+;;   a file.  This function will then be called whenever VC changes the
+;;   version control state in such a way that the headers would give
+;;   wrong information.
+;;
 ;; - rename-file (old new)
-;; - annotate-command (file buf)
-;; - annotate-difference (pos)
-;;     Only required if `annotate-command' is defined for the backend.
+;;
+;;   Rename file OLD to NEW, both in the working area and in the
+;;   repository.  If this function is not provided, the command
+;;   `vc-rename-file' will signal an error.
+
+;;; Code:
 
 (require 'vc-hooks)
 (require 'ring)
 (eval-when-compile
+  (require 'cl)
   (require 'compile)
   (require 'dired)      ; for dired-map-over-marks macro
   (require 'dired-aux))        ; for dired-kill-{line,tree}
@@ -372,6 +585,7 @@ and that its contents match what the master file says."
 (defvar vc-prefix-map
   (let ((map (make-sparse-keymap)))
     (define-key map "a" 'vc-update-change-log)
+    (define-key map "b" 'vc-switch-backend)
     (define-key map "c" 'vc-cancel-version)
     (define-key map "d" 'vc-directory)
     (define-key map "g" 'vc-annotate)
@@ -471,26 +685,25 @@ The keys are \(BUFFER . BACKEND\).  See also `vc-annotate-get-backend'.")
   (setq vc-comment-ring (make-ring vc-maximum-comment-ring-size)))
 
 (defmacro with-vc-properties (file form settings)
-  "Execute FORM, then set per-file properties for FILE, but only those
-that have not been set during the execution of FORM.  SETTINGS is a list 
-of two-element lists, each of which has the form (PROPERTY VALUE)."
+  "Execute FORM, then set per-file properties for FILE,
+but only those that have not been set during the execution of FORM.
+SETTINGS is a list of two-element lists, each of which has the
+  form (PROPERTY . VALUE)."
   `(let ((vc-touched-properties (list t))
         (filename ,file))
      ,form
      (mapcar (lambda (setting)
-              (let ((property (nth 0 setting))
-                    (value (nth 1 setting)))
+              (let ((property (car setting)))
                 (unless (memq property vc-touched-properties)
-                  (put (intern filename vc-file-prop-obarray) 
-                       property value))))
+                  (put (intern filename vc-file-prop-obarray)
+                       property (cdr setting)))))
             ,settings)))
 
 ;; Random helper functions
 
 (defsubst vc-editable-p (file)
   (or (eq (vc-checkout-model file) 'implicit)
-      (eq (vc-state file) 'edited)
-      (eq (vc-state file) 'needs-merge)))
+      (memq (vc-state file) '(edited needs-merge))))
 
 ;;; Two macros for elisp programming
 ;;;###autoload
@@ -501,7 +714,7 @@ FILE is passed through `expand-file-name'; BODY executed within
 `save-excursion'.  If FILE is not under version control, or locked by
 somebody else, signal error."
   `(let ((file (expand-file-name ,file)))
-     (or (vc-registered file)
+     (or (vc-backend file)
         (error (format "File not under version control: `%s'" file)))
      (unless (vc-editable-p file)
        (let ((state (vc-state file)))
@@ -510,6 +723,7 @@ somebody else, signal error."
      (save-excursion
        ,@body)
      (vc-checkin file nil ,comment)))
+(put 'with-vc-file 'indent-function 1)
 
 ;;;###autoload
 (defmacro edit-vc-file (file comment &rest body)
@@ -519,9 +733,10 @@ This macro uses `with-vc-file', passing args to it.
 However, before executing BODY, find FILE, and after BODY, save buffer."
   `(with-vc-file
     ,file ,comment
-    (find-file ,file)
+    (set-buffer (find-file-noselect ,file))
     ,@body
     (save-buffer)))
+(put 'edit-vc-file 'indent-function 1)
 
 (defun vc-ensure-vc-buffer ()
   "Make sure that the current buffer visits a version-controlled file."
@@ -632,11 +847,13 @@ that is inserted into the command line before the filename."
        (if (eq okstatus 'async)
            (let ((proc (apply 'start-process command (current-buffer) command
                               squeezed)))
-             (message "Running %s in the background..." command)
+              (unless (active-minibuffer-window)
+                (message "Running %s in the background..." command))
              ;;(set-process-sentinel proc (lambda (p msg) (delete-process p)))
              (set-process-filter proc 'vc-process-filter)
              (vc-exec-after
-              `(message "Running %s in the background... done" ',command)))
+              `(unless (active-minibuffer-window)
+                  (message "Running %s in the background... done" ',command))))
          (setq status (apply 'call-process command nil t nil squeezed))
          (when (or (not (integerp status)) (and okstatus (< okstatus status)))
            (pop-to-buffer (current-buffer))
@@ -795,11 +1012,16 @@ NOT-URGENT means it is ok to continue if the user says not to save."
       (let ((unchanged (vc-call workfile-unchanged-p file)))
         (vc-file-setprop file 'vc-checkout-time (if unchanged lastmod 0))
         unchanged))))
-      
+
 (defun vc-default-workfile-unchanged-p (file)
   "Default check whether FILE is unchanged: diff against master version."
   (zerop (vc-call diff file (vc-workfile-version file))))
 
+(defun vc-default-latest-on-branch-p (file)
+  "Default check whether the current workfile version of FILE is the 
+latest on its branch."
+  t)
+
 (defun vc-recompute-state (file)
   "Force a recomputation of the version control state of FILE.
 The state is computed using the exact, and possibly expensive
@@ -818,7 +1040,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
       ;; will check whether the file on disk is newer.
       (if vc-dired-mode
          (find-file-other-window file)
-       (find-file file))
+       (set-buffer (find-file-noselect file)))
       (if (not (verify-visited-file-modtime (current-buffer)))
          (if (yes-or-no-p "Replace file on disk with buffer contents? ")
              (write-file (buffer-file-name))
@@ -828,11 +1050,12 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
        (if (buffer-modified-p)
            (or (y-or-n-p "Operate on disk file, keeping modified buffer? ")
                (error "Aborted")))))
-    
+
     ;; Do the right thing
     (if (not (vc-registered file))
        (vc-register verbose comment)
       (vc-recompute-state file)
+      (if visited (vc-mode-line file))
       (setq state (vc-state file))
       (cond
        ;; up-to-date
@@ -841,15 +1064,20 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
        (cond
         (verbose
          ;; go to a different version
-         (setq version (read-string "Branch or version to move to: "))
-         (vc-checkout file (eq (vc-checkout-model file) 'implicit) version))
+         (setq version
+               (read-string "Branch, version, or backend to move to: "))
+         (let ((vsym (intern-soft (upcase version))))
+           (if (member vsym vc-handled-backends)
+               (vc-transfer-file file vsym)
+             (vc-checkout file (eq (vc-checkout-model file) 'implicit)
+                          version))))
         ((not (eq (vc-checkout-model file) 'implicit))
          ;; check the file out
          (vc-checkout file t))
         (t
          ;; do nothing
          (message "%s is up-to-date" file))))
-       
+
        ;; Abnormal: edited but read-only
        ((and visited (eq state 'edited) buffer-read-only)
        ;; Make the file+buffer read-write.  If the user really wanted to
@@ -858,7 +1086,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
        (set-file-modes buffer-file-name
                        (logior (file-modes buffer-file-name) 128))
        (toggle-read-only -1))
-       
+
        ;; edited
        ((eq state 'edited)
        (cond
@@ -876,9 +1104,14 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
          (if (yes-or-no-p "Revert to master version? ")
              (vc-revert-buffer)))
         (t ;; normal action
-         (if verbose (setq version (read-string "New version: ")))
-         (vc-checkin file version comment))))
-       
+         (if (not verbose)
+             (vc-checkin file nil comment)
+           (setq version (read-string "New version or backend: "))
+           (let ((vsym (intern (upcase version))))
+             (if (member vsym vc-handled-backends)
+                 (vc-transfer-file file vsym)
+               (vc-checkin file version comment)))))))
+
        ;; locked by somebody else
        ((stringp state)
        (if comment
@@ -888,7 +1121,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
                        (if verbose (read-string "Version to steal: ")
                          (vc-workfile-version file))
                       state))
-       
+
        ;; needs-patch
        ((eq state 'needs-patch)
        (if (yes-or-no-p (format
@@ -899,7 +1132,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
                   (yes-or-no-p "Lock this version? "))
              (vc-checkout file t)
            (error "Aborted"))))
-       
+
        ;; needs-merge
        ((eq state 'needs-merge)
        (if (yes-or-no-p (format
@@ -907,15 +1140,16 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
                          (file-name-nondirectory file)))
            (vc-maybe-resolve-conflicts file (vc-call merge-news file))
          (error "Aborted")))
-       
+
        ;; unlocked-changes
        ((eq state 'unlocked-changes)
        (if (not visited) (find-file-other-window file))
        (if (save-window-excursion
              (vc-version-diff file (vc-workfile-version file) nil)
              (goto-char (point-min))
-             (insert-string (format "Changes to %s since last lock:\n\n"
-                                    file))
+             (let ((inhibit-read-only t))
+               (insert-string
+                (format "Changes to %s since last lock:\n\n" file)))
              (not (beep))
              (yes-or-no-p (concat "File has unlocked changes.  "
                                   "Claim lock retaining changes? ")))
@@ -1003,7 +1237,7 @@ merge in the changes into your working copy."
                       (if (not (vc-up-to-date-p f)) "@" ""))
                     files ""))
                (vc-next-action-dired nil nil "dummy")
-             (vc-start-entry nil nil nil
+             (vc-start-entry nil nil nil nil
                              "Enter a change comment for the marked files."
                              'vc-next-action-dired))
            (throw 'nogo nil)))
@@ -1041,18 +1275,19 @@ first backend that could register the file is used."
           (not (file-exists-p buffer-file-name)))
       (set-buffer-modified-p t))
   (vc-buffer-sync)
-  
+
   (vc-start-entry buffer-file-name
                   (if set-version
-                      (read-string "Initial version level for %s: "
-                                   (buffer-name))
+                      (read-string (format "Initial version level for %s: "
+                                          (buffer-name)))
                     ;; TODO: Use backend-specific init version.
                     vc-default-init-version)
                   (or comment (not vc-initial-comment))
+                 nil
                   "Enter initial comment."
                  (lambda (file rev comment)
                    (message "Registering %s... " file)
-                   (let ((backend (vc-responsible-backend file)))
+                   (let ((backend (vc-responsible-backend file t)))
                      (vc-file-clearprops file)
                      (vc-call-backend backend 'register file rev comment)
                      (vc-file-setprop file 'vc-backend backend)
@@ -1061,30 +1296,47 @@ first backend that could register the file is used."
                        (setq backup-inhibited t)))
                    (message "Registering %s... done" file))))
 
+
 (defun vc-responsible-backend (file &optional register)
-  "Return the name of the backend system that is responsible for FILE.
-If no backend in variable `vc-handled-backends' declares itself
-responsible, the first backend in that list will be returned (if optional
-arg REGISTER is non-nil, return the first backend that could register the
-file).
-FILE can also be a directory name (ending with a slash)."
-  (if (null vc-handled-backends)
-      (error "Cannot register, no backends in `vc-handled-backends'"))
-  (or (and (not (file-directory-p file)) (vc-backend file))
+  "Return the name of a backend system that is responsible for FILE.
+The optional argument REGISTER means that a backend suitable for
+registration should be found.
+
+If REGISTER is nil, then if FILE is already registered, return the
+backend of FILE.  If FILE is not registered, or a directory, then the
+first backend in `vc-handled-backends' that declares itself
+responsible for FILE is returned.  If no backend declares itself
+responsible, return the first backend.
+
+If REGISTER is non-nil, return the first responsible backend under
+which FILE is not yet registered.  If there is no such backend, return
+the first backend under which FILE is not yet registered, but could
+be registered."
+  (if (not vc-handled-backends)
+      (error "No handled backends"))
+  (or (and (not (file-directory-p file)) (not register) (vc-backend file))
       (catch 'found
-       (mapcar (lambda (backend)
-                 (if (vc-call-backend backend 'responsible-p file)
-                     (throw 'found backend)))
-               vc-handled-backends)
-       (if register
-           (mapcar (lambda (backend)
-                     (if (vc-call-backend backend 'could-register file)
-                         (throw 'found backend)))
-                   vc-handled-backends)
-         (car vc-handled-backends)))))
+       ;; First try: find a responsible backend.  If this is for registration,
+       ;; it must be a backend under which FILE is not yet registered.
+       (dolist (backend vc-handled-backends)
+         (and (or (not register)
+                  (not (vc-call-backend backend 'registered file)))
+              (vc-call-backend backend 'responsible-p file)
+              (throw 'found backend)))
+       ;; no responsible backend
+       (if (not register)
+           ;; if this is not for registration, the first backend must do
+           (car vc-handled-backends)
+         ;; for registration, we need to find a new backend that
+         ;; could register FILE
+         (dolist (backend vc-handled-backends)
+           (and (not (vc-call-backend backend 'registered file))
+                (vc-call-backend backend 'could-register file)
+                (throw 'found backend)))
+         (error "No backend that could register")))))
 
 (defun vc-default-responsible-p (backend file)
-  "Indicate whether BACKEND is reponsible for FILE.  
+  "Indicate whether BACKEND is reponsible for FILE.
 The default is to return nil always."
   nil)
 
@@ -1128,19 +1380,24 @@ rather than user editing!"
            (vc-resynch-window file keep noquery)))))
   (vc-dired-resynch-file file))
 
-(defun vc-start-entry (file rev comment msg action &optional after-hook)
+(defun vc-start-entry (file rev comment initial-contents msg action &optional after-hook)
   "Accept a comment for an operation on FILE revision REV.
 If COMMENT is nil, pop up a VC-log buffer, emit MSG, and set the
-action on close to ACTION; otherwise, do action immediately.  Remember
-the file's buffer in `vc-parent-buffer' (current one if no file).
-AFTER-HOOK specifies the local value for vc-log-operation-hook."
-  (let ((parent (if file (find-file-noselect file) (current-buffer))))
+action on close to ACTION.  If COMMENT is a string and
+INITIAL-CONTENTS is non-nil, then COMMENT is used as the initial
+contents of the log entry buffer.  If COMMENT is a string and
+INITIAL-CONTENTS is nil, do action immediately as if the user had
+entered COMMENT.  If COMMENT is t, also do action immediately with an
+empty comment.  Remember the file's buffer in `vc-parent-buffer'
+\(current one if no file).  AFTER-HOOK specifies the local value
+for vc-log-operation-hook."
+  (let ((parent (or (and file (get-file-buffer file)) (current-buffer))))
     (if vc-before-checkin-hook
         (if file
             (with-current-buffer parent
               (run-hooks 'vc-before-checkin-hook))
           (run-hooks 'vc-before-checkin-hook)))
-    (if comment
+    (if (and comment (not initial-contents))
        (set-buffer (get-buffer-create "*VC-log*"))
       (pop-to-buffer (get-buffer-create "*VC-log*")))
     (set (make-local-variable 'vc-parent-buffer) parent)
@@ -1153,19 +1410,22 @@ AFTER-HOOK specifies the local value for vc-log-operation-hook."
        (setq vc-log-after-operation-hook after-hook))
     (setq vc-log-operation action)
     (setq vc-log-version rev)
-    (if comment
-       (progn
-         (erase-buffer)
-         (if (eq comment t)
-             (vc-finish-logentry t)
-           (insert comment)
-           (vc-finish-logentry nil)))
-      (message "%s  Type C-c C-c when done" msg))))
+    (when comment
+      (erase-buffer)
+      (when (stringp comment) (insert comment)))
+    (if (or (not comment) initial-contents)
+       (message "%s  Type C-c C-c when done" msg)
+      (vc-finish-logentry (eq comment t)))))
 
 (defun vc-checkout (file &optional writable rev)
   "Retrieve a copy of the revision REV of FILE.
 If WRITABLE is non-nil, make sure the retrieved file is writable.
 REV defaults to the latest revision."
+  (and writable
+       (not rev)
+       (vc-call make-version-backups-p file)
+       (vc-up-to-date-p file)
+       (vc-make-version-backup file))
   (with-vc-properties
    file
    (condition-case err
@@ -1176,13 +1436,13 @@ REV defaults to the latest revision."
        (let ((buf (get-file-buffer file)))
          (when buf (with-current-buffer buf (toggle-read-only -1)))))
       (signal (car err) (cdr err))))
-   `((vc-state ,(if (or (eq (vc-checkout-model file) 'implicit)
-                       (not writable))
-                   (if (vc-call latest-on-branch-p file)
-                       'up-to-date
-                     'needs-patch)
-                 'edited))
-     (vc-checkout-time ,(nth 5 (file-attributes file)))))
+   `((vc-state ,(if (or (eq (vc-checkout-model file) 'implicit)
+                         (not writable))
+                     (if (vc-call latest-on-branch-p file)
+                         'up-to-date
+                       'needs-patch)
+                   'edited))
+     (vc-checkout-time ,(nth 5 (file-attributes file)))))
   (vc-resynch-buffer file t t))
 
 (defun vc-steal-lock (file rev owner)
@@ -1208,25 +1468,27 @@ REV defaults to the latest revision."
 (defun vc-finish-steal (file version)
   ;; This is called when the notification has been sent.
   (message "Stealing lock on %s..." file)
-  (with-vc-properties 
+  (with-vc-properties
    file
    (vc-call steal-lock file version)
-   `((vc-state edited)))
+   `((vc-state edited)))
   (vc-resynch-buffer file t t)
   (message "Stealing lock on %s...done" file))
 
-(defun vc-checkin (file &optional rev comment)
+(defun vc-checkin (file &optional rev comment initial-contents)
   "Check in FILE.
 The optional argument REV may be a string specifying the new version
 level (if nil increment the current level).  COMMENT is a comment
-string; if omitted, a buffer is popped up to accept 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 initial contents
+of the log entry buffer.
 
 If `vc-keep-workfiles' is nil, FILE is deleted afterwards, provided
 that the version control system supports this mode of operation.
 
 Runs the normal hook `vc-checkin-hook'."
   (vc-start-entry
-   file rev comment
+   file rev comment initial-contents
    "Enter a change comment."
    (lambda (file rev comment)
      (message "Checking in %s..." file)
@@ -1234,14 +1496,16 @@ Runs the normal hook `vc-checkin-hook'."
      ;; RCS 5.7 gripes about white-space-only comments too.
      (or (and comment (string-match "[^\t\n ]" comment))
         (setq comment "*** empty log message ***"))
-     (with-vc-properties 
+     (with-vc-properties
       file
       ;; Change buffers to get local value of vc-checkin-switches.
       (with-current-buffer (or (get-file-buffer file) (current-buffer))
-       (vc-call checkin file rev comment))
-      `((vc-state up-to-date)
-       (vc-checkout-time ,(nth 5 (file-attributes file)))
-       (vc-workfile-version nil)))
+       (let ((backup-file (vc-version-backup-file file)))
+         (vc-call checkin file rev comment)
+         (if backup-file (delete-file backup-file))))
+      `((vc-state . up-to-date)
+       (vc-checkout-time . ,(nth 5 (file-attributes file)))
+       (vc-workfile-version . nil)))
      (message "Checking in %s...done" file))
    'vc-checkin-hook))
 
@@ -1334,14 +1598,14 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
             (delete-windows-on logbuf (selected-frame))
             ;; Kill buffer and delete any other dedicated windows/frames.
             (kill-buffer logbuf))
-           (t (pop-to-buffer "*VC-log*")
-              (bury-buffer)
-              (pop-to-buffer tmp-vc-parent-buffer))))
+           (logbuf (pop-to-buffer "*VC-log*")
+                   (bury-buffer)
+                   (pop-to-buffer tmp-vc-parent-buffer))))
     ;; Now make sure we see the expanded headers
-    (if buffer-file-name
-       (vc-resynch-buffer buffer-file-name vc-keep-workfiles t))
+    (if log-file
+       (vc-resynch-buffer log-file vc-keep-workfiles t))
     (if vc-dired-mode
-        (dired-move-to-filename))
+      (dired-move-to-filename))
     (run-hooks after-hook 'vc-finish-logentry-hook)))
 
 ;; Code for access to the comment ring
@@ -1373,7 +1637,7 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
   (vc-previous-comment (- arg)))
 
 (defun vc-comment-search-reverse (str &optional stride)
-  "Searches backwards through comment history for substring match."
+  "Search backwards through comment history for substring match."
   ;; Why substring rather than regexp ?   -sm
   (interactive
    (list (read-string "Comment substring: " nil nil vc-last-comment-match)))
@@ -1391,7 +1655,7 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
     (vc-previous-comment 0)))
 
 (defun vc-comment-search-forward (str)
-  "Searches forwards through comment history for substring match."
+  "Search forwards through comment history for substring match."
   (interactive
    (list (read-string "Comment substring: " nil nil vc-last-comment-match)))
   (vc-comment-search-reverse str -1))
@@ -1451,11 +1715,12 @@ files in or below it."
                                    rel2-default ") ")
                          "Newer version (default: current source): ")
                        nil nil rel2-default))))
-  (if (string-equal rel1 "") (setq rel1 nil))
-  (if (string-equal rel2 "") (setq rel2 nil))
   (vc-setup-buffer "*vc-diff*")
   (if (file-directory-p file)
+      ;; recursive directory diff
       (let ((inhibit-read-only t))
+       (if (string-equal rel1 "") (setq rel1 nil))
+       (if (string-equal rel2 "") (setq rel2 nil))
        (insert "Diffs between "
                (or rel1 "last version checked in")
                " and "
@@ -1472,9 +1737,24 @@ files in or below it."
               (vc-call-backend ',(vc-backend file) 'diff ',f ',rel1 ',rel2)))))
        (vc-exec-after `(let ((inhibit-read-only t))
                          (insert "\nEnd of diffs.\n"))))
-    
-    (cd (file-name-directory file))
-    (vc-call diff file rel1 rel2))
+    ;; single file diff
+    (if (or (not rel1) (string-equal rel1 ""))
+       (setq rel1 (vc-workfile-version file)))
+    (if (string-equal rel2 "")
+       (setq rel2 nil))
+    (let ((file-rel1 (vc-version-backup-file file rel1))
+         (file-rel2 (if (not rel2)
+                        file
+                      (vc-version-backup-file file rel2))))
+      (if (and file-rel1 file-rel2)
+         (apply 'vc-do-command t 1 "diff" nil
+                (append (if (listp diff-switches)
+                            diff-switches
+                          (list diff-switches))
+                        (list (file-relative-name file-rel1)
+                              (file-relative-name file-rel2))))
+       (cd (file-name-directory file))
+       (vc-call diff file rel1 rel2))))
   (if (and (zerop (buffer-size))
           (not (get-buffer-process (current-buffer))))
       (progn
@@ -1488,8 +1768,11 @@ files in or below it."
     ;; Gnus-5.8.5 sets up an autoload for diff-mode, even if it's
     ;; not available.  Work around that.
     (if (require 'diff-mode nil t) (diff-mode))
-    (vc-exec-after '(progn (goto-char (point-min))
-                          (shrink-window-if-larger-than-buffer)))
+    (vc-exec-after '(let ((inhibit-read-only t))
+                     (if (eq (buffer-size) 0)
+                         (insert "No differences found.\n"))
+                     (goto-char (point-min))
+                     (shrink-window-if-larger-than-buffer)))
     t))
 
 ;;;###autoload
@@ -1499,13 +1782,17 @@ If the current buffer is named `F', the version is named `F.~REV~'.
 If `F.~REV~' already exists, it is used instead of being re-created."
   (interactive "sVersion to visit (default is workfile version): ")
   (vc-ensure-vc-buffer)
-  (let* ((version (if (string-equal rev "")
-                     (vc-workfile-version buffer-file-name)
+  (let* ((file buffer-file-name)
+        (version (if (string-equal rev "")
+                     (vc-workfile-version file)
                    rev))
-        (filename (concat buffer-file-name ".~" version "~")))
-    (or (file-exists-p filename)
-       (vc-call checkout buffer-file-name nil version filename))
-    (find-file-other-window filename)))
+        (automatic-backup (vc-version-backup-file-name file version))
+         (manual-backup (vc-version-backup-file-name file version 'manual)))
+    (unless (file-exists-p manual-backup)
+      (if (file-exists-p automatic-backup)
+          (rename-file automatic-backup manual-backup nil)
+        (vc-call checkout file nil version manual-backup)))
+    (find-file-other-window manual-backup)))
 
 ;; Header-insertion code
 
@@ -1555,7 +1842,7 @@ I.e. reset them to the non-expanded form."
              (save-excursion
                (vc-call-backend backend 'clear-headers))
              (vc-restore-buffer-context context))
-         (find-file filename)
+         (set-buffer (find-file-noselect filename))
          (vc-call-backend backend 'clear-headers)
          (kill-buffer filename)))))
 
@@ -1583,7 +1870,7 @@ See Info node `Merging'."
           "File must be checked out for merging.  Check out now? ")
          (vc-checkout file t)
        (error "Merge aborted"))))
-    (setq first-version 
+    (setq first-version
          (read-string (concat "Branch or version to merge from "
                               "(default: news on current branch): ")))
     (if (string= first-version "")
@@ -1593,8 +1880,8 @@ See Info node `Merging'."
       (if (not (vc-find-backend-function backend 'merge))
          (error "Sorry, merging is not implemented for %s" backend)
        (if (not (vc-branch-p first-version))
-           (setq second-version 
-                 (read-string "Second version: " 
+           (setq second-version
+                 (read-string "Second version: "
                               (concat (vc-branch-part first-version) ".")))
          ;; We want to merge an entire branch.  Set versions
          ;; accordingly, so that vc-BACKEND-merge understands us.
@@ -2073,40 +2360,99 @@ allowed and simply skipped)."
                               'show-log-entry
                               ',(vc-workfile-version file))))))))
 
+(defun vc-default-comment-history (backend file)
+  "Return a string with all log entries that were made under BACKEND for FILE."
+  (if (vc-find-backend-function backend 'print-log)
+      (with-temp-buffer
+       (vc-call print-log file)
+       (vc-call wash-log file)
+       (buffer-string))))
+
+(defun vc-default-wash-log (backend file)
+  "Remove all non-comment information from log output.
+This default implementation works for RCS logs; backends should override
+it if their logs are not in RCS format."
+  (let ((separator (concat "^-+\nrevision [0-9.]+\ndate: .*\n"
+                          "\\(branches: .*;\n\\)?"
+                          "\\(\\*\\*\\* empty log message \\*\\*\\*\n\\)?")))
+    (goto-char (point-max)) (forward-line -1)
+    (while (looking-at "=*\n")
+      (delete-char (- (match-end 0) (match-beginning 0)))
+      (forward-line -1))
+    (goto-char (point-min))
+    (if (looking-at "[\b\t\n\v\f\r ]+")
+       (delete-char (- (match-end 0) (match-beginning 0))))
+    (goto-char (point-min))
+    (re-search-forward separator nil t)
+    (delete-region (point-min) (point))
+    (while (re-search-forward separator nil t)
+      (delete-region (match-beginning 0) (match-end 0)))))
+
 ;;;###autoload
 (defun vc-revert-buffer ()
   "Revert the current buffer's file back to the version it was based on.
 This asks for confirmation if the buffer contents are not identical
-to that version.  Note that for RCS and CVS, this function does not
-automatically pick up newer changes found in the master file;
-use \\[universal-argument] \\[vc-next-action] to do so."
+to that version.  This function does not automatically pick up newer
+changes found in the master file; use \\[universal-argument] \\[vc-next-action] to do so."
   (interactive)
   (vc-ensure-vc-buffer)
   (let ((file buffer-file-name)
        ;; This operation should always ask for confirmation.
        (vc-suppress-confirm nil)
-       (obuf (current-buffer)))
+       (obuf (current-buffer))
+       status)
+    (if (vc-up-to-date-p file)
+        (unless (yes-or-no-p "File seems up-to-date.  Revert anyway? ")
+          (error "Revert canceled")))
     (unless (vc-workfile-unchanged-p file)
-      (vc-diff nil t)
-      (vc-exec-after `(message nil))
-      (unwind-protect
-         (if (not (yes-or-no-p "Discard changes? "))
-             (error "Revert canceled"))
-       (if (or (window-dedicated-p (selected-window))
-               (one-window-p t 'selected-frame))
-           (make-frame-invisible (selected-frame))
-         (delete-window))))
+      ;; vc-diff selects the new window, which is not what we want:
+      ;; if the new window is on another frame, that'd require the user
+      ;; moving her mouse to answer the yes-or-no-p question.
+      (let ((win (save-selected-window
+                  (setq status (vc-diff nil t)) (selected-window))))
+       (vc-exec-after `(message nil))
+       (when status
+         (unwind-protect
+             (unless (yes-or-no-p "Discard changes? ")
+               (error "Revert canceled"))
+           (select-window win)
+           (if (one-window-p t)
+               (if (window-dedicated-p (selected-window))
+                   (make-frame-invisible))
+             (delete-window))))))
     (set-buffer obuf)
     ;; Do the reverting
     (message "Reverting %s..." file)
-    (with-vc-properties
-     file
-     (vc-call revert file)
-     `((vc-state up-to-date)
-       (vc-checkout-time ,(nth 5 (file-attributes file)))))
-    (vc-resynch-buffer file t t)
+    (vc-revert-file file)
     (message "Reverting %s...done" file)))
 
+(defun vc-version-backup-file (file &optional rev)
+  "Return name of backup file for revision REV of FILE.
+If version backups should be used for FILE, and there exists
+such a backup for REV or the current workfile version of file,
+return its name; otherwise return nil."
+  (when (vc-call make-version-backups-p file)
+    (let ((backup-file (vc-version-backup-file-name file rev)))
+      (if (file-exists-p backup-file)
+          backup-file
+        ;; there is no automatic backup, but maybe the user made one manually
+        (setq backup-file (vc-version-backup-file-name file rev 'manual))
+        (if (file-exists-p backup-file)
+            backup-file)))))
+
+(defun vc-revert-file (file)
+  "Revert FILE back to the version it was based on."
+  (with-vc-properties
+   file
+   (let ((backup-file (vc-version-backup-file file)))
+     (if (not backup-file)
+        (vc-call revert file)
+       (copy-file backup-file file 'ok-if-already-exists 'keep-date)
+       (vc-delete-automatic-version-backups file)))
+   `((vc-state . up-to-date)
+     (vc-checkout-time . ,(nth 5 (file-attributes file)))))
+  (vc-resynch-buffer file t t))
+
 ;;;###autoload
 (defun vc-cancel-version (norevert)
   "Get rid of most recently checked in version of this file.
@@ -2133,11 +2479,11 @@ A prefix argument NOREVERT means do not revert the buffer afterwards."
       (with-vc-properties
        file
        (vc-call cancel-version file norevert)
-       `((vc-state ,(if norevert 'edited 'up-to-date))
-        (vc-checkout-time ,(if norevert 
-                               0 
+       `((vc-state ,(if norevert 'edited 'up-to-date))
+        (vc-checkout-time . ,(if norevert
+                               0
                              (nth 5 (file-attributes file))))
-        (vc-workfile-version nil)))
+        (vc-workfile-version nil)))
       (message "Removing last change from %s...done" file)
 
       (cond
@@ -2155,6 +2501,110 @@ A prefix argument NOREVERT means do not revert the buffer afterwards."
        (vc-resynch-buffer file t t)))
       (message "Version %s has been removed from the master" target))))
 
+;;;autoload
+(defun vc-switch-backend (file backend)
+  "Make BACKEND the current version control system for FILE.
+FILE must already be registered in BACKEND.  The change is not
+permanent, only for the current session.  This function only changes
+VC's perspective on FILE, it does not register or unregister it.
+By default, this command cycles through the registered backends.
+To get a prompt, use a prefix argument."
+  (interactive
+   (list
+    buffer-file-name
+    (let ((backend (vc-backend buffer-file-name))
+         (backends nil))
+      ;; Find the registered backends.
+      (dolist (backend vc-handled-backends)
+       (when (vc-call-backend backend 'registered buffer-file-name)
+         (push backend backends)))
+      ;; Find the next backend.
+      (let ((def (car (delq backend (append (memq backend backends) backends))))
+           (others (delete backend backends)))
+       (cond
+        ((null others) (error "No other backend to switch to"))
+        (current-prefix-arg
+         (intern
+          (upcase
+           (completing-read
+            (format "Switch to backend [%s]: " def)
+            (mapcar (lambda (b) (list (downcase (symbol-name b)))) backends)
+            nil t nil nil (downcase (symbol-name def))))))
+       (t def))))))
+  (unless (eq backend (vc-backend file))
+    (vc-file-clearprops file)
+    (vc-file-setprop file 'vc-backend backend)
+    ;; Force recomputation of the state
+    (unless (vc-call-backend backend 'registered file)
+      (vc-file-clearprops file)
+      (error "%s is not registered in %s" file backend))
+    (vc-mode-line file)))
+
+;;;autoload
+(defun vc-transfer-file (file new-backend)
+  "Transfer FILE to another version control system NEW-BACKEND.
+If NEW-BACKEND has a higher precedence than FILE's current backend
+\(i.e.  it comes earlier in `vc-handled-backends'), then register FILE in
+NEW-BACKEND, using the version number from the current backend as the
+base level.  If NEW-BACKEND has a lower precedence than the current
+backend, then commit all changes that were made under the current
+backend to NEW-BACKEND, and unregister FILE from the current backend.
+\(If FILE is not yet registered under NEW-BACKEND, register it.)"
+  (let* ((old-backend (vc-backend file))
+        (edited (memq (vc-state file) '(edited needs-merge)))
+        (registered (vc-call-backend new-backend 'registered file))
+        (move
+         (and registered    ; Never move if not registered in new-backend yet.
+              ;; move if new-backend comes later in vc-handled-backends
+              (or (memq new-backend (memq old-backend vc-handled-backends))
+                  (y-or-n-p "Final transfer? "))))
+        (comment nil))
+    (if (eq old-backend new-backend)
+       (error "%s is the current backend of %s" new-backend file))
+    (if registered
+       (set-file-modes file (logior (file-modes file) 128))
+      ;; `registered' might have switched under us.
+      (vc-switch-backend file old-backend)
+      (let* ((rev (vc-workfile-version file))
+            (modified-file (and edited (make-temp-name file)))
+            (unmodified-file (and modified-file (vc-version-backup-file file))))
+       ;; Go back to the base unmodified file.
+       (unwind-protect
+           (progn
+             (when modified-file
+               (copy-file file modified-file)
+               ;; If we have a local copy of the unmodified file, handle that
+               ;; here and not in vc-revert-file because we don't want to
+               ;; delete that copy -- it is still useful for OLD-BACKEND.
+               (if unmodified-file
+                   (copy-file unmodified-file file 'ok-if-already-exists)
+                 (if (y-or-n-p "Get base version from master? ")
+                     (vc-revert-file file))))
+             (vc-call-backend new-backend 'receive-file file rev))
+         (when modified-file
+           (vc-switch-backend file new-backend)
+           (unless (eq (vc-checkout-model file) 'implicit)
+             (vc-checkout file t nil))
+           (rename-file modified-file file 'ok-if-already-exists)
+           (vc-file-setprop file 'vc-checkout-time nil)))))
+    (when move
+      (vc-switch-backend file old-backend)
+      (setq comment (vc-call comment-history file))
+      (vc-call unregister file))
+    (vc-switch-backend file new-backend)
+    (when (or move edited)
+      (vc-file-setprop file 'vc-state 'edited)
+      (vc-mode-line file)
+      (vc-checkin file nil comment (stringp comment)))))
+
+(defun vc-default-unregister (backend file)
+  "Default implementation of `vc-unregister', signals an error."
+  (error "Unregistering files is not supported for %s" backend))
+
+(defun vc-default-receive-file (backend file rev)
+  "Let BACKEND receive FILE from another version control system."
+  (vc-call-backend backend 'register file rev ""))
+
 (defun vc-rename-master (oldmaster newfile templates)
   "Rename OLDMASTER to be the master file for NEWFILE based on TEMPLATES."
   (let* ((dir (file-name-directory (expand-file-name oldmaster)))
@@ -2190,12 +2640,6 @@ A prefix argument NOREVERT means do not revert the buffer afterwards."
 (defun vc-rename-file (old new)
   "Rename file OLD to NEW, and rename its master file likewise."
   (interactive "fVC rename file: \nFRename to: ")
-  ;; There are several ways of renaming files under CVS 1.3, but they all
-  ;; have serious disadvantages.  See the FAQ (available from think.com in
-  ;; pub/cvs/).  I'd rather send the user an error, than do something he might
-  ;; consider to be wrong.  When the famous, long-awaited rename database is
-  ;; implemented things might change for the better.  This is unlikely to occur
-  ;; until CVS 2.0 is released.  --ceder 1994-01-23 21:27:51
   (let ((oldbuf (get-file-buffer old))
        (backend (vc-backend old)))
     (unless (or (null backend) (vc-find-backend-function backend 'rename-file))
@@ -2380,30 +2824,40 @@ menu items."
 ;;;;  the contents in BUFFER.
 
 ;;;###autoload
-(defun vc-annotate (ratio)
+(defun vc-annotate (prefix)
   "Display the result of the \"Annotate\" command using colors.
 \"Annotate\" is defined by `vc-BACKEND-annotate-command'.  New lines
-are displayed in red, old in blue.  A prefix argument specifies a
-factor for stretching the time scale.
+are displayed in red, old in blue.  When given a prefix argument, asks
+for a version to annotate from, and a factor for stretching the time 
+scale.
 
 `vc-annotate-menu-elements' customizes the menu elements of the
 mode-specific menu. `vc-annotate-color-map' and
 `vc-annotate-very-old-color' defines the mapping of time to
 colors. `vc-annotate-background' specifies the background color."
-  (interactive "p")
+  (interactive "P")
   (vc-ensure-vc-buffer)
-  (message "Annotating...")
   (let ((temp-buffer-name (concat "*Annotate " (buffer-name) "*"))
-       (temp-buffer-show-function 'vc-annotate-display)
-       (vc-annotate-ratio ratio)
-       (vc-annotate-backend (vc-backend (buffer-file-name))))
+        (temp-buffer-show-function 'vc-annotate-display)
+        (vc-annotate-version 
+         (if prefix (read-string 
+                     (format "Annotate from version: (default %s) "
+                             (vc-workfile-version (buffer-file-name)))
+                     nil nil (vc-workfile-version (buffer-file-name)))))
+        (vc-annotate-ratio 
+         (if prefix (string-to-number
+                     (read-string "Annotate ratio: (default 1.0) " 
+                                  nil nil "1.0"))))
+        (vc-annotate-backend (vc-backend (buffer-file-name))))
+    (message "Annotating...")
     (if (not (vc-find-backend-function vc-annotate-backend 'annotate-command))
        (error "Sorry, annotating is not implemented for %s"
               vc-annotate-backend))
-    (with-output-to-temp-buffer temp-buffer-name 
+    (with-output-to-temp-buffer temp-buffer-name
       (vc-call-backend vc-annotate-backend 'annotate-command
                       (file-name-nondirectory (buffer-file-name))
-                      (get-buffer temp-buffer-name)))
+                      (get-buffer temp-buffer-name)
+                       vc-annotate-version))
     ;; Don't use the temp-buffer-name until the buffer is created
     ;; (only after `with-output-to-temp-buffer'.)
     (setq vc-annotate-buffers
@@ -2442,16 +2896,6 @@ nil otherwise"
    tmp-cons))                          ; Return the appropriate value
 
 
-;;;; (defun vc-BACKEND-annotate-difference (point) ...)
-;;;;
-;;;;  Return the difference between the age of the line at point and
-;;;;  the current time.  Return NIL if there is no more comparison to
-;;;;  be made in the buffer.  Return value as defined for
-;;;;  `current-time'.  You can safely assume that point is placed at
-;;;;  the beginning of each line, starting at `point-min'.  The buffer
-;;;;  that point is placed in is the Annotate output, as defined by
-;;;;  the relevant backend.
-
 (defun vc-annotate-display (buffer &optional color-map backend)
   "Do the VC-Annotate display in BUFFER using COLOR-MAP.
 The original annotating file is supposed to be handled by BACKEND.
@@ -2515,6 +2959,10 @@ This function is destructive on VC-ANNOTATE-BACKEND when BACKEND is non-nil."
   (interactive)
   (vc-call-backend (vc-backend buffer-file-name) 'check-headers))
 
+(defun vc-default-check-headers (backend)
+  "Default implementation of check-headers; always returns nil."
+  nil)
+
 ;; Back-end-dependent stuff ends here.
 
 ;; Set up key bindings for use while editing log messages
@@ -2558,8 +3006,7 @@ Global user options:
        `vc-keep-workfiles'     Non-nil value prevents workfiles from being
                                deleted when changes are checked in
 
-        `vc-suppress-confirm'  Suppresses some confirmation prompts,
-                               notably for reversions.
+        `vc-suppress-confirm'  Suppresses some confirmation prompts.
 
        vc-BACKEND-header       Which keywords to insert when adding headers
                                with \\[vc-insert-headers].  Defaults to