*** empty log message ***
[bpt/emacs.git] / lisp / vc.el
index 2257363..17af1f8 100644 (file)
@@ -1,11 +1,11 @@
 ;;; vc.el --- drive a version-control system from within Emacs
 
-;; Copyright (C) 1992, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
+;; Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
 
 ;; Author:     Eric S. Raymond <esr@snark.thyrsus.com>
 ;; Maintainer: Andre Spiegel <spiegel@inf.fu-berlin.de>
 
-;; $Id: vc.el,v 1.214 1998/03/31 18:08:36 spiegel Exp spiegel $
+;; $Id: vc.el,v 1.223 1998/04/15 10:13:07 spiegel Exp done $
 
 ;; This file is part of GNU Emacs.
 
@@ -32,7 +32,7 @@
 ;; Paul Eggert <eggert@twinsun.com>, Sebastian Kremer <sk@thp.uni-koeln.de>,
 ;; and Richard Stallman contributed valuable criticism, support, and testing.
 ;; CVS support was added by Per Cederqvist <ceder@lysator.liu.se>
-;; in Jan-Feb 1994.  Further enhancements came from ttn.netcom.com and
+;; in Jan-Feb 1994.  Further enhancements came from ttn@netcom.com and
 ;; Andre Spiegel <spiegel@inf.fu-berlin.de>.
 ;;
 ;; Supported version-control systems presently include SCCS, RCS, and CVS.
