diff options
| author | Gerd Moellmann | 2000-09-04 19:47:43 +0000 |
|---|---|---|
| committer | Gerd Moellmann | 2000-09-04 19:47:43 +0000 |
| commit | d8aff0773f6e786ac26212b2fc2d877b034118e5 (patch) | |
| tree | bfcf94fde6ad88f7ddd2004eb19ddce8d9119e53 | |
| parent | 0e0d98319e3360bcfd12aabd3bc82a72aec700e6 (diff) | |
| download | emacs-d8aff0773f6e786ac26212b2fc2d877b034118e5.tar.gz emacs-d8aff0773f6e786ac26212b2fc2d877b034118e5.zip | |
(vc-rcs-show-log-entry): New function.
(vc-rcs-checkin, vc-rcs-checkout): Don't set all properties.
(vc-rcs-register): If there is no RCS subdir, ask the
user whether to create one.
(vc-rcs-state-heuristic): Use
file-ownership-preserved-p.
(vc-rcs-checkout): Remove the error-handling for missing-rcs.
(vc-rcs-state-heuristic): Don't use file-writable-p.
(vc-rcs-print-log): Insert in the current buffer.
(vc-rcs-diff): Insert in the current buffer and remove unused arg
CMP.
(vc-rcs-workfile-unchanged-p): Use vc-do-command
instead of vc-simple-command.
(vc-rcs-fetch-master-state): Removed check for unlocked-changes to
avoid doing a diff when opening a file.
(vc-rcs-state): Added check for unlocked-changes.
(vc-rcs-header): Escape Id.
(vc-rcs-workfile-unchanged-p): Remove optional arg VERSION.
(vc-rcs-state): Call vc-workfile-unchanged-p, not the RCS-specific
version.
(vc-rcs-state-heuristic): Use file-writable-p instead
of comparing userids.
(vc-rcs-fetch-master-state): Handle the case where rcs is missing.
Simplify the logic by eliminating unreachable code.
(vc-rcs-diff): Only pass `2' to vc-do-command if necessary and
just do a recursive call if we need to retry.
(vc-rcs-checkout): Handle the case where rcs is missing by making
the buffer read-write if requested and re-signalling the error.
(vc-rcs-find-most-recent-rev): New function. The code
derives from the old vc-parse-buffer but uses the revision number
rather than the date (much easier to compare robustly).
(vc-rcs-fetch-master-state): Use `with-temp-buffer'. Adapt to the
new vc-parse-buffer (and vc-rcs-find-most-recent-rev). Find the
locking-user more directly. Check strict locking and set
checkout-model appropriately.
(vc-rcs-parse-locks): Remove.
(vc-rcs-latest-on-branch-p): Use with-temp-buffer and adapt to the
new vc-parse-buffer (and vc-rcs-find-most-recent-rev).
(vc-rcs-system-release): Use with-current-buffer and
vc-parse-buffer.
(vc-rcs-register, vc-rcs-checkout): Use with-current-buffer.
Merge in code
from vc-rcs-hooks.el. Don't require 'vc anymore.
(vc-rcs-responsible-p): Use expand-file-name instead of concat and
file-directory-p instead of file-exists-p.
(vc-rcs-exists): Remove.
(vc-rcs-header): New var.
Update Copyright.
(vc-rcs-rename-file): New function.
(vc-rcs-diff): Remove unused `backend' variable.
(vc-rcs-clear-headers): New function; code moved here
from vc-clear-headers in vc.el.
(tail): Provide vc-rcs and remove vc-rcs-logentry-check.
(vc-rcs-register): Parse command output to find master
file name and workfile version.
(vc-rcs-checkout): Removed call to vc-file-clear-masterprops.
Require vc and vc-rcs-hooks.
(vc-rcs-trunk-p, vc-rcs-branch-part): Move to vc-rcs-hooks.
(vc-rcs-backend-release-p): Remove (use vc-rcs-release-p).
(vc-release-greater-or-equal-p): Move from vc.
(vc-rcs-trunk-p, vc-rcs-branch-p, vc-rcs-branch-part,
vc-rcs-minor-part, vc-rcs-previous-version): Remove duplicates.
(vc-rcs-checkout): Add a missing `new-version' argument in the
call to vc-rcs-latest-on-branch-p. Hopefully that was the right
one.
(vc-rcs-steal-lock): Renamed from `vc-rcs-steal'.
Updated everything to use `vc-checkout-model'.
(vc-rcs-backend-release-p): function added. other
stuff updated to reference this function instead of the old
`vc-backend-release-p'.
(vc-rcs-logentry-check): Function added.
(vc-rcs-checkin, vc-rcs-previous-version)
(vc-rcs-checkout): Name space cleaned up. No more revision number
crunching function names that are not prefixed with vc-rcs.
(vc-rcs-checkout-model): Function added. References to
`vc-checkout-model' replaced.
(vc-rcs-admin): Added the query-only option as
required by the vc.el file.
(vc-rcs-exists): Function added.
(vc-*-checkout):
Use with-temp-file instead of /bin/sh. Merged from mainline
(vc-rcs-latest-on-branch-p): Moved to vc-rcs-hooks.el.
(vc-rcs-latest-on-branch-p, vc-rcs-trunk-p)
(vc-rcs-branch-p, vc-rcs-branch-part, vc-rcs-minor-part)
(vc-rcs-previous-version): Functions added.
(vc-rcs-diff): Function added.
(vc-rcs-checkout) Bug (typo) found and fixed.
(vc-rcs-register-switches) Variable `vc-rcs-register-switches' added.
Require vc when compiling.
(vc-rcs-print-log, vc-rcs-assign-name, vc-rcs-merge)
(vc-rcs-check-headers, vc-rcs-steal, vc-rcs-uncheck, vc-rcs-revert)
(vc-rcs-checkin): New functions (code from vc.el).
(vc-rcs-previous-version, vc-rcs-system-release, vc-rcs-checkout):
Doc fix.
(vc-rcs-release): Deleted. (Duplicated vc-rcs-system-release).
(vc-rcs-trunk-p, vc-rcs-branch-p, vc-rcs-branch-part)
(vc-rcs-minor-part, vc-rcs-previous-version, vc-rcs-release)
(vc-rcs-release-p, vc-rcs-admin, vc-rcs-checkout): New functions
from vc.el.
(vc-rcs-system-release):
Renamed from vc-rcs-backend-release.
| -rw-r--r-- | lisp/vc-rcs.el | 737 |
1 files changed, 737 insertions, 0 deletions
diff --git a/lisp/vc-rcs.el b/lisp/vc-rcs.el new file mode 100644 index 00000000000..4a936d2ee57 --- /dev/null +++ b/lisp/vc-rcs.el | |||
| @@ -0,0 +1,737 @@ | |||
| 1 | ;;; vc-rcs.el --- support for RCS version-control | ||
| 2 | |||
| 3 | ;; Copyright (C) 1992,93,94,95,96,97,98,99,2000 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: FSF (see vc.el for full credits) | ||
| 6 | ;; Maintainer: Andre Spiegel <spiegel@gnu.org> | ||
| 7 | |||
| 8 | ;; $Id: vc-rcs.el,v 1.36 2000/08/12 18:51:30 spiegel Exp $ | ||
| 9 | |||
| 10 | ;; This file is part of GNU Emacs. | ||
| 11 | |||
| 12 | ;; GNU Emacs is free software; you can redistribute it and/or modify | ||
| 13 | ;; it under the terms of the GNU General Public License as published by | ||
| 14 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 15 | ;; any later version. | ||
| 16 | |||
| 17 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 20 | ;; GNU General Public License for more details. | ||
| 21 | |||
| 22 | ;; You should have received a copy of the GNU General Public License | ||
| 23 | ;; along with GNU Emacs; see the file COPYING. If not, write to the | ||
| 24 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
| 25 | ;; Boston, MA 02111-1307, USA. | ||
| 26 | |||
| 27 | ;;; Commentary: see vc.el | ||
| 28 | |||
| 29 | ;;; Code: | ||
| 30 | |||
| 31 | (defcustom vc-rcs-release nil | ||
| 32 | "*The release number of your RCS installation, as a string. | ||
| 33 | If nil, VC itself computes this value when it is first needed." | ||
| 34 | :type '(choice (const :tag "Auto" nil) | ||
| 35 | (string :tag "Specified") | ||
| 36 | (const :tag "Unknown" unknown)) | ||
| 37 | :group 'vc) | ||
| 38 | |||
| 39 | (defcustom vc-rcs-register-switches nil | ||
| 40 | "*A string or list of strings; extra switches for registering a file | ||
| 41 | in RCS. These are passed to the checkin program by | ||
| 42 | \\[vc-rcs-register]." | ||
| 43 | :type '(choice (const :tag "None" nil) | ||
| 44 | (string :tag "Argument String") | ||
| 45 | (repeat :tag "Argument List" | ||
| 46 | :value ("") | ||
| 47 | string)) | ||
| 48 | :group 'vc) | ||
| 49 | |||
| 50 | (defcustom vc-rcs-checkin-switches nil | ||
| 51 | "*A string or list of strings specifying extra switches for RCS checkin. | ||
| 52 | These are passed to the checkin program by \\[vc-rcs-checkin]." | ||
| 53 | :type '(choice (const :tag "None" nil) | ||
| 54 | (string :tag "Argument String") | ||
| 55 | (repeat :tag "Argument List" | ||
| 56 | :value ("") | ||
| 57 | string)) | ||
| 58 | :group 'vc) | ||
| 59 | |||
| 60 | (defcustom vc-rcs-checkout-switches nil | ||
| 61 | "*A string or list of strings specifying extra switches for RCS checkout. | ||
| 62 | These are passed to the checkout program by \\[vc-rcs-checkout]." | ||
| 63 | :type '(choice (const :tag "None" nil) | ||
| 64 | (string :tag "Argument String") | ||
| 65 | (repeat :tag "Argument List" | ||
| 66 | :value ("") | ||
| 67 | string)) | ||
| 68 | :group 'vc) | ||
| 69 | |||
| 70 | (defcustom vc-rcs-header (or (cdr (assoc 'RCS vc-header-alist)) '("\$Id\$")) | ||
| 71 | "*Header keywords to be inserted by `vc-insert-headers'." | ||
| 72 | :type 'string | ||
| 73 | :group 'vc) | ||
| 74 | |||
| 75 | (defcustom vc-rcsdiff-knows-brief nil | ||
| 76 | "*Indicates whether rcsdiff understands the --brief option. | ||
| 77 | The value is either `yes', `no', or nil. If it is nil, VC tries | ||
| 78 | to use --brief and sets this variable to remember whether it worked." | ||
| 79 | :type '(choice (const :tag "Work out" nil) (const yes) (const no)) | ||
| 80 | :group 'vc) | ||
| 81 | |||
| 82 | ;;;###autoload | ||
| 83 | (defcustom vc-rcs-master-templates | ||
| 84 | '("%sRCS/%s,v" "%s%s,v" "%sRCS/%s") | ||
| 85 | "*Where to look for RCS master files. | ||
| 86 | For a description of possible values, see `vc-check-master-templates'." | ||
| 87 | :type '(choice (const :tag "Use standard RCS file names" | ||
| 88 | '("%sRCS/%s,v" "%s%s,v" "%sRCS/%s")) | ||
| 89 | (repeat :tag "User-specified" | ||
| 90 | (choice string | ||
| 91 | function))) | ||
| 92 | :version "20.5" | ||
| 93 | :group 'vc) | ||
| 94 | |||
| 95 | ;;;###autoload | ||
| 96 | (progn (defun vc-rcs-registered (f) (vc-default-registered 'RCS f))) | ||
| 97 | |||
| 98 | (defun vc-rcs-state (file) | ||
| 99 | "Implementation of `vc-state' for RCS." | ||
| 100 | (or (boundp 'vc-rcs-headers-result) | ||
| 101 | (and vc-consult-headers | ||
| 102 | (vc-rcs-consult-headers file))) | ||
| 103 | (let ((state | ||
| 104 | ;; vc-workfile-version might not be known; in that case the | ||
| 105 | ;; property is nil. vc-rcs-fetch-master-state knows how to | ||
| 106 | ;; handle that. | ||
| 107 | (vc-rcs-fetch-master-state file | ||
| 108 | (vc-file-getprop file | ||
| 109 | 'vc-workfile-version)))) | ||
| 110 | (if (eq state 'up-to-date) | ||
| 111 | (if (vc-workfile-unchanged-p file) | ||
| 112 | 'up-to-date | ||
| 113 | 'unlocked-changes) | ||
| 114 | state))) | ||
| 115 | |||
| 116 | (defun vc-rcs-state-heuristic (file) | ||
| 117 | "State heuristic for RCS." | ||
| 118 | (let (vc-rcs-headers-result) | ||
| 119 | (if (and vc-consult-headers | ||
| 120 | (setq vc-rcs-headers-result | ||
| 121 | (vc-rcs-consult-headers file)) | ||
| 122 | (eq vc-rcs-headers-result 'rev-and-lock)) | ||
| 123 | (let ((state (vc-file-getprop file 'vc-state))) | ||
| 124 | ;; If the headers say that the file is not locked, the | ||
| 125 | ;; permissions can tell us whether locking is used for | ||
| 126 | ;; the file or not. | ||
| 127 | (if (and (eq state 'up-to-date) | ||
| 128 | (not (vc-mistrust-permissions file))) | ||
| 129 | (cond | ||
| 130 | ((string-match ".rw..-..-." (nth 8 (file-attributes file))) | ||
| 131 | (vc-file-setprop file 'vc-checkout-model 'implicit)) | ||
| 132 | ((string-match ".r-..-..-." (nth 8 (file-attributes file))) | ||
| 133 | (vc-file-setprop file 'vc-checkout-model 'locking)))) | ||
| 134 | state) | ||
| 135 | (if (not (vc-mistrust-permissions file)) | ||
| 136 | (let* ((attributes (file-attributes file)) | ||
| 137 | (owner-uid (nth 2 attributes)) | ||
| 138 | (permissions (nth 8 attributes))) | ||
| 139 | (cond ((string-match ".r-..-..-." permissions) | ||
| 140 | (vc-file-setprop file 'vc-checkout-model 'locking) | ||
| 141 | 'up-to-date) | ||
| 142 | ((string-match ".rw..-..-." permissions) | ||
| 143 | (if (file-ownership-preserved-p file) | ||
| 144 | 'edited | ||
| 145 | (vc-user-login-name owner-uid))) | ||
| 146 | (t | ||
| 147 | ;; Strange permissions. Fall through to | ||
| 148 | ;; expensive state computation. | ||
| 149 | (vc-rcs-state file)))) | ||
| 150 | (vc-rcs-state file))))) | ||
| 151 | |||
| 152 | (defun vc-rcs-workfile-version (file) | ||
| 153 | "RCS-specific version of `vc-workfile-version'." | ||
| 154 | (or (and vc-consult-headers | ||
| 155 | (vc-rcs-consult-headers file) | ||
| 156 | (vc-file-getprop file 'vc-workfile-version)) | ||
| 157 | (progn | ||
| 158 | (vc-rcs-fetch-master-state file) | ||
| 159 | (vc-file-getprop file 'vc-workfile-version)))) | ||
| 160 | |||
| 161 | (defun vc-rcs-checkout-model (file) | ||
| 162 | "RCS-specific version of `vc-checkout-model'." | ||
| 163 | (vc-rcs-consult-headers file) | ||
| 164 | (or (vc-file-getprop file 'vc-checkout-model) | ||
| 165 | (progn (vc-rcs-fetch-master-state file) | ||
| 166 | (vc-file-getprop file 'vc-checkout-model)))) | ||
| 167 | |||
| 168 | ;;; internal code | ||
| 169 | |||
| 170 | (defun vc-rcs-find-most-recent-rev (branch) | ||
| 171 | "Find most recent revision on BRANCH." | ||
| 172 | (goto-char (point-min)) | ||
| 173 | (let ((latest-rev -1) value) | ||
| 174 | (while (re-search-forward (concat "^\\(" (regexp-quote branch) | ||
| 175 | "\\.\\([0-9]+\\)\\)\ndate[ \t]+[0-9.]+;") | ||
| 176 | nil t) | ||
| 177 | (let ((rev (string-to-number (match-string 2)))) | ||
| 178 | (when (< latest-rev rev) | ||
| 179 | (setq latest-rev rev) | ||
| 180 | (setq value (match-string 1))))) | ||
| 181 | value)) | ||
| 182 | |||
| 183 | (defun vc-rcs-fetch-master-state (file &optional workfile-version) | ||
| 184 | "Compute the master file's idea of the state of FILE. If a | ||
| 185 | WORKFILE-VERSION is given, compute the state of that version, | ||
| 186 | otherwise determine the workfile version based on the master file. | ||
| 187 | This function sets the properties `vc-workfile-version' and | ||
| 188 | `vc-checkout-model' to their correct values, based on the master | ||
| 189 | file." | ||
| 190 | (with-temp-buffer | ||
| 191 | (vc-insert-file (vc-name file) "^[0-9]") | ||
| 192 | (let ((workfile-is-latest nil)) | ||
| 193 | (unless workfile-version | ||
| 194 | (let ((default-branch (vc-parse-buffer "^branch[ \t\n]+\\([^;]*\\);" 1))) | ||
| 195 | ;; Workfile version not known yet. Determine that first. It | ||
| 196 | ;; is either the head of the trunk, the head of the default | ||
| 197 | ;; branch, or the "default branch" itself, if that is a full | ||
| 198 | ;; revision number. | ||
| 199 | (cond | ||
| 200 | ;; no default branch | ||
| 201 | ((or (not default-branch) (string= "" default-branch)) | ||
| 202 | (setq workfile-version | ||
| 203 | (vc-parse-buffer "^head[ \t\n]+\\([^;]+\\);" 1)) | ||
| 204 | (setq workfile-is-latest t)) | ||
| 205 | ;; default branch is actually a revision | ||
| 206 | ((string-match "^[0-9]+\\.[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*$" | ||
| 207 | default-branch) | ||
| 208 | (setq workfile-version default-branch)) | ||
| 209 | ;; else, search for the head of the default branch | ||
| 210 | (t (vc-insert-file (vc-name file) "^desc") | ||
| 211 | (setq workfile-version | ||
| 212 | (vc-rcs-find-most-recent-rev default-branch)) | ||
| 213 | (setq workfile-is-latest t))) | ||
| 214 | (vc-file-setprop file 'vc-workfile-version workfile-version))) | ||
| 215 | ;; Check strict locking | ||
| 216 | (goto-char (point-min)) | ||
| 217 | (vc-file-setprop file 'vc-checkout-model | ||
| 218 | (if (re-search-forward ";[ \t\n]*strict;" nil t) | ||
| 219 | 'locking 'implicit)) | ||
| 220 | ;; Compute state of workfile version | ||
| 221 | (goto-char (point-min)) | ||
| 222 | (let ((locking-user | ||
| 223 | (vc-parse-buffer (concat "^locks[ \t\n]+[^;]*[ \t\n]+\\([^:]+\\):" | ||
| 224 | (regexp-quote workfile-version) | ||
| 225 | "[^0-9.]") | ||
| 226 | 1))) | ||
| 227 | (cond | ||
| 228 | ;; not locked | ||
| 229 | ((not locking-user) | ||
| 230 | (if (or workfile-is-latest | ||
| 231 | (vc-rcs-latest-on-branch-p file workfile-version)) | ||
| 232 | ;; workfile version is latest on branch | ||
| 233 | 'up-to-date | ||
| 234 | ;; workfile version is not latest on branch | ||
| 235 | 'needs-patch)) | ||
| 236 | ;; locked by the calling user | ||
| 237 | ((and (stringp locking-user) | ||
| 238 | (string= locking-user (vc-user-login-name))) | ||
| 239 | (if (or (eq (vc-checkout-model file) 'locking) | ||
| 240 | workfile-is-latest | ||
| 241 | (vc-rcs-latest-on-branch-p file workfile-version)) | ||
| 242 | 'edited | ||
| 243 | ;; Locking is not used for the file, but the owner does | ||
| 244 | ;; have a lock, and there is a higher version on the current | ||
| 245 | ;; branch. Not sure if this can occur, and if it is right | ||
| 246 | ;; to use `needs-merge' in this case. | ||
| 247 | 'needs-merge)) | ||
| 248 | ;; locked by somebody else | ||
| 249 | ((stringp locking-user) | ||
| 250 | locking-user) | ||
| 251 | (t | ||
| 252 | (error "Error getting state of RCS file"))))))) | ||
| 253 | |||
| 254 | (defun vc-rcs-consult-headers (file) | ||
| 255 | "Search for RCS headers in FILE, and set properties accordingly. | ||
| 256 | |||
| 257 | Returns: nil if no headers were found | ||
| 258 | 'rev if a workfile revision was found | ||
| 259 | 'rev-and-lock if revision and lock info was found" | ||
| 260 | (cond | ||
| 261 | ((not (get-file-buffer file)) nil) | ||
| 262 | ((let (status version locking-user) | ||
| 263 | (save-excursion | ||
| 264 | (set-buffer (get-file-buffer file)) | ||
| 265 | (goto-char (point-min)) | ||
| 266 | (cond | ||
| 267 | ;; search for $Id or $Header | ||
| 268 | ;; ------------------------- | ||
| 269 | ;; The `\ 's below avoid an RCS 5.7 bug when checking in this file. | ||
| 270 | ((or (and (search-forward "$Id\ : " nil t) | ||
| 271 | (looking-at "[^ ]+ \\([0-9.]+\\) ")) | ||
| 272 | (and (progn (goto-char (point-min)) | ||
| 273 | (search-forward "$Header\ : " nil t)) | ||
| 274 | (looking-at "[^ ]+ \\([0-9.]+\\) "))) | ||
| 275 | (goto-char (match-end 0)) | ||
| 276 | ;; if found, store the revision number ... | ||
| 277 | (setq version (match-string-no-properties 1)) | ||
| 278 | ;; ... and check for the locking state | ||
| 279 | (cond | ||
| 280 | ((looking-at | ||
| 281 | (concat "[0-9]+[/-][01][0-9][/-][0-3][0-9] " ; date | ||
| 282 | "[0-2][0-9]:[0-5][0-9]+:[0-6][0-9]+\\([+-][0-9:]+\\)? " ; time | ||
| 283 | "[^ ]+ [^ ]+ ")) ; author & state | ||
| 284 | (goto-char (match-end 0)) ; [0-6] in regexp handles leap seconds | ||
| 285 | (cond | ||
| 286 | ;; unlocked revision | ||
| 287 | ((looking-at "\\$") | ||
| 288 | (setq locking-user 'none) | ||
| 289 | (setq status 'rev-and-lock)) | ||
| 290 | ;; revision is locked by some user | ||
| 291 | ((looking-at "\\([^ ]+\\) \\$") | ||
| 292 | (setq locking-user (match-string-no-properties 1)) | ||
| 293 | (setq status 'rev-and-lock)) | ||
| 294 | ;; everything else: false | ||
| 295 | (nil))) | ||
| 296 | ;; unexpected information in | ||
| 297 | ;; keyword string --> quit | ||
| 298 | (nil))) | ||
| 299 | ;; search for $Revision | ||
| 300 | ;; -------------------- | ||
| 301 | ((re-search-forward (concat "\\$" | ||
| 302 | "Revision: \\([0-9.]+\\) \\$") | ||
| 303 | nil t) | ||
| 304 | ;; if found, store the revision number ... | ||
| 305 | (setq version (match-string-no-properties 1)) | ||
| 306 | ;; and see if there's any lock information | ||
| 307 | (goto-char (point-min)) | ||
| 308 | (if (re-search-forward (concat "\\$" "Locker:") nil t) | ||
| 309 | (cond ((looking-at " \\([^ ]+\\) \\$") | ||
| 310 | (setq locking-user (match-string-no-properties 1)) | ||
| 311 | (setq status 'rev-and-lock)) | ||
| 312 | ((looking-at " *\\$") | ||
| 313 | (setq locking-user 'none) | ||
| 314 | (setq status 'rev-and-lock)) | ||
| 315 | (t | ||
| 316 | (setq locking-user 'none) | ||
| 317 | (setq status 'rev-and-lock))) | ||
| 318 | (setq status 'rev))) | ||
| 319 | ;; else: nothing found | ||
| 320 | ;; ------------------- | ||
| 321 | (t nil))) | ||
| 322 | (if status (vc-file-setprop file 'vc-workfile-version version)) | ||
| 323 | (and (eq status 'rev-and-lock) | ||
| 324 | (vc-file-setprop file 'vc-state | ||
| 325 | (cond | ||
| 326 | ((eq locking-user 'none) 'up-to-date) | ||
| 327 | ((string= locking-user (vc-user-login-name)) 'edited) | ||
| 328 | (t locking-user))) | ||
| 329 | ;; If the file has headers, we don't want to query the | ||
| 330 | ;; master file, because that would eliminate all the | ||
| 331 | ;; performance gain the headers brought us. We therefore | ||
| 332 | ;; use a heuristic now to find out whether locking is used | ||
| 333 | ;; for this file. If we trust the file permissions, and the | ||
| 334 | ;; file is not locked, then if the file is read-only we | ||
| 335 | ;; assume that locking is used for the file, otherwise | ||
| 336 | ;; locking is not used. | ||
| 337 | (not (vc-mistrust-permissions file)) | ||
| 338 | (vc-up-to-date-p file) | ||
| 339 | (if (string-match ".r-..-..-." (nth 8 (file-attributes file))) | ||
| 340 | (vc-file-setprop file 'vc-checkout-model 'locking) | ||
| 341 | (vc-file-setprop file 'vc-checkout-model 'implicit))) | ||
| 342 | status)))) | ||
| 343 | |||
| 344 | (defun vc-rcs-workfile-unchanged-p (file) | ||
| 345 | "RCS-specific implementation of vc-workfile-unchanged-p." | ||
| 346 | ;; Try to use rcsdiff --brief. If rcsdiff does not understand that, | ||
| 347 | ;; do a double take and remember the fact for the future | ||
| 348 | (let* ((version (concat "-r" (vc-workfile-version file))) | ||
| 349 | (status (if (eq vc-rcsdiff-knows-brief 'no) | ||
| 350 | (vc-do-command nil 1 "rcsdiff" file version) | ||
| 351 | (vc-do-command nil 2 "rcsdiff" file "--brief" version)))) | ||
| 352 | (if (eq status 2) | ||
| 353 | (if (not vc-rcsdiff-knows-brief) | ||
| 354 | (setq vc-rcsdiff-knows-brief 'no | ||
| 355 | status (vc-do-command nil 1 "rcsdiff" file version)) | ||
| 356 | (error "rcsdiff failed")) | ||
| 357 | (if (not vc-rcsdiff-knows-brief) (setq vc-rcsdiff-knows-brief 'yes))) | ||
| 358 | ;; The workfile is unchanged if rcsdiff found no differences. | ||
| 359 | (zerop status))) | ||
| 360 | |||
| 361 | (defun vc-rcs-trunk-p (rev) | ||
| 362 | "Return t if REV is an RCS revision on the trunk." | ||
| 363 | (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev)))) | ||
| 364 | |||
| 365 | (defun vc-rcs-branch-part (rev) | ||
| 366 | "Return the branch part of an RCS revision number REV" | ||
| 367 | (substring rev 0 (string-match "\\.[0-9]+\\'" rev))) | ||
| 368 | |||
| 369 | (defun vc-rcs-latest-on-branch-p (file &optional version) | ||
| 370 | "Return non-nil if workfile version of FILE is the latest on its branch. | ||
| 371 | When VERSION is given, perform check for that version." | ||
| 372 | (unless version (setq version (vc-workfile-version file))) | ||
| 373 | (with-temp-buffer | ||
| 374 | (string= version | ||
| 375 | (if (vc-rcs-trunk-p version) | ||
| 376 | (progn | ||
| 377 | ;; Compare VERSION to the head version number. | ||
| 378 | (vc-insert-file (vc-name file) "^[0-9]") | ||
| 379 | (vc-parse-buffer "^head[ \t\n]+\\([^;]+\\);" 1)) | ||
| 380 | ;; If we are not on the trunk, we need to examine the | ||
| 381 | ;; whole current branch. | ||
| 382 | (vc-insert-file (vc-name file) "^desc") | ||
| 383 | (vc-rcs-find-most-recent-rev (vc-rcs-branch-part version)))))) | ||
| 384 | |||
| 385 | (defun vc-rcs-branch-p (rev) | ||
| 386 | "Return t if REV is an RCS branch revision" | ||
| 387 | (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev)))) | ||
| 388 | |||
| 389 | (defun vc-rcs-minor-part (rev) | ||
| 390 | "Return the minor version number of an RCS revision number REV." | ||
| 391 | (string-match "[0-9]+\\'" rev) | ||
| 392 | (substring rev (match-beginning 0) (match-end 0))) | ||
| 393 | |||
| 394 | (defun vc-rcs-previous-version (rev) | ||
| 395 | "Guess the previous RCS version number" | ||
| 396 | (let ((branch (vc-rcs-branch-part rev)) | ||
| 397 | (minor-num (string-to-number (vc-rcs-minor-part rev)))) | ||
| 398 | (if (> minor-num 1) | ||
| 399 | ;; version does probably not start a branch or release | ||
| 400 | (concat branch "." (number-to-string (1- minor-num))) | ||
| 401 | (if (vc-rcs-trunk-p rev) | ||
| 402 | ;; we are at the beginning of the trunk -- | ||
| 403 | ;; don't know anything to return here | ||
| 404 | "" | ||
| 405 | ;; we are at the beginning of a branch -- | ||
| 406 | ;; return version of starting point | ||
| 407 | (vc-rcs-branch-part branch))))) | ||
| 408 | |||
| 409 | (defun vc-rcs-print-log (file) | ||
| 410 | "Get change log associated with FILE." | ||
| 411 | (vc-do-command t 0 "rlog" (vc-name file))) | ||
| 412 | |||
| 413 | (defun vc-rcs-show-log-entry (version) | ||
| 414 | (when (re-search-forward | ||
| 415 | ;; also match some context, for safety | ||
| 416 | (concat "----\nrevision " version | ||
| 417 | "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t) | ||
| 418 | ;; set the display window so that | ||
| 419 | ;; the whole log entry is displayed | ||
| 420 | (let (start end lines) | ||
| 421 | (beginning-of-line) (forward-line -1) (setq start (point)) | ||
| 422 | (if (not (re-search-forward "^----*\nrevision" nil t)) | ||
| 423 | (setq end (point-max)) | ||
| 424 | (beginning-of-line) (forward-line -1) (setq end (point))) | ||
| 425 | (setq lines (count-lines start end)) | ||
| 426 | (cond | ||
| 427 | ;; if the global information and this log entry fit | ||
| 428 | ;; into the window, display from the beginning | ||
| 429 | ((< (count-lines (point-min) end) (window-height)) | ||
| 430 | (goto-char (point-min)) | ||
| 431 | (recenter 0) | ||
| 432 | (goto-char start)) | ||
| 433 | ;; if the whole entry fits into the window, | ||
| 434 | ;; display it centered | ||
| 435 | ((< (1+ lines) (window-height)) | ||
| 436 | (goto-char start) | ||
| 437 | (recenter (1- (- (/ (window-height) 2) (/ lines 2))))) | ||
| 438 | ;; otherwise (the entry is too large for the window), | ||
| 439 | ;; display from the start | ||
| 440 | (t | ||
| 441 | (goto-char start) | ||
| 442 | (recenter 0)))))) | ||
| 443 | |||
| 444 | (defun vc-rcs-assign-name (file name) | ||
| 445 | "Assign to FILE's latest version a given NAME." | ||
| 446 | (vc-do-command nil 0 "rcs" (vc-name file) (concat "-n" name ":"))) | ||
| 447 | |||
| 448 | (defun vc-rcs-merge (file first-version &optional second-version) | ||
| 449 | "Merge changes into current working copy of FILE. | ||
| 450 | The changes are between FIRST-VERSION and SECOND-VERSION." | ||
| 451 | (vc-do-command nil 1 "rcsmerge" (vc-name file) | ||
| 452 | "-kk" ; ignore keyword conflicts | ||
| 453 | (concat "-r" first-version) | ||
| 454 | (if second-version (concat "-r" second-version)))) | ||
| 455 | |||
| 456 | (defun vc-rcs-check-headers () | ||
| 457 | "Check if the current file has any headers in it." | ||
| 458 | (save-excursion | ||
| 459 | (goto-char (point-min)) | ||
| 460 | (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\ | ||
| 461 | \\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t))) | ||
| 462 | |||
| 463 | (defun vc-rcs-clear-headers () | ||
| 464 | "Implementation of vc-clear-headers for RCS." | ||
| 465 | (let ((case-fold-search nil)) | ||
| 466 | (goto-char (point-min)) | ||
| 467 | (while (re-search-forward | ||
| 468 | (concat "\\$\\(Author\\|Date\\|Header\\|Id\\|Locker\\|Name\\|" | ||
| 469 | "RCSfile\\|Revision\\|Source\\|State\\): [^$\n]+\\$") | ||
| 470 | nil t) | ||
| 471 | (replace-match "$\\1$")))) | ||
| 472 | |||
| 473 | (defun vc-rcs-steal-lock (file &optional rev) | ||
| 474 | "Steal the lock on the current workfile for FILE and revision REV. | ||
| 475 | Needs RCS 5.6.2 or later for -M." | ||
| 476 | (vc-do-command nil 0 "rcs" (vc-name file) "-M" | ||
| 477 | (concat "-u" rev) (concat "-l" rev))) | ||
| 478 | |||
| 479 | (defun vc-rcs-uncheck (file target) | ||
| 480 | "Undo the checkin of FILE's revision TARGET." | ||
| 481 | (vc-do-command nil 0 "rcs" (vc-name file) (concat "-o" target))) | ||
| 482 | |||
| 483 | (defun vc-rcs-revert (file) | ||
| 484 | "Revert FILE to the version it was based on." | ||
| 485 | (vc-do-command nil 0 "co" (vc-name file) "-f" | ||
| 486 | (concat "-u" (vc-workfile-version file)))) | ||
| 487 | |||
| 488 | (defun vc-rcs-rename-file (old new) | ||
| 489 | ;; Just move the master file (using vc-rcs-master-templates). | ||
| 490 | (vc-rename-master (vc-name old) new vc-rcs-master-templates)) | ||
| 491 | |||
| 492 | (defun vc-release-greater-or-equal (r1 r2) | ||
| 493 | "Compare release numbers, represented as strings. Release | ||
| 494 | components are assumed cardinal numbers, not decimal fractions \(5.10 | ||
| 495 | is a higher release than 5.9\). Omitted fields are considered lower | ||
| 496 | \(5.6.7 is earlier than 5.6.7.1\). Comparison runs till the end of | ||
| 497 | the string is found, or a non-numeric component shows up \(5.6.7 is | ||
| 498 | earlier than \"5.6.7 beta\", which is probably not what you want in | ||
| 499 | some cases\). This code is suitable for existing RCS release numbers. | ||
| 500 | CVS releases are handled reasonably, too \(1.3 < 1.4* < 1.5\)." | ||
| 501 | (let (v1 v2 i1 i2) | ||
| 502 | (catch 'done | ||
| 503 | (or (and (string-match "^\\.?\\([0-9]+\\)" r1) | ||
| 504 | (setq i1 (match-end 0)) | ||
| 505 | (setq v1 (string-to-number (match-string 1 r1))) | ||
| 506 | (or (and (string-match "^\\.?\\([0-9]+\\)" r2) | ||
| 507 | (setq i2 (match-end 0)) | ||
| 508 | (setq v2 (string-to-number (match-string 1 r2))) | ||
| 509 | (if (> v1 v2) (throw 'done t) | ||
| 510 | (if (< v1 v2) (throw 'done nil) | ||
| 511 | (throw 'done | ||
| 512 | (vc-release-greater-or-equal | ||
| 513 | (substring r1 i1) | ||
| 514 | (substring r2 i2))))))) | ||
| 515 | (throw 'done t))) | ||
| 516 | (or (and (string-match "^\\.?\\([0-9]+\\)" r2) | ||
| 517 | (throw 'done nil)) | ||
| 518 | (throw 'done t))))) | ||
| 519 | |||
| 520 | (defun vc-rcs-release-p (release) | ||
| 521 | "Return t if we have RELEASE or better" | ||
| 522 | (let ((installation (vc-rcs-system-release))) | ||
| 523 | (if (and installation | ||
| 524 | (not (eq installation 'unknown))) | ||
| 525 | (vc-release-greater-or-equal installation release)))) | ||
| 526 | |||
| 527 | (defun vc-rcs-checkin (file rev comment) | ||
| 528 | "RCS-specific version of `vc-backend-checkin'." | ||
| 529 | ;; Adaptation for RCS branch support: if this is an explicit checkin, | ||
| 530 | ;; or if the checkin creates a new branch, set the master file branch | ||
| 531 | ;; accordingly. | ||
| 532 | (let ((switches (if (stringp vc-checkin-switches) | ||
| 533 | (list vc-checkin-switches) | ||
| 534 | vc-checkin-switches))) | ||
| 535 | (let ((old-version (vc-workfile-version file)) new-version) | ||
| 536 | (apply 'vc-do-command nil 0 "ci" (vc-name file) | ||
| 537 | ;; if available, use the secure check-in option | ||
| 538 | (and (vc-rcs-release-p "5.6.4") "-j") | ||
| 539 | (concat (if vc-keep-workfiles "-u" "-r") rev) | ||
| 540 | (concat "-m" comment) | ||
| 541 | switches) | ||
| 542 | (vc-file-setprop file 'vc-workfile-version nil) | ||
| 543 | |||
| 544 | ;; determine the new workfile version | ||
| 545 | (set-buffer "*vc*") | ||
| 546 | (goto-char (point-min)) | ||
| 547 | (when (or (re-search-forward | ||
| 548 | "new revision: \\([0-9.]+\\);" nil t) | ||
| 549 | (re-search-forward | ||
| 550 | "reverting to previous revision \\([0-9.]+\\)" nil t)) | ||
| 551 | (setq new-version (match-string 1)) | ||
| 552 | (vc-file-setprop file 'vc-workfile-version new-version)) | ||
| 553 | |||
| 554 | ;; if we got to a different branch, adjust the default | ||
| 555 | ;; branch accordingly | ||
| 556 | (cond | ||
| 557 | ((and old-version new-version | ||
| 558 | (not (string= (vc-rcs-branch-part old-version) | ||
| 559 | (vc-rcs-branch-part new-version)))) | ||
| 560 | (vc-do-command nil 0 "rcs" (vc-name file) | ||
| 561 | (if (vc-rcs-trunk-p new-version) "-b" | ||
| 562 | (concat "-b" (vc-rcs-branch-part new-version)))) | ||
| 563 | ;; If this is an old RCS release, we might have | ||
| 564 | ;; to remove a remaining lock. | ||
| 565 | (if (not (vc-rcs-release-p "5.6.2")) | ||
| 566 | ;; exit status of 1 is also accepted. | ||
| 567 | ;; It means that the lock was removed before. | ||
| 568 | (vc-do-command nil 1 "rcs" (vc-name file) | ||
| 569 | (concat "-u" old-version)))))))) | ||
| 570 | |||
| 571 | (defun vc-rcs-system-release () | ||
| 572 | "Return the RCS release installed on this system, as a string. | ||
| 573 | Return symbol UNKNOWN if the release cannot be deducted. The user can | ||
| 574 | override this using variable `vc-rcs-release'. | ||
| 575 | |||
| 576 | If the user has not set variable `vc-rcs-release' and it is nil, | ||
| 577 | variable `vc-rcs-release' is set to the returned value." | ||
| 578 | (or vc-rcs-release | ||
| 579 | (setq vc-rcs-release | ||
| 580 | (or (and (zerop (vc-do-command nil nil "rcs" nil "-V")) | ||
| 581 | (with-current-buffer (get-buffer "*vc*") | ||
| 582 | (vc-parse-buffer "^RCS version \\([0-9.]+ *.*\\)" 1))) | ||
| 583 | 'unknown)))) | ||
| 584 | |||
| 585 | (defun vc-rcs-diff (file &optional oldvers newvers) | ||
| 586 | "Get a difference report using RCS between two versions of FILE." | ||
| 587 | (if (not oldvers) (setq oldvers (vc-workfile-version file))) | ||
| 588 | ;; If we know that --brief is not supported, don't try it. | ||
| 589 | (let* ((diff-switches-list (if (listp diff-switches) | ||
| 590 | diff-switches | ||
| 591 | (list diff-switches))) | ||
| 592 | (options (append (list "-q" | ||
| 593 | (concat "-r" oldvers) | ||
| 594 | (and newvers (concat "-r" newvers))) | ||
| 595 | diff-switches-list))) | ||
| 596 | (apply 'vc-do-command t 1 "rcsdiff" file options))) | ||
| 597 | |||
| 598 | (defun vc-rcs-responsible-p (file) | ||
| 599 | "Return non-nil if RCS thinks it would be responsible for registering FILE." | ||
| 600 | ;; TODO: check for all the patterns in vc-rcs-master-templates | ||
| 601 | (file-directory-p (expand-file-name "RCS" (file-name-directory file)))) | ||
| 602 | |||
| 603 | (defun vc-rcs-register (file &optional rev comment) | ||
| 604 | "Register FILE into the RCS version-control system. | ||
| 605 | REV is the optional revision number for the file. COMMENT can be used | ||
| 606 | to provide an initial description of FILE. | ||
| 607 | |||
| 608 | `vc-register-switches' and `vc-rcs-register-switches' are passed to | ||
| 609 | the RCS command (in that order). | ||
| 610 | |||
| 611 | Automatically retrieve a read-only version of the file with keywords | ||
| 612 | expanded if `vc-keep-workfiles' is non-nil, otherwise, delete the workfile." | ||
| 613 | (vc-file-clearprops file) | ||
| 614 | (let ((subdir (expand-file-name "RCS" (file-name-directory file))) | ||
| 615 | (switches (list | ||
| 616 | (if (stringp vc-register-switches) | ||
| 617 | (list vc-register-switches) | ||
| 618 | vc-register-switches) | ||
| 619 | (if (stringp vc-rcs-register-switches) | ||
| 620 | (list vc-rcs-register-switches) | ||
| 621 | vc-rcs-register-switches)))) | ||
| 622 | |||
| 623 | (and (not (file-exists-p subdir)) | ||
| 624 | (not (directory-files (file-name-directory file) | ||
| 625 | nil ".*,v$" t)) | ||
| 626 | (yes-or-no-p "Create RCS subdirectory? ") | ||
| 627 | (make-directory subdir)) | ||
| 628 | (apply 'vc-do-command nil 0 "ci" file | ||
| 629 | ;; if available, use the secure registering option | ||
| 630 | (and (vc-rcs-release-p "5.6.4") "-i") | ||
| 631 | (concat (if vc-keep-workfiles "-u" "-r") rev) | ||
| 632 | (and comment (concat "-t-" comment)) | ||
| 633 | switches) | ||
| 634 | ;; parse output to find master file name and workfile version | ||
| 635 | (with-current-buffer "*vc*" | ||
| 636 | (goto-char (point-min)) | ||
| 637 | (let ((name (if (looking-at (concat "^\\(.*\\) <-- " | ||
| 638 | (file-name-nondirectory file))) | ||
| 639 | (match-string 1)))) | ||
| 640 | (if (not name) | ||
| 641 | ;; if we couldn't find the master name, | ||
| 642 | ;; run vc-rcs-registered to get it | ||
| 643 | ;; (will be stored into the vc-name property) | ||
| 644 | (vc-rcs-registered file) | ||
| 645 | (vc-file-setprop file 'vc-name | ||
| 646 | (if (file-name-absolute-p name) | ||
| 647 | name | ||
| 648 | (expand-file-name | ||
| 649 | name | ||
| 650 | (file-name-directory file)))))) | ||
| 651 | (vc-file-setprop file 'vc-workfile-version | ||
| 652 | (if (re-search-forward | ||
| 653 | "^initial revision: \\([0-9.]+\\).*\n" | ||
| 654 | nil t) | ||
| 655 | (match-string 1)))))) | ||
| 656 | |||
| 657 | (defun vc-rcs-checkout (file &optional writable rev workfile) | ||
| 658 | "Retrieve a copy of a saved version of FILE into a workfile." | ||
| 659 | (let ((filename (or workfile file)) | ||
| 660 | (file-buffer (get-file-buffer file)) | ||
| 661 | switches) | ||
| 662 | (message "Checking out %s..." filename) | ||
| 663 | (save-excursion | ||
| 664 | ;; Change buffers to get local value of vc-checkout-switches. | ||
| 665 | (if file-buffer (set-buffer file-buffer)) | ||
| 666 | (setq switches (if (stringp vc-checkout-switches) | ||
| 667 | (list vc-checkout-switches) | ||
| 668 | vc-checkout-switches)) | ||
| 669 | ;; Save this buffer's default-directory | ||
| 670 | ;; and use save-excursion to make sure it is restored | ||
| 671 | ;; in the same buffer it was saved in. | ||
| 672 | (let ((default-directory default-directory)) | ||
| 673 | (save-excursion | ||
| 674 | ;; Adjust the default-directory so that the check-out creates | ||
| 675 | ;; the file in the right place. | ||
| 676 | (setq default-directory (file-name-directory filename)) | ||
| 677 | (if workfile ;; RCS | ||
| 678 | ;; RCS can't check out into arbitrary file names directly. | ||
| 679 | ;; Use `co -p' and make stdout point to the correct file. | ||
| 680 | (let ((vc-modes (logior (file-modes (vc-name file)) | ||
| 681 | (if writable 128 0))) | ||
| 682 | (failed t)) | ||
| 683 | (unwind-protect | ||
| 684 | (progn | ||
| 685 | (let ((coding-system-for-read 'no-conversion) | ||
| 686 | (coding-system-for-write 'no-conversion)) | ||
| 687 | (with-temp-file filename | ||
| 688 | (apply 'vc-do-command | ||
| 689 | (current-buffer) 0 "co" (vc-name file) | ||
| 690 | "-q" ;; suppress diagnostic output | ||
| 691 | (if writable "-l") | ||
| 692 | (concat "-p" rev) | ||
| 693 | switches))) | ||
| 694 | (set-file-modes filename | ||
| 695 | (logior (file-modes (vc-name file)) | ||
| 696 | (if writable 128 0))) | ||
| 697 | (setq failed nil)) | ||
| 698 | (and failed (file-exists-p filename) | ||
| 699 | (delete-file filename)))) | ||
| 700 | (let (new-version) | ||
| 701 | ;; if we should go to the head of the trunk, | ||
| 702 | ;; clear the default branch first | ||
| 703 | (and rev (string= rev "") | ||
| 704 | (vc-do-command nil 0 "rcs" (vc-name file) "-b")) | ||
| 705 | ;; now do the checkout | ||
| 706 | (apply 'vc-do-command | ||
| 707 | nil 0 "co" (vc-name file) | ||
| 708 | ;; If locking is not strict, force to overwrite | ||
| 709 | ;; the writable workfile. | ||
| 710 | (if (eq (vc-checkout-model file) 'implicit) "-f") | ||
| 711 | (if writable "-l") | ||
| 712 | (if rev (concat "-r" rev) | ||
| 713 | ;; if no explicit revision was specified, | ||
| 714 | ;; check out that of the working file | ||
| 715 | (let ((workrev (vc-workfile-version file))) | ||
| 716 | (if workrev (concat "-r" workrev) | ||
| 717 | nil))) | ||
| 718 | switches) | ||
| 719 | ;; determine the new workfile version | ||
| 720 | (with-current-buffer "*vc*" | ||
| 721 | (setq new-version | ||
| 722 | (vc-parse-buffer "^revision \\([0-9.]+\\).*\n" 1))) | ||
| 723 | (vc-file-setprop file 'vc-workfile-version new-version) | ||
| 724 | ;; if necessary, adjust the default branch | ||
| 725 | (and rev (not (string= rev "")) | ||
| 726 | (vc-do-command | ||
| 727 | nil 0 "rcs" (vc-name file) | ||
| 728 | (concat "-b" | ||
| 729 | (if (vc-rcs-latest-on-branch-p file new-version) | ||
| 730 | (if (vc-rcs-trunk-p new-version) nil | ||
| 731 | (vc-rcs-branch-part new-version)) | ||
| 732 | new-version))))))) | ||
| 733 | (message "Checking out %s...done" filename))))) | ||
| 734 | |||
| 735 | (provide 'vc-rcs) | ||
| 736 | |||
| 737 | ;;; vc-rcs.el ends here | ||