*** empty log message ***
[bpt/emacs.git] / lisp / vc-mcvs.el
index d2ac776..70d502c 100644 (file)
@@ -1,7 +1,6 @@
 ;;; vc-mcvs.el --- VC backend for the Meta-CVS version-control system
 
-;; Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004
-;;           Free Software Foundation, Inc.
+;; Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
 ;; Author:      FSF (see vc.el for full credits)
 ;; Maintainer:  Stefan Monnier <monnier@gnu.org>
@@ -10,7 +9,7 @@
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 ;; The home page of the Meta-CVS version control system is at
-;; 
+;;
 ;;      http://users.footprints.net/~kaz/mcvs.html
-;; 
+;;
 ;; This is derived from vc-cvs.el as follows:
 ;; - cp vc-cvs.el vc-mcvs.el
 ;; - Replace CVS/ with MCVS/CVS/
@@ -70,7 +69,7 @@
                 (repeat :tag "Argument List"
                         :value ("")
                         string))
-  :version "21.4"
+  :version "22.1"
   :group 'vc)
 
 (defcustom vc-mcvs-register-switches nil
@@ -82,7 +81,7 @@ A string or list of strings passed to the checkin program by
                 (repeat :tag "Argument List"
                         :value ("")
                         string))
-  :version "21.4"
+  :version "22.1"
   :group 'vc)
 
 (defcustom vc-mcvs-diff-switches nil
@@ -92,13 +91,13 @@ A string or list of strings passed to the checkin program by
                 (repeat :tag "Argument List"
                         :value ("")
                         string))
