;;; vc-rcs.el --- support for RCS version-control
-;; Copyright (C) 1992,93,94,95,96,97,98,99,2000 Free Software Foundation, Inc.
+;; Copyright (C) 1992,93,94,95,96,97,98,99,2000,2001 Free Software Foundation, Inc.
;; Author: FSF (see vc.el for full credits)
;; Maintainer: Andre Spiegel <spiegel@gnu.org>
-;; $Id: vc-rcs.el,v 1.13 2000/11/19 09:46:04 spiegel Exp $
+;; $Id: vc-rcs.el,v 1.36 2003/02/04 12:11:40 lektu Exp $
;; This file is part of GNU Emacs.
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
-;;; Commentary: see vc.el
+;;; Commentary:
+
+;; See vc.el
;;; Code:
;;;
(eval-when-compile
- (require 'cl))
+ (require 'cl)
+ (require 'vc))
(defcustom vc-rcs-release nil
"*The release number of your RCS installation, as a string.
:version "21.1"
:group 'vc)
-(defcustom vc-rcs-checkin-switches nil
- "*A string or list of strings specifying extra switches for RCS checkin.
-These are passed to the checkin program by \\[vc-rcs-checkin]."
- :type '(choice (const :tag "None" nil)
- (string :tag "Argument String")
- (repeat :tag "Argument List"
- :value ("")
- string))
- :version "21.1"
- :group 'vc)
-
-(defcustom vc-rcs-checkout-switches nil
- "*A string or list of strings specifying extra switches for RCS checkout.
-These are passed to the checkout program by \\[vc-rcs-checkout]."
+(defcustom vc-rcs-diff-switches nil
+ "*A string or list of strings specifying extra switches for rcsdiff under VC."
:type '(choice (const :tag "None" nil)
(string :tag "Argument String")
(repeat :tag "Argument List"
(defcustom vc-rcs-header (or (cdr (assoc 'RCS vc-header-alist)) '("\$Id\$"))
"*Header keywords to be inserted by `vc-insert-headers'."
- :type 'string
+ :type '(repeat string)
:version "21.1"
:group 'vc)
;;; State-querying functions
;;;
-;;;###autoload
-(progn (defun vc-rcs-registered (f) (vc-default-registered 'RCS f)))
+;;; The autoload cookie below places vc-rcs-registered directly into
+;;; loaddefs.el, so that vc-rcs.el does not need to be loaded for
+;;; every file that is visited. The definition is repeated below
+;;; so that Help and etags can find it.
+
+;;;###autoload (defun vc-rcs-registered (f) (vc-default-registered 'RCS f))
+(defun vc-rcs-registered (f) (vc-default-registered 'RCS f))
(defun vc-rcs-state (file)
"Implementation of `vc-state' for RCS."
'vc-workfile-version))))
(if (not (eq state 'up-to-date))
state
- (require 'vc)
(if (vc-workfile-unchanged-p file)
'up-to-date
(if (eq (vc-checkout-model file) 'locking)
(cond
((string-match ".rw..-..-." (nth 8 (file-attributes file)))
(vc-file-setprop file 'vc-checkout-model 'implicit)
- (setq state
- (if (vc-rcs-workfile-is-newer file)
- 'edited
+ (setq state
+ (if (vc-rcs-workfile-is-newer file)
+ 'edited
'up-to-date)))
((string-match ".r-..-..-." (nth 8 (file-attributes file)))
(vc-file-setprop file 'vc-checkout-model 'locking))))
(if (file-ownership-preserved-p file)
'edited
(vc-user-login-name owner-uid))
- (if (vc-rcs-workfile-is-newer file)
+ (if (vc-rcs-workfile-is-newer file)
'edited
'up-to-date)))
(t
(unless version (setq version (vc-workfile-version file)))
(with-temp-buffer
(string= version
- (if (vc-rcs-trunk-p version)
+ (if (vc-trunk-p version)
(progn
;; Compare VERSION to the head version number.
(vc-insert-file (vc-name file) "^[0-9]")
;; If we are not on the trunk, we need to examine the
;; whole current branch.
(vc-insert-file (vc-name file) "^desc")
- (vc-rcs-find-most-recent-rev (vc-rcs-branch-part version))))))
+ (vc-rcs-find-most-recent-rev (vc-branch-part version))))))
(defun vc-rcs-checkout-model (file)
"RCS-specific version of `vc-checkout-model'."
Automatically retrieve a read-only version of the file with keywords
expanded if `vc-keep-workfiles' is non-nil, otherwise, delete the workfile."
- (let ((subdir (expand-file-name "RCS" (file-name-directory file)))
- (switches (list
- (if (stringp vc-register-switches)
- (list vc-register-switches)
- vc-register-switches)
- (if (stringp vc-rcs-register-switches)
- (list vc-rcs-register-switches)
- vc-rcs-register-switches))))
-
+ (let ((subdir (expand-file-name "RCS" (file-name-directory file))))
(and (not (file-exists-p subdir))
(not (directory-files (file-name-directory file)
nil ".*,v$" t))
(and (vc-rcs-release-p "5.6.4") "-i")
(concat (if vc-keep-workfiles "-u" "-r") rev)
(and comment (concat "-t-" comment))
- switches)
+ (vc-switches 'RCS 'register))
;; parse output to find master file name and workfile version
(with-current-buffer "*vc*"
(goto-char (point-min))
(and (string= (file-name-nondirectory (directory-file-name dir)) "RCS")
;; check whether RCS dir is empty, i.e. it does not
;; contain any files except "." and ".."
- (not (directory-files dir nil
+ (not (directory-files dir nil
"^\\([^.]\\|\\.[^.]\\|\\.\\.[^.]\\).*"))
(yes-or-no-p (format "Directory %s is empty; remove it? " dir))
(delete-directory dir))))
(defun vc-rcs-checkin (file rev comment)
"RCS-specific version of `vc-backend-checkin'."
- (let ((switches (if (stringp vc-checkin-switches)
- (list vc-checkin-switches)
- vc-checkin-switches)))
+ (let ((switches (vc-switches 'RCS 'checkin)))
(let ((old-version (vc-workfile-version file)) new-version
(default-branch (vc-file-getprop file 'vc-rcs-default-branch)))
- ;; Force branch creation if an appropriate
+ ;; Force branch creation if an appropriate
;; default branch has been set.
(and (not rev)
default-branch
default-branch)
(setq rev default-branch)
(setq switches (cons "-f" switches)))
+ (if (and (not rev) old-version)
+ (setq rev (vc-branch-part old-version)))
(apply 'vc-do-command nil 0 "ci" (vc-name file)
;; if available, use the secure check-in option
(and (vc-rcs-release-p "5.6.4") "-j")
;; branch accordingly
(cond
((and old-version new-version
- (not (string= (vc-rcs-branch-part old-version)
- (vc-rcs-branch-part new-version))))
- (vc-rcs-set-default-branch file
- (if (vc-rcs-trunk-p new-version) nil
- (vc-rcs-branch-part new-version)))
+ (not (string= (vc-branch-part old-version)
+ (vc-branch-part new-version))))
+ (vc-rcs-set-default-branch file
+ (if (vc-trunk-p new-version) nil
+ (vc-branch-part new-version)))
;; If this is an old RCS release, we might have
;; to remove a remaining lock.
(if (not (vc-rcs-release-p "5.6.2"))
(vc-do-command nil 1 "rcs" (vc-name file)
(concat "-u" old-version))))))))
-(defun vc-rcs-checkout (file &optional writable rev workfile)
- "Retrieve a copy of a saved version of FILE into a workfile."
- (let ((filename (or workfile file))
- (file-buffer (get-file-buffer file))
+(defun vc-rcs-find-version (file rev buffer)
+ (apply 'vc-do-command
+ buffer 0 "co" (vc-name file)
+ "-q" ;; suppress diagnostic output
+ (concat "-p" rev)
+ (vc-switches 'RCS 'checkout)))
+
+(defun vc-rcs-checkout (file &optional editable rev)
+ "Retrieve a copy of a saved version of FILE."
+ (let ((file-buffer (get-file-buffer file))
switches)
- (message "Checking out %s..." filename)
+ (message "Checking out %s..." file)
(save-excursion
;; Change buffers to get local value of vc-checkout-switches.
(if file-buffer (set-buffer file-buffer))
- (setq switches (if (stringp vc-checkout-switches)
- (list vc-checkout-switches)
- vc-checkout-switches))
+ (setq switches (vc-switches 'RCS 'checkout))
;; Save this buffer's default-directory
;; and use save-excursion to make sure it is restored
;; in the same buffer it was saved in.
(save-excursion
;; Adjust the default-directory so that the check-out creates
;; the file in the right place.
- (setq default-directory (file-name-directory filename))
- (if workfile ;; RCS
- ;; RCS can't check out into arbitrary file names directly.
- ;; Use `co -p' and make stdout point to the correct file.
- (let ((vc-modes (logior (file-modes (vc-name file))
- (if writable 128 0)))
- (failed t))
- (unwind-protect
- (progn
- (let ((coding-system-for-read 'no-conversion)
- (coding-system-for-write 'no-conversion))
- (with-temp-file filename
- (apply 'vc-do-command
- (current-buffer) 0 "co" (vc-name file)
- "-q" ;; suppress diagnostic output
- (if writable "-l")
- (concat "-p" rev)
- switches)))
- (set-file-modes filename
- (logior (file-modes (vc-name file))
- (if writable 128 0)))
- (setq failed nil))
- (and failed (file-exists-p filename)
- (delete-file filename))))
- (let (new-version)
- ;; if we should go to the head of the trunk,
- ;; clear the default branch first
- (and rev (string= rev "")
- (vc-rcs-set-default-branch file nil))
- ;; now do the checkout
- (apply 'vc-do-command
- nil 0 "co" (vc-name file)
- ;; If locking is not strict, force to overwrite
- ;; the writable workfile.
- (if (eq (vc-checkout-model file) 'implicit) "-f")
- (if writable "-l")
- (if rev (concat "-r" rev)
- ;; if no explicit revision was specified,
- ;; check out that of the working file
- (let ((workrev (vc-workfile-version file)))
- (if workrev (concat "-r" workrev)
- nil)))
- switches)
- ;; determine the new workfile version
- (with-current-buffer "*vc*"
- (setq new-version
- (vc-parse-buffer "^revision \\([0-9.]+\\).*\n" 1)))
- (vc-file-setprop file 'vc-workfile-version new-version)
- ;; if necessary, adjust the default branch
- (and rev (not (string= rev ""))
- (vc-rcs-set-default-branch
- file
- (if (vc-rcs-latest-on-branch-p file new-version)
- (if (vc-rcs-trunk-p new-version) nil
- (vc-rcs-branch-part new-version))
- new-version))))))
- (message "Checking out %s...done" filename)))))
-
-(defun vc-rcs-revert (file)
+ (setq default-directory (file-name-directory file))
+ (let (new-version)
+ ;; if we should go to the head of the trunk,
+ ;; clear the default branch first
+ (and rev (string= rev "")
+ (vc-rcs-set-default-branch file nil))
+ ;; now do the checkout
+ (apply 'vc-do-command
+ nil 0 "co" (vc-name file)
+ ;; If locking is not strict, force to overwrite
+ ;; the writable workfile.
+ (if (eq (vc-checkout-model file) 'implicit) "-f")
+ (if editable "-l")
+ (if (stringp rev)
+ ;; a literal revision was specified
+ (concat "-r" rev)
+ (let ((workrev (vc-workfile-version file)))
+ (if workrev
+ (concat "-r"
+ (if (not rev)
+ ;; no revision specified:
+ ;; use current workfile version
+ workrev
+ ;; REV is t ...
+ (if (not (vc-trunk-p workrev))
+ ;; ... go to head of current branch
+ (vc-branch-part workrev)
+ ;; ... go to head of trunk
+ (vc-rcs-set-default-branch file
+ nil)
+ ""))))))
+ switches)
+ ;; determine the new workfile version
+ (with-current-buffer "*vc*"
+ (setq new-version
+ (vc-parse-buffer "^revision \\([0-9.]+\\).*\n" 1)))
+ (vc-file-setprop file 'vc-workfile-version new-version)
+ ;; if necessary, adjust the default branch
+ (and rev (not (string= rev ""))
+ (vc-rcs-set-default-branch
+ file
+ (if (vc-rcs-latest-on-branch-p file new-version)
+ (if (vc-trunk-p new-version) nil
+ (vc-branch-part new-version))
+ new-version)))))
+ (message "Checking out %s...done" file)))))
+
+(defun vc-rcs-revert (file &optional contents-done)
"Revert FILE to the version it was based on."
(vc-do-command nil 0 "co" (vc-name file) "-f"
- (concat "-u" (vc-workfile-version file))))
+ (concat (if (eq (vc-state file) 'edited) "-u" "-r")
+ (vc-workfile-version file))))
-(defun vc-rcs-cancel-version (file writable)
+(defun vc-rcs-cancel-version (file editable)
"Undo the most recent checkin of FILE.
-WRITABLE non-nil means previous version should be locked."
+EDITABLE non-nil means previous version should be locked."
(let* ((target (vc-workfile-version file))
(previous (if (vc-trunk-p target) "" (vc-branch-part target)))
(config (current-window-configuration))
(condition-case err
(progn
(vc-do-command nil 0 "co" (vc-name file) "-f"
- (concat (if writable "-l" "-u") previous))
+ (concat (if editable "-l" "-u") previous))
(setq done t))
(error (set-buffer "*vc*")
(goto-char (point-min))
(defun vc-rcs-steal-lock (file &optional rev)
"Steal the lock on the current workfile for FILE and revision REV.
Needs RCS 5.6.2 or later for -M."
- (vc-do-command nil 0 "rcs" (vc-name file) "-M"
- (concat "-u" rev) (concat "-l" rev)))
+ (vc-do-command nil 0 "rcs" (vc-name file) "-M" (concat "-u" rev))
+ ;; Do a real checkout after stealing the lock, so that we see
+ ;; expanded headers.
+ (vc-do-command nil 0 "co" (vc-name file) "-f" (concat "-l" rev)))
\f
(defun vc-rcs-print-log (file)
"Get change log associated with FILE."
- (vc-do-command t 0 "rlog" (vc-name file)))
-
-(defun vc-rcs-show-log-entry (version)
- (when (re-search-forward
- ;; also match some context, for safety
- (concat "----\nrevision " version
- "\\(\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-do-command nil 0 "rlog" (vc-name file)))
(defun vc-rcs-diff (file &optional oldvers newvers)
"Get a difference report using RCS between two versions of FILE."
(if (not oldvers) (setq oldvers (vc-workfile-version file)))
- ;; If we know that --brief is not supported, don't try it.
- (let* ((diff-switches-list (if (listp diff-switches)
- diff-switches
- (list diff-switches)))
- (options (append (list "-q"
- (concat "-r" oldvers)
- (and newvers (concat "-r" newvers)))
- diff-switches-list)))
- (apply 'vc-do-command t 1 "rcsdiff" file options)))
+ (apply 'vc-do-command "*vc-diff*" 1 "rcsdiff" file
+ (append (list "-q"
+ (concat "-r" oldvers)
+ (and newvers (concat "-r" newvers)))
+ (vc-switches 'RCS 'diff))))
\f
;;;
;;; Internal functions
;;;
-(defun vc-rcs-trunk-p (rev)
- "Return t if REV is an RCS revision on the trunk."
- (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
-
-(defun vc-rcs-branch-part (rev)
- "Return the branch part of an RCS revision number REV"
- (substring rev 0 (string-match "\\.[0-9]+\\'" rev)))
-
-(defun vc-rcs-branch-p (rev)
- "Return t if REV is an RCS branch revision"
- (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
-
-(defun vc-rcs-minor-part (rev)
- "Return the minor version number of an RCS revision number REV."
- (string-match "[0-9]+\\'" rev)
- (substring rev (match-beginning 0) (match-end 0)))
-
-(defun vc-rcs-previous-version (rev)
- "Guess the previous RCS version number"
- (let ((branch (vc-rcs-branch-part rev))
- (minor-num (string-to-number (vc-rcs-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-rcs-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-rcs-branch-part branch)))))
-
(defun vc-rcs-workfile-is-newer (file)
"Return non-nil if FILE is newer than its RCS master.
This likely means that FILE has been changed with respect
(setq latest-rev rev)
(setq value (match-string 1)))))
(or value
- (vc-rcs-branch-part branch))))
+ (vc-branch-part branch))))
(defun vc-rcs-fetch-master-state (file &optional workfile-version)
"Compute the master file's idea of the state of FILE.
`vc-checkout-model' to their correct values, based on the master
file."
(with-temp-buffer
- (vc-insert-file (vc-name file) "^[0-9]")
+ (if (or (not (vc-insert-file (vc-name file) "^[0-9]"))
+ (progn (goto-char (point-min))
+ (not (looking-at "^head[ \t\n]+[^;]+;$"))))
+ (error "File %s is not an RCS master file" (vc-name file)))
(let ((workfile-is-latest nil)
(default-branch (vc-parse-buffer "^branch[ \t\n]+\\([^;]*\\);" 1)))
(vc-file-setprop file 'vc-rcs-default-branch default-branch)