@@ -116,7 +116,8 @@ If FORM3 is `RCS', use FORM2 for CVS as well as RCS.
   "*A string used as the default version number when a new file is registered.
 This can be overriden by giving a prefix argument to \\[vc-register]."
   :type 'string
-  :group 'vc)
+  :group 'vc
+  :version "20.3")
 
 (defcustom vc-command-messages nil
   "*If non-nil, display run messages from back-end commands."
@@ -318,27 +319,6 @@ If nil, VC itself computes this value when it is first needed."
 (defvar vc-comment-ring-index nil)
 (defvar vc-last-comment-match nil)
 
-;; Back-portability to Emacs 18
-
-(defun file-executable-p-18 (f)
-  (let ((modes (file-modes f)))
-    (and modes (not (zerop (logand 292))))))
-
-(defun file-regular-p-18 (f)
-  (let ((attributes (file-attributes f)))
-    (and attributes (not (car attributes)))))
-
-; Conditionally rebind some things for Emacs 18 compatibility
-(if (not (boundp 'minor-mode-map-alist))
-    (progn
-      (setq compilation-old-error-list nil)
-      (fset 'file-executable-p 'file-executable-p-18)
-      (fset 'shrink-window-if-larger-than-buffer 'beginning-of-buffer)
-      ))
-
-(if (not (fboundp 'file-regular-p))
-    (fset 'file-regular-p 'file-regular-p-18))
-
 ;;; Find and compare backend releases
 
 (defun vc-backend-release (backend)
@@ -409,6 +389,10 @@ If nil, VC itself computes this value when it is first needed."
   ;; return t if REV is a revision on the trunk
   (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
 
+(defun vc-branch-p (rev)
+  ;; return t if REV is a branch revision
+  (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
+
 (defun vc-branch-part (rev)
   ;; return the branch part of a revision number REV
   (substring rev 0 (string-match "\\.[0-9]+\\'" rev)))
@@ -498,10 +482,16 @@ If nil, VC itself computes this value when it is first needed."
      ;; CVS
      t))
 
-(defun vc-registration-error (file)
-  (if file
-      (error "File %s is not under version control" file)
-    (error "Buffer %s is not associated with a file" (buffer-name))))
+(defun vc-ensure-vc-buffer ()
+  ;; Make sure that the current buffer visits a version-controlled file.
+  (if vc-dired-mode
+      (set-buffer (find-file-noselect (dired-get-filename)))
+    (while vc-parent-buffer
+      (pop-to-buffer vc-parent-buffer))
+    (if (not (buffer-file-name))
+       (error "Buffer %s is not associated with a file" (buffer-name))
+      (if (not (vc-backend (buffer-file-name)))
+         (error "File %s is not under version control" (buffer-file-name))))))
 
 (defvar vc-binary-assoc nil)
 
@@ -540,9 +530,8 @@ before the filename."
       (message "Running %s on %s..." command file))
   (let ((obuf (current-buffer)) (camefrom (current-buffer))
        (squeezed nil)
-       (vc-file (and file (vc-name file)))
        (olddir default-directory)
-       status)
+       vc-file status)
     (set-buffer (get-buffer-create buffer))
     (set (make-local-variable 'vc-parent-buffer) camefrom)
     (set (make-local-variable 'vc-parent-buffer-name)
@@ -554,7 +543,7 @@ before the filename."
     (mapcar
      (function (lambda (s) (and s (setq squeezed (append squeezed (list s))))))
      flags)
-    (if (and vc-file (eq last 'MASTER))
+    (if (and (eq last 'MASTER) file (setq vc-file (vc-name file)))
        (setq squeezed (append squeezed (list vc-file))))
     (if (and file (eq last 'WORKFILE))
        (progn
@@ -745,18 +734,13 @@ before the filename."
 
 (defun vc-next-action-on-file (file verbose &optional comment)
   ;;; If comment is specified, it will be used as an admin or checkin comment.
-  (let ((vc-file (vc-name file))
-       (vc-type (vc-backend file))
+  (let ((vc-type (vc-backend file))
        owner version buffer)
     (cond
 
-     ;; if there is no master file corresponding, create one
-     ((not vc-file)
-      (vc-register verbose comment)
-      (if vc-initial-comment
-         (setq vc-log-after-operation-hook
-               'vc-checkout-writable-buffer-hook)
-       (vc-checkout-writable-buffer file)))
+     ;; If the file is not under version control, register it
+     ((not vc-type)
+      (vc-register verbose comment))
 
      ;; CVS: changes to the master file need to be 
      ;; merged back into the working file
@@ -893,8 +877,7 @@ before the filename."
 (defun vc-next-action-dired (file rev comment)
   ;; Do a vc-next-action-on-file on all the marked files, possibly 
   ;; passing on the log comment we've just entered.
-  (let ((configuration (current-window-configuration))
-       (dired-buffer (current-buffer))
+  (let ((dired-buffer (current-buffer))
        (dired-dir default-directory))
     (dired-map-over-marks
      (let ((file (dired-get-filename)) p
@@ -906,10 +889,11 @@ before the filename."
        (vc-next-action-on-file file nil comment)
        (set-buffer dired-buffer)
        (setq default-directory dired-dir)
-       (vc-dired-update-line file)
-       (set-window-configuration configuration)
+       (dired-do-redisplay file)
+       (set-window-configuration vc-dired-window-configuration)
        (message "Processing %s...done" file))
-    nil t)))
+    nil t))
+  (dired-move-to-filename))
 
 ;; Here's the major entry point.
 
@@ -927,7 +911,7 @@ lock steals will raise an error.
 
 For RCS and SCCS files:
    If the file is not already registered, this registers it for version
-control and then retrieves a writable, locked copy for editing.
+control.
    If the file is registered and not locked by anyone, this checks out
 a writable and locked file ready for editing.
    If the file is checked out and locked by the calling user, this
@@ -956,6 +940,8 @@ merge in the changes into your working copy."
   (catch 'nogo
     (if vc-dired-mode
        (let ((files (dired-get-marked-files)))
+          (set (make-local-variable 'vc-dired-window-configuration)
+               (current-window-configuration))
          (if (string= "" 
                 (mapconcat
                     (function (lambda (f)
@@ -973,8 +959,8 @@ merge in the changes into your working copy."
     (while vc-parent-buffer
       (pop-to-buffer vc-parent-buffer))
     (if buffer-file-name
-       (vc-next-action-on-file buffer-file-name verbose)
-      (vc-registration-error nil))))
+        (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
 
@@ -1231,11 +1217,14 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
     ;; Remove checkin window (after the checkin so that if that fails
     ;; we don't zap the *VC-log* buffer and the typing therein).
     (let ((logbuf (get-buffer "*VC-log*")))
-      (delete-windows-on logbuf)
-      (kill-buffer logbuf))
+      (cond (logbuf
+             (delete-windows-on logbuf)
+             (kill-buffer logbuf))))
     ;; Now make sure we see the expanded headers
     (if buffer-file-name
        (vc-resynch-window buffer-file-name vc-keep-workfiles t))
+    (if vc-dired-mode 
+        (dired-move-to-filename))
     (run-hooks after-hook 'vc-finish-logentry-hook)))
 
 ;; Code for access to the comment ring
@@ -1310,15 +1299,9 @@ 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."
   (interactive (list current-prefix-arg t))
-  (if vc-dired-mode
-      (set-buffer (find-file-noselect (dired-get-filename))))
-  (while vc-parent-buffer
-      (pop-to-buffer vc-parent-buffer))
+  (vc-ensure-vc-buffer)
   (if historic
       (call-interactively 'vc-version-diff)
-    (if (or (null buffer-file-name) (null (vc-name buffer-file-name)))
-       (error
-        "There is no version-control master associated with this buffer"))
     (let ((file buffer-file-name)
          unchanged)
       (vc-buffer-sync not-urgent)
@@ -1419,19 +1402,14 @@ files in or below it."
 If the current buffer is named `F', the version is named `F.~REV~'.
 If `F.~REV~' already exists, it is used instead of being re-created."
   (interactive "sVersion to visit (default is latest version): ")
-  (if vc-dired-mode
-      (set-buffer (find-file-noselect (dired-get-filename))))
-  (while vc-parent-buffer
-      (pop-to-buffer vc-parent-buffer))
-  (if (and buffer-file-name (vc-name buffer-file-name))
-      (let* ((version (if (string-equal rev "")
-                         (vc-latest-version buffer-file-name)
-                       rev))
-            (filename (concat buffer-file-name ".~" version "~")))
-        (or (file-exists-p filename)
-            (vc-backend-checkout buffer-file-name nil version filename))
-        (find-file-other-window filename))
-    (vc-registration-error buffer-file-name)))
+  (vc-ensure-vc-buffer)
+  (let* ((version (if (string-equal rev "")
+                     (vc-latest-version buffer-file-name)
+                   rev))
+        (filename (concat buffer-file-name ".~" version "~")))
+    (or (file-exists-p filename)
+       (vc-backend-checkout buffer-file-name nil version filename))
+    (find-file-other-window filename)))
 
 ;; Header-insertion code
 
@@ -1441,10 +1419,7 @@ If `F.~REV~' already exists, it is used instead of being re-created."
 Headers desired are inserted at the start of the buffer, and are pulled from
 the variable `vc-header-alist'."
   (interactive)
-  (if vc-dired-mode
-      (find-file-other-window (dired-get-filename)))
-  (while vc-parent-buffer
-      (pop-to-buffer vc-parent-buffer))
+  (vc-ensure-vc-buffer)
   (save-excursion
     (save-restriction
       (widen)
@@ -1484,16 +1459,64 @@ the variable `vc-header-alist'."
        (replace-match "$\\1$")))
     (vc-restore-buffer-context context)))
 