-  :version "21.4"
+  :version "22.1"
   :group 'vc)
 
 (defcustom vc-mcvs-header (or (cdr (assoc 'MCVS vc-header-alist))
                              vc-cvs-header)
   "*Header keywords to be inserted by `vc-insert-headers'."
-  :version "21.4"
+  :version "22.1"
   :type '(repeat string)
   :group 'vc)
 
@@ -107,9 +106,14 @@ A string or list of strings passed to the checkin program by
 This is only meaningful if you don't use the implicit checkout model
 \(i.e. if you have $CVSREAD set)."
   :type 'boolean
-  :version "21.4"
+  :version "22.1"
   :group 'vc)
 
+;;; Properties of the backend
+
+(defun vc-mcvs-revision-granularity ()
+     'file)
+
 ;;;
 ;;; State-querying functions
 ;;;
@@ -174,7 +178,7 @@ This is only meaningful if you don't use the implicit checkout model
 (defalias 'vc-mcvs-state-heuristic 'vc-cvs-state-heuristic)
 
 (defun vc-mcvs-dir-state (dir)
-  "Find the Meta-CVS state of all files in DIR."
+  "Find the Meta-CVS state of all files in DIR and subdirectories."
   ;; if DIR is not under Meta-CVS control, don't do anything.
   (when (file-readable-p (expand-file-name "MCVS/CVS/Entries" dir))
     (if (vc-stay-local-p dir)
@@ -184,7 +188,7 @@ This is only meaningful if you don't use the implicit checkout model
        ;; enough.  Otherwise it might fail with remote repositories.
        (with-temp-buffer
          (setq default-directory (vc-mcvs-root dir))
-         (vc-mcvs-command t 0 nil "status" "-l")
+         (vc-mcvs-command t 0 nil "status")
          (goto-char (point-min))
          (while (re-search-forward "^=+\n\\([^=\n].*\n\\|\n\\)+" nil t)
            (narrow-to-region (match-beginning 0) (match-end 0))
@@ -192,8 +196,8 @@ This is only meaningful if you don't use the implicit checkout model
            (goto-char (point-max))
            (widen)))))))
 
-(defun vc-mcvs-workfile-version (file)
-  (vc-cvs-workfile-version
+(defun vc-mcvs-working-revision (file)
+  (vc-cvs-working-revision
    (expand-file-name (vc-file-getprop file 'mcvs-inode)
                     (vc-file-getprop file 'mcvs-root))))
 
@@ -203,13 +207,16 @@ This is only meaningful if you don't use the implicit checkout model
 ;;; State-changing functions
 ;;;
 
-(defun vc-mcvs-register (file &optional rev comment)
-  "Register FILE into the Meta-CVS version-control system.
+(defun vc-mcvs-register (files &optional rev comment)
+  "Register FILES into the Meta-CVS version-control system.
 COMMENT can be used to provide an initial description of FILE.
 
 `vc-register-switches' and `vc-mcvs-register-switches' are passed to
 the Meta-CVS command (in that order)."
-  (let* ((filename (file-name-nondirectory file))
+  ;; FIXME: multiple-file case should be made to work
+  (if (> (length files) 1) (error "Registering filesets is not yet supported."))
+  (let* ((file (car files)) 
+        (filename (file-name-nondirectory file))
         (extpos (string-match "\\." filename))
         (ext (if extpos (substring filename (1+ extpos))))
         (root (vc-mcvs-root file))
@@ -226,7 +233,7 @@ the Meta-CVS command (in that order)."
     ;; Make sure the `mcvs add' will not fire up the CVSEDITOR
     ;; to add a rule for the given file's extension.
     (when (and ext (not (assoc ext types)))
-      (let ((type (completing-read "Type to use [default]: "
+      (let ((type (completing-read "Type to use (default): "
                                   '("default" "name-only" "keep-old"
                                     "binary" "value-only")
                                   nil t nil nil "default")))
@@ -246,7 +253,7 @@ the Meta-CVS command (in that order)."
                (vc-switches 'MCVS 'register))
     ;; I'm not sure exactly why, but if we don't setup the inode and root
     ;; prop of the file, things break later on in vc-mode-line that
-    ;; ends up calling vc-mcvs-workfile-version.
+    ;; ends up calling vc-mcvs-working-revision.
     ;; We also need to set vc-checkout-time so that vc-workfile-unchanged-p
     ;; doesn't try to call `mcvs diff' on the file.
     (vc-mcvs-registered file)))
@@ -258,9 +265,9 @@ the Meta-CVS command (in that order)."
   "Return non-nil if FILE could be registered in Meta-CVS.
 This is only possible if Meta-CVS is responsible for FILE's directory.")
 
-(defun vc-mcvs-checkin (file rev comment)
+(defun vc-mcvs-checkin (files rev comment)
   "Meta-CVS-specific version of `vc-backend-checkin'."
-  (unless (or (not rev) (vc-mcvs-valid-version-number-p rev))
+  (unless (or (not rev) (vc-mcvs-valid-revision-number-p rev))
     (if (not (vc-mcvs-valid-symbolic-tag-name-p rev))
        (error "%s is not a valid symbolic tag name" rev)
       ;; If the input revision is a valid symbolic tag name, we create it
@@ -268,14 +275,15 @@ This is only possible if Meta-CVS is responsible for FILE's directory.")
       ;; This file-specific form of branching is deprecated.
       ;; We can't use `mcvs branch' and `mcvs switch' because they cannot
       ;; be applied just to this one file.
-      (apply 'vc-mcvs-command nil 0 file "tag" "-b" (list rev))
-      (apply 'vc-mcvs-command nil 0 file "update" "-r" (list rev))
-      (vc-file-setprop file 'vc-mcvs-sticky-tag rev)
+      (apply 'vc-mcvs-command nil 0 files "tag" "-b" (list rev))
+      (apply 'vc-mcvs-command nil 0 files "update" "-r" (list rev))
+      (mapc (lambda (file) (vc-file-setprop file 'vc-mcvs-sticky-tag rev))
+           files)
       (setq rev nil)))
   ;; This commit might cvs-commit several files (e.g. MAP and TYPES)
   ;; so using numbered revs here is dangerous and somewhat meaningless.
   (when rev (error "Cannot commit to a specific revision number"))
-  (let ((status (apply 'vc-mcvs-command nil 1 file
+  (let ((status (apply 'vc-mcvs-command nil 1 files
                       "ci" "-m" comment
                       (vc-switches 'MCVS 'checkin))))
     (set-buffer "*vc*")
@@ -284,8 +292,9 @@ This is only possible if Meta-CVS is responsible for FILE's directory.")
       ;; Check checkin problem.
       (cond
        ((re-search-forward "Up-to-date check failed" nil t)
-        (vc-file-setprop file 'vc-state 'needs-merge)
-        (error (substitute-command-keys
+       (mapc (lambda (file) (vc-file-setprop file 'vc-state 'needs-merge))
+             files)
+        (error "%s" (substitute-command-keys
                 (concat "Up-to-date check failed: "
                         "type \\[vc-next-action] to merge in changes"))))
        (t
@@ -293,22 +302,27 @@ This is only possible if Meta-CVS is responsible for FILE's directory.")
         (goto-char (point-min))
         (shrink-window-if-larger-than-buffer)
         (error "Check-in failed"))))
-    ;; Update file properties
-    (vc-file-setprop
-     file 'vc-workfile-version
-     (vc-parse-buffer "^\\(new\\|initial\\) revision: \\([0-9.]+\\)" 2))
-    ;; Forget the checkout model of the file, because we might have
+    ;; Single-file commit?  Then update the revision by parsing the buffer.
+    ;; Otherwise we can't necessarily tell what goes with what; clear
+    ;; its properties so they have to be refetched.
+    (if (= (length files) 1)
+       (vc-file-setprop
+        (car files) 'vc-working-revision
+        (vc-parse-buffer "^\\(new\\|initial\\) revision: \\([0-9.]+\\)" 2))
+      (mapc (lambda (file) (vc-file-clearprops file)) files))
+    ;; Anyway, forget the checkout model of the file, because we might have
     ;; guessed wrong when we found the file.  After commit, we can
     ;; tell it from the permissions of the file (see
     ;; vc-mcvs-checkout-model).
-    (vc-file-setprop file 'vc-checkout-model nil)
+    (mapc (lambda (file) (vc-file-setprop file 'vc-checkout-model nil))
+           files)
 
     ;; if this was an explicit check-in (does not include creation of
     ;; a branch), remove the sticky tag.
     (if (and rev (not (vc-mcvs-valid-symbolic-tag-name-p rev)))
-       (vc-mcvs-command nil 0 file "update" "-A"))))
+       (vc-mcvs-command nil 0 files "update" "-A"))))
 
-(defun vc-mcvs-find-version (file rev buffer)
+(defun vc-mcvs-find-revision (file rev buffer)
   (apply 'vc-mcvs-command
         buffer 0 file
         "-Q"                           ; suppress diagnostic output
@@ -334,8 +348,8 @@ This is only possible if Meta-CVS is responsible for FILE's directory.")
               (vc-mcvs-command nil 0 file "edit")
             (set-file-modes file (logior (file-modes file) 128))
             (if (equal file buffer-file-name) (toggle-read-only -1))))
-    ;; Check out a particular version (or recreate the file).
-    (vc-file-setprop file 'vc-workfile-version nil)
+    ;; Check out a particular revision (or recreate the file).
+    (vc-file-setprop file 'vc-working-revision nil)
     (apply 'vc-mcvs-command nil 0 file
           (if editable "-w")
           "update"
@@ -350,21 +364,21 @@ This is only possible if Meta-CVS is responsible for FILE's directory.")
   (vc-mcvs-command nil 0 new "move" (file-relative-name old)))
 
 (defun vc-mcvs-revert (file &optional contents-done)
-  "Revert FILE to the version it was based on."
-  (vc-default-revert file contents-done)
+  "Revert FILE to the working revision it was based on."
+  (vc-default-revert 'MCVS file contents-done)
   (unless (eq (vc-checkout-model file) 'implicit)
     (if vc-mcvs-use-edit
         (vc-mcvs-command nil 0 file "unedit")
       ;; Make the file read-only by switching off all w-bits
       (set-file-modes file (logand (file-modes file) 3950)))))
 
-(defun vc-mcvs-merge (file first-version &optional second-version)
+(defun vc-mcvs-merge (file first-revision &optional second-revision)
   "Merge changes into current working copy of FILE.
-The changes are between FIRST-VERSION and SECOND-VERSION."
+The changes are between FIRST-REVISION and SECOND-REVISION."
   (vc-mcvs-command nil 0 file
                   "update" "-kk"
-                  (concat "-j" first-version)
-                  (concat "-j" second-version))
+                  (concat "-j" first-revision)
+                  (concat "-j" second-revision))
   (vc-file-setprop file 'vc-state 'edited)
   (with-current-buffer (get-buffer "*vc*")
     (goto-char (point-min))
@@ -375,18 +389,18 @@ The changes are between FIRST-VERSION and SECOND-VERSION."
 (defun vc-mcvs-merge-news (file)
   "Merge in any new changes made to FILE."
   (message "Merging changes into %s..." file)
-  ;; (vc-file-setprop file 'vc-workfile-version nil)
+  ;; (vc-file-setprop file 'vc-working-revision nil)
   (vc-file-setprop file 'vc-checkout-time 0)
   (vc-mcvs-command nil 0 file "update")
   ;; Analyze the merge result reported by Meta-CVS, and set
   ;; file properties accordingly.
   (with-current-buffer (get-buffer "*vc*")
     (goto-char (point-min))
-    ;; get new workfile version
+    ;; get new working revision
     (if (re-search-forward
         "^Merging differences between [0-9.]* and \\([0-9.]*\\) into" nil t)
-       (vc-file-setprop file 'vc-workfile-version (match-string 1))
-      (vc-file-setprop file 'vc-workfile-version nil))
+       (vc-file-setprop file 'vc-working-revision (match-string 1))
+      (vc-file-setprop file 'vc-working-revision nil))
     ;; get file status
     (prog1
         (if (eq (buffer-size) 0)
@@ -422,63 +436,43 @@ The changes are between FIRST-VERSION and SECOND-VERSION."
 ;;; History functions
 ;;;
 
-(defun vc-mcvs-print-log (file &optional buffer)
-  "Get change log associated with FILE."
-  (let ((default-directory (vc-mcvs-root file)))
+(defun vc-mcvs-print-log (files &optional buffer)
+  "Get change log associated with FILES."
+  (let ((default-directory (vc-mcvs-root (car files))))
     ;; Run the command from the root dir so that `mcvs filt' returns
     ;; valid relative names.
     (vc-mcvs-command
      buffer
-     (if (and (vc-stay-local-p file) (fboundp 'start-process)) 'async 0)
-     file "log")))
-
-(defun vc-mcvs-diff (file &optional oldvers newvers buffer)
-  "Get a difference report using Meta-CVS between two versions of FILE."
-  (if (string= (vc-workfile-version file) "0")
-      ;; This file is added but not yet committed; there is no master file.
-      (if (or oldvers newvers)
-         (error "No revisions of %s exist" file)
-       ;; We regard this as "changed".
-       ;; Diff it against /dev/null.
-       ;; Note: this is NOT a "mcvs diff".
-       (apply 'vc-do-command (or buffer "*vc-diff*")
-              1 "diff" file
-              (append (vc-switches nil 'diff) '("/dev/null")))
-       ;; Even if it's empty, it's locally modified.
-       1)
-    (let* ((async (and (vc-stay-local-p file) (fboundp 'start-process)))
+     (if (vc-stay-local-p files) 'async 0)
+     files "log")))
+
+(defun vc-mcvs-diff (files &optional oldvers newvers buffer)
+  "Get a difference report using Meta-CVS between two revisions of FILES."
+    (let* ((async (and (not vc-disable-async-diff)
+                       (vc-stay-local-p files)))
           ;; Run the command from the root dir so that `mcvs filt' returns
           ;; valid relative names.
-          (default-directory (vc-mcvs-root file))
+          (default-directory (vc-mcvs-root (car files)))
           (status
            (apply 'vc-mcvs-command (or buffer "*vc-diff*")
                   (if async 'async 1)
-                  file "diff"
+                  files "diff"
                   (and oldvers (concat "-r" oldvers))
                   (and newvers (concat "-r" newvers))
                   (vc-switches 'MCVS 'diff))))
-      (if async 1 status))))          ; async diff, pessimistic assumption.
+      (if async 1 status)))           ; async diff, pessimistic assumption.
 
-(defun vc-mcvs-diff-tree (dir &optional rev1 rev2)
-  "Diff all files at and below DIR."
-  (with-current-buffer "*vc-diff*"
-    ;; Run the command from the root dir so that `mcvs filt' returns
-    ;; valid relative names.
-    (setq default-directory (vc-mcvs-root dir))
-    ;; cvs diff: use a single call for the entire tree
-    (let ((coding-system-for-read (or coding-system-for-read 'undecided)))
-      (apply 'vc-mcvs-command "*vc-diff*" 1 dir "diff"
-            (and rev1 (concat "-r" rev1))
-            (and rev2 (concat "-r" rev2))
-            (vc-switches 'MCVS 'diff)))))
-
-(defun vc-mcvs-annotate-command (file buffer &optional version)
+(defun vc-mcvs-annotate-command (file buffer &optional revision)
   "Execute \"mcvs annotate\" on FILE, inserting the contents in BUFFER.
-Optional arg VERSION is a version to annotate from."
+Optional arg REVISION is a revision to annotate from."
   (vc-mcvs-command
    buffer
-   (if (and (vc-stay-local-p file) (fboundp 'start-process)) 'async 0)
-   file "annotate" (if version (concat "-r" version))))
+   (if (vc-stay-local-p file) 'async 0)
+   file "annotate" (if revision (concat "-r" revision)))
+  (with-current-buffer buffer
+    (goto-char (point-min))
+    (re-search-forward "^[0-9]")
+    (delete-region (point-min) (1- (point)))))
 
 (defalias 'vc-mcvs-annotate-current-time 'vc-cvs-annotate-current-time)
 (defalias 'vc-mcvs-annotate-time 'vc-cvs-annotate-time)
@@ -488,7 +482,7 @@ Optional arg VERSION is a version to annotate from."
 ;;;
 
 (defun vc-mcvs-create-snapshot (dir name branchp)
-  "Assign to DIR's current version a given NAME.
+  "Assign to DIR's current revision a given NAME.
 If BRANCHP is non-nil, the name is created as a branch (and the current
 workspace is immediately moved to that new branch)."
   (if (not branchp)
@@ -520,13 +514,13 @@ If UPDATE is non-nil, then update (resynch) any affected buffers."
                   ((or (string= state "U")
                        (string= state "P"))
                    (vc-file-setprop file 'vc-state 'up-to-date)
-                   (vc-file-setprop file 'vc-workfile-version nil)
+                   (vc-file-setprop file 'vc-working-revision nil)
                    (vc-file-setprop file 'vc-checkout-time
                                     (nth 5 (file-attributes file))))
                   ((or (string= state "M")
                        (string= state "C"))
                    (vc-file-setprop file 'vc-state 'edited)
-                   (vc-file-setprop file 'vc-workfile-version nil)
+                   (vc-file-setprop file 'vc-working-revision nil)
                    (vc-file-setprop file 'vc-checkout-time 0)))
                  (vc-file-setprop file 'vc-mcvs-sticky-tag sticky-tag)
                  (vc-resynch-buffer file t t))))
@@ -588,7 +582,7 @@ and that it passes `vc-mcvs-global-switches' to it before FLAGS."
       (forward-line 1))))
 
 (defalias 'vc-mcvs-valid-symbolic-tag-name-p 'vc-cvs-valid-symbolic-tag-name-p)
-(defalias 'vc-mcvs-valid-version-number-p 'vc-cvs-valid-version-number-p)
+(defalias 'vc-mcvs-valid-revision-number-p 'vc-cvs-valid-revision-number-p)
 
 (provide 'vc-mcvs)