(Fexpand_file_name): In the no-handler case, after
[bpt/emacs.git] / lisp / vc.el
index 4dba0fe..504ca76 100644 (file)
@@ -1,9 +1,12 @@
 ;;; vc.el --- drive a version-control system from within Emacs
 
-;; Copyright (C) 1992,93,94,95,96,97,98,2000  Free Software Foundation, Inc.
+;; Copyright (C) 1992,93,94,95,96,97,98,2000,2001  Free Software Foundation, Inc.
 
 ;; Author:     FSF (see below for full credits)
 ;; Maintainer: Andre Spiegel <spiegel@gnu.org>
+;; Keywords: tools
+
+;; $Id: vc.el,v 1.349 2003/02/05 23:13:21 lektu Exp $
 
 ;; This file is part of GNU Emacs.
 
 ;;   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>
+;;   J.D. Smith <jdsmith@alum.mit.edu>
 ;;   Andre Spiegel <spiegel@gnu.org>
 ;;   Richard Stallman <rms@gnu.org>
-;;   ttn@netcom.com
+;;   Thien-Thi Nguyen <ttn@gnu.org>
 
 ;;; Commentary:
 
 ;;
 ;; 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 `*' are optional.
-
+;; 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)
+;;
+;;   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.  (Note that vc-BACKEND-diff must not run
+;;   asynchronously in this case.)
+;;
 ;; - 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)
-;; * unregister (file backend)
-;; - receive-file (file move)
+;;
+;;   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.
+;;   The implementation should pass the value of vc-register-switches
+;;   to the backend command.
+;;
+;; - init-version (file)
+;;
+;;   The initial version to use when registering FILE if one is not
+;;   specified by the user.  If not provided, the variable
+;;   vc-default-init-version is used instead.
+;;
 ;; - 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)
-;; - logentry-check ()
-;; * 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)
-;; - 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'.
-;; - 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.
+;;
+;;   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.  The implementation should pass the value of
+;;   vc-checkin-switches to the backend command.
+;;
+;; * find-version (file rev buffer)
+;;
+;;   Fetch revision REV of file FILE and put it into BUFFER.
+;;   If REV is the empty string, fetch the head of the trunk.
+;;   The implementation should pass the value of vc-checkout-switches
+;;   to the backend command.
+;;
+;; * checkout (file &optional editable rev)
+;;
+;;   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 t, that means to check out the head of the current branch;
+;;   if it is the empty string, check out the head of the trunk.
+;;   The implementation should pass the value of vc-checkout-switches
+;;   to the backend command.
+;;
+;; * revert (file &optional contents-done)
+;;
+;;   Revert FILE back to the current workfile version.  If optional
+;;   arg CONTENTS-DONE is non-nil, then the contents of FILE have
+;;   already been reverted from a version backup, and this function
+;;   only needs to update the status of FILE within the backend.
+;;
+;; - 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.
+;;
+;;   Insert the revision log of FILE into the *vc* 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)
-;;     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)
+;;
+;;   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 *vc-diff* 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
+;;   pass the value of (vc-diff-switches-list BACKEND) to the backend
+;;   command.  It should return a status of either 0 (no differences
+;;   found), or 1 (either non-empty diff or the diff is run
+;;   asynchronously).
+;;
+;; - diff-tree (dir &optional rev1 rev2)
+;;
+;;   Insert the diff for all files at and below DIR into the *vc-diff*
+;;   buffer.  The meaning of REV1 and REV2 is the same as for
+;;   vc-BACKEND-diff.  The default implementation does an explicit tree
+;;   walk, calling vc-BACKEND-diff for each individual file.
+;;
+;; - 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-time ()
+;;
+;;   Only required if `annotate-command' is defined for the backend.
+;;   Return the time of the next line of annotation at or after point,
+;;   as a floating point fractional number of days.  The helper
+;;   function `vc-annotate-convert-time' may be useful for converting
+;;   multi-part times as returned by `current-time' and `encode-time'
+;;   to this format.  Return nil if no more lines of annotation appear
+;;   in the buffer.  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.
+;;
+;; - annotate-current-time ()
+;;
+;;   Only required if `annotate-command' is defined for the backend,
+;;   AND you'd like the current time considered to be anything besides
+;;   (vs-annotate-convert-time (current-time)) -- i.e. the current
+;;   time with hours, minutes, and seconds included.  Probably safe to
+;;   ignore.  Return the current-time, in units of fractional days.
+;;
+;; 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 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.  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.
+;;
+;; - previous-version (file rev)
+;;
+;;   Return the version number that precedes REV for FILE.
+;;
+;; - 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}
@@ -185,7 +443,8 @@ preserve the setting."
 
 (defcustom vc-default-init-version "1.1"
   "*A string used as the default version number when a new file is registered.
-This can be overridden by giving a prefix argument to \\[vc-register]."
+This can be overridden by giving a prefix argument to \\[vc-register].  This
+can also be overridden by a particular VC backend."
   :type 'string
   :group 'vc
   :version "20.3")
@@ -244,16 +503,44 @@ These are passed to the checkin program by \\[vc-register]."
   :version "20.3")
 
 (defcustom vc-directory-exclusion-list '("SCCS" "RCS" "CVS")
-  "*List of directory names to be ignored while recursively walking file trees."
+  "*List of directory names to be ignored when walking directory trees."
   :type '(repeat string)
   :group 'vc)
 
 (defconst vc-maximum-comment-ring-size 32
   "Maximum number of saved comments in the comment ring.")
 
-;;; This is duplicated in diff.el.
-(defvar diff-switches "-c"
-  "*A string or list of strings specifying switches to be passed to diff.")
+(defcustom vc-diff-switches nil
+  "*A string or list of strings specifying switches for diff under VC.
+When running diff under a given BACKEND, VC concatenates the values of
+`diff-switches', `vc-diff-switches', and `vc-BACKEND-diff-switches' to
+get the switches for that command.  Thus, `vc-diff-switches' should
+contain switches that are specific to version control, but not
+specific to any particular backend."
+  :type '(choice (const :tag "None" nil)
+                (string :tag "Argument String")
+                (repeat :tag "Argument List"
+                        :value ("")
+                        string))
+  :group 'vc
+  :version "21.1")
+
+;;;###autoload
+(defcustom vc-checkout-hook nil
+  "*Normal hook (list of functions) run after checking out a file.
+See `run-hooks'."
+  :type 'hook
+  :group 'vc
+  :version "21.1")
+
+(defcustom vc-annotate-display-mode nil
+  "Which mode to color the output of \\[vc-annotate] with by default."
+  :type '(choice (const :tag "Default" nil)
+                (const :tag "Scale to Oldest" scale)
+                (const :tag "Scale Oldest->Newest" fullscale)
+                (number :tag "Specify Fractional Number of Days"
+                        :value "20.5"))
+  :group 'vc)
 
 ;;;###autoload
 (defcustom vc-checkin-hook nil
@@ -265,7 +552,7 @@ See `run-hooks'."
 
 ;;;###autoload
 (defcustom vc-before-checkin-hook nil
-  "*Normal hook (list of functions) run before a file gets checked in.
+  "*Normal hook (list of functions) run before a file is checked in.
 See `run-hooks'."
   :type 'hook
   :group 'vc)
@@ -279,31 +566,31 @@ version control backend imposes itself."
 
 ;; Annotate customization
 (defcustom vc-annotate-color-map
-  '(( 26.3672 . "#FF0000")
-    ( 52.7344 . "#FF3800")
-    ( 79.1016 . "#FF7000")
-    (105.4688 . "#FFA800")
-    (131.8359 . "#FFE000")
-    (158.2031 . "#E7FF00")
-    (184.5703 . "#AFFF00")
-    (210.9375 . "#77FF00")
-    (237.3047 . "#3FFF00")
-    (263.6719 . "#07FF00")
-    (290.0391 . "#00FF31")
-    (316.4063 . "#00FF69")
-    (342.7734 . "#00FFA1")
-    (369.1406 . "#00FFD9")
-    (395.5078 . "#00EEFF")
-    (421.8750 . "#00B6FF")
-    (448.2422 . "#007EFF"))
+  '(( 20. . "#FF0000")
+    ( 40. . "#FF3800")
+    ( 60. . "#FF7000")
+    ( 80. . "#FFA800")
+    (100. . "#FFE000")
+    (120. . "#E7FF00")
+    (140. . "#AFFF00")
+    (160. . "#77FF00")
+    (180. . "#3FFF00")
+    (200. . "#07FF00")
+    (220. . "#00FF31")
+    (240. . "#00FF69")
+    (260. . "#00FFA1")
+    (280. . "#00FFD9")
+    (300. . "#00EEFF")
+    (320. . "#00B6FF")
+    (340. . "#007EFF"))
   "*Association list of age versus color, for \\[vc-annotate].
-Ages are given in units of 2**-16 seconds.
-Default is eighteen steps using a twenty day increment."
+Ages are given in units of fractional days.  Default is eighteen steps
+using a twenty day increment."
   :type 'alist
   :group 'vc)
 
 (defcustom vc-annotate-very-old-color "#0046FF"
-  "*Color for lines older than CAR of last cons in `vc-annotate-color-map'."
+  "*Color for lines older than the current color range in \\[vc-annotate]]."
   :type 'string
   :group 'vc)
 
@@ -347,7 +634,7 @@ the file's version control type in `vc-header-alist'."
 
 (defcustom vc-comment-alist
   '((nroff-mode ".\\\"" ""))
-  "*Special comment delimiters to be used in generating vc headers only.
+  "*Special comment delimiters for generating VC headers.
 Add an entry in this list if you need to override the normal `comment-start'
 and `comment-end' variables.  This will only be necessary if the mode language
 is sensitive to blank lines."
@@ -357,43 +644,23 @@ is sensitive to blank lines."
                       (string :tag "Comment End")))
   :group 'vc)
 
-;; Default is to be extra careful for super-user.
-;; TODO: This variable is no longer used; the corresponding checks
-;;       are always done now.  If that turns out to be fast enough,
-;;       the variable can be obsoleted.
 (defcustom vc-checkout-carefully (= (user-uid) 0)
   "*Non-nil means be extra-careful in checkout.
 Verify that the file really is not locked
 and that its contents match what the master file says."
   :type 'boolean
   :group 'vc)
+(make-obsolete-variable 'vc-checkout-carefully
+                        "the corresponding checks are always done now."
+                        "21.1")
 
 \f
-;;; The main keymap
-
-(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)
-    (define-key map "h" 'vc-insert-headers)
-    (define-key map "i" 'vc-register)
-    (define-key map "l" 'vc-print-log)
-    (define-key map "m" 'vc-merge)
-    (define-key map "r" 'vc-retrieve-snapshot)
-    (define-key map "s" 'vc-create-snapshot)
-    (define-key map "u" 'vc-revert-buffer)
-    (define-key map "v" 'vc-next-action)
-    (define-key map "=" 'vc-diff)
-    (define-key map "~" 'vc-version-other-window)
-    map))
-(fset 'vc-prefix-map vc-prefix-map)
+;; The main keymap
 
 ;; Initialization code, to be done just once at load-time
 (defvar vc-log-mode-map
   (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map text-mode-map)
     (define-key map "\M-n" 'vc-next-comment)
     (define-key map "\M-p" 'vc-previous-comment)
     (define-key map "\M-r" 'vc-comment-search-reverse)
@@ -428,9 +695,9 @@ The keys are \(BUFFER . BACKEND\).  See also `vc-annotate-get-backend'.")
 (defvar vc-comment-ring-index nil)
 (defvar vc-last-comment-match "")
 
-;;; functions that operate on RCS revision numbers.  This code should
-;;; also be moved into the backends.  It stays for now, however, since
-;;; it is used in code below.
+;; functions that operate on RCS revision numbers.  This code should
+;; also be moved into the backends.  It stays for now, however, since
+;; it is used in code below.
 (defun vc-trunk-p (rev)
   "Return t if REV is a revision on the trunk."
   (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
@@ -439,80 +706,89 @@ The keys are \(BUFFER . BACKEND\).  See also `vc-annotate-get-backend'.")
   "Return t if REV is a branch revision."
   (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
 
+;;;###autoload
 (defun vc-branch-part (rev)
   "Return the branch part of a revision number REV."
-  (substring rev 0 (string-match "\\.[0-9]+\\'" rev)))
+  (let ((index (string-match "\\.[0-9]+\\'" rev)))
+    (if index
+        (substring rev 0 index))))
 
 (defun vc-minor-part (rev)
   "Return the minor version number of a revision number REV."
   (string-match "[0-9]+\\'" rev)
   (substring rev (match-beginning 0) (match-end 0)))
 
-(defun vc-previous-version (rev)
-  "Guess the version number immediately preceding REV."
+(defun vc-default-previous-version (backend file rev)
+  "Guess the version number immediately preceding REV for FILE.
+This default implementation works for <major>.<minor>-style version numbers
+as used by RCS and CVS."
   (let ((branch (vc-branch-part rev))
         (minor-num (string-to-number (vc-minor-part rev))))
-    (if (> minor-num 1)
-        ;; version does probably not start a branch or release
-        (concat branch "." (number-to-string (1- minor-num)))
-      (if (vc-trunk-p rev)
-          ;; we are at the beginning of the trunk --
-          ;; don't know anything to return here
-          ""
-        ;; we are at the beginning of a branch --
-        ;; return version of starting point
-        (vc-branch-part branch)))))
+    (when branch
+      (if (> minor-num 1)
+          ;; version does probably not start a branch or release
+          (concat branch "." (number-to-string (1- minor-num)))
+        (if (vc-trunk-p rev)
+            ;; we are at the beginning of the trunk --
+            ;; don't know anything to return here
+            nil
+          ;; we are at the beginning of a branch --
+          ;; return version of starting point
+          (vc-branch-part branch))))))
 
 ;; File property caching
 
 (defun vc-clear-context ()
   "Clear all cached file properties and the comment ring."
   (interactive)
-  (fillarray vc-file-prop-obarray nil)
+  (fillarray vc-file-prop-obarray 0)
   ;; Note: there is potential for minor lossage here if there is an open
   ;; log buffer with a nonzero local value of vc-comment-ring-index.
   (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)."
-  `(let ((vc-touched-properties (list t))
-        (filename ,file))
+  "Execute FORM, then maybe set per-file properties for FILE.
+SETTINGS is an association list of property/value pairs.  After
+executing FORM, set those properties from SETTINGS that have not yet
+been updated to their corresponding values."
+  `(let ((vc-touched-properties (list t)))
      ,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 ,file vc-file-prop-obarray)
+                       property (cdr setting)))))
             ,settings)))
 
 ;; Random helper functions
 
 (defsubst vc-editable-p (file)
+  "Return non-nil if FILE can be edited."
   (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
+;; Two macros for elisp programming
 ;;;###autoload
 (defmacro with-vc-file (file comment &rest body)
-  "Check out a writable copy of FILE if necessary and execute the body.
+  "Check out a writable copy of FILE if necessary, then execute BODY.
 Check in FILE with COMMENT (a string) after BODY has been executed.
 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)
-        (error (format "File not under version control: `%s'" file)))
-     (unless (vc-editable-p file)
-       (let ((state (vc-state file)))
-        (if (stringp state) (error (format "`%s' is locking `%s'" state file))
-          (vc-checkout file t))))
-     (save-excursion
-       ,@body)
-     (vc-checkin file nil ,comment)))
+  (let ((filevar (make-symbol "file")))
+    `(let ((,filevar (expand-file-name ,file)))
+       (or (vc-backend ,filevar)
+           (error (format "File not under version control: `%s'" file)))
+       (unless (vc-editable-p ,filevar)
+         (let ((state (vc-state ,filevar)))
+           (if (stringp state)
+               (error (format "`%s' is locking `%s'" state ,filevar))
+             (vc-checkout ,filevar t))))
+       (save-excursion
+         ,@body)
+       (vc-checkin ,filevar nil ,comment))))
+
+(put 'with-vc-file 'lisp-indent-function 2)
 
 ;;;###autoload
 (defmacro edit-vc-file (file comment &rest body)
@@ -520,11 +796,15 @@ somebody else, signal error."
 Checkin with COMMENT after executing BODY.
 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)
-    ,@body
-    (save-buffer)))
+  (let ((filevar (make-symbol "file")))
+    `(let ((,filevar (expand-file-name ,file)))
+       (with-vc-file
+        ,filevar ,comment
+        (set-buffer (find-file-noselect ,filevar))
+        ,@body
+        (save-buffer)))))
+
+(put 'edit-vc-file 'lisp-indent-function 2)
 
 (defun vc-ensure-vc-buffer ()
   "Make sure that the current buffer visits a version-controlled file."
@@ -537,12 +817,6 @@ However, before executing BODY, find FILE, and after BODY, save buffer."
       (if (not (vc-backend (buffer-file-name)))
          (error "File %s is not under version control" (buffer-file-name))))))
 
-(defvar vc-binary-assoc nil)
-(defvar vc-binary-suffixes
-  (if (memq system-type '(ms-dos windows-nt))
-      '(".exe" ".com" ".bat" ".cmd" ".btm" "")
-    '("")))
-
 (defun vc-process-filter (p s)
   "An alternative output filter for async process P.
 The only difference with the default filter is to insert S after markers."
@@ -554,7 +828,7 @@ The only difference with the default filter is to insert S after markers."
        (set-marker (process-mark p) (point))))))
 
 (defun vc-setup-buffer (&optional buf)
-  "Prepare BUF for executing a VC command and make it the current buffer.
+  "Prepare BUF for executing a VC command and make it current.
 BUF defaults to \"*vc*\", can be a string and will be created if necessary."
   (unless buf (setq buf "*vc*"))
   (let ((camefrom (current-buffer))
@@ -598,23 +872,29 @@ Else, add CODE to the process' sentinel."
 (defvar vc-post-command-functions nil
   "Hook run at the end of `vc-do-command'.
 Each function is called inside the buffer in which the command was run
-and is passed 3 argument: the COMMAND, the FILE and the FLAGS.")
+and is passed 3 arguments: the COMMAND, the FILE and the FLAGS.")
 
+;;;###autoload
 (defun vc-do-command (buffer okstatus command file &rest flags)
-  "Execute a version control command, notifying user and checking for errors.
-Output from COMMAND goes to BUFFER, or *vc* if BUFFER is nil or the current
-buffer (which is assumed to be properly setup) if BUFFER is t.  The
-command is considered successful if its exit status does not exceed
-OKSTATUS (if OKSTATUS is nil, that means to ignore errors, if it is 'async,
-that means not to wait for termination of the subprocess).  FILE is
-the name of the working file (may also be nil, to execute commands
-that don't expect a file name).  If an optional list of FLAGS is present,
+  "Execute a VC command, notifying user and checking for errors.
+Output from COMMAND goes to BUFFER, or *vc* if BUFFER is nil or the
+current buffer if BUFFER is t.  If the destination buffer is not
+already current, set it up properly and erase it.  The command is
+considered successful if its exit status does not exceed OKSTATUS (if
+OKSTATUS is nil, that means to ignore errors, if it is 'async, that
+means not to wait for termination of the subprocess).  FILE is the
+name of the working file (may also be nil, to execute commands that
+don't expect a file name).  If an optional list of FLAGS is present,
 that is inserted into the command line before the filename."
   (and file (setq file (expand-file-name file)))
   (if vc-command-messages
       (message "Running %s on %s..." command file))
   (save-current-buffer
-    (unless (eq buffer t) (vc-setup-buffer buffer))
+    (unless (or (eq buffer t)
+                (and (stringp buffer)
+                     (string= (buffer-name) buffer))
+                (eq buffer (current-buffer)))
+      (vc-setup-buffer buffer))
     (let ((squeezed nil)
          (inhibit-read-only t)
          (status 0))
@@ -635,11 +915,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))
@@ -665,7 +947,8 @@ if markers are destroyed or corrupted."
                          (min (point-max) (+ posn 100)))))
 
 (defun vc-find-position-by-context (context)
-  "Return the position of CONTEXT in the current buffer, or nil if not found."
+  "Return the position of CONTEXT in the current buffer.
+If CONTEXT cannot be found, return nil."
   (let ((context-string (nth 2 context)))
     (if (equal "" context-string)
        (point-max)
@@ -761,8 +1044,8 @@ CONTEXT is that which `vc-buffer-context' returns."
            (if new-mark (set-mark new-mark))))))
 
 (defun vc-revert-buffer1 (&optional arg no-confirm)
-  "Revert buffer, trying to keep point and mark where user expects them.
-Tries to be clever in the face of changes due to expanded version control
+  "Revert buffer, keeping point and mark where user expects them.
+Try to be clever in the face of changes due to expanded version control
 key words.  This is important for typeahead to work as expected.
 ARG and NO-CONFIRM are passed on to `revert-buffer'."
   (interactive "P")
@@ -789,19 +1072,11 @@ NOT-URGENT means it is ok to continue if the user says not to save."
        (unless not-urgent
          (error "Aborted")))))
 
-(defun vc-workfile-unchanged-p (file)
-  "Has FILE changed since last checkout?"
-  (let ((checkout-time (vc-file-getprop file 'vc-checkout-time))
-        (lastmod (nth 5 (file-attributes file))))
-    (if checkout-time
-        (equal checkout-time lastmod)
-      (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 (backend file)
+  "Return non-nil if FILE is the latest on its branch.
+This default implementation always returns non-nil, which means that
+editing non-current versions is not supported by default."
+  t)
 
 (defun vc-recompute-state (file)
   "Force a recomputation of the version control state of FILE.
@@ -810,18 +1085,22 @@ function `vc-BACKEND-state', not the heuristic."
   (vc-file-setprop file 'vc-state (vc-call state file)))
 
 (defun vc-next-action-on-file (file verbose &optional comment)
-  "Do The Right Thing for a given version-controlled FILE.
+  "Do The Right Thing for a given FILE under version control.
 If COMMENT is specified, it will be used as an admin or checkin comment.
 If VERBOSE is non-nil, query the user rather than using default parameters."
   (let ((visited (get-file-buffer file))
        state version)
     (when visited
+      (if vc-dired-mode
+          (switch-to-buffer-other-window visited)
+        (set-buffer visited))
       ;; Check relation of buffer and file, and make sure
       ;; user knows what he's doing.  First, finding the file
       ;; will check whether the file on disk is newer.
-      (if vc-dired-mode
-         (find-file-other-window file)
-       (find-file file))
+      ;; Ignore buffer-read-only during this test, and
+      ;; preserve find-file-literally.
+      (let ((buffer-read-only (not (file-writable-p file))))
+        (find-file-noselect file nil find-file-literally))
       (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))
@@ -831,12 +1110,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)
-      (vc-mode-line file)
+      (if visited (vc-mode-line file))
       (setq state (vc-state file))
       (cond
        ;; up-to-date
@@ -845,12 +1124,12 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
        (cond
         (verbose
          ;; go to a different version
-         (setq version 
+         (setq version
                (read-string "Branch, version, or backend to move to: "))
-         (let ((vsym (intern (upcase version))))
+         (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) 
+             (vc-checkout file (eq (vc-checkout-model file) 'implicit)
                           version))))
         ((not (eq (vc-checkout-model file) 'implicit))
          ;; check the file out
@@ -858,16 +1137,17 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
         (t
          ;; do nothing
          (message "%s is up-to-date" file))))
-       
+
        ;; Abnormal: edited but read-only
-       ((and visited (eq state 'edited) buffer-read-only)
+       ((and visited (eq state 'edited)
+            buffer-read-only (not (file-writable-p file)))
        ;; Make the file+buffer read-write.  If the user really wanted to
        ;; commit, he'll get a chance to do that next time around, anyway.
        (message "File is edited but read-only; making it writable")
        (set-file-modes buffer-file-name
                        (logior (file-modes buffer-file-name) 128))
        (toggle-read-only -1))
-       
+
        ;; edited
        ((eq state 'edited)
        (cond
@@ -892,7 +1172,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
              (if (member vsym vc-handled-backends)
                  (vc-transfer-file file vsym)
                (vc-checkin file version comment)))))))
-       
+
        ;; locked by somebody else
        ((stringp state)
        (if comment
@@ -902,18 +1182,18 @@ 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
                          "%s is not up-to-date.  Get latest version? "
                          (file-name-nondirectory file)))
-           (vc-checkout file (eq (vc-checkout-model file) 'implicit) "")
+           (vc-checkout file (eq (vc-checkout-model file) 'implicit) t)
          (if (and (not (eq (vc-checkout-model file) 'implicit))
                   (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
@@ -921,22 +1201,25 @@ 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
+                (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? ")))
            (progn (vc-call steal-lock file)
+                   (clear-visited-file-modtime)
                   ;; Must clear any headers here because they wouldn't
                   ;; show that the file is locked now.
                   (vc-clear-headers file)
+                  (write-file (buffer-file-name))
                   (vc-mode-line file))
          (if (not (yes-or-no-p
                    "Revert to checked-in version, instead? "))
@@ -965,7 +1248,7 @@ Ignores FILE and REV, but passes on COMMENT."
 
 ;;;###autoload
 (defun vc-next-action (verbose)
-  "Do the next logical checkin or checkout operation on the current file.
+  "Do the next logical version control operation on the current file.
 
 If you call this from within a VC dired buffer with no files marked,
 it will operate on the file in the current line.
@@ -1017,7 +1300,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)))
@@ -1027,7 +1310,7 @@ merge in the changes into your working copy."
         (vc-next-action-on-file buffer-file-name verbose)
       (error "Buffer %s is not associated with a file" (buffer-name)))))
 
-;;; These functions help the vc-next-action entry point
+;; These functions help the vc-next-action entry point
 
 ;;;###autoload
 (defun vc-register (&optional set-version comment)
@@ -1055,18 +1338,21 @@ 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 (format "Initial version level for %s: "
                                           (buffer-name)))
-                    ;; TODO: Use backend-specific init version.
-                    vc-default-init-version)
+                   (let ((backend (vc-responsible-backend buffer-file-name)))
+                     (if (vc-find-backend-function backend 'init-version)
+                         (vc-call-backend backend '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)
@@ -1075,30 +1361,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)
 
@@ -1107,15 +1410,6 @@ The default is to return nil always."
 The default implementation returns t for all files."
   t)
 
-(defun vc-unregister (file backend)
-  "Unregister FILE from version control system BACKEND."
-  (vc-call-backend backend 'unregister file)
-  (vc-file-clearprops file))
-
-(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-resynch-window (file &optional keep noquery)
   "If FILE is in the current buffer, either revert or unvisit it.
 The choice between revert (to see expanded keywords) and unvisit depends on
@@ -1151,19 +1445,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)
@@ -1176,19 +1475,24 @@ 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."
+REV defaults to the latest revision.
+
+After check-out, runs the normal hook `vc-checkout-hook'."
+  (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
@@ -1199,14 +1503,15 @@ 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-resynch-buffer file t t))
+   `((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)
+  (run-hooks 'vc-checkout-hook))
 
 (defun vc-steal-lock (file rev owner)
   "Steal the lock on FILE."
@@ -1217,9 +1522,16 @@ REV defaults to the latest revision."
     (if (not (yes-or-no-p (format "Steal the lock on %s from %s? "
                                  file-description owner)))
        (error "Steal canceled"))
-    (compose-mail owner (format "Stolen lock on %s" file-description)
-                 nil nil nil nil
-                 (list (list 'vc-finish-steal file rev)))
+    (message "Stealing lock on %s..." file)
+    (with-vc-properties
+     file
+     (vc-call steal-lock file rev)
+     `((vc-state . edited)))
+    (vc-resynch-buffer file t t)
+    (message "Stealing lock on %s...done" file)
+    ;; Write mail after actually stealing, because if the stealing
+    ;; goes wrong, we don't want to send any mail.
+    (compose-mail owner (format "Stolen lock on %s" file-description))
     (setq default-directory (expand-file-name "~/"))
     (goto-char (point-max))
     (insert
@@ -1228,28 +1540,20 @@ REV defaults to the latest revision."
      ".\n")
     (message "Please explain why you stole the lock.  Type C-c C-c when done.")))
 
-(defun vc-finish-steal (file version)
-  ;; This is called when the notification has been sent.
-  (message "Stealing lock on %s..." file)
-  (with-vc-properties 
-   file
-   (vc-call steal-lock file version)
-   `((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)
@@ -1257,24 +1561,27 @@ 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)))
+       (progn
+         (vc-call checkin file rev comment)
+         (vc-delete-automatic-version-backups 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))
 
 (defun vc-comment-to-change-log (&optional whoami file-name)
-  "Enter last VC comment into change log file for current buffer's file.
-Optional arg (interactive prefix) non-nil means prompt for user name and site.
-Second arg is file name of change log.  \
-If nil, uses `change-log-default-name'.
+  "Enter last VC comment into the change log for the current file.
+WHOAMI (interactive prefix) non-nil means prompt for user name
+and site.  FILE-NAME is the name of the change log; if nil, use
+`change-log-default-name'.
 
-May be useful as a `vc-checkin-hook' to update change logs automatically."
+This may be useful as a `vc-checkin-hook' to update change logs
+automatically."
   (interactive (if current-prefix-arg
                   (list current-prefix-arg
                         (prompt-for-change-log-name))))
@@ -1316,7 +1623,11 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
        (insert "\n"))))
 
 (defun vc-finish-logentry (&optional nocomment)
-  "Complete the operation implied by the current log entry."
+  "Complete the operation implied by the current log entry.
+Use the contents of the current buffer as a check-in or registration
+comment.  If the optional arg NOCOMMENT is non-nil, then don't check
+the buffer contents as a comment, and don't add it to
+`vc-comment-ring'."
   (interactive)
   ;; Check and record the comment, if any.
   (unless nocomment
@@ -1357,19 +1668,21 @@ 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
 
 (defun vc-new-comment-index (stride len)
+  "Return the comment index STRIDE elements from the current one.
+LEN is the length of `vc-comment-ring'."
   (mod (cond
        (vc-comment-ring-index (+ vc-comment-ring-index stride))
        ;; Initialize the index on the first use of this command
@@ -1380,7 +1693,8 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
        len))
 
 (defun vc-previous-comment (arg)
-  "Cycle backwards through comment history."
+  "Cycle backwards through comment history.
+With a numeric prefix ARG, go back ARG comments."
   (interactive "*p")
   (let ((len (ring-length vc-comment-ring)))
     (if (<= len 0)
@@ -1391,12 +1705,15 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
       (insert (ring-ref vc-comment-ring vc-comment-ring-index)))))
 
 (defun vc-next-comment (arg)
-  "Cycle forwards through comment history."
+  "Cycle forwards through comment history.
+With a numeric prefix ARG, go forward ARG comments."
   (interactive "*p")
   (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 of STR.
+If the optional argument STRIDE is present, that is a step-width to use
+when going through the comment ring."
   ;; Why substring rather than regexp ?   -sm
   (interactive
    (list (read-string "Comment substring: " nil nil vc-last-comment-match)))
@@ -1414,7 +1731,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 a substring match of STR."
   (interactive
    (list (read-string "Comment substring: " nil nil vc-last-comment-match)))
   (vc-comment-search-reverse str -1))
@@ -1424,14 +1741,16 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
 ;;;###autoload
 (defun vc-diff (historic &optional not-urgent)
   "Display diffs between file versions.
-Normally this compares the current file and buffer with the most recent
-checked in version of that file.  This uses no arguments.
-With a prefix argument, it reads the file name to use
-and two version designators specifying which versions to compare."
+Normally this compares the current file and buffer with the most
+recent checked in version of that file.  This uses no arguments.  With
+a prefix argument HISTORIC, it reads the file name to use and two
+version designators specifying which versions 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))
-  (vc-ensure-vc-buffer)
   (if historic
       (call-interactively 'vc-version-diff)
+    (vc-ensure-vc-buffer)
     (let ((file buffer-file-name))
       (vc-buffer-sync not-urgent)
       (if (vc-workfile-unchanged-p buffer-file-name)
@@ -1439,9 +1758,11 @@ and two version designators specifying which versions to compare."
        (vc-version-diff file nil nil)))))
 
 (defun vc-version-diff (file rel1 rel2)
-  "For FILE, report diffs between two stored versions REL1 and REL2 of it.
-If FILE is a directory, generate diffs between versions for all registered
-files in or below it."
+  "List the differences between FILE's versions REL1 and REL2.
+If REL1 is empty or nil it means to use the current workfile version;
+REL2 empty or nil means the current file contents.  FILE may also be
+a directory, in that case, generate diffs between the correponding
+versions of all registered files in or below it."
   (interactive
    (let ((file (expand-file-name
                 (read-file-name (if buffer-file-name
@@ -1459,7 +1780,8 @@ files in or below it."
        (setq rel1-default (vc-workfile-version file)))
       ;; if the file is not locked, use last and previous version as default
       (t
-       (setq rel1-default (vc-previous-version (vc-workfile-version file)))
+       (setq rel1-default (vc-call previous-version file
+                                   (vc-workfile-version file)))
        (if (string= rel1-default "") (setq rel1-default nil))
        (setq rel2-default (vc-workfile-version file))))
      ;; construct argument list
@@ -1474,30 +1796,26 @@ 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)
-      (let ((inhibit-read-only t))
-       (insert "Diffs between "
-               (or rel1 "last version checked in")
-               " and "
-               (or rel2 "current workfile(s)")
-               ":\n\n")
-       (setq default-directory (file-name-as-directory file))
-       ;; FIXME: this should do a single exec in CVS.
-       (vc-file-tree-walk
-        default-directory
-        (lambda (f)
-          (vc-exec-after
-           `(progn
-              (message "Looking at %s" ',f)
-              (vc-call-backend ',(vc-backend file) 'diff ',f ',rel1 ',rel2)))))
+      ;; recursive directory diff
+      (progn
+        (vc-setup-buffer "*vc-diff*")
+       (if (string-equal rel1 "") (setq rel1 nil))
+       (if (string-equal rel2 "") (setq rel2 nil))
+        (let ((inhibit-read-only t))
+          (insert "Diffs between "
+                  (or rel1 "last version checked in")
+                  " and "
+                  (or rel2 "current workfile(s)")
+                  ":\n\n"))
+        (let ((dir (file-name-as-directory file)))
+          (vc-call-backend (vc-responsible-backend dir)
+                           'diff-tree dir 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
+    (vc-diff-internal file rel1 rel2))
+  (set-buffer "*vc-diff*")
   (if (and (zerop (buffer-size))
           (not (get-buffer-process (current-buffer))))
       (progn
@@ -1511,32 +1829,145 @@ 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 (if (eq (buffer-size) 0)
-                            (insert "No differences found.\n"))
-                          (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))
 
+(defun vc-diff-internal (file rel1 rel2)
+  "Run diff to compare FILE's revisions REL1 and REL2.
+Output goes to the current buffer, which is assumed properly set up.
+The exit status of the diff command is returned.
+
+This function takes care to set up a proper coding system for diff output.
+If both revisions are available as local files, then it also does not
+actually call the backend, but performs a local 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)))
+        (coding-system-for-read (vc-coding-system-for-diff file)))
+    (if (and file-rel1 file-rel2)
+        (apply 'vc-do-command "*vc-diff*" 1 "diff" nil
+               (append (if (listp diff-switches)
+                           diff-switches
+                         (list diff-switches))
+                       (if (listp vc-diff-switches)
+                           vc-diff-switches
+                         (list vc-diff-switches))
+                       (list (file-relative-name file-rel1)
+                             (file-relative-name file-rel2))))
+      (vc-call diff file rel1 rel2))))
+
+(defmacro vc-diff-switches-list (backend)
+  "Return the list of switches to use for executing diff under BACKEND."
+  `(append
+    (if (listp diff-switches) diff-switches (list diff-switches))
+    (if (listp vc-diff-switches) vc-diff-switches (list vc-diff-switches))
+    (let* ((backend-switches-symbol
+           (intern (concat "vc-" (downcase (symbol-name ,backend))
+                           "-diff-switches")))
+          (backend-switches
+           (if (boundp backend-switches-symbol)
+               (eval backend-switches-symbol)
+             nil)))
+      (if (listp backend-switches) backend-switches (list backend-switches)))))
+
+(defun vc-default-diff-tree (backend dir rel1 rel2)
+  "List differences for all registered files at and below DIR.
+The meaning of REL1 and REL2 is the same as for `vc-version-diff'."
+  ;; This implementation does an explicit tree walk, and calls
+  ;; vc-BACKEND-diff directly for each file.  An optimization
+  ;; would be to use `vc-diff-internal', so that diffs can be local,
+  ;; and to call it only for files that are actually changed.
+  ;; However, this is expensive for some backends, and so it is left
+  ;; to backend-specific implementations.
+  (setq default-directory dir)
+  (vc-file-tree-walk
+   default-directory
+   (lambda (f)
+     (vc-exec-after
+      `(let ((coding-system-for-read (vc-coding-system-for-diff ',f)))
+         (message "Looking at %s" ',f)
+         (vc-call-backend ',(vc-backend f)
+                          'diff ',f ',rel1 ',rel2))))))
+
+(defun vc-coding-system-for-diff (file)
+  "Return the coding system for reading diff output for FILE."
+  (or coding-system-for-read
+      ;; if we already have this file open,
+      ;; use the buffer's coding system
+      (let ((buf (find-buffer-visiting file)))
+        (if buf (with-current-buffer buf
+                  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
+      'undecided))
+
 ;;;###autoload
 (defun vc-version-other-window (rev)
-  "Visit version REV of the current buffer in another window.
-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."
+  "Visit version REV of the current file in another window.
+If the current file is named `F', the version is named `F.~REV~'.
+If `F.~REV~' already exists, use it instead of checking it out again."
   (interactive "sVersion to visit (default is workfile version): ")
   (vc-ensure-vc-buffer)
-  (let* ((version (if (string-equal rev "")
-                     (vc-workfile-version buffer-file-name)
-                   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)))
+  (let* ((file buffer-file-name)
+        (version (if (string-equal rev "")
+                     (vc-workfile-version file)
+                   rev)))
+    (switch-to-buffer-other-window (vc-find-version file version))))
+
+(defun vc-find-version (file version)
+  "Read VERSION of FILE into a buffer and return the buffer."
+  (let ((automatic-backup (vc-version-backup-file-name file version))
+       (filebuf (or (get-file-buffer file) (current-buffer)))
+        (filename (vc-version-backup-file-name file version 'manual)))
+    (unless (file-exists-p filename)
+      (if (file-exists-p automatic-backup)
+          (rename-file automatic-backup filename nil)
+       (message "Checking out %s..." filename)
+       (with-current-buffer filebuf
+         (let ((failed t))
+           (unwind-protect
+               (let ((coding-system-for-read 'no-conversion)
+                     (coding-system-for-write 'no-conversion))
+                 (with-temp-file filename
+                   (let ((outbuf (current-buffer)))
+                     ;; Change buffer to get local value of
+                     ;; vc-checkout-switches.
+                     (with-current-buffer filebuf
+                       (vc-call find-version file version outbuf))))
+                 (setq failed nil))
+             (if (and failed (file-exists-p filename))
+                 (delete-file filename))))
+         (vc-mode-line file))
+       (message "Checking out %s...done" filename)))
+    (find-file-noselect filename)))
+
+(defun vc-default-find-version (backend file rev buffer)
+  "Provide the new `find-version' op based on the old `checkout' op.
+This is only for compatibility with old backends.  They should be updated
+to provide the `find-version' operation instead."
+  (let ((tmpfile (make-temp-file (expand-file-name file))))
+    (unwind-protect
+       (progn
+         (vc-call-backend backend 'checkout file nil rev tmpfile)
+         (with-current-buffer buffer
+           (insert-file-contents-literally tmpfile)))
+      (delete-file tmpfile))))
 
 ;; Header-insertion code
 
 ;;;###autoload
 (defun vc-insert-headers ()
-  "Insert headers in a file for use with your version control system.
+  "Insert headers into a file for use with a version control system.
 Headers desired are inserted at point, and are pulled from
 the variable `vc-BACKEND-header'."
   (interactive)
@@ -1567,7 +1998,7 @@ the variable `vc-BACKEND-header'."
 
 (defun vc-clear-headers (&optional file)
   "Clear all version headers in the current buffer (or FILE).
-I.e. reset them to the non-expanded form."
+The headers are reset to their non-expanded form."
   (let* ((filename (or file buffer-file-name))
         (visited (find-buffer-visiting filename))
         (backend (vc-backend filename)))
@@ -1580,7 +2011,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)))))
 
@@ -1608,7 +2039,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 "")
@@ -1618,8 +2049,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.
@@ -1632,102 +2063,13 @@ See Info node `Merging'."
 (defun vc-maybe-resolve-conflicts (file status &optional name-A name-B)
   (vc-resynch-buffer file t (not (buffer-modified-p)))
   (if (zerop status) (message "Merge successful")
-    (if (fboundp 'smerge-mode) (smerge-mode 1))
+    (smerge-mode 1)
     (if (y-or-n-p "Conflicts detected.  Resolve them now? ")
-       (if (fboundp 'smerge-ediff)
-           (smerge-ediff)
-         (vc-resolve-conflicts name-A name-B))
+       (vc-resolve-conflicts name-A name-B)
       (message "File contains conflict markers"))))
 
-(defvar vc-ediff-windows)
-(defvar vc-ediff-result)
-(eval-when-compile
-  (defvar ediff-buffer-A)
-  (defvar ediff-buffer-B)
-  (defvar ediff-buffer-C)
-  (require 'ediff-util))
 ;;;###autoload
-(defun vc-resolve-conflicts (&optional name-A name-B)
-  "Invoke ediff to resolve conflicts in the current buffer.
-The conflicts must be marked with rcsmerge conflict markers."
-  (interactive)
-  (vc-ensure-vc-buffer)
-  (let* ((found nil)
-         (file-name (file-name-nondirectory buffer-file-name))
-        (your-buffer   (generate-new-buffer
-                         (concat "*" file-name
-                                " " (or name-A "WORKFILE") "*")))
-        (other-buffer  (generate-new-buffer
-                         (concat "*" file-name
-                                " " (or name-B "CHECKED-IN") "*")))
-         (result-buffer (current-buffer)))
-    (save-excursion
-      (set-buffer your-buffer)
-      (erase-buffer)
-      (insert-buffer result-buffer)
-      (goto-char (point-min))
-      (while (re-search-forward (concat "^<<<<<<< "
-                                       (regexp-quote file-name) "\n") nil t)
-        (setq found t)
-       (replace-match "")
-       (if (not (re-search-forward "^=======\n" nil t))
-           (error "Malformed conflict marker"))
-       (replace-match "")
-       (let ((start (point)))
-         (if (not (re-search-forward "^>>>>>>> [0-9.]+\n" nil t))
-             (error "Malformed conflict marker"))
-         (delete-region start (point))))
-      (if (not found)
-          (progn
-            (kill-buffer your-buffer)
-            (kill-buffer other-buffer)
-            (error "No conflict markers found")))
-      (set-buffer other-buffer)
-      (erase-buffer)
-      (insert-buffer result-buffer)
-      (goto-char (point-min))
-      (while (re-search-forward (concat "^<<<<<<< "
-                                       (regexp-quote file-name) "\n") nil t)
-       (let ((start (match-beginning 0)))
-       (if (not (re-search-forward "^=======\n" nil t))
-           (error "Malformed conflict marker"))
-       (delete-region start (point))
-       (if (not (re-search-forward "^>>>>>>> [0-9.]+\n" nil t))
-           (error "Malformed conflict marker"))
-       (replace-match "")))
-      (let ((config (current-window-configuration))
-            (ediff-default-variant 'default-B))
-
-        ;; Fire up ediff.
-
-        (set-buffer (ediff-merge-buffers your-buffer other-buffer))
-
-        ;; Ediff is now set up, and we are in the control buffer.
-        ;; Do a few further adjustments and take precautions for exit.
-
-        (make-local-variable 'vc-ediff-windows)
-        (setq vc-ediff-windows config)
-        (make-local-variable 'vc-ediff-result)
-        (setq vc-ediff-result result-buffer)
-        (make-local-variable 'ediff-quit-hook)
-        (setq ediff-quit-hook
-              (lambda ()
-               (let ((buffer-A ediff-buffer-A)
-                     (buffer-B ediff-buffer-B)
-                     (buffer-C ediff-buffer-C)
-                     (result vc-ediff-result)
-                     (windows vc-ediff-windows))
-                 (ediff-cleanup-mess)
-                 (set-buffer result)
-                 (erase-buffer)
-                 (insert-buffer buffer-C)
-                 (kill-buffer buffer-A)
-                 (kill-buffer buffer-B)
-                 (kill-buffer buffer-C)
-                 (set-window-configuration windows)
-                 (message "Conflict resolution finished; you may save the buffer"))))
-        (message "Please resolve conflicts now; exit ediff when done")
-        nil))))
+(defalias 'vc-resolve-conflicts 'smerge-ediff)
 
 ;; The VC directory major mode.  Coopt Dired for this.
 ;; All VC commands get mapped into logical equivalents.
@@ -1738,12 +2080,9 @@ The conflicts must be marked with rcsmerge conflict markers."
 (defvar vc-dired-mode-map
   (let ((map (make-sparse-keymap))
        (vmap (make-sparse-keymap)))
-    (define-key map "\C-xv" vc-prefix-map)
-    ;; Emacs-20 has a lousy keymap inheritance that won't work here.
-    ;; Emacs-21's is still lousy but just better enough that it'd work.   -sm
-    ;; (set-keymap-parent vmap vc-prefix-map)
-    (setq vmap vc-prefix-map)
+    (define-key map "\C-xv" vmap)
     (define-key map "v" vmap)
+    (set-keymap-parent vmap vc-prefix-map)
     (define-key vmap "t" 'vc-dired-toggle-terse-mode)
     map))
 
@@ -1766,30 +2105,39 @@ There is a special command, `*l', to mark all files currently locked."
   ;; We do it here because dired might not be loaded yet
   ;; when vc-dired-mode-map is initialized.
   (set-keymap-parent vc-dired-mode-map dired-mode-map)
-  (make-local-hook 'dired-after-readin-hook)
   (add-hook 'dired-after-readin-hook 'vc-dired-hook nil t)
   ;; The following is slightly modified from dired.el,
-  ;; because file lines look a bit different in vc-dired-mode.
+  ;; because file lines look a bit different in vc-dired-mode
+  ;; (the column before the date does not end in a digit).
   (set (make-local-variable 'dired-move-to-filename-regexp)
-       (let*
-          ((l "\\([A-Za-z]\\|[^\0-\177]\\)")
-           ;; In some locales, month abbreviations are as short as 2 letters,
-           ;; and they can be padded on the right with spaces.
-           (month (concat l l "+ *"))
-           ;; Recognize any non-ASCII character.
-           ;; The purpose is to match a Kanji character.
-           (k "[^\0-\177]")
-           ;; (k "[^\x00-\x7f\x80-\xff]")
-           (s " ")
-           (yyyy "[0-9][0-9][0-9][0-9]")
-           (mm "[ 0-1][0-9]")
-           (dd "[ 0-3][0-9]")
-           (HH:MM "[ 0-2][0-9]:[0-5][0-9]")
-           (western (concat "\\(" month s dd "\\|" dd s month "\\)"
-                            s "\\(" HH:MM "\\|" s yyyy"\\|" yyyy s "\\)"))
-           (japanese (concat mm k s dd k s "\\(" s HH:MM "\\|" yyyy k "\\)")))
-        ;; the .* below ensures that we find the last match on a line
-         (concat ".*" s "\\(" western "\\|" japanese "\\)" s)))
+  (let* ((l "\\([A-Za-z]\\|[^\0-\177]\\)")
+         ;; In some locales, month abbreviations are as short as 2 letters,
+         ;; and they can be followed by ".".
+         (month (concat l l "+\\.?"))
+         (s " ")
+         (yyyy "[0-9][0-9][0-9][0-9]")
+         (dd "[ 0-3][0-9]")
+         (HH:MM "[ 0-2][0-9]:[0-5][0-9]")
+         (seconds "[0-6][0-9]\\([.,][0-9]+\\)?")
+         (zone "[-+][0-2][0-9][0-5][0-9]")
+         (iso-mm-dd "[01][0-9]-[0-3][0-9]")
+         (iso-time (concat HH:MM "\\(:" seconds "\\( ?" zone "\\)?\\)?"))
+         (iso (concat "\\(\\(" yyyy "-\\)?" iso-mm-dd "[ T]" iso-time
+                      "\\|" yyyy "-" iso-mm-dd "\\)"))
+         (western (concat "\\(" month s "+" dd "\\|" dd "\\.?" s month "\\)"
+                          s "+"
+                          "\\(" HH:MM "\\|" yyyy "\\)"))
+         (western-comma (concat month s "+" dd "," s "+" yyyy))
+         ;; Japanese MS-Windows ls-lisp has one-digit months, and
+         ;; omits the Kanji characters after month and day-of-month.
+         (mm "[ 0-1]?[0-9]")
+         (japanese
+          (concat mm l "?" s dd l "?" s "+"
+                  "\\(" HH:MM "\\|" yyyy l "?" "\\)")))
+    ;; the .* below ensures that we find the last match on a line
+    (concat ".*" s
+            "\\(" western "\\|" western-comma "\\|" japanese "\\|" iso "\\)"
+            s "+")))
   (and (boundp 'vc-dired-switches)
        vc-dired-switches
        (set (make-local-variable 'dired-actual-switches)
@@ -1827,30 +2175,18 @@ There is a special command, `*l', to mark all files currently locked."
      ((eq state 'needs-patch) "(patch)")
      ((eq state 'unlocked-changes) "(stale)"))))
 
-(defun vc-dired-reformat-line (x)
+(defun vc-dired-reformat-line (vc-info)
   "Reformat a directory-listing line.
-Replace various columns with version control information.
+Replace various columns with version control information, VC-INFO.
 This code, like dired, assumes UNIX -l format."
   (beginning-of-line)
-  (let ((pos (point)) limit perm date-and-file)
-    (end-of-line)
-    (setq limit (point))
-    (goto-char pos)
-    (when
-        (or
-         (re-search-forward  ;; owner and group
-          "^\\(..[drwxlts-]+ \\) *[0-9]+ [^ ]+ +[^ ]+ +[0-9]+\\( .*\\)"
-          limit t)
-         (re-search-forward  ;; only owner displayed
-          "^\\(..[drwxlts-]+ \\) *[0-9]+ [^ ]+ +[0-9]+\\( .*\\)"
-         limit t)
-         (re-search-forward  ;; OS/2 -l format, no links, owner, group
-          "^\\(..[drwxlts-]+ \\) *[0-9]+\\( .*\\)"
-          limit t))
-      (setq perm          (match-string 1)
-           date-and-file (match-string 2))
-      (setq x (substring (concat x "          ") 0 10))
-      (replace-match (concat perm x date-and-file)))))
+  (when (re-search-forward
+         ;; Match link count, owner, group, size.  Group may be missing,
+         ;; and only the size is present in OS/2 -l format.
+         "^..[drwxlts-]+ \\( *[0-9]+\\( [^ ]+ +\\([^ ]+ +\\)?[0-9]+\\)?\\) "
+         (line-end-position) t)
+      (replace-match (substring (concat vc-info "          ") 0 10)
+                     t t nil 1)))
 
 (defun vc-dired-hook ()
   "Reformat the listing according to version control.
@@ -1984,7 +2320,7 @@ With prefix arg READ-SWITCHES, specify a value to override
 ;; Named-configuration entry points
 
 (defun vc-snapshot-precondition (dir)
-  "Scan the tree below DIR, looking for non-uptodate files.
+  "Scan the tree below DIR, looking for files not up-to-date.
 If any file is not up-to-date, return the name of the first such file.
 \(This means, neither snapshot creation nor retrieval is allowed.\)
 If one or more of the files are currently visited, return `visited'.
@@ -2062,10 +2398,9 @@ allowed and simply skipped)."
         (setq update (and (eq result 'visited) update))
         (vc-file-tree-walk
          dir
-         (lambda (f) (and
-                      (vc-error-occurred
-                       (vc-call checkout f nil name)
-                       (if update (vc-resynch-buffer f t t))))))))))
+         (lambda (f) (vc-error-occurred
+                     (vc-call checkout f nil name)
+                     (if update (vc-resynch-buffer f t t)))))))))
 
 ;; Miscellaneous other entry points
 
@@ -2075,13 +2410,12 @@ allowed and simply skipped)."
   (interactive)
   (vc-ensure-vc-buffer)
   (let ((file buffer-file-name))
-    (vc-setup-buffer nil)
-    (setq default-directory (file-name-directory file))
     (vc-call print-log file)
+    (set-buffer "*vc*")
     (pop-to-buffer (current-buffer))
-    (if (fboundp 'log-view-mode) (log-view-mode))
+    (log-view-mode)
     (vc-exec-after
-     `(progn
+     `(let ((inhibit-read-only t))
        (goto-char (point-max)) (forward-line -1)
        (while (looking-at "=*\n")
          (delete-char (- (match-end 0) (match-beginning 0)))
@@ -2091,47 +2425,138 @@ allowed and simply skipped)."
            (delete-char (- (match-end 0) (match-beginning 0))))
        (shrink-window-if-larger-than-buffer)
        ;; move point to the log entry for the current version
-       (if (fboundp 'log-view-goto-rev)
-           (log-view-goto-rev ',(vc-workfile-version file))
-         (if (vc-find-backend-function ',(vc-backend file) 'show-log-entry)
-             (vc-call-backend ',(vc-backend file)
-                              'show-log-entry
-                              ',(vc-workfile-version file))))))))
+       (vc-call-backend ',(vc-backend file)
+                        'show-log-entry
+                        ',(vc-workfile-version file))
+        (set-buffer-modified-p nil)))))
+
+(defun vc-default-show-log-entry (backend rev)
+  (log-view-goto-rev rev))
+
+(defun vc-default-comment-history (backend file)
+  "Return a string with all log entries stored in BACKEND for FILE."
+  (if (vc-find-backend-function backend 'print-log)
+      (with-current-buffer "*vc*"
+       (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.
+  "Revert the current buffer's file 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)
+  ;; Make sure buffer is saved.  If the user says `no', abort since
+  ;; we cannot show the changes and ask for confirmation to discard them.
+  (vc-buffer-sync nil)
   (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)))
 
+;;;###autoload
+(defun vc-update ()
+  "Update the current buffer's file to the latest version on its branch.
+If the file contains no changes, and is not locked, then this simply replaces
+the working file with the latest version 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)
+  (vc-ensure-vc-buffer)
+  (vc-buffer-sync nil)
+  (let ((file buffer-file-name))
+    (if (vc-up-to-date-p file)
+        (vc-checkout file nil "")
+      (if (eq (vc-checkout-model file) 'locking)
+          (if (eq (vc-state file) 'edited)
+              (error
+               (substitute-command-keys
+           "File is locked--type \\[vc-revert-buffer] to discard changes"))
+            (error
+             (substitute-command-keys
+           "Unexpected file state (%s)--type \\[vc-next-action] to correct")
+                   (vc-state file)))
+        (if (not (vc-find-backend-function (vc-backend file) 'merge-news))
+            (error "Sorry, merging news is not implemented for %s"
+                   (vc-backend file))
+          (vc-call merge-news file)
+          (vc-resynch-window file t t))))))
+
+(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)))
+     (when backup-file
+       (copy-file backup-file file 'ok-if-already-exists 'keep-date)
+       (vc-delete-automatic-version-backups file))
+     (vc-call revert file backup-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.
@@ -2158,11 +2583,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
@@ -2180,80 +2605,109 @@ 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
+;;;###autoload
 (defun vc-switch-backend (file backend)
-  "Make BACKEND the current version control system for FILE.  
+  "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."
-  (interactive 
+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
-    (intern (upcase (read-string "Switch to backend: ")))))
-  (unless (vc-call-backend backend 'registered file)
-    (error "%s is not registered in %s" file backend))
-  (vc-file-clearprops file)
-  (vc-file-setprop file 'vc-backend backend)
-  (vc-resynch-buffer file t t))
-
-(defun vc-index-of (backend)
-  "Return the index of BACKEND in vc-handled-backends."
-  (- (length vc-handled-backends) 
-     (length (memq backend vc-handled-backends))))
+    (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
+;;;###autoload
 (defun vc-transfer-file (file new-backend)
-  "Transfer FILE to another version control system 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
+\(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)))
+  (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)
-      (with-vc-properties
-       file
-       (vc-call-backend new-backend 'receive-file file 
-                       (< (vc-index-of old-backend)
-                          (vc-index-of new-backend)))
-       `((vc-backend ,new-backend))))
-    (vc-resynch-buffer file t t)))
-
-(defun vc-default-receive-file (backend file move)
-  "Let BACKEND receive FILE from another version control system.
-If MOVE is non-nil, then FILE is unregistered from the old
-backend and its comment history is used as the initial contents
-of the log entry buffer."
-  (let ((old-backend (vc-backend file))
-       (rev (vc-workfile-version file))
-       (state (vc-state file))
-       (comment (and move
-                     (vc-find-backend-function old-backend 'comment-history)
-                     (vc-call 'comment-history file))))
-    (if move (vc-unregister file old-backend))
-    (vc-file-clearprops file)
-    (if (not (vc-call-backend backend 'registered file))
-       (with-vc-properties 
-        file
-        ;; TODO: If the file was 'edited under the old backend,
-        ;; this should actually register the version 
-        ;; it was based on.
-        (vc-call-backend backend 'register file rev "")
-        `((vc-backend ,backend)))
-      (vc-file-setprop file 'vc-backend backend)
-      (vc-file-setprop file 'vc-state 'edited)
-      (set-file-modes file
-                     (logior (file-modes file) 128)))
-    (when (or move (eq state 'edited))
+       (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-file 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 'ok-if-already-exists)
+               ;; 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)
-      ;; TODO: The comment history should actually become the
-      ;; initial contents of the log entry buffer.
-      (and comment (ring-insert vc-comment-ring comment))
-      (vc-checkin file))))
+      (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."
@@ -2267,7 +2721,7 @@ of the log entry buffer."
           templates)))
     (if (or (file-symlink-p oldmaster)
            (file-symlink-p (file-name-directory oldmaster)))
-       (error "This unsafe in the presence of symbolic links"))
+       (error "This is unsafe in the presence of symbolic links"))
     (rename-file
      oldmaster
      (catch 'found
@@ -2290,12 +2744,6 @@ of the log entry buffer."
 (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))
@@ -2369,8 +2817,7 @@ Uses `rcs2log' which only works for RCS and CVS."
   (let ((odefault default-directory)
        (changelog (find-change-log))
        ;; Presumably not portable to non-Unixy systems, along with rcs2log:
-       (tempfile (funcall
-                  (if (fboundp 'make-temp-file) 'make-temp-file 'make-temp-name)
+       (tempfile (make-temp-file
                   (expand-file-name "vc"
                                     (or small-temporary-file-directory
                                         temporary-file-directory))))
@@ -2415,102 +2862,216 @@ Uses `rcs2log' which only works for RCS and CVS."
               (setq default-directory (file-name-directory changelog))
               (delete-file tempfile)))))
 
-;;; Annotate functionality
+;; Annotate functionality
 
 ;; Declare globally instead of additional parameter to
 ;; temp-buffer-show-function (not possible to pass more than one
-;; parameter).
+;; parameter).  The use of annotate-ratio is deprecated in favor of
+;; annotate-mode, which replaces it with the more sensible "span-to
+;; days", along with autoscaling support.
 (defvar vc-annotate-ratio nil "Global variable.")
 (defvar vc-annotate-backend nil "Global variable.")
 
+(defconst vc-annotate-font-lock-keywords
+  ;; The fontification is done by vc-annotate-lines instead of font-lock.
+  '((vc-annotate-lines)))
+
 (defun vc-annotate-get-backend (buffer)
   "Return the backend matching \"Annotate\" buffer BUFFER.
-Return NIL if no match made.  Associations are made based on
+Return nil if no match made.  Associations are made based on
 `vc-annotate-buffers'."
   (cdr (assoc buffer vc-annotate-buffers)))
 
 (define-derived-mode vc-annotate-mode fundamental-mode "Annotate"
-  "Major mode for buffers displaying output from the `annotate' command.
+  "Major mode for output buffers of the `vc-annotate' command.
 
 You can use the mode-specific menu to alter the time-span of the used
 colors.  See variable `vc-annotate-menu-elements' for customizing the
 menu items."
+  (set (make-local-variable 'truncate-lines) t)
+  (set (make-local-variable 'font-lock-defaults)
+       '(vc-annotate-font-lock-keywords t))
   (vc-annotate-add-menu))
 
-(defun vc-annotate-display-default (&optional event)
-  "Use the default color spectrum for VC Annotate mode."
+(defun vc-annotate-display-default (&optional ratio)
+  "Display the output of \\[vc-annotate] using the default color range.
+The color range is given by `vc-annotate-color-map', scaled by RATIO
+if present.  The current time is used as the offset."
   (interactive "e")
   (message "Redisplaying annotation...")
-  (vc-annotate-display (current-buffer)
-                      nil
-                      (vc-annotate-get-backend (current-buffer)))
+  (vc-annotate-display
+   (if ratio (vc-annotate-time-span vc-annotate-color-map ratio)))
   (message "Redisplaying annotation...done"))
 
+(defun vc-annotate-display-autoscale (&optional full)
+  "Highlight the output of \\[vc-annotate]] using an autoscaled color map.
+Autoscaling means that the map is scaled from the current time to the
+oldest annotation in the buffer, or, with argument FULL non-nil, to
+cover the range from the oldest annotation to the newest."
+  (interactive)
+  (let ((newest 0.0)
+       (oldest 999999.)                ;Any CVS users at the founding of Rome?
+       (current (vc-annotate-convert-time (current-time)))
+       date)
+    (message "Redisplaying annotation...")
+    ;; Run through this file and find the oldest and newest dates annotated.
+    (save-excursion
+      (goto-char (point-min))
+      (while (setq date (vc-call-backend vc-annotate-backend 'annotate-time))
+       (if (> date newest)
+           (setq newest date))
+       (if (< date oldest)
+           (setq oldest date))))
+    (vc-annotate-display
+     (vc-annotate-time-span            ;return the scaled colormap.
+      vc-annotate-color-map
+      (/ (-  (if full newest current) oldest)
+        (vc-annotate-car-last-cons vc-annotate-color-map)))
+     (if full newest))
+    (message "Redisplaying annotation...done \(%s\)"
+            (if full
+                (format "Spanned from %.1f to %.1f days old"
+                        (- current oldest)
+                        (- current newest))
+              (format "Spanned to %.1f days old" (- current oldest))))))
+
+;; Menu -- Using easymenu.el
 (defun vc-annotate-add-menu ()
   "Add the menu 'Annotate' to the menu bar in VC-Annotate mode."
-  (setq vc-annotate-mode-menu (make-sparse-keymap "Annotate"))
-  (define-key vc-annotate-mode-map [menu-bar vc-annotate-mode]
-    (cons "VC-Annotate" vc-annotate-mode-menu))
-  (define-key vc-annotate-mode-menu [default]
-    '("Default" . vc-annotate-display-default))
-  (let ((menu-elements vc-annotate-menu-elements))
+  (let ((menu-elements vc-annotate-menu-elements)
+       (menu-def
+        '("VC-Annotate"
+          ["Default" (unless (null vc-annotate-display-mode)
+                       (setq vc-annotate-display-mode nil)
+                       (vc-annotate-display-select))
+           :style toggle :selected (null vc-annotate-display-mode)]))
+       (oldest-in-map (vc-annotate-car-last-cons vc-annotate-color-map)))
     (while menu-elements
       (let* ((element (car menu-elements))
-            (days (round (* element
-                            (vc-annotate-car-last-cons vc-annotate-color-map)
-                            0.7585))))
+            (days (* element oldest-in-map)))
        (setq menu-elements (cdr menu-elements))
-       (define-key vc-annotate-mode-menu
-         (vector days)
-         (cons (format "Span %d days"
-                       days)
-               `(lambda ()
-                  ,(format "Use colors spanning %d days" days)
-                  (interactive)
-                  (message "Redisplaying annotation...")
-                  (vc-annotate-display
-                   (get-buffer (buffer-name))
-                   (vc-annotate-time-span vc-annotate-color-map ,element)
-                   (vc-annotate-get-backend (current-buffer)))
-                  (message "Redisplaying annotation...done"))))))))
-
+       (setq menu-def
+             (append menu-def
+                     `([,(format "Span %.1f days" days)
+                        (unless (and (numberp vc-annotate-display-mode)
+                                     (= vc-annotate-display-mode ,days))
+                          (vc-annotate-display-select nil ,days))
+                        :style toggle :selected
+                        (and (numberp vc-annotate-display-mode)
+                             (= vc-annotate-display-mode ,days)) ])))))
+    (setq menu-def
+         (append menu-def
+                 (list
+                  ["Span ..."
+                   (let ((days
+                          (float (string-to-number
+                                  (read-string "Span how many days? ")))))
+                     (vc-annotate-display-select nil days)) t])
+                 (list "--")
+                 (list
+                  ["Span to Oldest"
+                   (unless (eq vc-annotate-display-mode 'scale)
+                     (vc-annotate-display-select nil 'scale))
+                   :style toggle :selected
+                   (eq vc-annotate-display-mode 'scale)])
+                 (list
+                  ["Span Oldest->Newest"
+                   (unless (eq vc-annotate-display-mode 'fullscale)
+                     (vc-annotate-display-select nil 'fullscale))
+                   :style toggle :selected
+                   (eq vc-annotate-display-mode 'fullscale)])))
+    ;; Define the menu
+    (if (or (featurep 'easymenu) (load "easymenu" t))
+       (easy-menu-define vc-annotate-mode-menu vc-annotate-mode-map
+                         "VC Annotate Display Menu" menu-def))))
+
+(defun vc-annotate-display-select (&optional buffer mode)
+  "Highlight the output of \\[vc-annotate].
+By default, the current buffer is highlighted, unless overridden by
+BUFFER.  `vc-annotate-display-mode' specifies the highlighting mode to
+use; you may override this using the second optional arg MODE."
+  (interactive)
+  (if mode (setq vc-annotate-display-mode mode))
+  (when buffer
+    (set-buffer buffer)
+    (display-buffer buffer))
+  (if (not vc-annotate-mode)           ; Turn on vc-annotate-mode if not done
+      (vc-annotate-mode))
+  (cond ((null vc-annotate-display-mode)
+        (vc-annotate-display-default vc-annotate-ratio))
+       ;; One of the auto-scaling modes
+       ((eq vc-annotate-display-mode 'scale)
+        (vc-annotate-display-autoscale))
+       ((eq vc-annotate-display-mode 'fullscale)
+        (vc-annotate-display-autoscale t))
+       ((numberp vc-annotate-display-mode) ; A fixed number of days lookback
+        (vc-annotate-display-default
+         (/ vc-annotate-display-mode (vc-annotate-car-last-cons
+                                      vc-annotate-color-map))))
+       (t (error "No such display mode: %s"
+                 vc-annotate-display-mode))))
 
 ;;;; (defun vc-BACKEND-annotate-command (file buffer) ...)
 ;;;;  Execute "annotate" on FILE by using `call-process' and insert
 ;;;;  the contents in BUFFER.
 
 ;;;###autoload
-(defun vc-annotate (ratio)
-  "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.
+(defun vc-annotate (prefix)
+  "Display the edit history of the current file using colours.
+
+This command creates a buffer that shows, for each line of the current
+file, when it was last edited and by whom.  Additionally, colours are
+used to show the age of each line--blue means oldest, red means
+youngest, and intermediate colours indicate intermediate ages.  By
+default, the time scale stretches back one year into the past;
+everything that is older than that is shown in blue.
+
+With a prefix argument, this command asks two questions in the
+minibuffer.  First, you may enter a version number; then the buffer
+displays and annotates that version instead of the current version
+\(type RET in the minibuffer to leave that default unchanged).  Then,
+you are prompted for the time span in days which the color range
+should cover.  For example, a time span of 20 days means that changes
+over the past 20 days are shown in red to blue, according to their
+age, and everything that is older than that is shown in blue.
+
+Customization variables:
 
 `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))))
+  (let* ((temp-buffer-name (concat "*Annotate " (buffer-name) "*"))
+         (temp-buffer-show-function 'vc-annotate-display-select)
+         (rev (vc-workfile-version (buffer-file-name)))
+         (vc-annotate-version
+          (if prefix (read-string
+                      (format "Annotate from version: (default %s) " rev)
+                      nil nil rev)
+            rev)))
+    (if prefix
+        (setq vc-annotate-display-mode
+              (float (string-to-number
+                      (read-string "Annotate span days: (default 20) "
+                                   nil nil "20")))))
+    (setq 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
          (append vc-annotate-buffers
-                 (list (cons (get-buffer temp-buffer-name) vc-annotate-backend)))))
-  (message "Annotating... done"))
-
+                 (list (cons (get-buffer temp-buffer-name) vc-annotate-backend))))
+  (message "Annotating... done")))
 
 (defun vc-annotate-car-last-cons (a-list)
   "Return car of last cons in association list A-LIST."
@@ -2519,7 +3080,7 @@ colors. `vc-annotate-background' specifies the background color."
     (car (car a-list))))
 
 (defun vc-annotate-time-span (a-list span &optional quantize)
-  "Apply factor SPAN  to the time-span of association list A-LIST.
+  "Apply factor SPAN to the time-span of association list A-LIST.
 Return the new alist.
 Optionally quantize to the factor of QUANTIZE."
   ;; Apply span to each car of every cons
@@ -2531,9 +3092,9 @@ Optionally quantize to the factor of QUANTIZE."
                                             a-list) span quantize))))
 
 (defun vc-annotate-compcar (threshold a-list)
-  "Test successive cons cells of association list A-LIST against THRESHOLD.
-Return the first cons cell which car is not less than THRESHOLD,
-nil otherwise"
+  "Test successive cons cells of A-LIST against THRESHOLD.
+Return the first cons cell with a car that is not less than THRESHOLD,
+nil if no such cell exists."
  (let ((i 1)
        (tmp-cons (car a-list)))
    (while (and tmp-cons (< (car tmp-cons) threshold))
@@ -2541,70 +3102,61 @@ nil otherwise"
      (setq i (+ i 1)))
    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.
-If BACKEND is NIL, variable VC-ANNOTATE-BACKEND is used instead.
-This function is destructive on VC-ANNOTATE-BACKEND when BACKEND is non-nil."
-
-  ;; Handle the case of the global variable vc-annotate-ratio being
-  ;; set. This variable is used to pass information from function
-  ;; vc-annotate since it is not possible to use another parameter
-  ;; (see temp-buffer-show-function).
-  (if (and (not color-map) vc-annotate-ratio)
-      ;; This will only be true if called from vc-annotate with ratio
-      ;; being non-nil.
-      (setq color-map (vc-annotate-time-span vc-annotate-color-map
-                                            vc-annotate-ratio)))
-  (set-buffer buffer)
-  (display-buffer buffer)
-  (if (not vc-annotate-mode)           ; Turn on vc-annotate-mode if not done
-      (vc-annotate-mode))
-  (goto-char (point-min))              ; Position at the top of the buffer.
-  ;; Delete old overlays
-  (mapcar
-   (lambda (overlay)
-     (if (overlay-get overlay 'vc-annotation)
-        (delete-overlay overlay)))
-   (overlays-in (point-min) (point-max)))
-  (goto-char (point-min))              ; Position at the top of the buffer.
-
-  (if backend (setq vc-annotate-backend backend)) ; Destructive on `vc-annotate-backend'
-
-  (let ((difference (vc-call-backend vc-annotate-backend 'annotate-difference (point))))
-    (while difference
-      (let*
-         ((color (or (vc-annotate-compcar
-                      difference (or color-map vc-annotate-color-map))
-                     (cons nil vc-annotate-very-old-color)))
-          ;; substring from index 1 to remove any leading `#' in the name
-          (face-name (concat "vc-annotate-face-" (substring (cdr color) 1)))
-          ;; Make the face if not done.
-          (face (or (intern-soft face-name)
-                    (let ((tmp-face (make-face (intern face-name))))
-                      (set-face-foreground tmp-face (cdr color))
-                      (if vc-annotate-background
-                          (set-face-background tmp-face vc-annotate-background))
-                      tmp-face)))      ; Return the face
-          (point (point))
-          overlay)
+(defun vc-annotate-convert-time (time)
+  "Convert a time value to a floating-point number of days.
+The argument TIME is a list as returned by `current-time' or
+`encode-time', only the first two elements of that list are considered."
+  (/ (+ (* (float (car time)) (lsh 1 16)) (cadr time)) 24 3600))
+
+(defun vc-annotate-difference (&optional offset)
+  "Return the time span in days to the next annotation.
+This calls the backend function annotate-time, and returns the
+difference in days between the time returned and the current time,
+or OFFSET if present."
+   (let ((next-time (vc-call-backend vc-annotate-backend 'annotate-time)))
+     (if next-time
+        (- (or offset
+               (vc-call-backend vc-annotate-backend 'annotate-current-time))
+           next-time))))
+
+(defun vc-default-annotate-current-time (backend)
+  "Return the current time, encoded as fractional days."
+  (vc-annotate-convert-time (current-time)))
+
+(defun vc-annotate-display (&optional color-map offset)
+  "Highlight `vc-annotate' output in the current buffer.
+COLOR-MAP, if present, overrides `vc-annotate-color-map'.
+The annotations are relative to the current time, unless overridden by OFFSET."
+  (if (and color-map (not (eq color-map vc-annotate-color-map)))
+      (set (make-local-variable 'vc-annotate-color-map) color-map))
+  (set (make-local-variable 'vc-annotate-offset) offset)
+  (font-lock-mode 1))
+(make-obsolete 'vc-annotate-display 'vc-annotate-display-select "21.4")
+
+(defvar vc-annotate-offset nil)
+
+(defun vc-annotate-lines (limit)
+  (let (difference)
+    (while (and (< (point) limit)
+               (setq difference (vc-annotate-difference vc-annotate-offset)))
+      (let* ((color (or (vc-annotate-compcar difference vc-annotate-color-map)
+                       (cons nil vc-annotate-very-old-color)))
+            ;; substring from index 1 to remove any leading `#' in the name
+            (face-name (concat "vc-annotate-face-" (substring (cdr color) 1)))
+            ;; Make the face if not done.
+            (face (or (intern-soft face-name)
+                      (let ((tmp-face (make-face (intern face-name))))
+                        (set-face-foreground tmp-face (cdr color))
+                        (if vc-annotate-background
+                            (set-face-background tmp-face
+                                                 vc-annotate-background))
+                        tmp-face)))    ; Return the face
+            (point (point))
+            overlay)
        (forward-line 1)
-       (setq overlay (make-overlay point (point)))
-       (overlay-put overlay 'face face)
-       (overlay-put overlay 'vc-annotation t))
-      (setq difference (vc-call-backend vc-annotate-backend 'annotate-difference (point))))))
-
+       (put-text-property point (point) 'face face)))
+    ;; Pretend to font-lock there were no matches.
+    nil))
 \f
 ;; Collect back-end-dependent stuff here
 
@@ -2615,90 +3167,31 @@ 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
 
-(define-derived-mode vc-log-mode text-mode "VC-Log"
-  "Major mode for editing VC log entries.
-These bindings are added to the global keymap when you enter this mode:
-\\[vc-next-action]             perform next logical version-control operation on current file
-\\[vc-register]                register current file
-\\[vc-toggle-read-only]                like next-action, but won't register files
-\\[vc-insert-headers]          insert version-control headers in current file
-\\[vc-print-log]               display change history of current file
-\\[vc-revert-buffer]           revert buffer to latest version
-\\[vc-cancel-version]          undo latest checkin
-\\[vc-diff]            show diffs between file versions
-\\[vc-version-other-window]            visit old version in another window
-\\[vc-directory]               show all files locked by any user in or below .
-\\[vc-annotate]                colorful display of the cvs annotate command
-\\[vc-update-change-log]               add change log entry from recent checkins
-
-While you are entering a change log message for a version, the following
-additional bindings will be in effect.
-
-\\[vc-finish-logentry] proceed with check in, ending log message entry
-
-Whenever you do a checkin, your log comment is added to a ring of
-saved comments.  These can be recalled as follows:
-
-\\[vc-next-comment]    replace region with next message in comment ring
-\\[vc-previous-comment]        replace region with previous message in comment ring
-\\[vc-comment-search-reverse]  search backward for regexp in the comment ring
-\\[vc-comment-search-forward]  search backward for regexp in the comment ring
-
-Entry to the change-log submode calls the value of `text-mode-hook', then
-the value of `vc-log-mode-hook'.
-
-Global user options:
-       `vc-initial-comment'    If non-nil, require user to enter a change
-                               comment upon first checkin of the file.
-
-       `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-BACKEND-header       Which keywords to insert when adding headers
-                               with \\[vc-insert-headers].  Defaults to
-                               '(\"\%\W\%\") under SCCS, '(\"\$Id\$\") under
-                               RCS and CVS.
-
-       `vc-static-header-alist' By default, version headers inserted in C files
-                               get stuffed in a static string area so that
-                               ident(RCS/CVS) or what(SCCS) can see them in
-                               the compiled object code.  You can override
-                               this by setting this variable to nil, or change
-                               the header template by changing it.
-
-       `vc-command-messages'   if non-nil, display run messages from the
-                               actual version-control utilities (this is
-                               intended primarily for people hacking vc
-                               itself)."
-  (make-local-variable 'vc-comment-ring-index))
-
 (defun vc-log-edit (file)
-  "Set up `log-edit' for use with VC on FILE.
-If `log-edit' is not available, resort to `vc-log-mode'."
+  "Set up `log-edit' for use with VC on FILE."
   (setq default-directory
        (if file (file-name-directory file)
          (with-current-buffer vc-parent-buffer default-directory)))
-  (if (fboundp 'log-edit)
-      (log-edit 'vc-finish-logentry nil
-               (if file `(lambda () ',(list (file-name-nondirectory file)))
-                 ;; If FILE is nil, we were called from vc-dired.
-                 (lambda ()
-                   (with-current-buffer vc-parent-buffer
-                     (dired-get-marked-files t)))))
-    (vc-log-mode))
+  (log-edit 'vc-finish-logentry nil
+           (if file `(lambda () ',(list (file-name-nondirectory file)))
+             ;; If FILE is nil, we were called from vc-dired.
+             (lambda ()
+               (with-current-buffer vc-parent-buffer
+                 (dired-get-marked-files t)))))
   (set (make-local-variable 'vc-log-file) file)
   (make-local-variable 'vc-log-version)
   (set-buffer-modified-p nil)
   (setq buffer-file-name nil))
 
-;;; These things should probably be generally available
+;; These things should probably be generally available
 
 (defun vc-file-tree-walk (dirname func &rest args)
   "Walk recursively through DIRNAME.
@@ -2724,350 +3217,350 @@ Invoke FUNC f ARGS on each VC-managed file f underneath it."
 
 (provide 'vc)
 
-;;; DEVELOPER'S NOTES ON CONCURRENCY PROBLEMS IN THIS CODE
-;;;
-;;; These may be useful to anyone who has to debug or extend the package.
-;;; (Note that this information corresponds to versions 5.x. Some of it
-;;; might have been invalidated by the additions to support branching
-;;; and RCS keyword lookup. AS, 1995/03/24)
-;;;
-;;; A fundamental problem in VC is that there are time windows between
-;;; vc-next-action's computations of the file's version-control state and
-;;; the actions that change it.  This is a window open to lossage in a
-;;; multi-user environment; someone else could nip in and change the state
-;;; of the master during it.
-;;;
-;;; The performance problem is that rlog/prs calls are very expensive; we want
-;;; to avoid them as much as possible.
-;;;
-;;; ANALYSIS:
-;;;
-;;; The performance problem, it turns out, simplifies in practice to the
-;;; problem of making vc-state fast.  The two other functions that call
-;;; prs/rlog will not be so commonly used that the slowdown is a problem; one
-;;; makes snapshots, the other deletes the calling user's last change in the
-;;; master.
-;;;
-;;; The race condition implies that we have to either (a) lock the master
-;;; during the entire execution of vc-next-action, or (b) detect and
-;;; recover from errors resulting from dispatch on an out-of-date state.
-;;;
-;;; Alternative (a) appears to be infeasible.  The problem is that we can't
-;;; guarantee that the lock will ever be removed.  Suppose a user starts a
-;;; checkin, the change message buffer pops up, and the user, having wandered
-;;; off to do something else, simply forgets about it?
-;;;
-;;; Alternative (b), on the other hand, works well with a cheap way to speed up
-;;; vc-state.  Usually, if a file is registered, we can read its locked/
-;;; unlocked state and its current owner from its permissions.
-;;;
-;;; This shortcut will fail if someone has manually changed the workfile's
-;;; permissions; also if developers are munging the workfile in several
-;;; directories, with symlinks to a master (in this latter case, the
-;;; permissions shortcut will fail to detect a lock asserted from another
-;;; directory).
-;;;
-;;; Note that these cases correspond exactly to the errors which could happen
-;;; because of a competing checkin/checkout race in between two instances of
-;;; vc-next-action.
-;;;
-;;; For VC's purposes, a workfile/master pair may have the following states:
-;;;
-;;; A. Unregistered.  There is a workfile, there is no master.
-;;;
-;;; B. Registered and not locked by anyone.
-;;;
-;;; C. Locked by calling user and unchanged.
-;;;
-;;; D. Locked by the calling user and changed.
-;;;
-;;; E. Locked by someone other than the calling user.
-;;;
-;;; This makes for 25 states and 20 error conditions.  Here's the matrix:
-;;;
-;;; VC's idea of state
-;;;  |
-;;;  V  Actual state   RCS action              SCCS action          Effect
-;;;    A  B  C  D  E
-;;;  A .  1  2  3  4   ci -u -t-          admin -fb -i<file>      initial admin
-;;;  B 5  .  6  7  8   co -l              get -e                  checkout
-;;;  C 9  10 .  11 12  co -u              unget; get              revert
-;;;  D 13 14 15 .  16  ci -u -m<comment>  delta -y<comment>; get  checkin
-;;;  E 17 18 19 20 .   rcs -u -M -l       unget -n ; get -g       steal lock
-;;;
-;;; All commands take the master file name as a last argument (not shown).
-;;;
-;;; In the discussion below, a "self-race" is a pathological situation in
-;;; which VC operations are being attempted simultaneously by two or more
-;;; Emacsen running under the same username.
-;;;
-;;; The vc-next-action code has the following windows:
-;;;
-;;; Window P:
-;;;    Between the check for existence of a master file and the call to
-;;; admin/checkin in vc-buffer-admin (apparent state A).  This window may
-;;; never close if the initial-comment feature is on.
-;;;
-;;; Window Q:
-;;;    Between the call to vc-workfile-unchanged-p in and the immediately
-;;; following revert (apparent state C).
-;;;
-;;; Window R:
-;;;    Between the call to vc-workfile-unchanged-p in and the following
-;;; checkin (apparent state D).  This window may never close.
-;;;
-;;; Window S:
-;;;    Between the unlock and the immediately following checkout during a
-;;; revert operation (apparent state C).  Included in window Q.
-;;;
-;;; Window T:
-;;;    Between vc-state and the following checkout (apparent state B).
-;;;
-;;; Window U:
-;;;    Between vc-state and the following revert (apparent state C).
-;;; Includes windows Q and S.
-;;;
-;;; Window V:
-;;;    Between vc-state and the following checkin (apparent state
-;;; D).  This window may never be closed if the user fails to complete the
-;;; checkin message.  Includes window R.
-;;;
-;;; Window W:
-;;;    Between vc-state and the following steal-lock (apparent
-;;; state E).  This window may never close if the user fails to complete
-;;; the steal-lock message.  Includes window X.
-;;;
-;;; Window X:
-;;;    Between the unlock and the immediately following re-lock during a
-;;; steal-lock operation (apparent state E).  This window may never close
-;;; if the user fails to complete the steal-lock message.
-;;;
-;;; Errors:
-;;;
-;;; Apparent state A ---
-;;;
-;;; 1. File looked unregistered but is actually registered and not locked.
-;;;
-;;;    Potential cause: someone else's admin during window P, with
-;;; caller's admin happening before their checkout.
-;;;
-;;;    RCS: Prior to version 5.6.4, ci fails with message
-;;;         "no lock set by <user>".  From 5.6.4 onwards, VC uses the new
-;;;         ci -i option and the message is "<file>,v: already exists".
-;;;    SCCS: admin will fail with error (ad19).
-;;;
-;;;    We can let these errors be passed up to the user.
-;;;
-;;; 2. File looked unregistered but is actually locked by caller, unchanged.
-;;;
-;;;    Potential cause: self-race during window P.
-;;;
-;;;    RCS: Prior to version 5.6.4, reverts the file to the last saved
-;;;         version and unlocks it.  From 5.6.4 onwards, VC uses the new
-;;;         ci -i option, failing with message "<file>,v: already exists".
-;;;    SCCS: will fail with error (ad19).
-;;;
-;;;    Either of these consequences is acceptable.
-;;;
-;;; 3. File looked unregistered but is actually locked by caller, changed.
-;;;
-;;;    Potential cause: self-race during window P.
-;;;
-;;;    RCS: Prior to version 5.6.4, VC registers the caller's workfile as
-;;;         a delta with a null change comment (the -t- switch will be
-;;;         ignored). From 5.6.4 onwards, VC uses the new ci -i option,
-;;;         failing with message "<file>,v: already exists".
-;;;    SCCS: will fail with error (ad19).
-;;;
-;;; 4. File looked unregistered but is locked by someone else.
-;;;
-;;;    Potential cause: someone else's admin during window P, with
-;;; caller's admin happening *after* their checkout.
-;;;
-;;;    RCS: Prior to version 5.6.4, ci fails with a
-;;;         "no lock set by <user>" message.  From 5.6.4 onwards,
-;;;         VC uses the new ci -i option, failing with message
-;;;         "<file>,v: already exists".
-;;;    SCCS: will fail with error (ad19).
-;;;
-;;;    We can let these errors be passed up to the user.
-;;;
-;;; Apparent state B ---
-;;;
-;;; 5. File looked registered and not locked, but is actually unregistered.
-;;;
-;;;    Potential cause: master file got nuked during window P.
-;;;
-;;;    RCS: will fail with "RCS/<file>: No such file or directory"
-;;;    SCCS: will fail with error ut4.
-;;;
-;;;    We can let these errors be passed up to the user.
-;;;
-;;; 6. File looked registered and not locked, but is actually locked by the
-;;; calling user and unchanged.
-;;;
-;;;    Potential cause: self-race during window T.
-;;;
-;;;    RCS: in the same directory as the previous workfile, co -l will fail
-;;; with "co error: writable foo exists; checkout aborted".  In any other
-;;; directory, checkout will succeed.
-;;;    SCCS: will fail with ge17.
-;;;
-;;;    Either of these consequences is acceptable.
-;;;
-;;; 7. File looked registered and not locked, but is actually locked by the
-;;; calling user and changed.
-;;;
-;;;    As case 6.
-;;;
-;;; 8. File looked registered and not locked, but is actually locked by another
-;;; user.
-;;;
-;;;    Potential cause: someone else checks it out during window T.
-;;;
-;;;    RCS: co error: revision 1.3 already locked by <user>
-;;;    SCCS: fails with ge4 (in directory) or ut7 (outside it).
-;;;
-;;;    We can let these errors be passed up to the user.
-;;;
-;;; Apparent state C ---
-;;;
-;;; 9. File looks locked by calling user and unchanged, but is unregistered.
-;;;
-;;;    As case 5.
-;;;
-;;; 10. File looks locked by calling user and unchanged, but is actually not
-;;; locked.
-;;;
-;;;    Potential cause: a self-race in window U, or by the revert's
-;;; landing during window X of some other user's steal-lock or window S
-;;; of another user's revert.
-;;;
-;;;    RCS: succeeds, refreshing the file from the identical version in
-;;; the master.
-;;;    SCCS: fails with error ut4 (p file nonexistent).
-;;;
-;;;    Either of these consequences is acceptable.
-;;;
-;;; 11. File is locked by calling user.  It looks unchanged, but is actually
-;;; changed.
-;;;
-;;;    Potential cause: the file would have to be touched by a self-race
-;;; during window Q.
-;;;
-;;;    The revert will succeed, removing whatever changes came with
-;;; the touch.  It is theoretically possible that work could be lost.
-;;;
-;;; 12. File looks like it's locked by the calling user and unchanged, but
-;;; it's actually locked by someone else.
-;;;
-;;;    Potential cause: a steal-lock in window V.
-;;;
-;;;    RCS: co error: revision <rev> locked by <user>; use co -r or rcs -u
-;;;    SCCS: fails with error un2
-;;;
-;;;    We can pass these errors up to the user.
-;;;
-;;; Apparent state D ---
-;;;
-;;; 13. File looks like it's locked by the calling user and changed, but it's
-;;; actually unregistered.
-;;;
-;;;    Potential cause: master file got nuked during window P.
-;;;
-;;;    RCS: Prior to version 5.6.4, checks in the user's version as an
-;;;         initial delta.  From 5.6.4 onwards, VC uses the new ci -j
-;;;         option, failing with message "no such file or directory".
-;;;    SCCS: will fail with error ut4.
-;;;
-;;;    This case is kind of nasty.  Under RCS prior to version 5.6.4,
-;;; VC may fail to detect the loss of previous version information.
-;;;
-;;; 14. File looks like it's locked by the calling user and changed, but it's
-;;; actually unlocked.
-;;;
-;;;    Potential cause: self-race in window V, or the checkin happening
-;;; during the window X of someone else's steal-lock or window S of
-;;; someone else's revert.
-;;;
-;;;    RCS: ci will fail with "no lock set by <user>".
-;;;    SCCS: delta will fail with error ut4.
-;;;
-;;; 15. File looks like it's locked by the calling user and changed, but it's
-;;; actually locked by the calling user and unchanged.
-;;;
-;;;    Potential cause: another self-race --- a whole checkin/checkout
-;;; sequence by the calling user would have to land in window R.
-;;;
-;;;    SCCS: checks in a redundant delta and leaves the file unlocked as usual.
-;;;    RCS: reverts to the file state as of the second user's checkin, leaving
-;;; the file unlocked.
-;;;
-;;;    It is theoretically possible that work could be lost under RCS.
-;;;
-;;; 16. File looks like it's locked by the calling user and changed, but it's
-;;; actually locked by a different user.
-;;;
-;;;    RCS: ci error: no lock set by <user>
-;;;    SCCS: unget will fail with error un2
-;;;
-;;;    We can pass these errors up to the user.
-;;;
-;;; Apparent state E ---
-;;;
-;;; 17. File looks like it's locked by some other user, but it's actually
-;;; unregistered.
-;;;
-;;;    As case 13.
-;;;
-;;; 18. File looks like it's locked by some other user, but it's actually
-;;; unlocked.
-;;;
-;;;    Potential cause: someone released a lock during window W.
-;;;
-;;;    RCS: The calling user will get the lock on the file.
-;;;    SCCS: unget -n will fail with cm4.
-;;;
-;;;    Either of these consequences will be OK.
-;;;
-;;; 19. File looks like it's locked by some other user, but it's actually
-;;; locked by the calling user and unchanged.
-;;;
-;;;    Potential cause: the other user relinquishing a lock followed by
-;;; a self-race, both in window W.
-;;;
-;;;     Under both RCS and SCCS, both unlock and lock will succeed, making
-;;; the sequence a no-op.
-;;;
-;;; 20. File looks like it's locked by some other user, but it's actually
-;;; locked by the calling user and changed.
-;;;
-;;;     As case 19.
-;;;
-;;; PROBLEM CASES:
-;;;
-;;;    In order of decreasing severity:
-;;;
-;;;    Cases 11 and 15 are the only ones that potentially lose work.
-;;; They would require a self-race for this to happen.
-;;;
-;;;    Case 13 in RCS loses information about previous deltas, retaining
-;;; only the information in the current workfile.  This can only happen
-;;; if the master file gets nuked in window P.
-;;;
-;;;    Case 3 in RCS and case 15 under SCCS insert a redundant delta with
-;;; no change comment in the master.  This would require a self-race in
-;;; window P or R respectively.
-;;;
-;;;    Cases 2, 10, 19 and 20 do extra work, but make no changes.
-;;;
-;;;    Unfortunately, it appears to me that no recovery is possible in these
-;;; cases.  They don't yield error messages, so there's no way to tell that
-;;; a race condition has occurred.
-;;;
-;;;    All other cases don't change either the workfile or the master, and
-;;; trigger command errors which the user will see.
+;; DEVELOPER'S NOTES ON CONCURRENCY PROBLEMS IN THIS CODE
+;;
+;; These may be useful to anyone who has to debug or extend the package.
+;; (Note that this information corresponds to versions 5.x. Some of it
+;; might have been invalidated by the additions to support branching
+;; and RCS keyword lookup. AS, 1995/03/24)
+;;
+;; A fundamental problem in VC is that there are time windows between
+;; vc-next-action's computations of the file's version-control state and
+;; the actions that change it.  This is a window open to lossage in a
+;; multi-user environment; someone else could nip in and change the state
+;; of the master during it.
+;;
+;; The performance problem is that rlog/prs calls are very expensive; we want
+;; to avoid them as much as possible.
+;;
+;; ANALYSIS:
+;;
+;; The performance problem, it turns out, simplifies in practice to the
+;; problem of making vc-state fast.  The two other functions that call
+;; prs/rlog will not be so commonly used that the slowdown is a problem; one
+;; makes snapshots, the other deletes the calling user's last change in the
+;; master.
+;;
+;; The race condition implies that we have to either (a) lock the master
+;; during the entire execution of vc-next-action, or (b) detect and
+;; recover from errors resulting from dispatch on an out-of-date state.
+;;
+;; Alternative (a) appears to be infeasible.  The problem is that we can't
+;; guarantee that the lock will ever be removed.  Suppose a user starts a
+;; checkin, the change message buffer pops up, and the user, having wandered
+;; off to do something else, simply forgets about it?
+;;
+;; Alternative (b), on the other hand, works well with a cheap way to speed up
+;; vc-state.  Usually, if a file is registered, we can read its locked/
+;; unlocked state and its current owner from its permissions.
+;;
+;; This shortcut will fail if someone has manually changed the workfile's
+;; permissions; also if developers are munging the workfile in several
+;; directories, with symlinks to a master (in this latter case, the
+;; permissions shortcut will fail to detect a lock asserted from another
+;; directory).
+;;
+;; Note that these cases correspond exactly to the errors which could happen
+;; because of a competing checkin/checkout race in between two instances of
+;; vc-next-action.
+;;
+;; For VC's purposes, a workfile/master pair may have the following states:
+;;
+;; A. Unregistered.  There is a workfile, there is no master.
+;;
+;; B. Registered and not locked by anyone.
+;;
+;; C. Locked by calling user and unchanged.
+;;
+;; D. Locked by the calling user and changed.
+;;
+;; E. Locked by someone other than the calling user.
+;;
+;; This makes for 25 states and 20 error conditions.  Here's the matrix:
+;;
+;; VC's idea of state
+;;  |
+;;  V  Actual state   RCS action              SCCS action          Effect
+;;    A  B  C  D  E
+;;  A .  1  2  3  4   ci -u -t-          admin -fb -i<file>      initial admin
+;;  B 5  .  6  7  8   co -l              get -e                  checkout
+;;  C 9  10 .  11 12  co -u              unget; get              revert
+;;  D 13 14 15 .  16  ci -u -m<comment>  delta -y<comment>; get  checkin
+;;  E 17 18 19 20 .   rcs -u -M -l       unget -n ; get -g       steal lock
+;;
+;; All commands take the master file name as a last argument (not shown).
+;;
+;; In the discussion below, a "self-race" is a pathological situation in
+;; which VC operations are being attempted simultaneously by two or more
+;; Emacsen running under the same username.
+;;
+;; The vc-next-action code has the following windows:
+;;
+;; Window P:
+;;    Between the check for existence of a master file and the call to
+;; admin/checkin in vc-buffer-admin (apparent state A).  This window may
+;; never close if the initial-comment feature is on.
+;;
+;; Window Q:
+;;    Between the call to vc-workfile-unchanged-p in and the immediately
+;; following revert (apparent state C).
+;;
+;; Window R:
+;;    Between the call to vc-workfile-unchanged-p in and the following
+;; checkin (apparent state D).  This window may never close.
+;;
+;; Window S:
+;;    Between the unlock and the immediately following checkout during a
+;; revert operation (apparent state C).  Included in window Q.
+;;
+;; Window T:
+;;    Between vc-state and the following checkout (apparent state B).
+;;
+;; Window U:
+;;    Between vc-state and the following revert (apparent state C).
+;; Includes windows Q and S.
+;;
+;; Window V:
+;;    Between vc-state and the following checkin (apparent state
+;; D).  This window may never be closed if the user fails to complete the
+;; checkin message.  Includes window R.
+;;
+;; Window W:
+;;    Between vc-state and the following steal-lock (apparent
+;; state E).  This window may never close if the user fails to complete
+;; the steal-lock message.  Includes window X.
+;;
+;; Window X:
+;;    Between the unlock and the immediately following re-lock during a
+;; steal-lock operation (apparent state E).  This window may never close
+;; if the user fails to complete the steal-lock message.
+;;
+;; Errors:
+;;
+;; Apparent state A ---
+;;
+;; 1. File looked unregistered but is actually registered and not locked.
+;;
+;;    Potential cause: someone else's admin during window P, with
+;; caller's admin happening before their checkout.
+;;
+;;    RCS: Prior to version 5.6.4, ci fails with message
+;;         "no lock set by <user>".  From 5.6.4 onwards, VC uses the new
+;;         ci -i option and the message is "<file>,v: already exists".
+;;    SCCS: admin will fail with error (ad19).
+;;
+;;    We can let these errors be passed up to the user.
+;;
+;; 2. File looked unregistered but is actually locked by caller, unchanged.
+;;
+;;    Potential cause: self-race during window P.
+;;
+;;    RCS: Prior to version 5.6.4, reverts the file to the last saved
+;;         version and unlocks it.  From 5.6.4 onwards, VC uses the new
+;;         ci -i option, failing with message "<file>,v: already exists".
+;;    SCCS: will fail with error (ad19).
+;;
+;;    Either of these consequences is acceptable.
+;;
+;; 3. File looked unregistered but is actually locked by caller, changed.
+;;
+;;    Potential cause: self-race during window P.
+;;
+;;    RCS: Prior to version 5.6.4, VC registers the caller's workfile as
+;;         a delta with a null change comment (the -t- switch will be
+;;         ignored). From 5.6.4 onwards, VC uses the new ci -i option,
+;;         failing with message "<file>,v: already exists".
+;;    SCCS: will fail with error (ad19).
+;;
+;; 4. File looked unregistered but is locked by someone else.
 ;;;
-;;;    Thus, there is no explicit recovery code.
+;;    Potential cause: someone else's admin during window P, with
+;; caller's admin happening *after* their checkout.
+;;
+;;    RCS: Prior to version 5.6.4, ci fails with a
+;;         "no lock set by <user>" message.  From 5.6.4 onwards,
+;;         VC uses the new ci -i option, failing with message
+;;         "<file>,v: already exists".
+;;    SCCS: will fail with error (ad19).
+;;
+;;    We can let these errors be passed up to the user.
+;;
+;; Apparent state B ---
+;;
+;; 5. File looked registered and not locked, but is actually unregistered.
+;;
+;;    Potential cause: master file got nuked during window P.
+;;
+;;    RCS: will fail with "RCS/<file>: No such file or directory"
+;;    SCCS: will fail with error ut4.
+;;
+;;    We can let these errors be passed up to the user.
+;;
+;; 6. File looked registered and not locked, but is actually locked by the
+;; calling user and unchanged.
+;;
+;;    Potential cause: self-race during window T.
+;;
+;;    RCS: in the same directory as the previous workfile, co -l will fail
+;; with "co error: writable foo exists; checkout aborted".  In any other
+;; directory, checkout will succeed.
+;;    SCCS: will fail with ge17.
+;;
+;;    Either of these consequences is acceptable.
+;;
+;; 7. File looked registered and not locked, but is actually locked by the
+;; calling user and changed.
+;;
+;;    As case 6.
+;;
+;; 8. File looked registered and not locked, but is actually locked by another
+;; user.
+;;
+;;    Potential cause: someone else checks it out during window T.
+;;
+;;    RCS: co error: revision 1.3 already locked by <user>
+;;    SCCS: fails with ge4 (in directory) or ut7 (outside it).
+;;
+;;    We can let these errors be passed up to the user.
+;;
+;; Apparent state C ---
+;;
+;; 9. File looks locked by calling user and unchanged, but is unregistered.
+;;
+;;    As case 5.
+;;
+;; 10. File looks locked by calling user and unchanged, but is actually not
+;; locked.
+;;
+;;    Potential cause: a self-race in window U, or by the revert's
+;; landing during window X of some other user's steal-lock or window S
+;; of another user's revert.
+;;
+;;    RCS: succeeds, refreshing the file from the identical version in
+;; the master.
+;;    SCCS: fails with error ut4 (p file nonexistent).
+;;
+;;    Either of these consequences is acceptable.
+;;
+;; 11. File is locked by calling user.  It looks unchanged, but is actually
+;; changed.
+;;
+;;    Potential cause: the file would have to be touched by a self-race
+;; during window Q.
+;;
+;;    The revert will succeed, removing whatever changes came with
+;; the touch.  It is theoretically possible that work could be lost.
+;;
+;; 12. File looks like it's locked by the calling user and unchanged, but
+;; it's actually locked by someone else.
+;;
+;;    Potential cause: a steal-lock in window V.
+;;
+;;    RCS: co error: revision <rev> locked by <user>; use co -r or rcs -u
+;;    SCCS: fails with error un2
+;;
+;;    We can pass these errors up to the user.
+;;
+;; Apparent state D ---
+;;
+;; 13. File looks like it's locked by the calling user and changed, but it's
+;; actually unregistered.
+;;
+;;    Potential cause: master file got nuked during window P.
+;;
+;;    RCS: Prior to version 5.6.4, checks in the user's version as an
+;;         initial delta.  From 5.6.4 onwards, VC uses the new ci -j
+;;         option, failing with message "no such file or directory".
+;;    SCCS: will fail with error ut4.
+;;
+;;    This case is kind of nasty.  Under RCS prior to version 5.6.4,
+;; VC may fail to detect the loss of previous version information.
+;;
+;; 14. File looks like it's locked by the calling user and changed, but it's
+;; actually unlocked.
+;;
+;;    Potential cause: self-race in window V, or the checkin happening
+;; during the window X of someone else's steal-lock or window S of
+;; someone else's revert.
+;;
+;;    RCS: ci will fail with "no lock set by <user>".
+;;    SCCS: delta will fail with error ut4.
+;;
+;; 15. File looks like it's locked by the calling user and changed, but it's
+;; actually locked by the calling user and unchanged.
+;;
+;;    Potential cause: another self-race --- a whole checkin/checkout
+;; sequence by the calling user would have to land in window R.
+;;
+;;    SCCS: checks in a redundant delta and leaves the file unlocked as usual.
+;;    RCS: reverts to the file state as of the second user's checkin, leaving
+;; the file unlocked.
+;;
+;;    It is theoretically possible that work could be lost under RCS.
+;;
+;; 16. File looks like it's locked by the calling user and changed, but it's
+;; actually locked by a different user.
+;;
+;;    RCS: ci error: no lock set by <user>
+;;    SCCS: unget will fail with error un2
+;;
+;;    We can pass these errors up to the user.
+;;
+;; Apparent state E ---
+;;
+;; 17. File looks like it's locked by some other user, but it's actually
+;; unregistered.
+;;
+;;    As case 13.
+;;
+;; 18. File looks like it's locked by some other user, but it's actually
+;; unlocked.
+;;
+;;    Potential cause: someone released a lock during window W.
+;;
+;;    RCS: The calling user will get the lock on the file.
+;;    SCCS: unget -n will fail with cm4.
+;;
+;;    Either of these consequences will be OK.
+;;
+;; 19. File looks like it's locked by some other user, but it's actually
+;; locked by the calling user and unchanged.
+;;
+;;    Potential cause: the other user relinquishing a lock followed by
+;; a self-race, both in window W.
+;;
+;;     Under both RCS and SCCS, both unlock and lock will succeed, making
+;; the sequence a no-op.
+;;
+;; 20. File looks like it's locked by some other user, but it's actually
+;; locked by the calling user and changed.
+;;
+;;     As case 19.
+;;
+;; PROBLEM CASES:
+;;
+;;    In order of decreasing severity:
+;;
+;;    Cases 11 and 15 are the only ones that potentially lose work.
+;; They would require a self-race for this to happen.
+;;
+;;    Case 13 in RCS loses information about previous deltas, retaining
+;; only the information in the current workfile.  This can only happen
+;; if the master file gets nuked in window P.
+;;
+;;    Case 3 in RCS and case 15 under SCCS insert a redundant delta with
+;; no change comment in the master.  This would require a self-race in
+;; window P or R respectively.
+;;
+;;    Cases 2, 10, 19 and 20 do extra work, but make no changes.
+;;
+;;    Unfortunately, it appears to me that no recovery is possible in these
+;; cases.  They don't yield error messages, so there's no way to tell that
+;; a race condition has occurred.
+;;
+;;    All other cases don't change either the workfile or the master, and
+;; trigger command errors which the user will see.
+;;
+;;    Thus, there is no explicit recovery code.
 
 ;;; vc.el ends here