-(defun vc-resolve-conflicts ()
+;;;###autoload
+(defun vc-merge ()
+  (interactive)
+  (vc-ensure-vc-buffer)
+  (vc-buffer-sync)
+  (let* ((file buffer-file-name)
+        (backend (vc-backend file))
+        first-version second-version locking-user)
+    (if (eq backend 'SCCS)
+       (error "Sorry, merging is not implemented for SCCS")
+      (setq locking-user (vc-locking-user file))
+      (if (eq (vc-checkout-model file) 'manual)
+         (if (not locking-user)
+             (if (not (y-or-n-p 
+                       (format "File must be %s for merging.  %s now? "
+                               (if (eq backend 'RCS) "locked" "writable")
+                               (if (eq backend 'RCS) "Lock" "Check out"))))
+                 (error "Merge aborted")
+               (vc-checkout file t))
+           (if (not (string= locking-user (vc-user-login-name)))
+               (error "File is locked by %s" locking-user))))
+      (setq first-version (read-string "Branch or version to merge from: "))
+      (if (and (>= (elt first-version 0) ?0)
+              (<= (elt first-version 0) ?9))
+         (if (not (vc-branch-p first-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.
+           (setq second-version first-version)
+           ;; first-version must be the starting point of the branch
+           (setq first-version (vc-branch-part first-version))))
+      (let ((status (vc-backend-merge file first-version second-version)))
+       (if (and (eq (vc-checkout-model file) 'implicit)
+                (not (vc-locking-user file)))
+           (vc-file-setprop file 'vc-locking-user nil))
+       (vc-resynch-buffer file t t)
+       (if (not (zerop status))
+           (if (y-or-n-p "Conflicts detected.  Resolve them now? ")
+               (vc-resolve-conflicts "WORKFILE" "MERGE SOURCE")
+             (message "File contains conflict markers"))
+         (message "Merge successful"))))))
+
+;;;###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 " WORKFILE*")))
+                         (concat "*" file-name 
+                                " " (or name-A "WORKFILE") "*")))
         (other-buffer  (generate-new-buffer 
-                         (concat "*" file-name " CHECKED-IN*")))
+                         (concat "*" file-name 
+                                " " (or name-B "CHECKED-IN") "*")))
          (result-buffer (current-buffer)))
     (save-excursion 
       (set-buffer your-buffer)
@@ -1568,187 +1591,162 @@ The conflicts must be marked with rcsmerge conflict markers."
 ;; All VC commands get mapped into logical equivalents.
 
 (define-derived-mode vc-dired-mode dired-mode "Dired under VC"
-  "The major mode used in VC directory buffers.  It is derived from Dired.
-All Dired commands operate normally.  Users currently locking listed files
-are listed in place of the file's owner and group.
-Keystrokes bound to VC commands will execute as though they had been called
-on a buffer attached to the file named in the current Dired buffer line."
+  "The major mode used in VC directory buffers.  It works like Dired,
+but lists only files under version control, with the current VC state of 
+each file being indicated in the place of the file's link count, owner, 
+group and size.  Subdirectories are also listed, and you may insert them 
+into the buffer as desired, like in Dired.
+  All Dired commands operate normally, with the exception of `v', which
+is redefined as the version control prefix, so that you can type 
+`vl', `v=' etc. to invoke `vc-print-log', `vc-diff', and the like on
+the file named in the current Dired buffer line.  `vv' invokes
+`vc-next-action' on this file, or on all files currently marked.
+There is a special command, `*l', to mark all files currently locked."
+  (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.
+  (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 "\\)"))
+           (japanese (concat mm k s dd k s "\\(" s HH:MM "\\|" yyyy k "\\)")))
+         (concat s "\\(" western "\\|" japanese "\\)" s)))
   (setq vc-dired-mode t))
 
 (define-key vc-dired-mode-map "\C-xv" vc-prefix-map)
-(define-key vc-dired-mode-map "g" 'vc-dired-update)
+(define-key vc-dired-mode-map "v" vc-prefix-map)
 (define-key vc-dired-mode-map "=" 'vc-diff)
 
+(defun vc-dired-mark-locked ()
+  "Mark all files currently locked."
+  (interactive)
+  (dired-mark-if (let ((f (dired-get-filename nil t)))
+                  (and f
+                       (not (file-directory-p f))
+                       (vc-locking-user f)))
+                "locked file"))
+
+(define-key vc-dired-mode-map "*l" 'vc-dired-mark-locked)
+
+(defun vc-fetch-cvs-status (dir)
+  (let ((default-directory dir))
+    (vc-do-command "*vc-info*" 0 "cvs" nil nil "status" dir)
+    (save-excursion
+      (set-buffer (get-buffer "*vc-info*"))
+      (goto-char (point-min))
+      (while (re-search-forward "^=+\n\\([^=\n].*\n\\|\n\\)+" nil t)
+        (narrow-to-region (match-beginning 0) (match-end 0))
+        (vc-parse-cvs-status)
+        (goto-char (point-max))
+        (widen)))))
+
 (defun vc-dired-state-info (file)
   ;; Return the string that indicates the version control status
   ;; on a VC dired line.
-  (let ((cvs-state (and (eq (vc-backend file) 'CVS)
-                       (vc-cvs-status file))))
-    (if cvs-state
-       (cond ((eq cvs-state 'up-to-date) nil)
-             ((eq cvs-state 'needs-checkout)      "patch")
-             ((eq cvs-state 'locally-modified)    "modified")
-             ((eq cvs-state 'needs-merge)         "merge")
-             ((eq cvs-state 'unresolved-conflict) "conflict")
-             ((eq cvs-state 'locally-added)       "added"))
-      (vc-locking-user file))))
+  (let* ((cvs-state (and (eq (vc-backend file) 'CVS)
+                         (vc-cvs-status file)))
+         (state 
+          (if cvs-state
+              (cond ((eq cvs-state 'up-to-date) nil)
+                    ((eq cvs-state 'needs-checkout)      "patch")
+                    ((eq cvs-state 'locally-modified)    "modified")
+                    ((eq cvs-state 'needs-merge)         "merge")
+                    ((eq cvs-state 'unresolved-conflict) "conflict")
+                    ((eq cvs-state 'locally-added)       "added"))
+            (vc-locking-user file))))
+    (if state (concat "(" state ")"))))
 
 (defun vc-dired-reformat-line (x)
-  ;; Hack a directory-listing line, plugging in locking-user info in
-  ;; place of the user and group info.  Should have the beneficial
-  ;; side-effect of shortening the listing line.  Each call starts with
-  ;; point immediately following the dired mark area on the line to be
-  ;; hacked.
-  ;;
-  ;; Simplest possible one:
-  ;; (insert (concat x "\t")))
-  ;;
+  ;; Reformat a directory-listing line, replacing various columns with 
+  ;; version control information.
   ;; This code, like dired, assumes UNIX -l format.
-  (let ((pos (point)) limit perm owner date-and-file)
+  (beginning-of-line)
+  (let ((pos (point)) limit perm date-and-file)
     (end-of-line)
     (setq limit (point))
     (goto-char pos)
-    (cond
-     ((or
-       (re-search-forward  ;; owner and group
-"\\([drwxlts-]+ \\) *[0-9]+ \\([^ ]+\\) +[^ ]+ +[0-9]+\\( [^ 0-9]+ [0-9 ][0-9] .*\\)"
-         limit t)       
-       (re-search-forward  ;; only owner displayed
-"\\([drwxlts-]+ \\) *[0-9]+ \\([^ ]+\\) +[0-9]+\\( [^ 0-9]+ [0-9 ][0-9] .*\\)" 
-         limit t))
-      (setq perm          (match-string 1)
-           owner         (match-string 2)
-           date-and-file (match-string 3)))
-     ((re-search-forward  ;; OS/2 -l format, no links, owner, group
-"\\([drwxlts-]+ \\) *[0-9]+\\( [^ 0-9]+ [0-9 ][0-9] .*\\)"
-         limit t)
+    (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))))
-    (if x (setq x (concat "(" x ")")))
-    (let ((rep (substring (concat x "                 ") 0 10)))
-      (replace-match (concat perm rep date-and-file)))))
-       
-(defun vc-dired-update-line (file)
-  ;; Update the vc-dired listing line of file -- it is assumed 
-  ;; that point is already on this line.  Don't use dired-do-redisplay
-  ;; for this, because it cannot handle the way vc-dired deals with 
-  ;; subdirectories.
-  (beginning-of-line)
-  (forward-char 2)
-  (let ((start (point)))
-    (forward-line 1)
-    (beginning-of-line)
-    (delete-region start (point))
-    (insert-directory file dired-listing-switches)
-    (forward-line -1)
-    (end-of-line)
-    (delete-char (- (length file)))
-    (insert (substring file (length (expand-file-name default-directory))))
-    (goto-char start))
-  (vc-dired-reformat-line (vc-dired-state-info file)))
-
-(defun vc-dired-update (verbose)
-  (interactive "P")
-  (vc-directory default-directory verbose))
+           date-and-file (match-string 2))
+      (setq x (substring (concat x "          ") 0 10))
+      (replace-match (concat perm x date-and-file)))))
+
+(defun vc-dired-hook ()
+  ;; Called by dired after any portion of a vc-dired buffer has been read in.
+  ;; Reformat the listing according to version control.
+  (message "Getting version information... ")
+  (let (subdir filename (buffer-read-only nil))
+    (goto-char (point-min))
+    (while (not (eq (point) (point-max)))
+      (cond 
+       ;; subdir header line
+       ((setq subdir (dired-get-subdir))
+        (if (file-directory-p (concat subdir "/CVS"))
+            (vc-fetch-cvs-status (file-name-as-directory subdir)))
+        (forward-line 1)
+        ;; erase (but don't remove) the "total" line
+        (let ((start (point)))
+          (end-of-line)
+          (delete-region start (point))
+          (beginning-of-line)
+          (forward-line 1)))
+       ;; an ordinary file line
+       ((setq filename (dired-get-filename nil t))
+        (cond
+         ((file-directory-p filename)
+          (if (member (file-name-nondirectory filename) 
+                      vc-directory-exclusion-list)
+              (dired-kill-line)
+            (vc-dired-reformat-line nil)
+            (forward-line 1)))
+         ((vc-backend filename)
+          (vc-dired-reformat-line (vc-dired-state-info filename))
+          (forward-line 1))
+         (t 
+          (dired-kill-line))))
+       ;; any other line
+       (t (forward-line 1)))))
+  (message "Getting version information... done"))
 
-;;; Note in Emacs 18 the following defun gets overridden
-;;; with the symbol 'vc-directory-18.  See below.
 ;;;###autoload
-(defun vc-directory (dirname verbose)
-  "Show version-control status of the current directory and subdirectories.
-Normally it creates a Dired buffer that lists only the locked files
-in all these directories.  With a prefix argument, it lists all files."
+(defun vc-directory (dirname read-switches)
   (interactive "DDired under VC (directory): \nP")
-  (require 'dired)
-  (setq dirname (expand-file-name dirname))
-  ;; force a trailing slash
-  (if (not (eq (elt dirname (1- (length dirname))) ?/))
-      (setq dirname (concat dirname "/")))
-  (let (nonempty
-       (dl (length dirname))
-       (filelist nil) (statelist nil)
-       (old-dir default-directory)
-       dired-buf
-       dired-buf-mod-count)
-    (vc-file-tree-walk
-     dirname
-     (function 
-      (lambda (f)
-       (if (vc-registered f)
-           (let ((state (vc-dired-state-info f)))
-             (and (or verbose state)
-                  (setq filelist (cons (substring f dl) filelist))
-                  (setq statelist (cons state statelist))))))))
-    (save-window-excursion
-      (save-excursion
-       ;; This uses a semi-documented feature of dired; giving a switch
-       ;; argument forces the buffer to refresh each time.
-       (setq dired-buf
-             (dired-internal-noselect
-              (cons dirname (nreverse filelist))
-              dired-listing-switches 'vc-dired-mode))
-       (setq nonempty (not (eq 0 (length filelist))))))
-    (switch-to-buffer dired-buf)
-    ;; Make a few modifications to the header
-    (setq buffer-read-only nil)
-    (goto-char (point-min))
-    (forward-line 1)         ;; Skip header line
-    (let ((start (point)))    ;; Erase (but don't remove) the 
-      (end-of-line)           ;; "wildcard" line.
-      (delete-region start (point)))
-    (beginning-of-line)
-    (if nonempty
-       (progn
-         ;; Plug the version information into the individual lines
-         (mapcar
-          (function
-           (lambda (x)
-            (forward-char 2)   ;; skip dired's mark area
-            (vc-dired-reformat-line x)
-            (forward-line 1))) ;; go to next line
-          (nreverse statelist))
-         (setq buffer-read-only t)
-         (goto-char (point-min))
-         (dired-next-line 2)
-         )
-      (dired-next-line 1) 
-      (insert "  ")
-      (setq buffer-read-only t)
-      (message "No files are currently %s under %s"
-              (if verbose "registered" "locked") dirname))
-    ))
-
-;; Emacs 18 version
-(defun vc-directory-18 (verbose)
-  "Show version-control status of all files under the current directory."
-  (interactive "P")
-  (let (nonempty (dir default-directory))
-    (save-excursion
-      (set-buffer (get-buffer-create "*vc-status*"))
-      (erase-buffer)
-      (cd dir)
-      (vc-file-tree-walk
-       default-directory
-       (function (lambda (f)
-                  (if (vc-registered f)
-                      (let ((user (vc-locking-user f)))
-                        (if (or user verbose)
-                            (insert (format
-                                     "%s       %s\n"
-                                     (concat user) f))))))))
-      (setq nonempty (not (zerop (buffer-size)))))
-
-    (if nonempty
-       (progn
-         (pop-to-buffer "*vc-status*" t)
-         (goto-char (point-min))
-         (shrink-window-if-larger-than-buffer)))
-      (message "No files are currently %s under %s"
-              (if verbose "registered" "locked") default-directory))
-    )
-
-(or (boundp 'minor-mode-map-alist)
-    (fset 'vc-directory 'vc-directory-18))
+  (let ((switches 
+         (if read-switches (read-string "Dired listing switches: "
+                                        dired-listing-switches))))
+    (require 'dired)
+    (require 'dired-aux)
+    ;; force a trailing slash
+    (if (not (eq (elt dirname (1- (length dirname))) ?/))
+        (setq dirname (concat dirname "/")))
+    (switch-to-buffer 
+     (dired-internal-noselect (expand-file-name dirname)
+                              (or switches dired-listing-switches)
+                              'vc-dired-mode))))
 
 ;; Named-configuration support for SCCS
 
@@ -1871,58 +1869,50 @@ locked are updated to the latest versions."
 (defun vc-print-log ()
   "List the change log of the current buffer in a window."
   (interactive)
-  (if vc-dired-mode
-      (set-buffer (find-file-noselect (dired-get-filename))))
-  (while vc-parent-buffer
-      (pop-to-buffer vc-parent-buffer))
-  (if (and buffer-file-name (vc-name buffer-file-name))
-      (let ((file buffer-file-name))
-       (vc-backend-print-log file)
-       (pop-to-buffer (get-buffer-create "*vc*"))
-       (setq default-directory (file-name-directory file))
-       (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))))
-       (shrink-window-if-larger-than-buffer)
-       ;; move point to the log entry for the current version
-       (and (not (eq (vc-backend file) 'SCCS))
-            (re-search-forward
-             ;; also match some context, for safety
-             (concat "----\nrevision " (vc-workfile-version file)
-                     "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
-            ;; set the display window so that 
-            ;; the whole log entry is displayed
-            (let (start end lines)
-              (beginning-of-line) (forward-line -1) (setq start (point))
-              (if (not (re-search-forward "^----*\nrevision" nil t))
-                  (setq end (point-max))
-                (beginning-of-line) (forward-line -1) (setq end (point)))
-              (setq lines (count-lines start end))
-              (cond
-               ;; if the global information and this log entry fit
-               ;; into the window, display from the beginning
-               ((< (count-lines (point-min) end) (window-height))
-                (goto-char (point-min))
-                (recenter 0)
-                (goto-char start))
-               ;; if the whole entry fits into the window,
-               ;; display it centered
-               ((< (1+ lines) (window-height))
-                (goto-char start)
-                (recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
-               ;; otherwise (the entry is too large for the window),
-               ;; display from the start
-               (t
-                (goto-char start)
-                (recenter 0)))))
-       )
-    (vc-registration-error buffer-file-name)
-    )
-  )
+  (vc-ensure-vc-buffer)
+  (let ((file buffer-file-name))
+    (vc-backend-print-log file)
+    (pop-to-buffer (get-buffer-create "*vc*"))
+    (setq default-directory (file-name-directory file))
+    (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))))
+    (shrink-window-if-larger-than-buffer)
+    ;; move point to the log entry for the current version
+    (and (not (eq (vc-backend file) 'SCCS))
+        (re-search-forward
+         ;; also match some context, for safety
+         (concat "----\nrevision " (vc-workfile-version file)
+                 "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
+        ;; set the display window so that 
+        ;; the whole log entry is displayed
+        (let (start end lines)
+          (beginning-of-line) (forward-line -1) (setq start (point))
+          (if (not (re-search-forward "^----*\nrevision" nil t))
+              (setq end (point-max))
+            (beginning-of-line) (forward-line -1) (setq end (point)))
+          (setq lines (count-lines start end))
+          (cond
+           ;; if the global information and this log entry fit
+           ;; into the window, display from the beginning
+           ((< (count-lines (point-min) end) (window-height))
+            (goto-char (point-min))
+            (recenter 0)
+            (goto-char start))
+           ;; if the whole entry fits into the window,
+           ;; display it centered
+           ((< (1+ lines) (window-height))
+            (goto-char start)
+            (recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
+           ;; otherwise (the entry is too large for the window),
+           ;; display from the start
+           (t
+            (goto-char start)
+            (recenter 0)))))))
 
 ;;;###autoload
 (defun vc-revert-buffer ()
@@ -1932,10 +1922,7 @@ to that version.  Note that for RCS and CVS, this function does not
 automatically pick up newer changes found in the master file; 
 use C-u \\[vc-next-action] RET to do so."
   (interactive)
-  (if vc-dired-mode
-      (find-file-other-window (dired-get-filename)))
-  (while vc-parent-buffer
-      (pop-to-buffer vc-parent-buffer))
+  (vc-ensure-vc-buffer)
   (let ((file buffer-file-name)
        ;; This operation should always ask for confirmation.
        (vc-suppress-confirm nil)
@@ -1957,13 +1944,8 @@ use C-u \\[vc-next-action] RET to do so."
   "Get rid of most recently checked in version of this file.
 A prefix argument means do not revert the buffer afterwards."
   (interactive "P")
-  (if vc-dired-mode
-      (find-file-other-window (dired-get-filename)))
-  (while vc-parent-buffer
-    (pop-to-buffer vc-parent-buffer))
+  (vc-ensure-vc-buffer)
   (cond 
-   ((not (vc-registered (buffer-file-name)))
-    (vc-registration-error (buffer-file-name)))
    ((eq (vc-backend (buffer-file-name)) 'CVS)
     (error "Unchecking files under CVS is dangerous and not supported in VC"))
    ((vc-locking-user (buffer-file-name))
@@ -2267,8 +2249,9 @@ 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")
-  (if (not (eq (vc-buffer-backend) 'CVS)) ; This only works with CVS
-      (vc-registration-error (buffer-file-name)))
+  (vc-ensure-vc-buffer)
+  (if (not (eq (vc-backend (buffer-file-name)) 'CVS))
+      (error "Sorry, vc-annotate is only implemented for CVS"))
   (message "Annotating...")
   (let ((temp-buffer-name (concat "*cvs annotate " (buffer-name) "*"))
        (temp-buffer-show-function 'vc-annotate-display)
@@ -2718,7 +2701,7 @@ THRESHOLD, nil otherwise"
      ;; Checking out explicit versions is not supported under SCCS, yet.
      ;; We always "revert" to the latest version; therefore 
      ;; vc-workfile-version is cleared here so that it gets recomputed.
-     (vc-file-setprop 'vc-workfile-version nil))
+     (vc-file-setprop file 'vc-workfile-version nil))
    ;; RCS
    (vc-do-command nil 0 "co" file 'MASTER
                  "-f" (concat "-u" (vc-workfile-version file)))
@@ -2833,9 +2816,7 @@ THRESHOLD, nil otherwise"
               (and newvers (concat "-r" newvers))
               (if (listp diff-switches)
                   diff-switches
-                (list diff-switches)))))
-     (t
-      (vc-registration-error file)))))
+                (list diff-switches))))))))
 
 (defun vc-backend-merge-news (file)
   ;; Merge in any new changes made to FILE.
@@ -2888,6 +2869,32 @@ THRESHOLD, nil otherwise"
            (error "Couldn't analyze cvs update result"))))
     (message "Merging changes into %s...done" file)))
 
+(defun vc-backend-merge (file first-version &optional second-version)
+  ;; Merge the changes between FIRST-VERSION and SECOND-VERSION into
+  ;; the current working copy of FILE.  It is assumed that FILE is
+  ;; locked and writable (vc-merge ensures this).
+  (vc-backend-dispatch file
+   ;; SCCS
+   (error "Sorry, merging is not implemented for SCCS")
+   ;; RCS
+   (vc-do-command nil 1 "rcsmerge" file 'MASTER
+                 "-kk" ;; ignore keyword conflicts
+                 (concat "-r" first-version)
+                 (if second-version (concat "-r" second-version)))
+   ;; CVS
+   (progn
+     (vc-do-command nil 0 "cvs" file 'WORKFILE
+                   "update" "-kk"
+                   (concat "-j" first-version)
+                   (concat "-j" second-version))
+     (save-excursion
+       (set-buffer (get-buffer "*vc*"))
+       (goto-char (point-min))
+       (if (re-search-forward "conflicts during merge" nil t)
+          1  ;; signal error
+        0  ;; signal success
+        )))))
+
 (defun vc-check-headers ()
   "Check if the current file has any headers in it."
   (interactive)