;;; vc-cvs.el --- non-resident support for CVS version-control
;; Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003,
-;; 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;; 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
;; Author: FSF (see vc.el for full credits)
;; Maintainer: Andre Spiegel <spiegel@gnu.org>
'edited)))
(defun vc-cvs-dir-state (dir)
- "Find the CVS state of all files in 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)
;; Don't specify DIR in this command, the default-directory is
;; enough. Otherwise it might fail with remote repositories.
(with-temp-buffer
- (vc-cvs-command t 0 nil "status" "-l")
+ (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))
(cond ((eq cvs-state 'edited)
(if (equal (vc-working-revision file) "0")
"(added)" "(modified)"))
- ((eq cvs-state 'needs-patch) "(patch)")
- ((eq cvs-state 'needs-merge) "(merge)"))))
-
+ (t
+ (vc-default-dired-state-info 'CVS file)))))
;;;
;;; State-changing functions
`vc-register-switches' and `vc-cvs-register-switches' are passed to
the CVS command (in that order)."
- (when (and (not (vc-cvs-responsible-p file))
- (vc-cvs-could-register file))
- ;; Register the directory if needed.
- (vc-cvs-register (directory-file-name (file-name-directory file))))
- (apply 'vc-cvs-command nil 0 files
- "add"
- (and comment (string-match "[^\t\n ]" comment)
- (concat "-m" comment))
- (vc-switches 'CVS 'register)))
+ ;; Register the directories if needed.
+ (let (dirs)
+ (dolist (file files)
+ (and (not (vc-cvs-responsible-p file))
+ (vc-cvs-could-register file)
+ (push (directory-file-name (file-name-directory file)) dirs)))
+ (if dirs (vc-cvs-register dirs)))
+ (apply 'vc-cvs-command nil 0 files
+ "add"
+ (and comment (string-match "[^\t\n ]" comment)
+ (concat "-m" comment))
+ (vc-switches 'CVS 'register)))
(defun vc-cvs-responsible-p (file)
"Return non-nil if CVS thinks it is responsible for FILE."
((re-search-forward "Up-to-date check failed" nil t)
(mapc (lambda (file) (vc-file-setprop file 'vc-state 'needs-merge))
files)
- (error (substitute-command-keys
+ (error "%s" (substitute-command-keys
(concat "Up-to-date check failed: "
"type \\[vc-next-action] to merge in changes"))))
(t
(message "Merging changes into %s..." file)
;; (vc-file-setprop file 'vc-working-revision nil)
(vc-file-setprop file 'vc-checkout-time 0)
- (vc-cvs-command nil 0 file "update")
+ (vc-cvs-command nil nil file "update")
;; Analyze the merge result reported by CVS, and set
;; file properties accordingly.
(with-current-buffer (get-buffer "*vc*")
(error "Couldn't analyze cvs update result")))
(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.
+Will fail unless you have administrative privileges on the repo."
+ (vc-cvs-command nil 0 files "rcs" (concat "-m" comment ":" rev)))
;;;
;;; History functions
;;;
(defun vc-cvs-print-log (files &optional buffer)
- "Get change log associated with FILE."
+ "Get change logs associated with FILES."
+ ;; It's just the catenation of the individual logs.
(vc-cvs-command
buffer
(if (vc-stay-local-p files) 'async 0)
(defun vc-cvs-diff (files &optional oldvers newvers buffer)
"Get a difference report using CVS between two revisions of FILE."
- (let* ((async (and (not vc-disable-async-diff)
- (vc-stay-local-p files)))
- (status (apply 'vc-cvs-command (or buffer "*vc-diff*")
+ (let* ((async (and (not vc-disable-async-diff)
+ (vc-stay-local-p files)))
+ (invoke-cvs-diff-list nil)
+ status)
+ ;; Look through the file list and see if any files have backups
+ ;; that can be used to do a plain "diff" instead of "cvs diff".
+ (dolist (file files)
+ (let ((ov oldvers)
+ (nv newvers))
+ (when (or (not ov) (string-equal ov ""))
+ (setq ov (vc-working-revision file)))
+ (when (string-equal nv "")
+ (setq nv nil))
+ (let ((file-oldvers (vc-version-backup-file file ov))
+ (file-newvers (if (not nv)
+ file
+ (vc-version-backup-file file nv)))
+ (coding-system-for-read (vc-coding-system-for-diff file)))
+ (if (and file-oldvers file-newvers)
+ (progn
+ (apply 'vc-do-command (or buffer "*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-oldvers)
+ (file-relative-name file-newvers))))
+ (setq status 0))
+ (push file invoke-cvs-diff-list)))))
+ (when invoke-cvs-diff-list
+ (setq status (apply 'vc-cvs-command (or buffer "*vc-diff*")
(if async 'async 1)
- files "diff"
+ invoke-cvs-diff-list "diff"
(and oldvers (concat "-r" oldvers))
(and newvers (concat "-r" newvers))
(vc-switches 'CVS 'diff))))
- (if async 1 status))) ; async diff, pessimistic assumption
+ (if async 1 status))) ; async diff, pessimistic assumption
+
(defun vc-cvs-diff-tree (dir &optional rev1 rev2)
"Diff all files at and below DIR."
bol (1+ bol) 'vc-cvs-annotate-time
(setq cache (cons
;; Position at end makes for nicer overlay result.
- (match-end 0)
+ ;; Don't put actual buffer pos here, but only relative
+ ;; distance, so we don't ever move backward in the
+ ;; goto-char below, even if the text is moved.
+ (- (match-end 0) (match-beginning 0))
(vc-annotate-convert-time
(encode-time 0 0 0 day month year))))))))
(when cache
- (goto-char (car cache)) ; fontify from here to eol
+ (goto-char (+ bol (car cache))) ; Fontify from here to eol.
(cdr cache)))) ; days (float)
(defun vc-cvs-annotate-extract-revision-at-line ()
;;; 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',
(defun vc-cvs-parse-status (&optional full)
"Parse output of \"cvs status\" command in the current buffer.
Set file properties accordingly. Unless FULL is t, parse only
-essential information."
+essential information. Note that this can never set the 'ignored
+state."
(let (file status)
(goto-char (point-min))
+ (while (looking-at "? \\(.*\\)")
+ (setq file (expand-file-name (match-string 1)))
+ (vc-file-setprop file 'vc-state 'unregistered)
+ (forward-line 1))
(if (re-search-forward "^File: " nil t)
(cond
((looking-at "no file") 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 'edited)))
;; 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)