Fix comment typo.
[bpt/emacs.git] / lisp / vc-cvs.el
index 407ca03..5d2b560 100644 (file)
@@ -196,16 +196,16 @@ See also variable `vc-cvs-sticky-date-format-string'."
         ;; make sure that the file name is searched case-sensitively
         (case-fold-search nil))
     (if (file-readable-p (expand-file-name "CVS/Entries" dirname))
-       (with-temp-buffer
-          (vc-cvs-get-entries dirname)
-          (goto-char (point-min))
-         (cond
-          ((re-search-forward
-            (concat "^/" (regexp-quote basename) "/[^/]") nil t)
-           (beginning-of-line)
-           (vc-cvs-parse-entry file)
-           t)
-          (t nil)))
+        (or (string= basename "")
+            (with-temp-buffer
+              (vc-cvs-get-entries dirname)
+              (goto-char (point-min))
+              (cond ((re-search-forward
+                      (concat "^/" (regexp-quote basename) "/[^/]") nil t)
+                     (beginning-of-line)
+                     (vc-cvs-parse-entry file)
+                     t)
+                    (t nil))))
       nil)))
 
 (defun vc-cvs-state (file)
@@ -231,27 +231,9 @@ See also variable `vc-cvs-sticky-date-format-string'."
     (cond
      ((equal checkout-time lastmod) 'up-to-date)
      ((string= (vc-working-revision file) "0") 'added)
+     ((null checkout-time) 'unregistered)
      (t 'edited))))
 
-(defun vc-cvs-dir-state (dir)
-  "Find the CVS state of all files in DIR and subdirectories."
-  ;; if DIR is not under CVS control, don't do anything.
-  (when (file-readable-p (expand-file-name "CVS/Entries" dir))
-    (if (vc-stay-local-p dir)
-       (vc-cvs-dir-state-heuristic dir)
-      (let ((default-directory dir))
-       ;; Don't specify DIR in this command, the default-directory is
-       ;; enough.  Otherwise it might fail with remote repositories.
-       (with-temp-buffer
-         (buffer-disable-undo)         ;; Because these buffers can get huge
-         (vc-cvs-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))
-           (vc-cvs-parse-status)
-           (goto-char (point-max))
-           (widen)))))))
-
 (defun vc-cvs-working-revision (file)
   "CVS-specific version of `vc-working-revision'."
   ;; There is no need to consult RCS headers under CVS, because we
@@ -269,13 +251,13 @@ committed and support display of sticky tags."
         help-echo
         (string
           (let ((def-ml (vc-default-mode-line-string 'CVS file)))
-            (setq help-echo 
+            (setq help-echo
                   (get-text-property 0 'help-echo def-ml))
             def-ml)))
-    (propertize 
+    (propertize
      (if (zerop (length sticky-tag))
         string
-       (setq help-echo (format "%s on the '%s' branch" 
+       (setq help-echo (format "%s on the '%s' branch"
                               help-echo sticky-tag))
        (concat string "[" sticky-tag "]"))
      'help-echo help-echo)))
@@ -440,7 +422,7 @@ The changes are between FIRST-REVISION and SECOND-REVISION."
   (with-current-buffer (get-buffer "*vc*")
     (goto-char (point-min))
     (if (re-search-forward "conflicts during merge" nil t)
-       (progn 
+       (progn
          (vc-file-setprop file 'vc-state 'conflict)
          ;; signal error
          1)
@@ -495,7 +477,7 @@ The changes are between FIRST-REVISION and SECOND-REVISION."
       (message "Merging changes into %s...done" file))))
 
 (defun vc-cvs-modify-change-comment (files rev comment)
-  "Modify the change comments for FILES on a specified REV. 
+  "Modify the change comments for FILES on a specified REV.
 Will fail unless you have administrative privileges on the repo."
   (vc-cvs-command nil 0 files "admin" (concat "-m" rev ":" comment)))
 
@@ -511,10 +493,9 @@ Will fail unless you have administrative privileges on the repo."
    (if (vc-stay-local-p files) 'async 0)
    files "log"))
 
-(defun vc-cvs-wash-log ()
-  "Remove all non-comment information from log output."
-  (vc-call-backend 'RCS 'wash-log)
-  nil)
+(defun vc-cvs-comment-history (file)
+  "Get comment history of a file."
+  (vc-call-backend 'RCS 'comment-history file))
 
 (defun vc-cvs-diff (files &optional oldvers newvers buffer)
   "Get a difference report using CVS between two revisions of FILE."
@@ -589,6 +570,8 @@ Optional arg REVISION is a revision to annotate from."
         (re-search-forward vc-cvs-annotate-first-line-re)
         (delete-region (point-min) (1- (point)))))))
 
+(declare-function vc-annotate-convert-time "vc-annotate" (time))
+
 (defun vc-cvs-annotate-current-time ()
   "Return the current time, based at midnight of the current day, and
 encoded as fractional days."
@@ -642,19 +625,19 @@ systime, or nil if there is none."
       nil)))
 
 ;;;
-;;; Snapshot system
+;;; Tag system
 ;;;
 
-(defun vc-cvs-create-snapshot (dir name branchp)
+(defun vc-cvs-create-tag (dir name branchp)
   "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)."
   (vc-cvs-command nil 0 dir "tag" "-c" (if branchp "-b") name)
   (when branchp (vc-cvs-command nil 0 dir "update" "-r" name)))
 
-(defun vc-cvs-retrieve-snapshot (dir name update)
-  "Retrieve a snapshot at and below DIR.
-NAME is the name of the snapshot; if it is empty, do a `cvs update'.
+(defun vc-cvs-retrieve-tag (dir name update)
+  "Retrieve a tag at and below DIR.
+NAME is the name of the tag; if it is empty, do a `cvs update'.
 If UPDATE is non-nil, then update (resynch) any affected buffers."
   (with-current-buffer (get-buffer-create "*vc*")
     (let ((default-directory dir)
@@ -708,14 +691,11 @@ If UPDATE is non-nil, then update (resynch) any affected buffers."
 ;;; Internal functions
 ;;;
 
-(defun vc-cvs-root (dir)
-  (vc-find-root dir "CVS" t))
-
 (defun vc-cvs-command (buffer okstatus files &rest flags)
   "A wrapper around `vc-do-command' for use in vc-cvs.el.
 The difference to vc-do-command is that this function always invokes `cvs',
 and that it passes `vc-cvs-global-switches' to it before FLAGS."
-  (apply 'vc-do-command buffer okstatus "cvs" files
+  (apply 'vc-do-command (or buffer "*vc*") okstatus "cvs" files
          (if (stringp vc-cvs-global-switches)
              (cons vc-cvs-global-switches flags)
            (append vc-cvs-global-switches
@@ -819,10 +799,8 @@ state."
       (cond
        ((re-search-forward "\\=\\([^ \t]+\\)" nil t)
        (setq file (expand-file-name (match-string 1)))
-       (vc-file-setprop file 'vc-backend 'CVS)
-       (if (not (re-search-forward "\\=[ \t]+Status: \\(.*\\)" nil t))
-           (setq status "Unknown")
-         (setq status (match-string 1)))
+       (setq status(if (re-search-forward "\\=[ \t]+Status: \\(.*\\)" nil t)
+                        (match-string 1) "Unknown"))
        (when (and full
                   (re-search-forward
                    "\\(RCS Version\\|RCS Revision\\|Repository revision\\):\
@@ -843,21 +821,9 @@ state."
          ((string-match "Locally Added" status)                'added)
          ((string-match "Locally Removed" status)              'removed)
          ((string-match "File had conflicts " status)          'conflict)
+          ((string-match "Unknown" status)                     'unregistered)
          (t 'edited))))))))
 
-(defun vc-cvs-dir-state-heuristic (dir)
-  "Find the CVS state of all files in DIR, using only local information."
-  (with-temp-buffer
-    (vc-cvs-get-entries dir)
-    (goto-char (point-min))
-    (while (not (eobp))
-      ;; CVS-removed files are not taken under VC control.
-      (when (looking-at "/\\([^/]*\\)/[^/-]")
-       (let ((file (expand-file-name (match-string 1) dir)))
-         (unless (vc-file-getprop file 'vc-state)
-           (vc-cvs-parse-entry file t))))
-      (forward-line 1))))
-
 (defun vc-cvs-after-dir-status (update-function)
   ;; Heavily inspired by vc-cvs-parse-status. AKA a quick hack.
   ;; This needs a lot of testing.
@@ -874,7 +840,7 @@ state."
        (re-search-forward
         "\\(^=+\n\\([^=c?\n].*\n\\|\n\\)+\\)\\|\\(\\(^?? .*\n\\)+\\)\\|\\(^cvs status: Examining .*\n\\)"
         nil t)
-      ;; XXX: get rid of narrowing here.
+      ;; FIXME: get rid of narrowing here.
       (narrow-to-region (match-beginning 0) (match-end 0))
       (goto-char (point-min))
       ;; The subdir
@@ -882,37 +848,33 @@ state."
        (setq subdir (expand-file-name (match-string 1))))
       ;; Unregistered files
       (while (looking-at "? \\(.*\\)")
-       (setq file (file-relative-name 
+       (setq file (file-relative-name
                    (expand-file-name (match-string 1) subdir)))
        (push (list file 'unregistered) result)
        (forward-line 1))
       ;; A file entry.
-      (when (re-search-forward "^File: " nil t)
-       (when (setq missing (looking-at "no file "))
-         (goto-char (match-end 0)))
-       (cond
-        ((re-search-forward "\\=\\([^ \t]+\\)" nil t)
-         (setq file (file-relative-name 
-                     (expand-file-name (match-string 1) subdir)))
-         (if (not (re-search-forward "\\=[ \t]+Status: \\(.*\\)" nil t))
-             (push (list file 'unregistered) result)
-           (setq status-str (match-string 1))
-           (setq status
-                 (cond
-                  ((string-match "Up-to-date" status-str) 'up-to-date)
-                  ((string-match "Locally Modified" status-str) 'edited)
-                  ((string-match "Needs Merge" status-str) 'needs-merge)
-                  ((string-match "Needs \\(Checkout\\|Patch\\)" status-str)
-                   (if missing 'missing 'needs-update))
-                  ((string-match "Locally Added" status-str) 'added)
-                  ((string-match "Locally Removed" status-str) 'removed)
-                  ((string-match "File had conflicts " status-str) 'conflict)
-                  (t 'edited)))
-           (unless (eq status 'up-to-date)
-             (push (list file status) result))))))
+      (when (re-search-forward "^File: \\(no file \\)?\\(.*[^ \t]\\)[ \t]+Status: \\(.*\\)" nil t)
+       (setq missing (match-string 1))
+       (setq file (file-relative-name
+                   (expand-file-name (match-string 2) subdir)))
+       (setq status-str (match-string 3))
+       (setq status
+             (cond
+              ((string-match "Up-to-date" status-str) 'up-to-date)
+              ((string-match "Locally Modified" status-str) 'edited)
+              ((string-match "Needs Merge" status-str) 'needs-merge)
+              ((string-match "Needs \\(Checkout\\|Patch\\)" status-str)
+               (if missing 'missing 'needs-update))
+              ((string-match "Locally Added" status-str) 'added)
+              ((string-match "Locally Removed" status-str) 'removed)
+              ((string-match "File had conflicts " status-str) 'conflict)
+              ((string-match "Unknown" status-str) 'unregistered)
+              (t 'edited)))
+       (unless (eq status 'up-to-date)
+         (push (list file status) result)))
       (goto-char (point-max))
       (widen))
-      (funcall update-function result))
+    (funcall update-function result))
   ;; Alternative implementation: use the "update" command instead of
   ;; the "status" command.
   ;; (let ((result nil)
@@ -926,8 +888,8 @@ state."
   ;;   (goto-char (point-min))
   ;;   (while (not (eobp))
   ;;     (if (looking-at "^[ACMPRU?] \\(.*\\)$")
-  ;;     (push (list (match-string 1) 
-  ;;                 (cdr (assoc (char-after) translation))) 
+  ;;     (push (list (match-string 1)
+  ;;                 (cdr (assoc (char-after) translation)))
   ;;           result)
   ;;   (cond
   ;;    ((looking-at "cvs update: warning: \\(.*\\) was lost")
@@ -945,7 +907,7 @@ state."
 
 (defun vc-cvs-dir-status (dir update-function)
   "Create a list of conses (file . state) for DIR."
-  (vc-cvs-command (current-buffer) 'async dir "status")
+  (vc-cvs-command (current-buffer) 'async dir "-f" "status")
   ;; Alternative implementation: use the "update" command instead of
   ;; the "status" command.
   ;; (vc-cvs-command (current-buffer) 'async
@@ -954,18 +916,54 @@ state."
   (vc-exec-after
    `(vc-cvs-after-dir-status (quote ,update-function))))
 
+(defun vc-cvs-dir-status-files (dir files default-state update-function)
+  "Create a list of conses (file . state) for DIR."
+  (apply 'vc-cvs-command (current-buffer) 'async dir "-f" "status" files)
+  (vc-exec-after
+   `(vc-cvs-after-dir-status (quote ,update-function))))
+
+(defun vc-cvs-file-to-string (file)
+  "Read the content of FILE and return it as a string."
+  (condition-case nil
+      (with-temp-buffer
+       (insert-file-contents file)
+       (goto-char (point-min))
+       (buffer-substring (point) (point-max)))
+    (file-error nil)))
+
 (defun vc-cvs-status-extra-headers (dir)
-  (concat
-   ;; FIXME: see how PCL-CVS gets the data to print all these
-   (propertize "Module     : " 'face 'font-lock-type-face)
-   (propertize "ADD CODE TO PRINT THE MODULE\n"
-              'face 'font-lock-warning-face)
-   (propertize "Repository : " 'face 'font-lock-type-face)
-   (propertize "ADD CODE TO PRINT THE REPOSITORY\n"
-              'face 'font-lock-warning-face)
-   (propertize "Branch     : " 'face 'font-lock-type-face)
-   (propertize "ADD CODE TO PRINT THE BRANCH NAME\n"
-              'face 'font-lock-warning-face)))
+  "Extract and represent per-directory properties of a CVS working copy."
+  (let ((repo
+        (condition-case nil
+            (with-temp-buffer
+              (insert-file-contents "CVS/Root")
+              (goto-char (point-min))
+              (and (looking-at ":ext:") (delete-char 5))
+              (buffer-substring (point) (1- (point-max))))
+          (file-error nil)))
+       (module
+        (condition-case nil
+            (with-temp-buffer
+              (insert-file-contents "CVS/Repository")
+              (goto-char (point-min))
+              (re-search-forward "[^/\n]*" nil t)
+              (concat (match-string 0) "\n"))
+          (file-error nil))))
+    (concat
+     (cond (module
+           (concat (propertize "Module     : " 'face 'font-lock-type-face)
+                    (propertize module 'face 'font-lock-variable-name-face)))
+          (t ""))
+     (cond (repo
+           (concat (propertize "Repository : " 'face 'font-lock-type-face)
+                    (propertize repo 'face 'font-lock-variable-name-face)))
+          (t ""))
+     ;; In CVS, branch is a per-file property, not a per-directory property.
+     ;; We can't really do this here without making dangerous assumptions.
+     ;;(propertize "Branch:     " 'face 'font-lock-type-face)
+     ;;(propertize "ADD CODE TO PRINT THE BRANCH NAME\n"
+     ;;         'face 'font-lock-warning-face)
+     )))
 
 (defun vc-cvs-get-entries (dir)
   "Insert the CVS/Entries file from below DIR into the current buffer.
@@ -1048,7 +1046,6 @@ is non-nil."
   (cond
    ;; entry for a "locally added" file (not yet committed)
    ((looking-at "/[^/]+/0/")
-    (vc-file-setprop file 'vc-backend 'CVS)
     (vc-file-setprop file 'vc-checkout-time 0)
     (vc-file-setprop file 'vc-working-revision "0")
     (if set-state (vc-file-setprop file 'vc-state 'added)))
@@ -1064,7 +1061,6 @@ is non-nil."
             ;; sticky tag
             "\\(.\\|\\)" ;Sticky tag type (date or tag name, could be empty)
             "\\(.*\\)"))               ;Sticky tag
-    (vc-file-setprop file 'vc-backend 'CVS)
     (vc-file-setprop file 'vc-working-revision (match-string 1))
     (vc-file-setprop file 'vc-cvs-sticky-tag
                     (vc-cvs-parse-sticky-tag (match-string 4)
@@ -1111,7 +1107,7 @@ is non-nil."
     (setq table (lazy-completion-table
                  table (lambda () (vc-cvs-revision-table (car files)))))
     table))
-                                           
+
 
 (provide 'vc-cvs)