aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarl Heuer1995-04-26 21:42:20 +0000
committerKarl Heuer1995-04-26 21:42:20 +0000
commite1c0c2d1bf45a7fd909b7865419a3013ec79b141 (patch)
treef9a93bf9e7b6bf7d2167cfb418875744fc81a784
parenta01c76d72b36c295a2b38db269aaf6ea838c84b6 (diff)
downloademacs-e1c0c2d1bf45a7fd909b7865419a3013ec79b141.tar.gz
emacs-e1c0c2d1bf45a7fd909b7865419a3013ec79b141.zip
(vc-default-backend, vc-path, vc-consult-headers):
(vc-mistrust-permissions, vc-keep-workfiles): Customization variables, moved here from vc.el. (vc-trunk-p, vc-minor-revision, vc-branch-part): Moved to vc.el. (vc-backend): Renamed from vc-backend-deduce. Callers changed. (vc-match-substring, vc-lock-file, vc-parse-buffer, vc-master-info): (vc-log-info, vc-consult-rcs-headers, vc-fetch-properties): (vc-backend-subdirectory-name, vc-locking-user, vc-true-locking-user): (vc-latest-version, vc-your-latest-version, vc-branch-version): (vc-workfile-version): Functions moved here from vc.el. (vc-log-info): Log program is no longer called through vc-do-command, to avoid including the lengthy vc-do-command here. It is done directly through call-process now. Removed obsolete parameter LAST. (vc-status): Replaced by the much simpler version that gets the information from the file properties. Removed the obsolete parameter vc-type. (vc-parse-buffer): changed format of PATTERNS. Each pattern is now a list of 2 to 3 elements, the first being the pattern, the remaining ones the numbers of subexpressions to refer to. (vc-cvs-status): New per-file property, only used in the CVS case. (vc-cvs-status): New function. (vc-log-info): Adapted to new version of vc-parse-buffer (vc-fetch-properties): Adapted to new version of vc-parse-buffer. Better search regexp for CVS latest version. (vc-log-info): Search for branch version only in the RCS case, since this doesn't make sense for SCCS or CVS. (vc-fetch-properties): CVS case: set vc-cvs-status. (vc-locking-user): CVS case: use vc-cvs-status to determine if the file is up-to-date, thus avoiding an expensive call to vc-workfile-unchanged-p. (vc-mode-line): Re-activated the code that makes the buffer read-only if the work file is unchanged. But the status of the work file is now determined by looking at the already-computed mode string.
-rw-r--r--lisp/vc-hooks.el702
1 files changed, 526 insertions, 176 deletions
diff --git a/lisp/vc-hooks.el b/lisp/vc-hooks.el
index c3ca8c57c8b..83e91697ca9 100644
--- a/lisp/vc-hooks.el
+++ b/lisp/vc-hooks.el
@@ -32,6 +32,18 @@
32 32
33;;; Code: 33;;; Code:
34 34
35;; Customization Variables (the rest is in vc.el)
36
37(defvar vc-default-back-end nil
38 "*Back-end actually used by this interface; may be SCCS or RCS.
39The value is only computed when needed to avoid an expensive search.")
40
41(defvar vc-path
42 (if (file-directory-p "/usr/sccs")
43 '("/usr/sccs")
44 nil)
45 "*List of extra directories to search for version control commands.")
46
35(defvar vc-master-templates 47(defvar vc-master-templates
36 '(("%sRCS/%s,v" . RCS) ("%s%s,v" . RCS) ("%sRCS/%s" . RCS) 48 '(("%sRCS/%s,v" . RCS) ("%s%s,v" . RCS) ("%sRCS/%s" . RCS)
37 ("%sSCCS/s.%s" . SCCS) ("%ss.%s". SCCS) 49 ("%sSCCS/s.%s" . SCCS) ("%ss.%s". SCCS)
@@ -48,6 +60,17 @@ If nil (the default), files covered by version control don't get backups.")
48 "*If non-nil, display revision number and lock status in modeline. 60 "*If non-nil, display revision number and lock status in modeline.
49Otherwise, not displayed.") 61Otherwise, not displayed.")
50 62
63(defvar vc-consult-headers t
64 "*Identify work files by searching for version headers.")
65
66(defvar vc-mistrust-permissions nil
67 "*Don't assume that permissions and ownership track version-control status.")
68
69(defvar vc-keep-workfiles t
70 "*If non-nil, don't delete working files after registering changes.
71If the back-end is CVS, workfiles are always kept, regardless of the
72value of this flag.")
73
51;; Tell Emacs about this new kind of minor mode 74;; Tell Emacs about this new kind of minor mode
52(if (not (assoc 'vc-mode minor-mode-alist)) 75(if (not (assoc 'vc-mode minor-mode-alist))
53 (setq minor-mode-alist (cons '(vc-mode vc-mode) 76 (setq minor-mode-alist (cons '(vc-mode vc-mode)
@@ -56,6 +79,24 @@ Otherwise, not displayed.")
56(make-variable-buffer-local 'vc-mode) 79(make-variable-buffer-local 'vc-mode)
57(put 'vc-mode 'permanent-local t) 80(put 'vc-mode 'permanent-local t)
58 81
82
83;; branch identification
84
85(defun vc-occurrences (object sequence)
86 ;; return the number of occurences of OBJECT in SEQUENCE
87 ;; (is it really true that Emacs Lisp doesn't provide such a function?)
88 (let ((len (length sequence)) (index 0) (occ 0))
89 (while (< index len)
90 (if (eq object (elt sequence index))
91 (setq occ (1+ occ)))
92 (setq index (1+ index)))
93 occ))
94
95(defun vc-branch-p (rev)
96 ;; return t if REV is the branch part of a revision,
97 ;; i.e. a revision without a minor number
98 (eq 0 (% (vc-occurrences ?. rev) 2)))
99
59;; We need a notion of per-file properties because the version 100;; We need a notion of per-file properties because the version
60;; control state of a file is expensive to derive --- we compute 101;; control state of a file is expensive to derive --- we compute
61;; them when the file is initially found, keep them up to date 102;; them when the file is initially found, keep them up to date
@@ -79,35 +120,456 @@ Otherwise, not displayed.")
79 ;; get per-file property 120 ;; get per-file property
80 (get (intern file vc-file-prop-obarray) property)) 121 (get (intern file vc-file-prop-obarray) property))
81 122
82;;; functions that operate on RCS revision numbers 123(defun vc-file-clearprops (file)
124 ;; clear all properties of a given file
125 (setplist (intern file vc-file-prop-obarray) nil))
83 126
84(defun vc-occurrences (object sequence) 127;; basic properties
85 ;; return the number of occurences of OBJECT in SEQUENCE
86 ;; (is it really true that Emacs Lisp doesn't provide such a function?)
87 (let ((len (length sequence)) (index 0) (occ 0))
88 (while (< index len)
89 (if (eq object (elt sequence index))
90 (setq occ (1+ occ)))
91 (setq index (1+ index)))
92 occ))
93 128
94(defun vc-trunk-p (rev) 129(defun vc-name (file)
95 ;; return t if REV is a revision on the trunk 130 "Return the master name of a file, nil if it is not registered."
96 (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev)))) 131 (or (vc-file-getprop file 'vc-name)
132 (let ((name-and-type (vc-registered file)))
133 (if name-and-type
134 (progn
135 (vc-file-setprop file 'vc-backend (cdr name-and-type))
136 (vc-file-setprop file 'vc-name (car name-and-type)))))))
97 137
98(defun vc-branch-p (rev) 138(defun vc-backend (file)
99 ;; return t if REV is the branch part of a revision, 139 "Return the version-control type of a file, nil if it is not registered."
100 ;; i.e. a revision without a minor number 140 (and file
101 (eq 0 (% (vc-occurrences ?. rev) 2))) 141 (or (vc-file-getprop file 'vc-backend)
142 (let ((name-and-type (vc-registered file)))
143 (if name-and-type
144 (progn
145 (vc-file-setprop file 'vc-name (car name-and-type))
146 (vc-file-setprop file 'vc-backend (cdr name-and-type))))))))
102 147
103(defun vc-minor-revision (rev) 148;; Functions for querying the master and lock files.
104 ;; return the minor revision number of REV, 149
105 ;; i.e. the number after the last dot. 150(defun vc-match-substring (bn)
106 (substring rev (1+ (string-match "\\.[0-9]+\\'" rev)))) 151 (buffer-substring (match-beginning bn) (match-end bn)))
152
153(defun vc-lock-file (file)
154 ;; Generate lock file name corresponding to FILE
155 (let ((master (vc-name file)))
156 (and
157 master
158 (string-match "\\(.*/\\)s\\.\\(.*\\)" master)
159 (concat
160 (substring master (match-beginning 1) (match-end 1))
161 "p."
162 (substring master (match-beginning 2) (match-end 2))))))
163
164(defun vc-parse-buffer (patterns &optional file properties)
165 ;; Use PATTERNS to parse information out of the current buffer.
166 ;; Each element of PATTERNS is a list of 2 to 3 elements. The first element
167 ;; is the pattern to be matched, and the second (an integer) is the
168 ;; number of the subexpression that should be returned. If there's
169 ;; a third element (also the number of a subexpression), that
170 ;; subexpression is assumed to be a date field and we want the most
171 ;; recent entry matching the template.
172 ;; If FILE and PROPERTIES are given, the latter must be a list of
173 ;; properties of the same length as PATTERNS; each property is assigned
174 ;; the corresponding value.
175 (mapcar (function (lambda (p)
176 (goto-char (point-min))
177 (cond
178 ((eq (length p) 2) ;; search for first entry
179 (let ((value nil))
180 (if (re-search-forward (car p) nil t)
181 (setq value (vc-match-substring (elt p 1))))
182 (if file
183 (progn (vc-file-setprop file (car properties) value)
184 (setq properties (cdr properties))))
185 value))
186 ((eq (length p) 3) ;; search for latest entry
187 (let ((latest-date "") (latest-val))
188 (while (re-search-forward (car p) nil t)
189 (let ((date (vc-match-substring (elt p 2))))
190 (if (string< latest-date date)
191 (progn
192 (setq latest-date date)
193 (setq latest-val
194 (vc-match-substring (elt p 1)))))))
195 (if file
196 (progn (vc-file-setprop file (car properties) latest-val)
197 (setq properties (cdr properties))))
198 latest-val)))))
199 patterns)
200 )
201
202(defun vc-master-info (file fields &optional rfile properties)
203 ;; Search for information in a master file.
204 (if (and file (file-exists-p file))
205 (save-excursion
206 (let ((buf))
207 (setq buf (create-file-buffer file))
208 (set-buffer buf))
209 (erase-buffer)
210 (insert-file-contents file)
211 (set-buffer-modified-p nil)
212 (auto-save-mode nil)
213 (prog1
214 (vc-parse-buffer fields rfile properties)
215 (kill-buffer (current-buffer)))
216 )
217 (if rfile
218 (mapcar
219 (function (lambda (p) (vc-file-setprop rfile p nil)))
220 properties))
221 )
222 )
223
224(defun vc-log-info (command file flags patterns &optional properties)
225 ;; Search for information in log program output.
226 ;; If there is a string `\X' in any of the PATTERNS, replace
227 ;; it with a regexp to search for a branch revision.
228 (if (and file (file-exists-p file))
229 (save-excursion
230 ;; Run the command (not using vc-do-command, as that is
231 ;; only available within vc.el)
232 ;; Don't switch to the *vc* buffer before running the command
233 ;; because that would change its default-directory.
234 (save-excursion (set-buffer (get-buffer-create "*vc*"))
235 (erase-buffer))
236 (let ((exec-path (append vc-path exec-path))
237 ;; Add vc-path to PATH for the execution of this command.
238 (process-environment
239 (cons (concat "PATH=" (getenv "PATH")
240 ":" (mapconcat 'identity vc-path ":"))
241 process-environment)))
242 (apply 'call-process command nil "*vc*" nil
243 (append flags (list (file-name-nondirectory file)))))
244 (set-buffer (get-buffer "*vc*"))
245 (set-buffer-modified-p nil)
246 ;; in the RCS case, insert branch version into
247 ;; any patterns that contain \X
248 (if (eq (vc-backend file) 'RCS)
249 (let ((branch
250 (car (vc-parse-buffer
251 '(("^branch:[ \t]+\\([0-9.]+\\)$" 1))))))
252 (setq patterns
253 (mapcar
254 (function
255 (lambda (p)
256 (if (string-match "\\\\X" (car p))
257 (if branch
258 (cond ((vc-branch-p branch)
259 (cons
260 (concat
261 (substring (car p) 0 (match-beginning 0))
262 (regexp-quote branch)
263 "\\.[0-9]+"
264 (substring (car p) (match-end 0)))
265 (cdr p)))
266 (t
267 (cons
268 (concat
269 (substring (car p) 0 (match-beginning 0))
270 (regexp-quote branch)
271 (substring (car p) (match-end 0)))
272 (cdr p))))
273 ;; if there is no current branch,
274 ;; return a completely different regexp,
275 ;; which searches for the *head*
276 '("^head:[ \t]+\\([0-9.]+\\)$" 1))
277 p)))
278 patterns))))
279 (prog1
280 (vc-parse-buffer patterns file properties)
281 (kill-buffer (current-buffer))
282 )
283 )
284 (if file
285 (mapcar
286 (function (lambda (p) (vc-file-setprop file p nil)))
287 properties))
288 )
289 )
107 290
108(defun vc-branch-part (rev) 291;;; Functions that determine property values, by examining the
109 ;; return the branch part of a revision number REV 292;;; working file, the master file, or log program output
110 (substring rev 0 (string-match "\\.[0-9]+\\'" rev))) 293
294(defun vc-consult-rcs-headers (file)
295 ;; Search for RCS headers in FILE, and set properties
296 ;; accordingly. This function can be disabled by setting
297 ;; vc-consult-headers to nil.
298 ;; Returns: nil if no headers were found
299 ;; (or if the feature is disabled,
300 ;; or if there is currently no buffer
301 ;; visiting FILE)
302 ;; 'rev if a workfile revision was found
303 ;; 'rev-and-lock if revision and lock info was found
304 (cond
305 ((or (not vc-consult-headers)
306 (not (get-file-buffer file)) nil))
307 ((save-excursion
308 (set-buffer (get-file-buffer file))
309 (goto-char (point-min))
310 (cond
311 ;; search for $Id or $Header
312 ;; -------------------------
313 ((re-search-forward "\\$\\(Id\\|Header\\): [^ ]+ \\([0-9.]+\\) "
314 nil t)
315 ;; if found, store the revision number ...
316 (let ((rev (buffer-substring (match-beginning 2)
317 (match-end 2))))
318 ;; ... and check for the locking state
319 (if (re-search-forward
320 (concat "\\=[0-9]+/[0-9]+/[0-9]+ " ; date
321 "[0-9]+:[0-9]+:[0-9]+ " ; time
322 "[^ ]+ [^ ]+ ") ; author & state
323 nil t)
324 (cond
325 ;; unlocked revision
326 ((looking-at "\\$")
327 (vc-file-setprop file 'vc-workfile-version rev)
328 (vc-file-setprop file 'vc-locking-user nil)
329 (vc-file-setprop file 'vc-locked-version nil)
330 'rev-and-lock)
331 ;; revision is locked by some user
332 ((looking-at "\\([^ ]+\\) \\$")
333 (vc-file-setprop file 'vc-workfile-version rev)
334 (vc-file-setprop file 'vc-locking-user
335 (buffer-substring (match-beginning 1)
336 (match-end 1)))
337 (vc-file-setprop file 'vc-locked-version rev)
338 'rev-and-lock)
339 ;; everything else: false
340 (nil))
341 ;; unexpected information in
342 ;; keyword string --> quit
343 nil)))
344 ;; search for $Revision
345 ;; --------------------
346 ((re-search-forward (concat "\\$"
347 "Revision: \\([0-9.]+\\) \\$")
348 nil t)
349 ;; if found, store the revision number ...
350 (let ((rev (buffer-substring (match-beginning 1)
351 (match-end 1))))
352 ;; and see if there's any lock information
353 (goto-char (point-min))
354 (if (re-search-forward (concat "\\$" "Locker:") nil t)
355 (cond ((looking-at " \\([^ ]+\\) \\$")
356 (vc-file-setprop file 'vc-workfile-version rev)
357 (vc-file-setprop file 'vc-locking-user
358 (buffer-substring (match-beginning 1)
359 (match-end 1)))
360 (vc-file-setprop file 'vc-locked-version rev)
361 'rev-and-lock)
362 ((looking-at " *\\$")
363 (vc-file-setprop file 'vc-workfile-version rev)
364 (vc-file-setprop file 'vc-locking-user nil)
365 (vc-file-setprop file 'vc-locked-version nil)
366 'rev-and-lock)
367 (t
368 (vc-file-setprop file 'vc-workfile-version rev)
369 'rev-and-lock))
370 (vc-file-setprop file 'vc-workfile-version rev)
371 'rev)))
372 ;; else: nothing found
373 ;; -------------------
374 (t nil))))))
375
376(defun vc-fetch-properties (file)
377 ;; Re-fetch some properties associated with the given file.
378 (cond
379 ((eq (vc-backend file) 'SCCS)
380 (progn
381 (vc-master-info (vc-lock-file file)
382 (list
383 '("^[^ ]+ [^ ]+ \\([^ ]+\\)" 1)
384 '("^\\([^ ]+\\)" 1))
385 file
386 '(vc-locking-user vc-locked-version))
387 (vc-master-info (vc-name file)
388 (list
389 '("^\001d D \\([^ ]+\\)" 1)
390 (list (concat "^\001d D \\([^ ]+\\) .* "
391 (regexp-quote (user-login-name)) " ")
392 1)
393 )
394 file
395 '(vc-latest-version vc-your-latest-version))
396 ))
397 ((eq (vc-backend file) 'RCS)
398 (vc-log-info "rlog" file nil
399 (list
400 '("^locks: strict\n\t\\([^:]+\\)" 1)
401 '("^locks: strict\n\t[^:]+: \\(.+\\)" 1)
402 '("^revision[\t ]+\\([0-9.]+\\)\\(\n\\|[ \t].*\n\\)date: \\([ /0-9:]+\\);" 1 3)
403 (list
404 (concat
405 "^revision[\t ]+\\([0-9.]+\\)\\(\n\\|[ \t].*\n\\)date: \\([ /0-9:]+\\); *author: "
406 (regexp-quote (user-login-name))
407 ";") 1 3)
408 ;; special regexp to search for branch revision:
409 ;; \X will be replaced by vc-log-info (see there)
410 '("^revision[\t ]+\\(\\X\\)\\(\n\\|[ \t].*\n\\)date: \\([ /0-9:]+\\);" 1 3))
411
412 '(vc-locking-user
413 vc-locked-version
414 vc-latest-version
415 vc-your-latest-version
416 vc-branch-version)))
417 ((eq (vc-backend file) 'CVS)
418 (vc-log-info "cvs" file '("status")
419 ;; CVS 1.3 says "RCS Version:", other releases "RCS Revision:",
420 ;; and CVS 1.4a1 says "Repository revision:".
421 '(("\\(RCS Version\\|RCS Revision\\|Repository revision\\):[\t ]+\\([0-9.]+\\)" 2)
422 ("^File: [^ \t]+[ \t]+Status: \\(.*\\)" 1))
423 '(vc-latest-version vc-cvs-status))
424 ;; Translate those status values that are needed into symbols.
425 ;; Any other value is converted to nil.
426 (let ((status (vc-file-getprop file 'vc-cvs-status)))
427 (cond ((string-match "Up-to-date" status)
428 (vc-file-setprop file 'vc-cvs-status 'up-to-date)
429 (vc-file-setprop file 'vc-checkout-time
430 (nth 5 (file-attributes file))))
431 ((string-match "Locally Modified" status)
432 (vc-file-setprop file 'vc-cvs-status 'locally-modified))
433 ((string-match "Needs Merge" status)
434 (vc-file-setprop file 'vc-cvs-status 'needs-merge))
435 (t (vc-file-setprop file 'vc-cvs-status nil))))
436 )))
437
438(defun vc-backend-subdirectory-name (&optional file)
439 ;; Where the master and lock files for the current directory are kept
440 (symbol-name
441 (or
442 (and file (vc-backend file))
443 vc-default-back-end
444 (setq vc-default-back-end (if (vc-find-binary "rcs") 'RCS 'SCCS)))))
445
446
447;;; Access functions to file properties
448;;; (Properties should be _set_ using vc-file-setprop, but
449;;; _retrieved_ only through these functions, which decide
450;;; if the property is already known or not. A property should
451;;; only be retrieved by vc-file-getprop if there is no
452;;; access function.)
453
454;; functions vc-name and vc-backend come earlier above,
455;; because they are needed by vc-log-info etc.
456
457(defun vc-cvs-status (file)
458 ;; Return the cvs status of FILE
459 ;; (Status field in output of "cvs status")
460 (cond ((vc-file-getprop file 'vc-cvs-status))
461 (t (vc-fetch-properties file)
462 (vc-file-getprop file 'vc-cvs-status))))
463
464(defun vc-locking-user (file)
465 "Return the name of the person currently holding a lock on FILE.
466Return nil if there is no such person.
467Under CVS, a file is considered locked if it has been modified since it
468was checked out. Under CVS, this will sometimes return the uid of
469the owner of the file (as a number) instead of a string."
470 ;; The property is cached. If it is non-nil, it is simply returned.
471 ;; The other routines clear it when the locking state changes.
472 (setq file (expand-file-name file));; ??? Work around bug in 19.0.4
473 (cond
474 ((vc-file-getprop file 'vc-locking-user))
475 ((eq (vc-backend file) 'CVS)
476 (if (eq (vc-cvs-status file) 'up-to-date)
477 nil
478 ;; The expression below should return the username of the owner
479 ;; of the file. It doesn't. It returns the username if it is
480 ;; you, or otherwise the UID of the owner of the file. The
481 ;; return value from this function is only used by
482 ;; vc-dired-reformat-line, and it does the proper thing if a UID
483 ;; is returned.
484 ;;
485 ;; The *proper* way to fix this would be to implement a built-in
486 ;; function in Emacs, say, (username UID), that returns the
487 ;; username of a given UID.
488 ;;
489 ;; The result of this hack is that vc-directory will print the
490 ;; name of the owner of the file for any files that are
491 ;; modified.
492 (let ((uid (nth 2 (file-attributes file))))
493 (if (= uid (user-uid))
494 (vc-file-setprop file 'vc-locking-user (user-login-name))
495 (vc-file-setprop file 'vc-locking-user uid)))))
496 (t
497 (if (and (eq (vc-backend file) 'RCS)
498 (eq (vc-consult-rcs-headers file) 'rev-and-lock))
499 (vc-file-getprop file 'vc-locking-user)
500 (if (or (not vc-keep-workfiles)
501 (eq vc-mistrust-permissions 't)
502 (and vc-mistrust-permissions
503 (funcall vc-mistrust-permissions
504 (vc-backend-subdirectory-name file))))
505 (vc-file-setprop file 'vc-locking-user (vc-true-locking-user file))
506 ;; This implementation assumes that any file which is under version
507 ;; control and has -rw-r--r-- is locked by its owner. This is true
508 ;; for both RCS and SCCS, which keep unlocked files at -r--r--r--.
509 ;; We have to be careful not to exclude files with execute bits on;
510 ;; scripts can be under version control too. Also, we must ignore
511 ;; the group-read and other-read bits, since paranoid users turn them off.
512 ;; This hack wins because calls to the very expensive vc-fetch-properties
513 ;; function only have to be made if (a) the file is locked by someone
514 ;; other than the current user, or (b) some untoward manipulation
515 ;; behind vc's back has changed the owner or the `group' or `other'
516 ;; write bits.
517 (let ((attributes (file-attributes file)))
518 (cond ((string-match ".r-..-..-." (nth 8 attributes))
519 nil)
520 ((and (= (nth 2 attributes) (user-uid))
521 (string-match ".rw..-..-." (nth 8 attributes)))
522 (vc-file-setprop file 'vc-locking-user (user-login-name)))
523 (t
524 (vc-file-setprop file 'vc-locking-user
525 (vc-true-locking-user file))))))))))
526
527(defun vc-true-locking-user (file)
528 ;; The slow but reliable version
529 (vc-fetch-properties file)
530 (vc-file-getprop file 'vc-locking-user))
531
532(defun vc-latest-version (file)
533 ;; Return version level of the latest version of FILE
534 (vc-fetch-properties file)
535 (vc-file-getprop file 'vc-latest-version))
536
537(defun vc-your-latest-version (file)
538 ;; Return version level of the latest version of FILE checked in by you
539 (vc-fetch-properties file)
540 (vc-file-getprop file 'vc-your-latest-version))
541
542(defun vc-branch-version (file)
543 ;; Return version level of the highest revision on the default branch
544 ;; If there is no default branch, return the highest version number
545 ;; on the trunk.
546 ;; This property is defined for RCS only.
547 (vc-fetch-properties file)
548 (vc-file-getprop file 'vc-branch-version))
549
550(defun vc-workfile-version (file)
551 ;; Return version level of the current workfile FILE
552 ;; This is attempted by first looking at the RCS keywords.
553 ;; If there are no keywords in the working file,
554 ;; vc-branch-version is taken.
555 ;; Note that this property is cached, that is, it is only
556 ;; looked up if it is nil.
557 ;; For SCCS, this property is equivalent to vc-latest-version.
558 (cond ((vc-file-getprop file 'vc-workfile-version))
559 ((eq (vc-backend file) 'SCCS) (vc-latest-version file))
560 ((eq (vc-backend file) 'RCS)
561 (if (vc-consult-rcs-headers file)
562 (vc-file-getprop file 'vc-workfile-version)
563 (let ((rev (cond ((vc-branch-version file))
564 ((vc-latest-version file)))))
565 (vc-file-setprop file 'vc-workfile-version rev)
566 rev)))
567 ((eq (vc-backend file) 'CVS)
568 (if (vc-consult-rcs-headers file) ;; CVS
569 (vc-file-getprop file 'vc-workfile-version)
570 (vc-find-cvs-master (file-name-directory file)
571 (file-name-nondirectory file))
572 (vc-file-getprop file 'vc-workfile-version)))))
111 573
112;;; actual version-control code starts here 574;;; actual version-control code starts here
113 575
@@ -187,29 +649,10 @@ Otherwise, not displayed.")
187 nil))) 649 nil)))
188 (mapcar (function kill-buffer) bufs))))) 650 (mapcar (function kill-buffer) bufs)))))
189 651
190(defun vc-name (file)
191 "Return the master name of a file, nil if it is not registered."
192 (or (vc-file-getprop file 'vc-name)
193 (let ((name-and-type (vc-registered file)))
194 (if name-and-type
195 (progn
196 (vc-file-setprop file 'vc-backend (cdr name-and-type))
197 (vc-file-setprop file 'vc-name (car name-and-type)))))))
198
199(defun vc-backend-deduce (file)
200 "Return the version-control type of a file, nil if it is not registered."
201 (and file
202 (or (vc-file-getprop file 'vc-backend)
203 (let ((name-and-type (vc-registered file)))
204 (if name-and-type
205 (progn
206 (vc-file-setprop file 'vc-name (car name-and-type))
207 (vc-file-setprop file 'vc-backend (cdr name-and-type))))))))
208
209(defun vc-buffer-backend () 652(defun vc-buffer-backend ()
210 "Return the version-control type of the visited file, or nil if none." 653 "Return the version-control type of the visited file, or nil if none."
211 (if (eq vc-buffer-backend t) 654 (if (eq vc-buffer-backend t)
212 (setq vc-buffer-backend (vc-backend-deduce (buffer-file-name))) 655 (setq vc-buffer-backend (vc-backend (buffer-file-name)))
213 vc-buffer-backend)) 656 vc-buffer-backend))
214 657
215(defun vc-toggle-read-only (&optional verbose) 658(defun vc-toggle-read-only (&optional verbose)
@@ -218,7 +661,7 @@ If the buffer is visiting a file registered with version control,
218then check the file in or out. Otherwise, just change the read-only flag 661then check the file in or out. Otherwise, just change the read-only flag
219of the buffer. With prefix argument, ask for version number." 662of the buffer. With prefix argument, ask for version number."
220 (interactive "P") 663 (interactive "P")
221 (if (vc-backend-deduce (buffer-file-name)) 664 (if (vc-backend (buffer-file-name))
222 (vc-next-action verbose) 665 (vc-next-action verbose)
223 (toggle-read-only))) 666 (toggle-read-only)))
224(define-key global-map "\C-x\C-q" 'vc-toggle-read-only) 667(define-key global-map "\C-x\C-q" 'vc-toggle-read-only)
@@ -229,14 +672,19 @@ The value is set in the current buffer, which should be the buffer
229visiting FILE. Second optional arg LABEL is put in place of version 672visiting FILE. Second optional arg LABEL is put in place of version
230control system name." 673control system name."
231 (interactive (list buffer-file-name nil)) 674 (interactive (list buffer-file-name nil))
232 (let ((vc-type (vc-backend-deduce file))) 675 (let ((vc-type (vc-backend file))
676 (vc-status-string (and vc-display-status (vc-status file))))
233 (setq vc-mode 677 (setq vc-mode
234 (concat " " (or label (symbol-name vc-type)) 678 (concat " " (or label (symbol-name vc-type)) vc-status-string))
235 (if vc-display-status (vc-status file vc-type)))) 679 ;; Make the buffer read-only if the file is not locked
236;;; ;; Make the buffer read-only if the file is not locked 680 ;; (or unchanged, in the CVS case).
237;;; ;; (or unchanged, in the CVS case). 681 ;; Determine this by looking at the mode string,
238;;; (if (not (vc-locking-user file)) 682 ;; so that no further external status query is necessary
239;;; (setq buffer-read-only t)) 683 (if vc-status-string
684 (if (eq (elt vc-status-string 0) ?-)
685 (setq buffer-read-only t))
686 (if (not (vc-locking-user file))
687 (setq buffer-read-only t)))
240 ;; Even root shouldn't modify a registered file without 688 ;; Even root shouldn't modify a registered file without
241 ;; locking it first. 689 ;; locking it first.
242 (and vc-type 690 (and vc-type
@@ -247,7 +695,7 @@ control system name."
247 (setq buffer-read-only t)) 695 (setq buffer-read-only t))
248 (and (null vc-type) 696 (and (null vc-type)
249 (file-symlink-p file) 697 (file-symlink-p file)
250 (let ((link-type (vc-backend-deduce (file-symlink-p file)))) 698 (let ((link-type (vc-backend (file-symlink-p file))))
251 (if link-type 699 (if link-type
252 (message 700 (message
253 "Warning: symbolic link to %s-controlled source file" 701 "Warning: symbolic link to %s-controlled source file"
@@ -256,130 +704,32 @@ control system name."
256 ;;(set-buffer-modified-p (buffer-modified-p)) ;;use this if Emacs 18 704 ;;(set-buffer-modified-p (buffer-modified-p)) ;;use this if Emacs 18
257 vc-type)) 705 vc-type))
258 706
259(defun vc-status (file vc-type) 707(defun vc-status (file)
260 ;; Return string for placement in modeline by `vc-mode-line'. 708 ;; Return string for placement in modeline by `vc-mode-line'.
261 ;; If FILE is not registered, return nil. 709 ;; Format:
262 ;; If FILE is registered but not locked, return " REV" if there is a head
263 ;; revision and " @@" otherwise.
264 ;; If FILE is locked then return all locks in a string of the
265 ;; form " LOCKER1:REV1 LOCKER2:REV2 ...", where "LOCKERi:" is empty if you
266 ;; are the locker, and otherwise is the name of the locker followed by ":".
267
268 ;; Algorithm:
269
270 ;; Check for master file corresponding to FILE being visited.
271 ;;
272 ;; RCS: Insert the first few characters of the master file into a
273 ;; work buffer. Search work buffer for "locks...;" phrase; if not
274 ;; found, then keep inserting more characters until the phrase is
275 ;; found. Extract the locks, and remove control characters
276 ;; separating them, like newlines; the string " user1:revision1
277 ;; user2:revision2 ..." is returned.
278 ;; 710 ;;
279 ;; SCCS: Check if the p-file exists. If it does, read it and 711 ;; "-REV" if the revision is not locked
280 ;; extract the locks, giving them the right format. Else use prs to 712 ;; ":REV" if the revision is locked by the user
281 ;; find the revision number. 713 ;; ":LOCKER:REV" if the revision is locked by somebody else
714 ;; " @@" for a CVS file that is added, but not yet committed
282 ;; 715 ;;
283 ;; CVS: vc-find-cvs-master has already stored the current revision 716 ;; In the CVS case, a "locked" working file is a
284 ;; number. Fetch it from the file property. 717 ;; working file that is modified with respect to the master.
285 718 ;; The file is "locked" from the moment when the user makes
286 ;; Limitations: 719 ;; the buffer writable.
287 720 ;;
288 ;; The output doesn't show which version you are actually looking at. 721 ;; This function assumes that the file is registered.
289 ;; The modeline can get quite cluttered when there are multiple locks. 722
290 ;; The head revision is probably not what you want if you've used `rcs -b'. 723 (let ((locker (vc-locking-user file))
291 724 (rev (vc-workfile-version file)))
292 (let ((master (vc-name file)) 725 (cond ((string= "0" rev)
293 found 726 " @@")
294 status) 727 ((not locker)
295 728 (concat "-" rev))
296 ;; If master file exists, then parse its contents, otherwise we 729 ((string= locker (user-login-name))
297 ;; return the nil value of this if form. 730 (concat ":" rev))
298 (if (and master vc-type) 731 (t
299 (save-excursion 732 (concat ":" locker ":" rev)))))
300
301 ;; Create work buffer.
302 (set-buffer (get-buffer-create " *vc-status*"))
303 (setq buffer-read-only nil
304 default-directory (file-name-directory master))
305 (erase-buffer)
306
307 ;; Set the `status' var to the return value.
308 (cond
309
310 ;; RCS code.
311 ((eq vc-type 'RCS)
312 ;; Check if we have enough of the header.
313 ;; If not, then keep including more.
314 (while
315 (not (or found
316 (let ((s (buffer-size)))
317 (goto-char (1+ s))
318 (zerop (car (cdr (insert-file-contents
319 master nil s (+ s 8192))))))))
320 (beginning-of-line)
321 (setq found (re-search-forward "^locks\\([^;]*\\);" nil t)))
322
323 (if found
324 ;; Clean control characters and self-locks from text.
325 (let* ((lock-pattern
326 (concat "[ \b\t\n\v\f\r]+\\("
327 (regexp-quote (user-login-name))
328 ":\\)?"))
329 (locks
330 (save-restriction
331 (narrow-to-region (match-beginning 1) (match-end 1))
332 (goto-char (point-min))
333 (while (re-search-forward lock-pattern nil t)
334 (replace-match (if (eobp) "" ":") t t))
335 (buffer-string))))
336 (setq status
337 (if (not (string-equal locks ""))
338 locks
339 (goto-char (point-min))
340 (if (looking-at "head[ \b\t\n\v\f\r]+\\([.0-9]+\\)")
341 (concat "-"
342 (buffer-substring (match-beginning 1)
343 (match-end 1)))
344 " @@"))))))
345
346 ;; SCCS code.
347 ((eq vc-type 'SCCS)
348 ;; Build the name of the p-file and put it in the work buffer.
349 (insert master)
350 (search-backward "/s.")
351 (delete-char 2)
352 (insert "/p")
353 (if (not (file-exists-p (buffer-string)))
354 ;; No lock.
355 (let ((exec-path (if vc-path (append exec-path vc-path)
356 exec-path)))
357 (erase-buffer)
358 (insert "-")
359 (if (zerop (call-process "prs" nil t nil "-d:I:" master))
360 (setq status (buffer-substring 1 (1- (point-max))))))
361 ;; Locks exist.
362 (insert-file-contents (buffer-string) nil nil nil t)
363 (while (looking-at "[^ ]+ \\([^ ]+\\) \\([^ ]+\\).*\n")
364 (replace-match " \\2:\\1"))
365 (setq status (buffer-string))
366 (aset status 0 ?:)))
367 ;; CVS code.
368 ((eq vc-type 'CVS)
369 (let ((version (vc-file-getprop
370 file 'vc-your-latest-version)))
371 (setq status (concat ":" (if (string= "0" version)
372 " @@" ;added, not yet committed.
373 version))))))
374
375 ;; Clean work buffer.
376 (erase-buffer)
377 (set-buffer-modified-p nil)
378 status))))
379
380(defun vc-file-clearprops (file)
381 ;; clear all properties of a given file
382 (setplist (intern file vc-file-prop-obarray) nil))
383 733
384;;; install a call to the above as a find-file hook 734;;; install a call to the above as a find-file hook
385(defun vc-find-file-hook () 735(defun vc-find-file-hook ()
@@ -389,7 +739,7 @@ control system name."
389 (buffer-file-name 739 (buffer-file-name
390 (vc-file-clearprops buffer-file-name) 740 (vc-file-clearprops buffer-file-name)
391 (cond 741 (cond
392 ((vc-backend-deduce buffer-file-name) 742 ((vc-backend buffer-file-name)
393 (vc-mode-line buffer-file-name) 743 (vc-mode-line buffer-file-name)
394 (cond ((not vc-make-backup-files) 744 (cond ((not vc-make-backup-files)
395 ;; Use this variable, not make-backup-files, 745 ;; Use this variable, not make-backup-files,
@@ -403,7 +753,7 @@ control system name."
403(defun vc-file-not-found-hook () 753(defun vc-file-not-found-hook ()
404 "When file is not found, try to check it out from RCS or SCCS. 754 "When file is not found, try to check it out from RCS or SCCS.
405Returns t if checkout was successful, nil otherwise." 755Returns t if checkout was successful, nil otherwise."
406 (if (vc-backend-deduce buffer-file-name) 756 (if (vc-backend buffer-file-name)
407 (save-excursion 757 (save-excursion
408 (require 'vc) 758 (require 'vc)
409 (not (vc-error-occurred (vc-checkout buffer-file-name)))))) 759 (not (vc-error-occurred (vc-checkout buffer-file-name))))))