1 ;;; vc-hooks.el --- resident support for version-control
3 ;; Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
5 ;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
7 ;; Per Cederqvist <ceder@lysator.liu.se>
8 ;; Andre Spiegel <spiegel@berlin.informatik.uni-stuttgart.de>
10 ;; This file is part of GNU Emacs.
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)
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.
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
24 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
28 ;; This is the always-loaded portion of VC.
29 ;; It takes care VC-related activities that are done when you visit a file,
30 ;; so that vc.el itself is loaded only when you use a VC command.
31 ;; See the commentary of vc.el.
35 ;; Customization Variables (the rest is in vc.el)
37 (defvar vc-default-back-end nil
38 "*Back-end actually used by this interface; may be SCCS or RCS.
39 The value is only computed when needed to avoid an expensive search.")
42 (if (file-directory-p "/usr/sccs")
45 "*List of extra directories to search for version control commands.")
47 (defvar vc-master-templates
48 '(("%sRCS/%s,v" . RCS) ("%s%s,v" . RCS) ("%sRCS/%s" . RCS)
49 ("%sSCCS/s.%s" . SCCS) ("%ss.%s". SCCS)
51 "*Where to look for version-control master files.
52 The first pair corresponding to a given back end is used as a template
53 when creating new masters.")
55 (defvar vc-make-backup-files nil
56 "*If non-nil, backups of registered files are made as with other files.
57 If nil (the default), files covered by version control don't get backups.")
59 (defvar vc-display-status t
60 "*If non-nil, display revision number and lock status in modeline.
61 Otherwise, not displayed.")
63 (defvar vc-consult-headers t
64 "*Identify work files by searching for version headers.")
66 (defvar vc-mistrust-permissions nil
67 "*Don't assume that permissions and ownership track version-control status.")
69 (defvar vc-keep-workfiles t
70 "*If non-nil, don't delete working files after registering changes.
71 If the back-end is CVS, workfiles are always kept, regardless of the
74 ;; Tell Emacs about this new kind of minor mode
75 (if (not (assoc 'vc-mode minor-mode-alist))
76 (setq minor-mode-alist (cons '(vc-mode vc-mode)
79 (make-variable-buffer-local 'vc-mode)
80 (put 'vc-mode 'permanent-local t)
83 ;; branch identification
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))
90 (if (eq object (elt sequence index))
92 (setq index (1+ index)))
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)))
100 ;; We need a notion of per-file properties because the version
101 ;; control state of a file is expensive to derive --- we compute
102 ;; them when the file is initially found, keep them up to date
103 ;; during any subsequent VC operations, and forget them when
104 ;; the buffer is killed.
106 (defmacro vc-error-occurred (&rest body)
107 (list 'condition-case nil (cons 'progn (append body '(nil))) '(error t)))
109 (defvar vc-file-prop-obarray [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
110 "Obarray for per-file properties.")
112 (defvar vc-buffer-backend t)
113 (make-variable-buffer-local 'vc-buffer-backend)
115 (defun vc-file-setprop (file property value)
116 ;; set per-file property
117 (put (intern file vc-file-prop-obarray) property value))
119 (defun vc-file-getprop (file property)
120 ;; get per-file property
121 (get (intern file vc-file-prop-obarray) property))
123 (defun vc-file-clearprops (file)
124 ;; clear all properties of a given file
125 (setplist (intern file vc-file-prop-obarray) nil))
129 (defun vc-name (file)
130 "Return the master name of a file, nil if it is not registered."
131 (or (vc-file-getprop file 'vc-name)
132 (let ((name-and-type (vc-registered file)))
135 (vc-file-setprop file 'vc-backend (cdr name-and-type))
136 (vc-file-setprop file 'vc-name (car name-and-type)))))))
138 (defun vc-backend (file)
139 "Return the version-control type of a file, nil if it is not registered."
141 (or (vc-file-getprop file 'vc-backend)
142 (let ((name-and-type (vc-registered file)))
145 (vc-file-setprop file 'vc-name (car name-and-type))
146 (vc-file-setprop file 'vc-backend (cdr name-and-type))))))))
148 ;; Functions for querying the master and lock files.
150 (defun vc-match-substring (bn)
151 (buffer-substring (match-beginning bn) (match-end bn)))
153 (defun vc-lock-file (file)
154 ;; Generate lock file name corresponding to FILE
155 (let ((master (vc-name file)))
158 (string-match "\\(.*/\\)s\\.\\(.*\\)" master)
160 (substring master (match-beginning 1) (match-end 1))
162 (substring master (match-beginning 2) (match-end 2))))))
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))
178 ((eq (length p) 2) ;; search for first entry
180 (if (re-search-forward (car p) nil t)
181 (setq value (vc-match-substring (elt p 1))))
183 (progn (vc-file-setprop file (car properties) value)
184 (setq properties (cdr properties))))
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)
192 (setq latest-date date)
194 (vc-match-substring (elt p 1)))))))
196 (progn (vc-file-setprop file (car properties) latest-val)
197 (setq properties (cdr properties))))
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))
207 (setq buf (create-file-buffer file))
210 (insert-file-contents file)
211 (set-buffer-modified-p nil)
214 (vc-parse-buffer fields rfile properties)
215 (kill-buffer (current-buffer)))
219 (function (lambda (p) (vc-file-setprop rfile p nil)))
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))
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*"))
236 (let ((exec-path (append vc-path exec-path))
237 ;; Add vc-path to PATH for the execution of this command.
239 (cons (concat "PATH=" (getenv "PATH")
241 (mapconcat 'identity vc-path path-separator))
242 process-environment)))
243 (apply 'call-process command nil "*vc*" nil
244 (append flags (list (file-name-nondirectory file)))))
245 (set-buffer (get-buffer "*vc*"))
246 (set-buffer-modified-p nil)
247 ;; in the RCS case, insert branch version into
248 ;; any patterns that contain \X
249 (if (eq (vc-backend file) 'RCS)
251 (car (vc-parse-buffer
252 '(("^branch:[ \t]+\\([0-9.]+\\)$" 1))))))
257 (if (string-match "\\\\X" (car p))
259 (cond ((vc-branch-p branch)
262 (substring (car p) 0 (match-beginning 0))
263 (regexp-quote branch)
265 (substring (car p) (match-end 0)))
270 (substring (car p) 0 (match-beginning 0))
271 (regexp-quote branch)
272 (substring (car p) (match-end 0)))
274 ;; if there is no current branch,
275 ;; return a completely different regexp,
276 ;; which searches for the *head*
277 '("^head:[ \t]+\\([0-9.]+\\)$" 1))
281 (vc-parse-buffer patterns file properties)
282 (kill-buffer (current-buffer))
287 (function (lambda (p) (vc-file-setprop file p nil)))
292 ;;; Functions that determine property values, by examining the
293 ;;; working file, the master file, or log program output
295 (defun vc-consult-rcs-headers (file)
296 ;; Search for RCS headers in FILE, and set properties
297 ;; accordingly. This function can be disabled by setting
298 ;; vc-consult-headers to nil.
299 ;; Returns: nil if no headers were found
300 ;; (or if the feature is disabled,
301 ;; or if there is currently no buffer
303 ;; 'rev if a workfile revision was found
304 ;; 'rev-and-lock if revision and lock info was found
306 ((or (not vc-consult-headers)
307 (not (get-file-buffer file)) nil))
309 (set-buffer (get-file-buffer file))
310 (goto-char (point-min))
312 ;; search for $Id or $Header
313 ;; -------------------------
314 ((re-search-forward "\\$\\(Id\\|Header\\): [^ ]+ \\([0-9.]+\\) "
316 ;; if found, store the revision number ...
317 (let ((rev (buffer-substring (match-beginning 2)
319 ;; ... and check for the locking state
320 (if (re-search-forward
321 (concat "\\=[0-9]+/[0-9]+/[0-9]+ " ; date
322 "[0-9]+:[0-9]+:[0-9]+ " ; time
323 "[^ ]+ [^ ]+ ") ; author & state
328 (vc-file-setprop file 'vc-workfile-version rev)
329 (vc-file-setprop file 'vc-locking-user nil)
330 (vc-file-setprop file 'vc-locked-version nil)
332 ;; revision is locked by some user
333 ((looking-at "\\([^ ]+\\) \\$")
334 (vc-file-setprop file 'vc-workfile-version rev)
335 (vc-file-setprop file 'vc-locking-user
336 (buffer-substring (match-beginning 1)
338 (vc-file-setprop file 'vc-locked-version rev)
340 ;; everything else: false
342 ;; unexpected information in
343 ;; keyword string --> quit
345 ;; search for $Revision
346 ;; --------------------
347 ((re-search-forward (concat "\\$"
348 "Revision: \\([0-9.]+\\) \\$")
350 ;; if found, store the revision number ...
351 (let ((rev (buffer-substring (match-beginning 1)
353 ;; and see if there's any lock information
354 (goto-char (point-min))
355 (if (re-search-forward (concat "\\$" "Locker:") nil t)
356 (cond ((looking-at " \\([^ ]+\\) \\$")
357 (vc-file-setprop file 'vc-workfile-version rev)
358 (vc-file-setprop file 'vc-locking-user
359 (buffer-substring (match-beginning 1)
361 (vc-file-setprop file 'vc-locked-version rev)
363 ((looking-at " *\\$")
364 (vc-file-setprop file 'vc-workfile-version rev)
365 (vc-file-setprop file 'vc-locking-user nil)
366 (vc-file-setprop file 'vc-locked-version nil)
369 (vc-file-setprop file 'vc-workfile-version rev)
371 (vc-file-setprop file 'vc-workfile-version rev)
373 ;; else: nothing found
374 ;; -------------------
377 (defun vc-fetch-properties (file)
378 ;; Re-fetch some properties associated with the given file.
380 ((eq (vc-backend file) 'SCCS)
382 (vc-master-info (vc-lock-file file)
384 '("^[^ ]+ [^ ]+ \\([^ ]+\\)" 1)
387 '(vc-locking-user vc-locked-version))
388 (vc-master-info (vc-name file)
390 '("^\001d D \\([^ ]+\\)" 1)
391 (list (concat "^\001d D \\([^ ]+\\) .* "
392 (regexp-quote (user-login-name)) " ")
396 '(vc-latest-version vc-your-latest-version))
398 ((eq (vc-backend file) 'RCS)
399 (vc-log-info "rlog" file nil
401 '("^locks: strict\n\t\\([^:]+\\)" 1)
402 '("^locks: strict\n\t[^:]+: \\(.+\\)" 1)
403 '("^revision[\t ]+\\([0-9.]+\\)\\(\n\\|[ \t].*\n\\)date: \\([ /0-9:]+\\);" 1 3)
406 "^revision[\t ]+\\([0-9.]+\\)\\(\n\\|[ \t].*\n\\)date: \\([ /0-9:]+\\); *author: "
407 (regexp-quote (user-login-name))
409 ;; special regexp to search for branch revision:
410 ;; \X will be replaced by vc-log-info (see there)
411 '("^revision[\t ]+\\(\\X\\)\\(\n\\|[ \t].*\n\\)date: \\([ /0-9:]+\\);" 1 3))
416 vc-your-latest-version
418 ((eq (vc-backend file) 'CVS)
419 (vc-log-info "cvs" file '("status")
420 ;; CVS 1.3 says "RCS Version:", other releases "RCS Revision:",
421 ;; and CVS 1.4a1 says "Repository revision:".
422 '(("\\(RCS Version\\|RCS Revision\\|Repository revision\\):[\t ]+\\([0-9.]+\\)" 2)
423 ("^File: [^ \t]+[ \t]+Status: \\(.*\\)" 1))
424 '(vc-latest-version vc-cvs-status))
425 ;; Translate those status values that are needed into symbols.
426 ;; Any other value is converted to nil.
427 (let ((status (vc-file-getprop file 'vc-cvs-status)))
428 (cond ((string-match "Up-to-date" status)
429 (vc-file-setprop file 'vc-cvs-status 'up-to-date)
430 (vc-file-setprop file 'vc-checkout-time
431 (nth 5 (file-attributes file))))
432 ((string-match "Locally Modified" status)
433 (vc-file-setprop file 'vc-cvs-status 'locally-modified))
434 ((string-match "Needs Merge" status)
435 (vc-file-setprop file 'vc-cvs-status 'needs-merge))
436 (t (vc-file-setprop file 'vc-cvs-status nil))))
439 (defun vc-backend-subdirectory-name (&optional file)
440 ;; Where the master and lock files for the current directory are kept
443 (and file (vc-backend file))
445 (setq vc-default-back-end (if (vc-find-binary "rcs") 'RCS 'SCCS)))))
448 ;;; Access functions to file properties
449 ;;; (Properties should be _set_ using vc-file-setprop, but
450 ;;; _retrieved_ only through these functions, which decide
451 ;;; if the property is already known or not. A property should
452 ;;; only be retrieved by vc-file-getprop if there is no
453 ;;; access function.)
455 ;; functions vc-name and vc-backend come earlier above,
456 ;; because they are needed by vc-log-info etc.
458 (defun vc-cvs-status (file)
459 ;; Return the cvs status of FILE
460 ;; (Status field in output of "cvs status")
461 (cond ((vc-file-getprop file 'vc-cvs-status))
462 (t (vc-fetch-properties file)
463 (vc-file-getprop file 'vc-cvs-status))))
465 (defun vc-locking-user (file)
466 "Return the name of the person currently holding a lock on FILE.
467 Return nil if there is no such person.
468 Under CVS, a file is considered locked if it has been modified since it
469 was checked out. Under CVS, this will sometimes return the uid of
470 the owner of the file (as a number) instead of a string."
471 ;; The property is cached. If it is non-nil, it is simply returned.
472 ;; The other routines clear it when the locking state changes.
473 (setq file (expand-file-name file));; ??? Work around bug in 19.0.4
475 ((vc-file-getprop file 'vc-locking-user))
476 ((eq (vc-backend file) 'CVS)
477 (if (eq (vc-cvs-status file) 'up-to-date)
479 ;; The expression below should return the username of the owner
480 ;; of the file. It doesn't. It returns the username if it is
481 ;; you, or otherwise the UID of the owner of the file. The
482 ;; return value from this function is only used by
483 ;; vc-dired-reformat-line, and it does the proper thing if a UID
486 ;; The *proper* way to fix this would be to implement a built-in
487 ;; function in Emacs, say, (username UID), that returns the
488 ;; username of a given UID.
490 ;; The result of this hack is that vc-directory will print the
491 ;; name of the owner of the file for any files that are
493 (let ((uid (nth 2 (file-attributes file))))
494 (if (= uid (user-uid))
495 (vc-file-setprop file 'vc-locking-user (user-login-name))
496 (vc-file-setprop file 'vc-locking-user uid)))))
498 (if (and (eq (vc-backend file) 'RCS)
499 (eq (vc-consult-rcs-headers file) 'rev-and-lock))
500 (vc-file-getprop file 'vc-locking-user)
501 (if (or (not vc-keep-workfiles)
502 (eq vc-mistrust-permissions 't)
503 (and vc-mistrust-permissions
504 (funcall vc-mistrust-permissions
505 (vc-backend-subdirectory-name file))))
506 (vc-file-setprop file 'vc-locking-user (vc-true-locking-user file))
507 ;; This implementation assumes that any file which is under version
508 ;; control and has -rw-r--r-- is locked by its owner. This is true
509 ;; for both RCS and SCCS, which keep unlocked files at -r--r--r--.
510 ;; We have to be careful not to exclude files with execute bits on;
511 ;; scripts can be under version control too. Also, we must ignore
512 ;; the group-read and other-read bits, since paranoid users turn them off.
513 ;; This hack wins because calls to the very expensive vc-fetch-properties
514 ;; function only have to be made if (a) the file is locked by someone
515 ;; other than the current user, or (b) some untoward manipulation
516 ;; behind vc's back has changed the owner or the `group' or `other'
518 (let ((attributes (file-attributes file)))
519 (cond ((string-match ".r-..-..-." (nth 8 attributes))
521 ((and (= (nth 2 attributes) (user-uid))
522 (string-match ".rw..-..-." (nth 8 attributes)))
523 (vc-file-setprop file 'vc-locking-user (user-login-name)))
525 (vc-file-setprop file 'vc-locking-user
526 (vc-true-locking-user file))))))))))
528 (defun vc-true-locking-user (file)
529 ;; The slow but reliable version
530 (vc-fetch-properties file)
531 (vc-file-getprop file 'vc-locking-user))
533 (defun vc-latest-version (file)
534 ;; Return version level of the latest version of FILE
535 (vc-fetch-properties file)
536 (vc-file-getprop file 'vc-latest-version))
538 (defun vc-your-latest-version (file)
539 ;; Return version level of the latest version of FILE checked in by you
540 (vc-fetch-properties file)
541 (vc-file-getprop file 'vc-your-latest-version))
543 (defun vc-branch-version (file)
544 ;; Return version level of the highest revision on the default branch
545 ;; If there is no default branch, return the highest version number
547 ;; This property is defined for RCS only.
548 (vc-fetch-properties file)
549 (vc-file-getprop file 'vc-branch-version))
551 (defun vc-workfile-version (file)
552 ;; Return version level of the current workfile FILE
553 ;; This is attempted by first looking at the RCS keywords.
554 ;; If there are no keywords in the working file,
555 ;; vc-branch-version is taken.
556 ;; Note that this property is cached, that is, it is only
557 ;; looked up if it is nil.
558 ;; For SCCS, this property is equivalent to vc-latest-version.
559 (cond ((vc-file-getprop file 'vc-workfile-version))
560 ((eq (vc-backend file) 'SCCS) (vc-latest-version file))
561 ((eq (vc-backend file) 'RCS)
562 (if (vc-consult-rcs-headers file)
563 (vc-file-getprop file 'vc-workfile-version)
564 (let ((rev (cond ((vc-branch-version file))
565 ((vc-latest-version file)))))
566 (vc-file-setprop file 'vc-workfile-version rev)
568 ((eq (vc-backend file) 'CVS)
569 (if (vc-consult-rcs-headers file) ;; CVS
570 (vc-file-getprop file 'vc-workfile-version)
571 (vc-find-cvs-master (file-name-directory file)
572 (file-name-nondirectory file))
573 (vc-file-getprop file 'vc-workfile-version)))))
575 ;;; actual version-control code starts here
577 (defun vc-registered (file)
578 (let (handler handlers)
579 (if (boundp 'file-name-handler-alist)
580 (setq handler (find-file-name-handler file 'vc-registered)))
582 (funcall handler 'vc-registered file)
583 ;; Search for a master corresponding to the given file
584 (let ((dirname (or (file-name-directory file) ""))
585 (basename (file-name-nondirectory file)))
588 (function (lambda (s)
590 (funcall s dirname basename)
591 (let ((trial (format (car s) dirname basename)))
592 (if (and (file-exists-p trial)
593 ;; Make sure the file we found with name
594 ;; TRIAL is not the source file itself.
595 ;; That can happen with RCS-style names
596 ;; if the file name is truncated
597 ;; (e.g. to 14 chars). See if either
598 ;; directory or attributes differ.
599 (or (not (string= dirname
600 (file-name-directory trial)))
602 (file-attributes file)
603 (file-attributes trial)))))
604 (throw 'found (cons trial (cdr s))))))))
608 (defun vc-find-cvs-master (dirname basename)
609 ;; Check if DIRNAME/BASENAME is handled by CVS.
610 ;; If it is, do a (throw 'found (cons MASTER 'CVS)).
611 ;; Note: If the file is ``cvs add''ed but not yet ``cvs commit''ed
612 ;; the MASTER will not actually exist yet. The other parts of VC
613 ;; checks for this condition. This function returns nil if
614 ;; DIRNAME/BASENAME is not handled by CVS.
615 (if (and (file-directory-p (concat dirname "CVS/"))
616 (file-readable-p (concat dirname "CVS/Entries")))
617 (let ((bufs nil) (fold case-fold-search))
621 (find-file-noselect (concat dirname "CVS/Entries"))))
622 (set-buffer (car bufs))
623 (goto-char (point-min))
624 ;; make sure the file name is searched
626 (setq case-fold-search nil)
629 (concat "^/" (regexp-quote basename) "/\\([^/]*\\)/")
631 (setq case-fold-search fold) ;; restore the old value
632 ;; We found it. Store away version number, now
633 ;; that we are anyhow so close to finding it.
634 (vc-file-setprop (concat dirname basename)
636 (buffer-substring (match-beginning 1)
638 (setq bufs (cons (find-file-noselect
639 (concat dirname "CVS/Repository"))
641 (set-buffer (car bufs))
643 (concat (file-name-as-directory
644 (buffer-substring (point-min)
648 (throw 'found (cons master 'CVS))))
649 (t (setq case-fold-search fold) ;; restore the old value
651 (mapcar (function kill-buffer) bufs)))))
653 (defun vc-buffer-backend ()
654 "Return the version-control type of the visited file, or nil if none."
655 (if (eq vc-buffer-backend t)
656 (setq vc-buffer-backend (vc-backend (buffer-file-name)))
659 (defun vc-toggle-read-only (&optional verbose)
660 "Change read-only status of current buffer, perhaps via version control.
661 If the buffer is visiting a file registered with version control,
662 then check the file in or out. Otherwise, just change the read-only flag
663 of the buffer. With prefix argument, ask for version number."
665 (if (vc-backend (buffer-file-name))
666 (vc-next-action verbose)
668 (define-key global-map "\C-x\C-q" 'vc-toggle-read-only)
670 (defun vc-mode-line (file &optional label)
671 "Set `vc-mode' to display type of version control for FILE.
672 The value is set in the current buffer, which should be the buffer
673 visiting FILE. Second optional arg LABEL is put in place of version
674 control system name."
675 (interactive (list buffer-file-name nil))
676 (let ((vc-type (vc-backend file))
677 (vc-status-string (and vc-display-status (vc-status file))))
679 (concat " " (or label (symbol-name vc-type)) vc-status-string))
680 ;; Make the buffer read-only if the file is not locked
681 ;; (or unchanged, in the CVS case).
682 ;; Determine this by looking at the mode string,
683 ;; so that no further external status query is necessary
685 (if (eq (elt vc-status-string 0) ?-)
686 (setq buffer-read-only t))
687 (if (not (vc-locking-user file))
688 (setq buffer-read-only t)))
689 ;; Even root shouldn't modify a registered file without
692 (not buffer-read-only)
695 (not (equal (user-login-name) (vc-locking-user file)))
696 (setq buffer-read-only t))
698 (file-symlink-p file)
699 (let ((link-type (vc-backend (file-symlink-p file))))
702 "Warning: symbolic link to %s-controlled source file"
704 (force-mode-line-update)
705 ;;(set-buffer-modified-p (buffer-modified-p)) ;;use this if Emacs 18
708 (defun vc-status (file)
709 ;; Return string for placement in modeline by `vc-mode-line'.
712 ;; "-REV" if the revision is not locked
713 ;; ":REV" if the revision is locked by the user
714 ;; ":LOCKER:REV" if the revision is locked by somebody else
715 ;; " @@" for a CVS file that is added, but not yet committed
717 ;; In the CVS case, a "locked" working file is a
718 ;; working file that is modified with respect to the master.
719 ;; The file is "locked" from the moment when the user makes
720 ;; the buffer writable.
722 ;; This function assumes that the file is registered.
724 (let ((locker (vc-locking-user file))
725 (rev (vc-workfile-version file)))
726 (cond ((string= "0" rev)
730 ((if (stringp locker)
731 (string= locker (user-login-name))
732 (= locker (user-uid)))
735 (concat ":" locker ":" rev)))))
737 ;;; install a call to the above as a find-file hook
738 (defun vc-find-file-hook ()
739 ;; Recompute whether file is version controlled,
740 ;; if user has killed the buffer and revisited.
743 (vc-file-clearprops buffer-file-name)
745 ((vc-backend buffer-file-name)
746 (vc-mode-line buffer-file-name)
747 (cond ((not vc-make-backup-files)
748 ;; Use this variable, not make-backup-files,
749 ;; because this is for things that depend on the file name.
750 (make-local-variable 'backup-inhibited)
751 (setq backup-inhibited t))))))))
753 (add-hook 'find-file-hooks 'vc-find-file-hook)
755 ;;; more hooks, this time for file-not-found
756 (defun vc-file-not-found-hook ()
757 "When file is not found, try to check it out from RCS or SCCS.
758 Returns t if checkout was successful, nil otherwise."
759 (if (vc-backend buffer-file-name)
762 (not (vc-error-occurred (vc-checkout buffer-file-name))))))
764 (add-hook 'find-file-not-found-hooks 'vc-file-not-found-hook)
766 ;; Discard info about a file when we kill its buffer.
767 (defun vc-kill-buffer-hook ()
768 (if (stringp (buffer-file-name))
770 (vc-file-clearprops (buffer-file-name))
771 (kill-local-variable 'vc-buffer-backend))))
773 ;;;(add-hook 'kill-buffer-hook 'vc-kill-buffer-hook)
775 ;;; Now arrange for bindings and autoloading of the main package.
776 ;;; Bindings for this have to go in the global map, as we'll often
777 ;;; want to call them from random buffers.
779 (setq vc-prefix-map (lookup-key global-map "\C-xv"))
780 (if (not (keymapp vc-prefix-map))
782 (setq vc-prefix-map (make-sparse-keymap))
783 (define-key global-map "\C-xv" vc-prefix-map)
784 (define-key vc-prefix-map "a" 'vc-update-change-log)
785 (define-key vc-prefix-map "c" 'vc-cancel-version)
786 (define-key vc-prefix-map "d" 'vc-directory)
787 (define-key vc-prefix-map "h" 'vc-insert-headers)
788 (define-key vc-prefix-map "i" 'vc-register)
789 (define-key vc-prefix-map "l" 'vc-print-log)
790 (define-key vc-prefix-map "r" 'vc-retrieve-snapshot)
791 (define-key vc-prefix-map "s" 'vc-create-snapshot)
792 (define-key vc-prefix-map "u" 'vc-revert-buffer)
793 (define-key vc-prefix-map "v" 'vc-next-action)
794 (define-key vc-prefix-map "=" 'vc-diff)
795 (define-key vc-prefix-map "~" 'vc-version-other-window)))
797 (if (not (boundp 'vc-menu-map))
798 ;; Don't do the menu bindings if menu-bar.el wasn't loaded to defvar
801 ;;(define-key vc-menu-map [show-files]
802 ;; '("Show Files under VC" . (vc-directory t)))
803 (define-key vc-menu-map [vc-directory] '("Show Locked Files" . vc-directory))
804 (define-key vc-menu-map [separator1] '("----"))
805 (define-key vc-menu-map [vc-rename-file] '("Rename File" . vc-rename-file))
806 (define-key vc-menu-map [vc-version-other-window]
807 '("Show Other Version" . vc-version-other-window))
808 (define-key vc-menu-map [vc-diff] '("Compare with Last Version" . vc-diff))
809 (define-key vc-menu-map [vc-update-change-log]
810 '("Update ChangeLog" . vc-update-change-log))
811 (define-key vc-menu-map [vc-print-log] '("Show History" . vc-print-log))
812 (define-key vc-menu-map [separator2] '("----"))
813 (define-key vc-menu-map [undo] '("Undo Last Check-In" . vc-cancel-version))
814 (define-key vc-menu-map [vc-revert-buffer]
815 '("Revert to Last Version" . vc-revert-buffer))
816 (define-key vc-menu-map [vc-insert-header]
817 '("Insert Header" . vc-insert-headers))
818 (define-key vc-menu-map [vc-menu-check-in] '("Check In" . vc-next-action))
819 (define-key vc-menu-map [vc-check-out] '("Check Out" . vc-toggle-read-only))
820 (define-key vc-menu-map [vc-register] '("Register" . vc-register))
821 (put 'vc-rename-file 'menu-enable 'vc-mode)
822 (put 'vc-version-other-window 'menu-enable 'vc-mode)
823 (put 'vc-diff 'menu-enable 'vc-mode)
824 (put 'vc-update-change-log 'menu-enable
825 '(eq (vc-buffer-backend) 'RCS))
826 (put 'vc-print-log 'menu-enable 'vc-mode)
827 (put 'vc-cancel-version 'menu-enable 'vc-mode)
828 (put 'vc-revert-buffer 'menu-enable 'vc-mode)
829 (put 'vc-insert-headers 'menu-enable 'vc-mode)
830 (put 'vc-next-action 'menu-enable '(and vc-mode (not buffer-read-only)))
831 (put 'vc-toggle-read-only 'menu-enable '(and vc-mode buffer-read-only))
832 (put 'vc-register 'menu-enable '(not vc-mode))
837 ;;; vc-hooks.el ends here