Mercurial > hg > xemacs-beta
comparison lisp/packages/vc-hooks.el @ 151:59463afc5666 r20-3b2
Import from CVS: tag r20-3b2
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:37:19 +0200 |
parents | 538048ae2ab8 |
children | 25f70ba0133c |
comparison
equal
deleted
inserted
replaced
150:8ebb1c0f0f6f | 151:59463afc5666 |
---|---|
1 ;;; vc-hooks.el --- resident support for version-control | 1 ;;; vc-hooks.el --- resident support for version-control |
2 | 2 |
3 ;; Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. | 3 ;; Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc. |
4 ;; Copyright (C) 1995 Tinker Systems and INS Engineering Corp. | 4 |
5 | 5 ;; Author: Eric S. Raymond <esr@snark.thyrsus.com> |
6 ;; Author: Eric S. Raymond <esr@snark.thyrsus.com> | 6 ;; Maintainer: Andre Spiegel <spiegel@inf.fu-berlin.de> |
7 ;; Maintainer: ttn@netcom.com | 7 ;; XEmacs conversion: Steve Baur <steve@altair.xemacs.org> |
8 ;; Version: 5.3 + CVS hacks by ceder@lysator.liu.se made in Jan-Feb 1994. | 8 |
9 ;; | 9 ;; This file is part of GNU Emacs. |
10 ;; XEmacs fixes, CVS fixes, and general improvements | 10 |
11 ;; by Jonathan Stigelman <Stig@hackvan.com> | 11 ;; GNU Emacs is free software; you can redistribute it and/or modify |
12 | 12 ;; it under the terms of the GNU General Public License as published by |
13 ;; This file is part of XEmacs. | |
14 | |
15 ;; XEmacs is free software; you can redistribute it and/or modify it | |
16 ;; under the terms of the GNU General Public License as published by | |
17 ;; the Free Software Foundation; either version 2, or (at your option) | 13 ;; the Free Software Foundation; either version 2, or (at your option) |
18 ;; any later version. | 14 ;; any later version. |
19 | 15 |
20 ;; XEmacs is distributed in the hope that it will be useful, but | 16 ;; GNU Emacs is distributed in the hope that it will be useful, |
21 ;; WITHOUT ANY WARRANTY; without even the implied warranty of | 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 ;; General Public License for more details. | 19 ;; GNU General Public License for more details. |
24 | 20 |
25 ;; You should have received a copy of the GNU General Public License | 21 ;; You should have received a copy of the GNU General Public License |
26 ;; along with XEmacs; see the file COPYING. If not, write to the | 22 ;; along with GNU Emacs; see the file COPYING. If not, write to the |
27 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | 23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
28 ;; Boston, MA 02111-1307, USA. | 24 ;; Boston, MA 02111-1307, USA. |
29 | 25 |
30 ;;; Synched up with: FSF 19.28. | |
31 | |
32 ;;; Commentary: | 26 ;;; Commentary: |
33 | 27 |
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. | |
34 ;; See the commentary of vc.el. | 31 ;; See the commentary of vc.el. |
35 | 32 |
36 ;;; Code: | 33 ;;; Code: |
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. | |
39 The value is only computed when needed to avoid an expensive search.") | |
40 | |
41 (defvar vc-handle-cvs t | |
42 "*If non-nil, use VC for files managed with CVS. | |
43 If it is nil, don't use VC for those files.") | |
44 | |
45 (defvar vc-rcsdiff-knows-brief nil | |
46 "*Indicates whether rcsdiff understands the --brief option. | |
47 The value is either `yes', `no', or nil. If it is nil, VC tries | |
48 to use --brief and sets this variable to remember whether it worked.") | |
49 | |
50 (defvar vc-path | |
51 (if (file-directory-p "/usr/sccs") | |
52 '("/usr/sccs") | |
53 nil) | |
54 "*List of extra directories to search for version control commands.") | |
37 | 55 |
38 (defvar vc-master-templates | 56 (defvar vc-master-templates |
39 '(("%sRCS/%s,v" . RCS) ("%s%s,v" . RCS) ("%sRCS/%s" . RCS) | 57 '(("%sRCS/%s,v" . RCS) ("%s%s,v" . RCS) ("%sRCS/%s" . RCS) |
40 ("%sSCCS/s.%s" . SCCS) ("%ss.%s". SCCS) | 58 ("%sSCCS/s.%s" . SCCS) ("%ss.%s". SCCS) |
41 ("%s%s@@" . CC) | |
42 vc-find-cvs-master) | 59 vc-find-cvs-master) |
43 "*Where to look for version-control master files. | 60 "*Where to look for version-control master files. |
44 The first pair corresponding to a given back end is used as a template | 61 The first pair corresponding to a given back end is used as a template |
45 when creating new masters.") | 62 when creating new masters.") |
46 | 63 |
47 (defvar vc-make-backup-files nil | 64 (defvar vc-make-backup-files nil |
48 "*If non-nil, backups of registered files are made as with other files. | 65 "*If non-nil, backups of registered files are made as with other files. |
49 If nil (the default), files covered by version control don't get backups.") | 66 If nil (the default), files covered by version control don't get backups.") |
50 | 67 |
68 (defvar vc-follow-symlinks 'ask | |
69 "*Indicates what to do if you visit a symbolic link to a file | |
70 that is under version control. Editing such a file through the | |
71 link bypasses the version control system, which is dangerous and | |
72 probably not what you want. | |
73 If this variable is t, VC follows the link and visits the real file, | |
74 telling you about it in the echo area. If it is `ask', VC asks for | |
75 confirmation whether it should follow the link. If nil, the link is | |
76 visited and a warning displayed.") | |
77 | |
51 (defvar vc-display-status t | 78 (defvar vc-display-status t |
52 "*If non-nil, display revision number and lock status in modeline. | 79 "*If non-nil, display revision number and lock status in modeline. |
53 Otherwise, not displayed.") | 80 Otherwise, not displayed.") |
54 | 81 |
82 (defvar vc-consult-headers t | |
83 "*If non-nil, identify work files by searching for version headers.") | |
84 | |
85 (defvar vc-keep-workfiles t | |
86 "*If non-nil, don't delete working files after registering changes. | |
87 If the back-end is CVS, workfiles are always kept, regardless of the | |
88 value of this flag.") | |
89 | |
90 (defvar vc-mistrust-permissions nil | |
91 "*If non-nil, don't assume that permissions and ownership track | |
92 version-control status. If nil, do rely on the permissions. | |
93 See also variable `vc-consult-headers'.") | |
94 | |
95 (defun vc-mistrust-permissions (file) | |
96 ;; Access function to the above. | |
97 (or (eq vc-mistrust-permissions 't) | |
98 (and vc-mistrust-permissions | |
99 (funcall vc-mistrust-permissions | |
100 (vc-backend-subdirectory-name file))))) | |
101 | |
55 ;; Tell Emacs about this new kind of minor mode | 102 ;; Tell Emacs about this new kind of minor mode |
56 ;(if (not (assoc 'vc-mode minor-mode-alist)) | 103 ;;(if (not (assoc 'vc-mode minor-mode-alist)) |
57 ; (setq minor-mode-alist (cons '(vc-mode vc-mode) | 104 ;; (setq minor-mode-alist (cons '(vc-mode vc-mode) |
58 ; minor-mode-alist))) | 105 ;; minor-mode-alist))) |
59 ;; NO! Do it right. | 106 |
107 ;; XEmacs: | |
60 (add-minor-mode 'vc-mode 'vc-mode) | 108 (add-minor-mode 'vc-mode 'vc-mode) |
61 | 109 |
62 (defvar vc-mode nil) ; used for modeline flag | 110 (defvar vc-mode nil) ; used for modeline flag |
111 ;; End XEmacs addition. | |
112 | |
63 (make-variable-buffer-local 'vc-mode) | 113 (make-variable-buffer-local 'vc-mode) |
64 (put 'vc-mode 'permanent-local t) | 114 (put 'vc-mode 'permanent-local t) |
65 | 115 |
66 ;; We need a notion of per-file properties because the version | 116 ;; We need a notion of per-file properties because the version |
67 ;; control state of a file is expensive to derive --- we don't | 117 ;; control state of a file is expensive to derive --- we compute |
68 ;; want to recompute it even on every find. | 118 ;; them when the file is initially found, keep them up to date |
119 ;; during any subsequent VC operations, and forget them when | |
120 ;; the buffer is killed. | |
69 | 121 |
70 (defmacro vc-error-occurred (&rest body) | 122 (defmacro vc-error-occurred (&rest body) |
71 (list 'condition-case nil (cons 'progn (append body '(nil))) '(error t))) | 123 (list 'condition-case nil (cons 'progn (append body '(nil))) '(error t))) |
72 | 124 |
73 (defvar vc-file-prop-obarray (make-vector 17 0) | 125 (defvar vc-file-prop-obarray [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] |
74 "Obarray for per-file properties.") | 126 "Obarray for per-file properties.") |
127 | |
128 (defvar vc-buffer-backend t) | |
129 (make-variable-buffer-local 'vc-buffer-backend) | |
75 | 130 |
76 (defun vc-file-setprop (file property value) | 131 (defun vc-file-setprop (file property value) |
77 ;; set per-file property | 132 ;; set per-file property |
78 (put (intern file vc-file-prop-obarray) property value)) | 133 (put (intern file vc-file-prop-obarray) property value)) |
79 | 134 |
80 (defun vc-file-getprop (file property) | 135 (defun vc-file-getprop (file property) |
81 ;; get per-file property | 136 ;; get per-file property |
82 (get (intern file vc-file-prop-obarray) property)) | 137 (get (intern file vc-file-prop-obarray) property)) |
83 | 138 |
139 (defun vc-file-clearprops (file) | |
140 ;; clear all properties of a given file | |
141 (setplist (intern file vc-file-prop-obarray) nil)) | |
142 | |
143 ;;; Functions that determine property values, by examining the | |
144 ;;; working file, the master file, or log program output | |
145 | |
146 (defun vc-match-substring (bn) | |
147 (buffer-substring (match-beginning bn) (match-end bn))) | |
148 | |
149 (defun vc-lock-file (file) | |
150 ;; Generate lock file name corresponding to FILE | |
151 (let ((master (vc-name file))) | |
152 (and | |
153 master | |
154 (string-match "\\(.*/\\)s\\.\\(.*\\)" master) | |
155 (concat | |
156 (substring master (match-beginning 1) (match-end 1)) | |
157 "p." | |
158 (substring master (match-beginning 2) (match-end 2)))))) | |
159 | |
160 (defun vc-parse-buffer (patterns &optional file properties) | |
161 ;; Use PATTERNS to parse information out of the current buffer. | |
162 ;; Each element of PATTERNS is a list of 2 to 3 elements. The first element | |
163 ;; is the pattern to be matched, and the second (an integer) is the | |
164 ;; number of the subexpression that should be returned. If there's | |
165 ;; a third element (also the number of a subexpression), that | |
166 ;; subexpression is assumed to be a date field and we want the most | |
167 ;; recent entry matching the template. | |
168 ;; If FILE and PROPERTIES are given, the latter must be a list of | |
169 ;; properties of the same length as PATTERNS; each property is assigned | |
170 ;; the corresponding value. | |
171 (mapcar (function (lambda (p) | |
172 (goto-char (point-min)) | |
173 (cond | |
174 ((eq (length p) 2) ;; search for first entry | |
175 (let ((value nil)) | |
176 (if (re-search-forward (car p) nil t) | |
177 (setq value (vc-match-substring (elt p 1)))) | |
178 (if file | |
179 (progn (vc-file-setprop file (car properties) value) | |
180 (setq properties (cdr properties)))) | |
181 value)) | |
182 ((eq (length p) 3) ;; search for latest entry | |
183 (let ((latest-date "") (latest-val)) | |
184 (while (re-search-forward (car p) nil t) | |
185 (let ((date (vc-match-substring (elt p 2)))) | |
186 (if (string< latest-date date) | |
187 (progn | |
188 (setq latest-date date) | |
189 (setq latest-val | |
190 (vc-match-substring (elt p 1))))))) | |
191 (if file | |
192 (progn (vc-file-setprop file (car properties) latest-val) | |
193 (setq properties (cdr properties)))) | |
194 latest-val))))) | |
195 patterns) | |
196 ) | |
197 | |
198 (defun vc-insert-file (file &optional limit blocksize) | |
199 ;; Insert the contents of FILE into the current buffer. | |
200 ;; Optional argument LIMIT is a regexp. If present, | |
201 ;; the file is inserted in chunks of size BLOCKSIZE | |
202 ;; (default 8 kByte), until the first occurrence of | |
203 ;; LIMIT is found. The function returns nil if FILE | |
204 ;; doesn't exist. | |
205 (erase-buffer) | |
206 (cond ((file-exists-p file) | |
207 (cond (limit | |
208 (if (not blocksize) (setq blocksize 8192)) | |
209 (let (found s) | |
210 (while (not found) | |
211 (setq s (buffer-size)) | |
212 (goto-char (1+ s)) | |
213 (setq found | |
214 (or (zerop (car (cdr | |
215 (insert-file-contents file nil s | |
216 (+ s blocksize))))) | |
217 (progn (beginning-of-line) | |
218 (re-search-forward limit nil t))))))) | |
219 (t (insert-file-contents file))) | |
220 (set-buffer-modified-p nil) | |
221 (auto-save-mode nil) | |
222 t) | |
223 (t nil))) | |
224 | |
225 (defun vc-parse-locks (file locks) | |
226 ;; Parse RCS or SCCS locks. | |
227 ;; The result is a list of the form ((VERSION USER) (VERSION USER) ...), | |
228 ;; which is returned and stored into the property `vc-master-locks'. | |
229 (if (not locks) | |
230 (vc-file-setprop file 'vc-master-locks 'none) | |
231 (let ((found t) (index 0) master-locks version user) | |
232 (cond ((eq (vc-backend file) 'SCCS) | |
233 (while (string-match "^\\([0-9.]+\\) [0-9.]+ \\([^ ]+\\) .*\n?" | |
234 locks index) | |
235 (setq version (substring locks | |
236 (match-beginning 1) (match-end 1))) | |
237 (setq user (substring locks | |
238 (match-beginning 2) (match-end 2))) | |
239 (setq master-locks (append master-locks | |
240 (list (cons version user)))) | |
241 (setq index (match-end 0)))) | |
242 ((eq (vc-backend file) 'RCS) | |
243 (while (string-match "[ \t\n]*\\([^:]+\\):\\([0-9.]+\\)" | |
244 locks index) | |
245 (setq version (substring locks | |
246 (match-beginning 2) (match-end 2))) | |
247 (setq user (substring locks | |
248 (match-beginning 1) (match-end 1))) | |
249 (setq master-locks (append master-locks | |
250 (list (cons version user)))) | |
251 (setq index (match-end 0))) | |
252 (if (string-match ";[ \t\n]+strict;" locks index) | |
253 (vc-file-setprop file 'vc-checkout-model 'manual) | |
254 (vc-file-setprop file 'vc-checkout-model 'implicit)))) | |
255 (vc-file-setprop file 'vc-master-locks (or master-locks 'none))))) | |
256 | |
257 (defun vc-simple-command (okstatus command file &rest args) | |
258 ;; Simple version of vc-do-command, for use in vc-hooks only. | |
259 ;; Don't switch to the *vc-info* buffer before running the | |
260 ;; command, because that would change its default directory | |
261 (save-excursion (set-buffer (get-buffer-create "*vc-info*")) | |
262 (erase-buffer)) | |
263 (let ((exec-path (append vc-path exec-path)) exec-status | |
264 ;; Add vc-path to PATH for the execution of this command. | |
265 (process-environment | |
266 (cons (concat "PATH=" (getenv "PATH") | |
267 path-separator | |
268 (mapconcat 'identity vc-path path-separator)) | |
269 process-environment))) | |
270 (setq exec-status | |
271 (apply 'call-process command nil "*vc-info*" nil | |
272 (append args (list file)))) | |
273 (cond ((> exec-status okstatus) | |
274 (switch-to-buffer (get-file-buffer file)) | |
275 (shrink-window-if-larger-than-buffer | |
276 (display-buffer "*vc-info*")) | |
277 (error "Couldn't find version control information"))) | |
278 exec-status)) | |
279 | |
280 (defun vc-fetch-master-properties (file) | |
281 ;; Fetch those properties of FILE that are stored in the master file. | |
282 ;; For an RCS file, we don't get vc-latest-version vc-your-latest-version | |
283 ;; here because that is slow. | |
284 ;; That gets done if/when the functions vc-latest-version | |
285 ;; and vc-your-latest-version get called. | |
286 (save-excursion | |
287 (cond | |
288 ((eq (vc-backend file) 'SCCS) | |
289 (set-buffer (get-buffer-create "*vc-info*")) | |
290 (if (vc-insert-file (vc-lock-file file)) | |
291 (vc-parse-locks file (buffer-string)) | |
292 (vc-file-setprop file 'vc-master-locks 'none)) | |
293 (vc-insert-file (vc-name file) "^\001e") | |
294 (vc-parse-buffer | |
295 (list '("^\001d D \\([^ ]+\\)" 1) | |
296 (list (concat "^\001d D \\([^ ]+\\) .* " | |
297 (regexp-quote (vc-user-login-name)) " ") 1)) | |
298 file | |
299 '(vc-latest-version vc-your-latest-version))) | |
300 | |
301 ((eq (vc-backend file) 'RCS) | |
302 (set-buffer (get-buffer-create "*vc-info*")) | |
303 (vc-insert-file (vc-name file) "^[0-9]") | |
304 (vc-parse-buffer | |
305 (list '("^head[ \t\n]+\\([^;]+\\);" 1) | |
306 '("^branch[ \t\n]+\\([^;]+\\);" 1) | |
307 '("^locks[ \t\n]*\\([^;]*;\\([ \t\n]*strict;\\)?\\)" 1)) | |
308 file | |
309 '(vc-head-version | |
310 vc-default-branch | |
311 vc-master-locks)) | |
312 ;; determine vc-master-workfile-version: it is either the head | |
313 ;; of the trunk, the head of the default branch, or the | |
314 ;; "default branch" itself, if that is a full revision number. | |
315 (let ((default-branch (vc-file-getprop file 'vc-default-branch))) | |
316 (cond | |
317 ;; no default branch | |
318 ((or (not default-branch) (string= "" default-branch)) | |
319 (vc-file-setprop file 'vc-master-workfile-version | |
320 (vc-file-getprop file 'vc-head-version))) | |
321 ;; default branch is actually a revision | |
322 ((string-match "^[0-9]+\\.[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*$" | |
323 default-branch) | |
324 (vc-file-setprop file 'vc-master-workfile-version default-branch)) | |
325 ;; else, search for the head of the default branch | |
326 (t (vc-insert-file (vc-name file) "^desc") | |
327 (vc-parse-buffer (list (list | |
328 (concat "^\\(" | |
329 (regexp-quote default-branch) | |
330 "\\.[0-9]+\\)\ndate[ \t]+\\([0-9.]+\\);") 1 2)) | |
331 file '(vc-master-workfile-version))))) | |
332 ;; translate the locks | |
333 (vc-parse-locks file (vc-file-getprop file 'vc-master-locks))) | |
334 | |
335 ((eq (vc-backend file) 'CVS) | |
336 (save-excursion | |
337 ;; Call "cvs status" in the right directory, passing only the | |
338 ;; nondirectory part of the file name -- otherwise CVS might | |
339 ;; silently give a wrong result. | |
340 (let ((default-directory (file-name-directory file))) | |
341 (vc-simple-command 0 "cvs" (file-name-nondirectory file) "status")) | |
342 (set-buffer (get-buffer "*vc-info*")) | |
343 (vc-parse-buffer | |
344 ;; CVS 1.3 says "RCS Version:", other releases "RCS Revision:", | |
345 ;; and CVS 1.4a1 says "Repository revision:". | |
346 '(("\\(RCS Version\\|RCS Revision\\|Repository revision\\):[\t ]+\\([0-9.]+\\)" 2) | |
347 ("^File: [^ \t]+[ \t]+Status: \\(.*\\)" 1)) | |
348 file | |
349 '(vc-latest-version vc-cvs-status)) | |
350 ;; Translate those status values that we understand into symbols. | |
351 ;; Any other value is converted to nil. | |
352 (let ((status (vc-file-getprop file 'vc-cvs-status))) | |
353 (cond | |
354 ((string-match "Up-to-date" status) | |
355 (vc-file-setprop file 'vc-cvs-status 'up-to-date) | |
356 (vc-file-setprop file 'vc-checkout-time | |
357 (nth 5 (file-attributes file)))) | |
358 ((vc-file-setprop file 'vc-cvs-status | |
359 (cond | |
360 ((string-match "Locally Modified" status) 'locally-modified) | |
361 ((string-match "Needs Merge" status) 'needs-merge) | |
362 ((string-match "Needs \\(Checkout\\|Patch\\)" status) | |
363 'needs-checkout) | |
364 ((string-match "Unresolved Conflict" status) 'unresolved-conflict) | |
365 ((string-match "Locally Added" status) 'locally-added) | |
366 (t 'unknown) | |
367 )))))))) | |
368 (if (get-buffer "*vc-info*") | |
369 (kill-buffer (get-buffer "*vc-info*"))))) | |
370 | |
371 ;;; Functions that determine property values, by examining the | |
372 ;;; working file, the master file, or log program output | |
373 | |
374 (defun vc-consult-rcs-headers (file) | |
375 ;; Search for RCS headers in FILE, and set properties | |
376 ;; accordingly. This function can be disabled by setting | |
377 ;; vc-consult-headers to nil. | |
378 ;; Returns: nil if no headers were found | |
379 ;; (or if the feature is disabled, | |
380 ;; or if there is currently no buffer | |
381 ;; visiting FILE) | |
382 ;; 'rev if a workfile revision was found | |
383 ;; 'rev-and-lock if revision and lock info was found | |
384 (cond | |
385 ((or (not vc-consult-headers) | |
386 (not (get-file-buffer file))) nil) | |
387 ((let (status version locking-user) | |
388 (save-excursion | |
389 (set-buffer (get-file-buffer file)) | |
390 (goto-char (point-min)) | |
391 (cond | |
392 ;; search for $Id or $Header | |
393 ;; ------------------------- | |
394 ((or (and (search-forward "$Id: " nil t) | |
395 (looking-at "[^ ]+ \\([0-9.]+\\) ")) | |
396 (and (progn (goto-char (point-min)) | |
397 (search-forward "$Header: " nil t)) | |
398 (looking-at "[^ ]+ \\([0-9.]+\\) "))) | |
399 (goto-char (match-end 0)) | |
400 ;; if found, store the revision number ... | |
401 (setq version (buffer-substring-no-properties (match-beginning 1) | |
402 (match-end 1))) | |
403 ;; ... and check for the locking state | |
404 (cond | |
405 ((looking-at | |
406 (concat "[0-9]+[/-][01][0-9][/-][0-3][0-9] " ; date | |
407 "[0-2][0-9]:[0-5][0-9]+:[0-6][0-9]+\\([+-][0-9:]+\\)? " ; time | |
408 "[^ ]+ [^ ]+ ")) ; author & state | |
409 (goto-char (match-end 0)) ; [0-6] in regexp handles leap seconds | |
410 (cond | |
411 ;; unlocked revision | |
412 ((looking-at "\\$") | |
413 (setq locking-user 'none) | |
414 (setq status 'rev-and-lock)) | |
415 ;; revision is locked by some user | |
416 ((looking-at "\\([^ ]+\\) \\$") | |
417 (setq locking-user | |
418 (buffer-substring-no-properties (match-beginning 1) | |
419 (match-end 1))) | |
420 (setq status 'rev-and-lock)) | |
421 ;; everything else: false | |
422 (nil))) | |
423 ;; unexpected information in | |
424 ;; keyword string --> quit | |
425 (nil))) | |
426 ;; search for $Revision | |
427 ;; -------------------- | |
428 ((re-search-forward (concat "\\$" | |
429 "Revision: \\([0-9.]+\\) \\$") | |
430 nil t) | |
431 ;; if found, store the revision number ... | |
432 (setq version (buffer-substring-no-properties (match-beginning 1) | |
433 (match-end 1))) | |
434 ;; and see if there's any lock information | |
435 (goto-char (point-min)) | |
436 (if (re-search-forward (concat "\\$" "Locker:") nil t) | |
437 (cond ((looking-at " \\([^ ]+\\) \\$") | |
438 (setq locking-user (buffer-substring-no-properties | |
439 (match-beginning 1) | |
440 (match-end 1))) | |
441 (setq status 'rev-and-lock)) | |
442 ((looking-at " *\\$") | |
443 (setq locking-user 'none) | |
444 (setq status 'rev-and-lock)) | |
445 (t | |
446 (setq locking-user 'none) | |
447 (setq status 'rev-and-lock))) | |
448 (setq status 'rev))) | |
449 ;; else: nothing found | |
450 ;; ------------------- | |
451 (t nil))) | |
452 (if status (vc-file-setprop file 'vc-workfile-version version)) | |
453 (and (eq status 'rev-and-lock) | |
454 (eq (vc-backend file) 'RCS) | |
455 (vc-file-setprop file 'vc-locking-user locking-user) | |
456 ;; If the file has headers, we don't want to query the master file, | |
457 ;; because that would eliminate all the performance gain the headers | |
458 ;; brought us. We therefore use a heuristic for the checkout model | |
459 ;; now: If we trust the file permissions, and the file is not | |
460 ;; locked, then if the file is read-only the checkout model is | |
461 ;; `manual', otherwise `implicit'. | |
462 (not (vc-mistrust-permissions file)) | |
463 (not (vc-locking-user file)) | |
464 (if (string-match ".r-..-..-." (nth 8 (file-attributes file))) | |
465 (vc-file-setprop file 'vc-checkout-model 'manual) | |
466 (vc-file-setprop file 'vc-checkout-model 'implicit))) | |
467 status)))) | |
468 | |
469 ;;; Access functions to file properties | |
470 ;;; (Properties should be _set_ using vc-file-setprop, but | |
471 ;;; _retrieved_ only through these functions, which decide | |
472 ;;; if the property is already known or not. A property should | |
473 ;;; only be retrieved by vc-file-getprop if there is no | |
474 ;;; access function.) | |
475 | |
476 ;;; properties indicating the backend | |
477 ;;; being used for FILE | |
478 | |
479 (defun vc-backend-subdirectory-name (&optional file) | |
480 ;; Where the master and lock files for the current directory are kept | |
481 (symbol-name | |
482 (or | |
483 (and file (vc-backend file)) | |
484 vc-default-back-end | |
485 (setq vc-default-back-end (if (vc-find-binary "rcs") 'RCS 'SCCS))))) | |
486 | |
487 (defun vc-name (file) | |
488 "Return the master name of a file, nil if it is not registered. | |
489 For CVS, the full name of CVS/Entries is returned." | |
490 (or (vc-file-getprop file 'vc-name) | |
491 (let ((name-and-type (vc-registered file))) | |
492 (if name-and-type | |
493 (progn | |
494 (vc-file-setprop file 'vc-backend (cdr name-and-type)) | |
495 (vc-file-setprop file 'vc-name (car name-and-type))))))) | |
496 | |
497 (defun vc-backend (file) | |
498 "Return the version-control type of a file, nil if it is not registered." | |
499 (and file | |
500 (or (vc-file-getprop file 'vc-backend) | |
501 (let ((name-and-type (vc-registered file))) | |
502 (if name-and-type | |
503 (progn | |
504 (vc-file-setprop file 'vc-name (car name-and-type)) | |
505 (vc-file-setprop file 'vc-backend (cdr name-and-type)))))))) | |
506 | |
507 (defun vc-checkout-model (file) | |
508 ;; Return `manual' if the user has to type C-x C-q to check out FILE. | |
509 ;; Return `implicit' if the file can be modified without locking it first. | |
510 (or | |
511 (vc-file-getprop file 'vc-checkout-model) | |
512 (cond | |
513 ((eq (vc-backend file) 'SCCS) | |
514 (vc-file-setprop file 'vc-checkout-model 'manual)) | |
515 ((eq (vc-backend file) 'RCS) | |
516 (vc-consult-rcs-headers file) | |
517 (or (vc-file-getprop file 'vc-checkout-model) | |
518 (progn (vc-fetch-master-properties file) | |
519 (vc-file-getprop file 'vc-checkout-model)))) | |
520 ((eq (vc-backend file) 'CVS) | |
521 (vc-file-setprop file 'vc-checkout-model | |
522 (if (getenv "CVSREAD") 'manual 'implicit)))))) | |
523 | |
524 ;;; properties indicating the locking state | |
525 | |
526 (defun vc-cvs-status (file) | |
527 ;; Return the cvs status of FILE | |
528 ;; (Status field in output of "cvs status") | |
529 (cond ((vc-file-getprop file 'vc-cvs-status)) | |
530 (t (vc-fetch-master-properties file) | |
531 (vc-file-getprop file 'vc-cvs-status)))) | |
532 | |
533 (defun vc-master-locks (file) | |
534 ;; Return the lock entries in the master of FILE. | |
535 ;; Return 'none if there are no such entries, and a list | |
536 ;; of the form ((VERSION USER) (VERSION USER) ...) otherwise. | |
537 (cond ((vc-file-getprop file 'vc-master-locks)) | |
538 (t (vc-fetch-master-properties file) | |
539 (vc-file-getprop file 'vc-master-locks)))) | |
540 | |
541 (defun vc-master-locking-user (file) | |
542 ;; Return the master file's idea of who is locking | |
543 ;; the current workfile version of FILE. | |
544 ;; Return 'none if it is not locked. | |
545 (let ((master-locks (vc-master-locks file)) lock) | |
546 (if (eq master-locks 'none) 'none | |
547 ;; search for a lock on the current workfile version | |
548 (setq lock (assoc (vc-workfile-version file) master-locks)) | |
549 (cond (lock (cdr lock)) | |
550 ('none))))) | |
551 | |
552 (defun vc-lock-from-permissions (file) | |
553 ;; If the permissions can be trusted for this file, determine the | |
554 ;; locking state from them. Returns (user-login-name), `none', or nil. | |
555 ;; This implementation assumes that any file which is under version | |
556 ;; control and has -rw-r--r-- is locked by its owner. This is true | |
557 ;; for both RCS and SCCS, which keep unlocked files at -r--r--r--. | |
558 ;; We have to be careful not to exclude files with execute bits on; | |
559 ;; scripts can be under version control too. Also, we must ignore the | |
560 ;; group-read and other-read bits, since paranoid users turn them off. | |
561 ;; This hack wins because calls to the somewhat expensive | |
562 ;; `vc-fetch-master-properties' function only have to be made if | |
563 ;; (a) the file is locked by someone other than the current user, | |
564 ;; or (b) some untoward manipulation behind vc's back has changed | |
565 ;; the owner or the `group' or `other' write bits. | |
566 (let ((attributes (file-attributes file))) | |
567 (if (not (vc-mistrust-permissions file)) | |
568 (cond ((string-match ".r-..-..-." (nth 8 attributes)) | |
569 (vc-file-setprop file 'vc-locking-user 'none)) | |
570 ((and (= (nth 2 attributes) (user-uid)) | |
571 (string-match ".rw..-..-." (nth 8 attributes))) | |
572 (vc-file-setprop file 'vc-locking-user (vc-user-login-name))) | |
573 (nil))))) | |
574 | |
575 (defun vc-user-login-name (&optional uid) | |
576 ;; Return the name under which the user is logged in, as a string. | |
577 ;; (With optional argument UID, return the name of that user.) | |
578 ;; This function does the same as `user-login-name', but unlike | |
579 ;; that, it never returns nil. If a UID cannot be resolved, that | |
580 ;; UID is returned as a string. | |
581 (or (user-login-name uid) | |
582 (and uid (number-to-string uid)) | |
583 (number-to-string (user-uid)))) | |
584 | |
585 (defun vc-file-owner (file) | |
586 ;; Return who owns FILE (user name, as a string). | |
587 (vc-user-login-name (nth 2 (file-attributes file)))) | |
588 | |
589 (defun vc-rcs-lock-from-diff (file) | |
590 ;; Diff the file against the master version. If differences are found, | |
591 ;; mark the file locked. This is only used for RCS with non-strict | |
592 ;; locking. (If "rcsdiff" doesn't understand --brief, we do a double-take | |
593 ;; and remember the fact for the future.) | |
594 (let* ((version (concat "-r" (vc-workfile-version file))) | |
595 (status (if (eq vc-rcsdiff-knows-brief 'no) | |
596 (vc-simple-command 1 "rcsdiff" file version) | |
597 (vc-simple-command 2 "rcsdiff" file "--brief" version)))) | |
598 (if (eq status 2) | |
599 (if (not vc-rcsdiff-knows-brief) | |
600 (setq vc-rcsdiff-knows-brief 'no | |
601 status (vc-simple-command 1 "rcsdiff" file version)) | |
602 (error "rcsdiff failed.")) | |
603 (if (not vc-rcsdiff-knows-brief) (setq vc-rcsdiff-knows-brief 'yes))) | |
604 (if (zerop status) | |
605 (vc-file-setprop file 'vc-locking-user 'none) | |
606 (vc-file-setprop file 'vc-locking-user (vc-file-owner file))))) | |
607 | |
608 (defun vc-locking-user (file) | |
609 ;; Return the name of the person currently holding a lock on FILE. | |
610 ;; Return nil if there is no such person. | |
611 ;; Under CVS, a file is considered locked if it has been modified since | |
612 ;; it was checked out. | |
613 ;; The property is cached. It is only looked up if it is currently nil. | |
614 ;; Note that, for a file that is not locked, the actual property value | |
615 ;; is `none', to distinguish it from an unknown locking state. That value | |
616 ;; is converted to nil by this function, and returned to the caller. | |
617 (let ((locking-user (vc-file-getprop file 'vc-locking-user))) | |
618 (if locking-user | |
619 ;; if we already know the property, return it | |
620 (if (eq locking-user 'none) nil locking-user) | |
621 | |
622 ;; otherwise, infer the property... | |
623 (cond | |
624 ((eq (vc-backend file) 'CVS) | |
625 (or (and (eq (vc-checkout-model file) 'manual) | |
626 (vc-lock-from-permissions file)) | |
627 (and (equal (vc-file-getprop file 'vc-checkout-time) | |
628 (nth 5 (file-attributes file))) | |
629 (vc-file-setprop file 'vc-locking-user 'none)) | |
630 (vc-file-setprop file 'vc-locking-user (vc-file-owner file)))) | |
631 | |
632 ((eq (vc-backend file) 'RCS) | |
633 (let (p-lock) | |
634 | |
635 ;; Check for RCS headers first | |
636 (or (eq (vc-consult-rcs-headers file) 'rev-and-lock) | |
637 | |
638 ;; If there are no headers, try to learn it | |
639 ;; from the permissions. | |
640 (and (setq p-lock (vc-lock-from-permissions file)) | |
641 (if (eq p-lock 'none) | |
642 | |
643 ;; If the permissions say "not locked", we know | |
644 ;; that the checkout model must be `manual'. | |
645 (vc-file-setprop file 'vc-checkout-model 'manual) | |
646 | |
647 ;; If the permissions say "locked", we can only trust | |
648 ;; this *if* the checkout model is `manual'. | |
649 (eq (vc-checkout-model file) 'manual))) | |
650 | |
651 ;; Otherwise, use lock information from the master file. | |
652 (vc-file-setprop file 'vc-locking-user | |
653 (vc-master-locking-user file))) | |
654 | |
655 ;; Finally, if the file is not explicitly locked | |
656 ;; it might still be locked implicitly. | |
657 (and (eq (vc-file-getprop file 'vc-locking-user) 'none) | |
658 (eq (vc-checkout-model file) 'implicit) | |
659 (vc-rcs-lock-from-diff file)))) | |
660 | |
661 ((eq (vc-backend file) 'SCCS) | |
662 (or (vc-lock-from-permissions file) | |
663 (vc-file-setprop file 'vc-locking-user | |
664 (vc-master-locking-user file))))) | |
665 | |
666 ;; convert a possible 'none value | |
667 (setq locking-user (vc-file-getprop file 'vc-locking-user)) | |
668 (if (eq locking-user 'none) nil locking-user)))) | |
669 | |
670 ;;; properties to store current and recent version numbers | |
671 | |
672 (defun vc-latest-version (file) | |
673 ;; Return version level of the latest version of FILE | |
674 (cond ((vc-file-getprop file 'vc-latest-version)) | |
675 (t (vc-fetch-properties file) | |
676 (vc-file-getprop file 'vc-latest-version)))) | |
677 | |
678 (defun vc-your-latest-version (file) | |
679 ;; Return version level of the latest version of FILE checked in by you | |
680 (cond ((vc-file-getprop file 'vc-your-latest-version)) | |
681 (t (vc-fetch-properties file) | |
682 (vc-file-getprop file 'vc-your-latest-version)))) | |
683 | |
684 (defun vc-master-workfile-version (file) | |
685 ;; Return the master file's idea of what is the current workfile version. | |
686 ;; This property is defined for RCS only. | |
687 (cond ((vc-file-getprop file 'vc-master-workfile-version)) | |
688 (t (vc-fetch-master-properties file) | |
689 (vc-file-getprop file 'vc-master-workfile-version)))) | |
690 | |
691 (defun vc-fetch-properties (file) | |
692 ;; Fetch vc-latest-version and vc-your-latest-version | |
693 ;; if that wasn't already done. | |
694 (cond | |
695 ((eq (vc-backend file) 'RCS) | |
696 (save-excursion | |
697 (set-buffer (get-buffer-create "*vc-info*")) | |
698 (vc-insert-file (vc-name file) "^desc") | |
699 (vc-parse-buffer | |
700 (list '("^\\([0-9]+\\.[0-9.]+\\)\ndate[ \t]+\\([0-9.]+\\);" 1 2) | |
701 (list (concat "^\\([0-9]+\\.[0-9.]+\\)\n" | |
702 "date[ \t]+\\([0-9.]+\\);[ \t]+" | |
703 "author[ \t]+" | |
704 (regexp-quote (vc-user-login-name)) ";") 1 2)) | |
705 file | |
706 '(vc-latest-version vc-your-latest-version)) | |
707 (if (get-buffer "*vc-info*") | |
708 (kill-buffer (get-buffer "*vc-info*"))))) | |
709 (t (vc-fetch-master-properties file)) | |
710 )) | |
711 | |
712 (defun vc-workfile-version (file) | |
713 ;; Return version level of the current workfile FILE | |
714 ;; This is attempted by first looking at the RCS keywords. | |
715 ;; If there are no keywords in the working file, | |
716 ;; vc-master-workfile-version is taken. | |
717 ;; Note that this property is cached, that is, it is only | |
718 ;; looked up if it is nil. | |
719 ;; For SCCS, this property is equivalent to vc-latest-version. | |
720 (cond ((vc-file-getprop file 'vc-workfile-version)) | |
721 ((eq (vc-backend file) 'SCCS) (vc-latest-version file)) | |
722 ((eq (vc-backend file) 'RCS) | |
723 (if (vc-consult-rcs-headers file) | |
724 (vc-file-getprop file 'vc-workfile-version) | |
725 (let ((rev (cond ((vc-master-workfile-version file)) | |
726 ((vc-latest-version file))))) | |
727 (vc-file-setprop file 'vc-workfile-version rev) | |
728 rev))) | |
729 ((eq (vc-backend file) 'CVS) | |
730 (if (vc-consult-rcs-headers file) ;; CVS | |
731 (vc-file-getprop file 'vc-workfile-version) | |
732 (catch 'found | |
733 (vc-find-cvs-master (file-name-directory file) | |
734 (file-name-nondirectory file))) | |
735 (vc-file-getprop file 'vc-workfile-version))))) | |
736 | |
84 ;;; actual version-control code starts here | 737 ;;; actual version-control code starts here |
85 | 738 |
86 (defun vc-registered (file) | 739 (defun vc-registered (file) |
87 (let (handler) | 740 (let (handler handlers) |
88 (if (boundp 'file-name-handler-alist) | 741 (if (boundp 'file-name-handler-alist) |
89 (setq handler (find-file-name-handler file 'vc-registered))) | 742 (setq handler (find-file-name-handler file 'vc-registered))) |
90 (if handler | 743 (if handler |
91 (funcall handler 'vc-registered file) | 744 (funcall handler 'vc-registered file) |
92 ;; Search for a master corresponding to the given file | 745 ;; Search for a master corresponding to the given file |
93 (let ((dirname (or (file-name-directory file) "")) | 746 (let ((dirname (or (file-name-directory file) "")) |
94 (basename (file-name-nondirectory file))) | 747 (basename (file-name-nondirectory file))) |
95 (catch 'found | 748 (catch 'found |
96 (mapcar | 749 (mapcar |
97 (function (lambda (s) | 750 (function (lambda (s) |
98 (if (atom s) | 751 (if (atom s) |
99 (funcall s dirname basename) | 752 (funcall s dirname basename) |
100 (let ((trial (format (car s) dirname basename))) | 753 (let ((trial (format (car s) dirname basename))) |
101 (if (and (file-exists-p trial) | 754 (if (and (file-exists-p trial) |
102 ;; Make sure the file we found with name | 755 ;; Make sure the file we found with name |
103 ;; TRIAL is not the source file itself. | 756 ;; TRIAL is not the source file itself. |
104 ;; That can happen with RCS-style names | 757 ;; That can happen with RCS-style names |
105 ;; if the file name is truncated | 758 ;; if the file name is truncated |
106 ;; (e.g. to 14 chars). See if either | 759 ;; (e.g. to 14 chars). See if either |
107 ;; directory or attributes differ. | 760 ;; directory or attributes differ. |
108 (or (not (string= dirname | 761 (or (not (string= dirname |
109 (file-name-directory trial))) | 762 (file-name-directory trial))) |
110 (not (equal | 763 (not (equal |
111 (file-attributes file) | 764 (file-attributes file) |
112 (file-attributes trial))))) | 765 (file-attributes trial))))) |
113 (throw 'found (cons trial (cdr s)))))))) | 766 (throw 'found (cons trial (cdr s)))))))) |
114 vc-master-templates) | 767 vc-master-templates) |
115 nil))))) | 768 nil))))) |
116 | 769 |
117 (defun vc-find-cvs-master (dirname basename) | 770 (defun vc-find-cvs-master (dirname basename) |
118 ;; Check if DIRNAME/BASENAME is handled by CVS. | 771 ;; Check if DIRNAME/BASENAME is handled by CVS. |
119 ;; If it is, do a (throw 'found (cons MASTER 'CVS)). | 772 ;; If it is, do a (throw 'found (cons MASTER 'CVS)). |
120 ;; Note: If the file is ``cvs add''ed but not yet ``cvs commit''ed | 773 ;; Note: This function throws the name of CVS/Entries |
121 ;; the MASTER will not actually exist yet. The other parts of VC | 774 ;; NOT that of the RCS master file (because we wouldn't be able |
122 ;; checks for this condition. This function returns something random if | 775 ;; to access it under remote CVS). |
123 ;; DIRNAME/BASENAME is not handled by CVS. | 776 ;; The function returns nil if DIRNAME/BASENAME is not handled by CVS. |
124 (and (string= "" dirname) (setq dirname default-directory)) | 777 (if (and vc-handle-cvs |
125 (if (and (file-directory-p (concat dirname "CVS/")) | 778 (file-directory-p (concat dirname "CVS/")) |
126 (file-readable-p (concat dirname "CVS/Entries"))) | 779 (file-readable-p (concat dirname "CVS/Entries"))) |
127 (let ((fname (concat dirname basename)) | 780 (let (buffer time (fold case-fold-search) |
128 sbuf rev) | 781 (file (concat dirname basename))) |
129 (unwind-protect | 782 (unwind-protect |
130 (save-excursion | 783 (save-excursion |
131 (set-buffer (generate-new-buffer " vc-scratch")) | 784 (setq buffer (set-buffer (get-buffer-create "*vc-info*"))) |
132 (setq sbuf (current-buffer)) | 785 (vc-insert-file (concat dirname "CVS/Entries")) |
133 (insert-file-contents (concat dirname "CVS/Entries")) | 786 (goto-char (point-min)) |
787 ;; make sure the file name is searched | |
788 ;; case-sensitively | |
789 (setq case-fold-search nil) | |
134 (cond | 790 (cond |
791 ;; normal entry | |
135 ((re-search-forward | 792 ((re-search-forward |
136 (concat "^/" (regexp-quote basename) "/\\([0-9.]*\\)/.*/\\(T\\([^/\n]+\\)\\)?$") | 793 (concat "^/" (regexp-quote basename) |
794 "/\\([^/]*\\)/[^ /]* \\([A-Z][a-z][a-z]\\) *\\([0-9]*\\) \\([0-9]*\\):\\([0-9]*\\):\\([0-9]*\\) \\([0-9]*\\)") | |
137 nil t) | 795 nil t) |
138 ;; We found it. Store version number, and branch tag | 796 (setq case-fold-search fold) ;; restore the old value |
139 (setq rev (buffer-substring (match-beginning 1) | 797 ;; We found it. Store away version number now that we |
140 (match-end 1))) | 798 ;; are anyhow so close to finding it. |
141 (vc-file-setprop fname 'vc-your-latest-version rev) | 799 (vc-file-setprop file |
142 ;; XEmacs - we put something useful in the modeline | 800 'vc-workfile-version |
143 (vc-file-setprop fname 'sticky-tag | 801 (match-string 1)) |
144 (cond ((string= "0" rev) "newfile") | 802 ;; If the file hasn't been modified since checkout, |
145 ((match-beginning 3) | 803 ;; store the checkout-time. |
146 (buffer-substring (match-beginning 3) | 804 (let ((mtime (nth 5 (file-attributes file))) |
147 (match-end 3))) | 805 (second (string-to-number (match-string 6))) |
148 (t "main"))) | 806 (minute (string-to-number (match-string 5))) |
149 (erase-buffer) | 807 (hour (string-to-number (match-string 4))) |
150 (insert-file-contents (concat dirname "CVS/Repository")) | 808 (day (string-to-number (match-string 3))) |
151 (let ((master | 809 (year (string-to-number (match-string 7)))) |
152 (concat (file-name-as-directory | 810 (if (equal mtime |
153 (buffer-substring (point-min) | 811 (encode-time |
154 (1- (point-max)))) | 812 second minute hour day |
155 basename | 813 (/ (string-match |
156 ",v"))) | 814 (match-string 2) |
157 (throw 'found (cons master 'CVS)))))) | 815 "xxxJanFebMarAprMayJunJulAugSepOctNovDec") |
158 (kill-buffer sbuf))))) | 816 3) |
159 | 817 year 0)) |
160 (defun vc-name (file) | 818 (vc-file-setprop file 'vc-checkout-time mtime) |
161 "Return the master name of a file, nil if it is not registered." | 819 (vc-file-setprop file 'vc-checkout-time 0))) |
162 (or (vc-file-getprop file 'vc-name) | 820 (throw 'found (cons (concat dirname "CVS/Entries") 'CVS))) |
163 (let ((name-and-type (vc-registered file))) | 821 ;; entry for a "locally added" file (not yet committed) |
164 (if name-and-type | 822 ((re-search-forward |
165 (progn | 823 (concat "^/" (regexp-quote basename) "/0/Initial ") nil t) |
166 (vc-file-setprop file 'vc-backend (cdr name-and-type)) | 824 (setq case-fold-search fold) ;; restore the old value |
167 (vc-file-setprop file 'vc-name (car name-and-type))))))) | 825 (vc-file-setprop file 'vc-checkout-time 0) |
168 | 826 (vc-file-setprop file 'vc-workfile-version "0") |
169 (defun vc-backend-deduce (file) | 827 (throw 'found (cons (concat dirname "CVS/Entries") 'CVS))) |
170 "Return the version-control type of a file, nil if it is not registered." | 828 (t (setq case-fold-search fold) ;; restore the old value |
171 (and file | 829 nil))) |
172 (or (vc-file-getprop file 'vc-backend) | 830 (kill-buffer buffer))))) |
173 (let ((name-and-type (vc-registered file))) | 831 |
174 (if name-and-type | 832 (defun vc-buffer-backend () |
175 (progn | 833 "Return the version-control type of the visited file, or nil if none." |
176 (vc-file-setprop file 'vc-name (car name-and-type)) | 834 (if (eq vc-buffer-backend t) |
177 (vc-file-setprop file 'vc-backend (cdr name-and-type)))))))) | 835 (setq vc-buffer-backend (vc-backend (buffer-file-name))) |
836 vc-buffer-backend)) | |
178 | 837 |
179 (defun vc-toggle-read-only (&optional verbose) | 838 (defun vc-toggle-read-only (&optional verbose) |
180 "Change read-only status of current buffer, perhaps via version control. | 839 "Change read-only status of current buffer, perhaps via version control. |
181 If the buffer is visiting a file registered with a form of version control | 840 If the buffer is visiting a file registered with version control, |
182 that locks files by making them read-only (i.e.: not CVS), then check the | 841 then check the file in or out. Otherwise, just change the read-only flag |
183 file in or out. Otherwise, just change the read-only flag of the buffer. | 842 of the buffer. With prefix argument, ask for version number." |
184 | |
185 If you provide a prefix argument, we pass it on to `vc-next-action'." | |
186 (interactive "P") | 843 (interactive "P") |
187 (let ((vc-type (vc-backend-deduce (buffer-file-name)))) | 844 (if (vc-backend (buffer-file-name)) |
188 (cond ((and vc-type | 845 (vc-next-action verbose) |
189 buffer-read-only | 846 (toggle-read-only))) |
190 (file-writable-p buffer-file-name) | |
191 (/= 0 (user-uid))) | |
192 ;; XEmacs - The buffer isn't read-only because it's locked, so | |
193 ;; keep vc out of this... | |
194 (toggle-read-only)) | |
195 ((and vc-type (not (eq 'CVS vc-type))) | |
196 (vc-next-action verbose)) | |
197 (t | |
198 (toggle-read-only))) | |
199 )) | |
200 | |
201 (define-key global-map "\C-x\C-q" 'vc-toggle-read-only) | 847 (define-key global-map "\C-x\C-q" 'vc-toggle-read-only) |
202 | 848 |
203 (defun vc-file-owner (file) | 849 (defun vc-after-save () |
204 ;; XEmacs - vc-locking-user is just WAY too slow. | 850 ;; Function to be called by basic-save-buffer (in files.el). |
205 (let* ((fa (file-attributes file))) | 851 ;; If the file in the current buffer is under version control, |
206 (cond ((eq ?w (aref (nth 8 fa) 2)) ; -rw-r--r-- | 852 ;; not locked, and the checkout model for it is `implicit', |
207 ;; #### - if it's writable, we trust unix...dumb move? | 853 ;; mark it "locked" and redisplay the mode line. |
208 (user-login-name (nth 2 fa))) | 854 (let ((file (buffer-file-name))) |
209 (t | 855 (and (vc-file-getprop file 'vc-backend) |
210 ;; big slowness here... | 856 ;; ...check the property directly, not through the function of the |
211 (require 'vc) | 857 ;; same name. Otherwise Emacs would check for a master file |
212 (vc-locking-user file) | 858 ;; each time a non-version-controlled buffer is saved. |
213 )))) | 859 ;; The property is computed when the file is visited, so if it |
860 ;; is `nil' now, it is certain that the file is NOT | |
861 ;; version-controlled. | |
862 (or (and (equal (vc-file-getprop file 'vc-checkout-time) | |
863 (nth 5 (file-attributes file))) | |
864 ;; File has been saved in the same second in which | |
865 ;; it was checked out. Clear the checkout-time | |
866 ;; to avoid confusion. | |
867 (vc-file-setprop file 'vc-checkout-time nil)) | |
868 t) | |
869 (not (vc-locking-user file)) | |
870 (eq (vc-checkout-model file) 'implicit) | |
871 (vc-file-setprop file 'vc-locking-user (vc-user-login-name)) | |
872 (or (and (eq (vc-backend file) 'CVS) | |
873 (vc-file-setprop file 'vc-cvs-status nil)) | |
874 t) | |
875 (vc-mode-line file)))) | |
214 | 876 |
215 (defun vc-mode-line (file &optional label) | 877 (defun vc-mode-line (file &optional label) |
216 "Set `vc-mode' to display type of version control for FILE. | 878 "Set `vc-mode' to display type of version control for FILE. |
217 The value is set in the current buffer, which should be the buffer | 879 The value is set in the current buffer, which should be the buffer |
218 visiting FILE. Second optional arg LABEL is put in place of version | 880 visiting FILE. Second optional arg LABEL is put in place of version |
219 control system name." | 881 control system name." |
220 (interactive (list buffer-file-name nil)) | 882 (interactive (list buffer-file-name nil)) |
221 (if file | 883 (let ((vc-type (vc-backend file))) |
222 (let ((vc-type (vc-backend-deduce file))) | 884 (setq vc-mode |
223 (setq vc-mode | 885 (and vc-type |
224 (if vc-type | 886 (concat " " (or label (symbol-name vc-type)) |
225 (concat " " (or label (symbol-name vc-type)) | 887 (and vc-display-status (vc-status file))))) |
226 (if vc-display-status | 888 ;; If the file is locked by some other user, make |
227 (vc-status file vc-type))))) | 889 ;; the buffer read-only. Like this, even root |
228 ;; Even root shouldn't modify a registered file without | 890 ;; cannot modify a file that someone else has locked. |
229 ;; locking it first. | 891 (and vc-type |
230 (and vc-type | 892 (equal file (buffer-file-name)) |
231 (not (string= (user-login-name) (vc-file-owner file))) | 893 (vc-locking-user file) |
232 (setq buffer-read-only t)) | 894 (not (string= (vc-user-login-name) (vc-locking-user file))) |
233 (and (null vc-type) | 895 (setq buffer-read-only t)) |
234 (file-symlink-p file) | 896 ;; If the user is root, and the file is not owner-writable, |
235 (let ((link-type (vc-backend-deduce (file-symlink-p file)))) | 897 ;; then pretend that we can't write it |
236 (if link-type | 898 ;; even though we can (because root can write anything). |
237 (message | 899 ;; This way, even root cannot modify a file that isn't locked. |
238 "Warning: symbolic link to %s-controlled source file" | 900 (and vc-type |
239 link-type)))) | 901 (equal file (buffer-file-name)) |
240 (redraw-modeline) | 902 (not buffer-read-only) |
241 ;;(set-buffer-modified-p (buffer-modified-p)) ;;use this if Emacs 18 | 903 (zerop (user-real-uid)) |
242 vc-type))) | 904 (zerop (logand (file-modes (buffer-file-name)) 128)) |
243 | 905 (setq buffer-read-only t)) |
244 (defun vc-status (file vc-type) | 906 (force-mode-line-update) |
907 ;;(set-buffer-modified-p (buffer-modified-p)) ;;use this if Emacs 18 | |
908 vc-type)) | |
909 | |
910 (defun vc-status (file) | |
245 ;; Return string for placement in modeline by `vc-mode-line'. | 911 ;; Return string for placement in modeline by `vc-mode-line'. |
246 ;; If FILE is not registered, return nil. | 912 ;; Format: |
247 ;; If FILE is registered but not locked, return " REV" if there is a head | 913 ;; |
248 ;; revision and " @@" otherwise. | 914 ;; "-REV" if the revision is not locked |
249 ;; If FILE is locked then return all locks in a string of the | 915 ;; ":REV" if the revision is locked by the user |
250 ;; form " LOCKER1:REV1 LOCKER2:REV2 ...", where "LOCKERi:" is empty if you | 916 ;; ":LOCKER:REV" if the revision is locked by somebody else |
251 ;; are the locker, and otherwise is the name of the locker followed by ":". | 917 ;; " @@" for a CVS file that is added, but not yet committed |
252 | 918 ;; |
253 ;; Algorithm: | 919 ;; In the CVS case, a "locked" working file is a |
254 | 920 ;; working file that is modified with respect to the master. |
255 ;; Check for master file corresponding to FILE being visited. | 921 ;; The file is "locked" from the moment when the user saves |
922 ;; the modified buffer. | |
256 ;; | 923 ;; |
257 ;; RCS: Insert the first few characters of the master file into a | 924 ;; This function assumes that the file is registered. |
258 ;; work buffer. Search work buffer for "locks...;" phrase; if not | 925 |
259 ;; found, then keep inserting more characters until the phrase is | 926 (let ((locker (vc-locking-user file)) |
260 ;; found. Extract the locks, and remove control characters | 927 (rev (vc-workfile-version file))) |
261 ;; separating them, like newlines; the string " user1:revision1 | 928 (cond ((string= "0" rev) |
262 ;; user2:revision2 ..." is returned. | 929 " @@") |
263 ;; | 930 ((not locker) |
264 ;; SCCS: Check if the p-file exists. If it does, read it and | 931 (concat "-" rev)) |
265 ;; extract the locks, giving them the right format. Else use prs to | 932 ((string= locker (vc-user-login-name)) |
266 ;; find the revision number. | 933 (concat ":" rev)) |
267 ;; | 934 (t |
268 ;; CVS: vc-find-cvs-master has already stored the current revision | 935 (concat ":" locker ":" rev))))) |
269 ;; number and sticky-tag for the file. XEmacs displays the sticky-tag. | 936 |
270 | 937 (defun vc-follow-link () |
271 ;; Limitations: | 938 ;; If the current buffer visits a symbolic link, this function makes it |
272 | 939 ;; visit the real file instead. If the real file is already visited in |
273 ;; The output doesn't show which version you are actually looking at. | 940 ;; another buffer, make that buffer current, and kill the buffer |
274 ;; The modeline can get quite cluttered when there are multiple locks. | 941 ;; that visits the link. |
275 ;; The head revision is probably not what you want if you've used `rcs -b'. | 942 (let* ((truename (abbreviate-file-name (file-chase-links buffer-file-name))) |
276 | 943 (true-buffer (find-buffer-visiting truename)) |
277 (let ((master (vc-name file)) | 944 (this-buffer (current-buffer))) |
278 found | 945 (if (eq true-buffer this-buffer) |
279 status) | 946 (progn |
280 | 947 (kill-buffer this-buffer) |
281 ;; If master file exists, then parse its contents, otherwise we | 948 ;; In principle, we could do something like set-visited-file-name. |
282 ;; return the nil value of this if form. | 949 ;; However, it can't be exactly the same as set-visited-file-name. |
283 (if (and master vc-type) | 950 ;; I'm not going to work out the details right now. -- rms. |
284 (save-excursion | 951 (set-buffer (find-file-noselect truename))) |
285 | 952 (set-buffer true-buffer) |
286 ;; Create work buffer. | 953 (kill-buffer this-buffer)))) |
287 (set-buffer (get-buffer-create " *vc-status*")) | |
288 (setq buffer-read-only nil | |
289 default-directory (file-name-directory master)) | |
290 (erase-buffer) | |
291 | |
292 ;; Set the `status' var to the return value. | |
293 (cond | |
294 | |
295 ;; RCS code. | |
296 ((eq vc-type 'RCS) | |
297 ;; Check if we have enough of the header. | |
298 ;; If not, then keep including more. | |
299 (while | |
300 (not (or found | |
301 (let ((s (buffer-size))) | |
302 (goto-char (1+ s)) | |
303 (zerop (car (cdr (insert-file-contents | |
304 master nil s (+ s 8192)))))))) | |
305 (beginning-of-line) | |
306 (setq found (re-search-forward "^locks\\([^;]*\\);" nil t))) | |
307 | |
308 (if found | |
309 ;; Clean control characters and self-locks from text. | |
310 (let* ((lock-pattern | |
311 (concat "[ \b\t\n\v\f\r]+\\(" | |
312 (regexp-quote (user-login-name)) | |
313 ":\\)?")) | |
314 (locks | |
315 (save-restriction | |
316 (narrow-to-region (match-beginning 1) (match-end 1)) | |
317 (goto-char (point-min)) | |
318 (while (re-search-forward lock-pattern nil t) | |
319 (replace-match (if (eobp) "" ":") t t)) | |
320 (buffer-string)))) | |
321 (setq status | |
322 (if (not (string-equal locks "")) | |
323 locks | |
324 (goto-char (point-min)) | |
325 (if (looking-at "head[ \b\t\n\v\f\r]+\\([.0-9]+\\)") | |
326 (concat "-" | |
327 (buffer-substring (match-beginning 1) | |
328 (match-end 1))) | |
329 " @@")))))) | |
330 | |
331 ;; SCCS code. | |
332 ((eq vc-type 'SCCS) | |
333 ;; Build the name of the p-file and put it in the work buffer. | |
334 (insert master) | |
335 (search-backward "/s.") | |
336 (delete-char 2) | |
337 (insert "/p") | |
338 (if (not (file-exists-p (buffer-string))) | |
339 ;; No lock. | |
340 (let ((exec-path (if (boundp 'vc-path) (append exec-path vc-path) | |
341 exec-path))) | |
342 (erase-buffer) | |
343 (insert "-") | |
344 (if (zerop (call-process "prs" nil t nil "-d:I:" master)) | |
345 (setq status (buffer-substring 1 (1- (point-max)))))) | |
346 ;; Locks exist. | |
347 (insert-file-contents (buffer-string) nil nil nil t) | |
348 (while (looking-at "[^ ]+ \\([^ ]+\\) \\([^ ]+\\).*\n") | |
349 (replace-match " \\2:\\1")) | |
350 (setq status (buffer-string)) | |
351 (aset status 0 ?:))) | |
352 ;; CVS code. | |
353 ((eq vc-type 'CVS) | |
354 ;; sticky-tag is initialized by vc-backend-deduce | |
355 (setq status (concat ":" (vc-file-getprop file 'sticky-tag) "-" | |
356 (vc-file-getprop file 'vc-your-latest-version) | |
357 )) | |
358 ) | |
359 ;; ClearCase code. | |
360 ((eq vc-type 'CC) | |
361 (require 'vc) | |
362 (setq status (concat ":" (vc-latest-version file))) | |
363 )) | |
364 | |
365 ;; Clean work buffer. | |
366 (erase-buffer) | |
367 (set-buffer-modified-p nil) | |
368 status)))) | |
369 | 954 |
370 ;;; install a call to the above as a find-file hook | 955 ;;; install a call to the above as a find-file hook |
371 (defun vc-find-file-hook () | 956 (defun vc-find-file-hook () |
372 ;; Recompute whether file is version controlled, | 957 ;; Recompute whether file is version controlled, |
373 ;; if user has killed the buffer and revisited. | 958 ;; if user has killed the buffer and revisited. |
374 (if buffer-file-name | 959 (cond |
375 (vc-file-setprop buffer-file-name 'vc-backend nil)) | 960 (buffer-file-name |
376 (if (and (vc-mode-line buffer-file-name) (not vc-make-backup-files)) | 961 (vc-file-clearprops buffer-file-name) |
377 (progn | 962 (cond |
378 ;; Use this variable, not make-backup-files, | 963 ((vc-backend buffer-file-name) |
379 ;; because this is for things that depend on the file name. | 964 (vc-mode-line buffer-file-name) |
380 (set (make-local-variable 'backup-inhibited) t)))) | 965 (cond ((not vc-make-backup-files) |
966 ;; Use this variable, not make-backup-files, | |
967 ;; because this is for things that depend on the file name. | |
968 (make-local-variable 'backup-inhibited) | |
969 (setq backup-inhibited t)))) | |
970 ((let* ((link (file-symlink-p buffer-file-name)) | |
971 (link-type (and link (vc-backend (file-chase-links link))))) | |
972 (if link-type | |
973 (cond ((eq vc-follow-symlinks nil) | |
974 (message | |
975 "Warning: symbolic link to %s-controlled source file" link-type)) | |
976 ((or (not (eq vc-follow-symlinks 'ask)) | |
977 ;; If we already visited this file by following | |
978 ;; the link, don't ask again if we try to visit | |
979 ;; it again. GUD does that, and repeated questions | |
980 ;; are painful. | |
981 (get-file-buffer | |
982 (abbreviate-file-name (file-chase-links buffer-file-name)))) | |
983 | |
984 (vc-follow-link) | |
985 (message "Followed link to %s" buffer-file-name) | |
986 (vc-find-file-hook)) | |
987 (t | |
988 (if (yes-or-no-p (format | |
989 "Symbolic link to %s-controlled source file; follow link? " link-type)) | |
990 (progn (vc-follow-link) | |
991 (message "Followed link to %s" buffer-file-name) | |
992 (vc-find-file-hook)) | |
993 (message | |
994 "Warning: editing through the link bypasses version control") | |
995 )))))))))) | |
381 | 996 |
382 (add-hook 'find-file-hooks 'vc-find-file-hook) | 997 (add-hook 'find-file-hooks 'vc-find-file-hook) |
383 | 998 |
384 ;;; more hooks, this time for file-not-found | 999 ;;; more hooks, this time for file-not-found |
385 (defun vc-file-not-found-hook () | 1000 (defun vc-file-not-found-hook () |
386 "When file is not found, try to check it out from RCS or SCCS. | 1001 "When file is not found, try to check it out from RCS or SCCS. |
387 Returns t if checkout was successful, nil otherwise." | 1002 Returns t if checkout was successful, nil otherwise." |
388 (if (vc-backend-deduce buffer-file-name) | 1003 (if (vc-backend buffer-file-name) |
389 (save-excursion | 1004 (save-excursion |
390 (require 'vc) | 1005 (require 'vc) |
1006 (setq default-directory (file-name-directory (buffer-file-name))) | |
391 (not (vc-error-occurred (vc-checkout buffer-file-name)))))) | 1007 (not (vc-error-occurred (vc-checkout buffer-file-name)))))) |
392 | 1008 |
393 (add-hook 'find-file-not-found-hooks 'vc-file-not-found-hook) | 1009 (add-hook 'find-file-not-found-hooks 'vc-file-not-found-hook) |
1010 | |
1011 ;; Discard info about a file when we kill its buffer. | |
1012 (defun vc-kill-buffer-hook () | |
1013 (if (stringp (buffer-file-name)) | |
1014 (progn | |
1015 (vc-file-clearprops (buffer-file-name)) | |
1016 (kill-local-variable 'vc-buffer-backend)))) | |
1017 | |
1018 ;;;(add-hook 'kill-buffer-hook 'vc-kill-buffer-hook) | |
394 | 1019 |
395 ;;; Now arrange for bindings and autoloading of the main package. | 1020 ;;; Now arrange for bindings and autoloading of the main package. |
396 ;;; Bindings for this have to go in the global map, as we'll often | 1021 ;;; Bindings for this have to go in the global map, as we'll often |
397 ;;; want to call them from random buffers. | 1022 ;;; want to call them from random buffers. |
398 | 1023 |
399 ; XEmacs - this is preloaded. let's not be obtuse! | 1024 (setq vc-prefix-map (lookup-key global-map "\C-xv")) |
400 (defconst vc-prefix-map | 1025 (if (not (keymapp vc-prefix-map)) |
401 (let ((map (make-sparse-keymap))) | 1026 (progn |
402 (set-keymap-name map 'vc-prefix-map) | 1027 (setq vc-prefix-map (make-sparse-keymap)) |
403 (define-key map "a" 'vc-update-change-log) | 1028 (define-key global-map "\C-xv" vc-prefix-map) |
404 (define-key map "c" 'vc-cancel-version) | 1029 (define-key vc-prefix-map "a" 'vc-update-change-log) |
405 (define-key map "d" 'vc-directory) | 1030 (define-key vc-prefix-map "c" 'vc-cancel-version) |
406 (define-key map "h" 'vc-insert-headers) | 1031 (define-key vc-prefix-map "d" 'vc-directory) |
407 (define-key map "i" 'vc-register) | 1032 (define-key vc-prefix-map "h" 'vc-insert-headers) |
408 (define-key map "l" 'vc-print-log) | 1033 (define-key vc-prefix-map "i" 'vc-register) |
409 (define-key map "r" 'vc-retrieve-snapshot) | 1034 (define-key vc-prefix-map "l" 'vc-print-log) |
410 (define-key map "s" 'vc-create-snapshot) | 1035 (define-key vc-prefix-map "r" 'vc-retrieve-snapshot) |
411 (define-key map "u" 'vc-revert-buffer) | 1036 (define-key vc-prefix-map "s" 'vc-create-snapshot) |
412 (define-key map "v" 'vc-next-action) | 1037 (define-key vc-prefix-map "u" 'vc-revert-buffer) |
413 (define-key map "=" 'vc-diff) | 1038 (define-key vc-prefix-map "v" 'vc-next-action) |
414 (define-key map "?" 'vc-file-status) ; XEmacs - this doesn't fit elsewhere | 1039 (define-key vc-prefix-map "=" 'vc-diff) |
415 (define-key map "~" 'vc-version-other-window) | 1040 #+xemacs (define-key vc-prefix-map "?" 'vc-file-status) |
416 (global-set-key "\C-xv" map) | 1041 (define-key vc-prefix-map "~" 'vc-version-other-window))) |
417 map | 1042 |
418 )) | 1043 ;; Emacs menus |
419 | 1044 ;(if (not (boundp 'vc-menu-map)) |
420 ;; FSF menus... | 1045 ; ;; Don't do the menu bindings if menu-bar.el wasn't loaded to defvar |
421 ;; (if (not (boundp 'vc-menu-map)) | 1046 ; ;; vc-menu-map. |
422 ;; ;; Don't do the menu bindings if menu-bar.el wasn't loaded to defvar | 1047 ; () |
423 ;; ;; vc-menu-map. | 1048 ; ;;(define-key vc-menu-map [show-files] |
424 ;; () | 1049 ; ;; '("Show Files under VC" . (vc-directory t))) |
425 ;; ;;(define-key vc-menu-map [show-files] | 1050 ; (define-key vc-menu-map [vc-directory] '("Show Locked Files" . vc-directory)) |
426 ;; ;; '("Show Files under VC" . (vc-directory t))) | 1051 ; (define-key vc-menu-map [separator1] '("----")) |
427 ;; (define-key vc-menu-map [vc-directory] '("Show Locked Files" . vc-directory)) | 1052 ; (define-key vc-menu-map [vc-rename-file] '("Rename File" . vc-rename-file)) |
428 ;; (define-key vc-menu-map [separator1] '("----")) | 1053 ; (define-key vc-menu-map [vc-version-other-window] |
429 ;; (define-key vc-menu-map [vc-rename-file] '("Rename File" . vc-rename-file)) | 1054 ; '("Show Other Version" . vc-version-other-window)) |
430 ;; (define-key vc-menu-map [vc-version-other-window] | 1055 ; (define-key vc-menu-map [vc-diff] '("Compare with Last Version" . vc-diff)) |
431 ;; '("Show Other Version" . vc-version-other-window)) | 1056 ; (define-key vc-menu-map [vc-update-change-log] |
432 ;; (define-key vc-menu-map [vc-diff] '("Compare with Last Version" . vc-diff)) | 1057 ; '("Update ChangeLog" . vc-update-change-log)) |
433 ;; (define-key vc-menu-map [vc-update-change-log] | 1058 ; (define-key vc-menu-map [vc-print-log] '("Show History" . vc-print-log)) |
434 ;; '("Update ChangeLog" . vc-update-change-log)) | 1059 ; (define-key vc-menu-map [separator2] '("----")) |
435 ;; (define-key vc-menu-map [vc-print-log] '("Show History" . vc-print-log)) | 1060 ; (define-key vc-menu-map [undo] '("Undo Last Check-In" . vc-cancel-version)) |
436 ;; (define-key vc-menu-map [separator2] '("----")) | 1061 ; (define-key vc-menu-map [vc-revert-buffer] |
437 ;; (define-key vc-menu-map [undo] '("Undo Last Check-In" . vc-cancel-version)) | 1062 ; '("Revert to Last Version" . vc-revert-buffer)) |
438 ;; (define-key vc-menu-map [vc-revert-buffer] | 1063 ; (define-key vc-menu-map [vc-insert-header] |
439 ;; '("Revert to Last Version" . vc-revert-buffer)) | 1064 ; '("Insert Header" . vc-insert-headers)) |
440 ;; (define-key vc-menu-map [vc-insert-header] | 1065 ; (define-key vc-menu-map [vc-menu-check-in] '("Check In" . vc-next-action)) |
441 ;; '("Insert Header" . vc-insert-headers)) | 1066 ; (define-key vc-menu-map [vc-check-out] '("Check Out" . vc-toggle-read-only)) |
442 ;; (define-key vc-menu-map [vc-menu-check-in] '("Check In" . vc-next-action)) | 1067 ; (define-key vc-menu-map [vc-register] '("Register" . vc-register))) |
443 ;; (define-key vc-menu-map [vc-check-out] '("Check Out" . vc-toggle-read-only)) | 1068 |
444 ;; (define-key vc-menu-map [vc-register] '("Register" . vc-register)) | 1069 ;(put 'vc-rename-file 'menu-enable 'vc-mode) |
445 ;; (put 'vc-rename-file 'menu-enable 'vc-mode) | 1070 ;(put 'vc-version-other-window 'menu-enable 'vc-mode) |
446 ;; (put 'vc-version-other-window 'menu-enable 'vc-mode) | 1071 ;(put 'vc-diff 'menu-enable 'vc-mode) |
447 ;; (put 'vc-diff 'menu-enable 'vc-mode) | 1072 ;(put 'vc-update-change-log 'menu-enable |
448 ;; (put 'vc-update-change-log 'menu-enable | 1073 ; '(eq (vc-buffer-backend) 'RCS)) |
449 ;; '(eq (vc-backend-deduce (buffer-file-name)) 'RCS)) | 1074 ;(put 'vc-print-log 'menu-enable 'vc-mode) |
450 ;; (put 'vc-print-log 'menu-enable 'vc-mode) | 1075 ;(put 'vc-cancel-version 'menu-enable 'vc-mode) |
451 ;; (put 'vc-cancel-version 'menu-enable 'vc-mode) | 1076 ;(put 'vc-revert-buffer 'menu-enable 'vc-mode) |
452 ;; (put 'vc-revert-buffer 'menu-enable 'vc-mode) | 1077 ;(put 'vc-insert-headers 'menu-enable 'vc-mode) |
453 ;; (put 'vc-insert-headers 'menu-enable 'vc-mode) | 1078 ;(put 'vc-next-action 'menu-enable 'vc-mode) |
454 ;; (put 'vc-next-action 'menu-enable '(and vc-mode (not buffer-read-only))) | 1079 ;(put 'vc-toggle-read-only 'menu-enable 'vc-mode) |
455 ;; (put 'vc-toggle-read-only 'menu-enable '(and vc-mode buffer-read-only)) | 1080 ;(put 'vc-register 'menu-enable '(and buffer-file-name (not vc-mode))) |
456 ;; (put 'vc-register 'menu-enable '(not vc-mode)) | 1081 |
457 ;; ) | |
458 | |
459 ;; #### - sync with fsf menus | |
460 (defconst vc-menu | 1082 (defconst vc-menu |
461 '("Version Control" | 1083 '("VC" |
462 :filter vc-menu-filter | 1084 :filter vc-menu-filter |
463 ["" vc-next-action buffer-file-name nil] | 1085 ["" vc-next-action buffer-file-name nil] |
464 ;; ^^^ this gets changed to checkin, checkout, register, or steal | 1086 ;; ^^^ this gets changed to checkin, checkout, register, or steal |
465 ["Show status of" vc-file-status nil nil] | 1087 ["Show status of" vc-file-status nil nil] |
466 ;;["Show Locked Files" vc-directory t] ;; needs new dired | 1088 ;;["Show Locked Files" vc-directory t] ;; needs new dired |
490 "Menubar entry for using the revision control system.") | 1112 "Menubar entry for using the revision control system.") |
491 | 1113 |
492 (defun vc-menu-filter (menu-items) | 1114 (defun vc-menu-filter (menu-items) |
493 (let* ((result menu-items) ; modify in-place | 1115 (let* ((result menu-items) ; modify in-place |
494 (case-fold-search t) | 1116 (case-fold-search t) |
495 (type (vc-backend-deduce buffer-file-name)) | 1117 (type (vc-backend buffer-file-name)) |
496 (file (if buffer-file-name | 1118 (file (if buffer-file-name |
497 (file-name-nondirectory buffer-file-name) | 1119 (file-name-nondirectory buffer-file-name) |
498 (buffer-name))) | 1120 (buffer-name))) |
499 op owner item status) | 1121 op owner item status) |
500 (setq op (cond ((null type) | 1122 (setq op (cond ((null type) |
539 (car (find-menu-item current-menubar '("Tools"))) | 1161 (car (find-menu-item current-menubar '("Tools"))) |
540 (add-submenu '("Tools") vc-menu "Compare") | 1162 (add-submenu '("Tools") vc-menu "Compare") |
541 (add-menu-button '("Tools") "---" "Compare")) | 1163 (add-menu-button '("Tools") "---" "Compare")) |
542 )) | 1164 )) |
543 | 1165 |
544 ;; #### called by files.el. Define it like this until we're merged. | 1166 ;;; End XEmacs menus |
545 (defun vc-after-save ()) | |
546 | 1167 |
547 (provide 'vc-hooks) | 1168 (provide 'vc-hooks) |
548 | 1169 |
549 ;;; vc-hooks.el ends here | 1170 ;;; vc-hooks.el ends here |