Commit | Line | Data |
---|---|---|
aae56ea7 | 1 | ;;; vc-hooks.el --- resident support for version-control |
594722a8 | 2 | |
0e0d9831 | 3 | ;; Copyright (C) 1992,93,94,95,96,98,99,2000 Free Software Foundation, Inc. |
594722a8 | 4 | |
0e0d9831 GM |
5 | ;; Author: FSF (see vc.el for full credits) |
6 | ;; Maintainer: Andre Spiegel <spiegel@gnu.org> | |
594722a8 | 7 | |
d455f4f7 | 8 | ;; $Id: vc-hooks.el,v 1.132 2001/07/11 22:05:01 monnier Exp $ |
f4c72097 | 9 | |
594722a8 ER |
10 | ;; This file is part of GNU Emacs. |
11 | ||
12 | ;; GNU Emacs is free software; you can redistribute it and/or modify | |
13 | ;; it under the terms of the GNU General Public License as published by | |
14 | ;; the Free Software Foundation; either version 2, or (at your option) | |
15 | ;; any later version. | |
16 | ||
17 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ;; GNU General Public License for more details. | |
21 | ||
22 | ;; You should have received a copy of the GNU General Public License | |
b578f267 EN |
23 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
24 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
25 | ;; Boston, MA 02111-1307, USA. | |
594722a8 ER |
26 | |
27 | ;;; Commentary: | |
28 | ||
0e0d9831 GM |
29 | ;; This is the always-loaded portion of VC. It takes care of |
30 | ;; VC-related activities that are done when you visit a file, so that | |
31 | ;; vc.el itself is loaded only when you use a VC command. See the | |
32 | ;; commentary of vc.el. | |
594722a8 ER |
33 | |
34 | ;;; Code: | |
35 | ||
c3ce5e29 AS |
36 | (eval-when-compile |
37 | (require 'cl)) | |
099bd78a | 38 | |
e1c0c2d1 KH |
39 | ;; Customization Variables (the rest is in vc.el) |
40 | ||
0e0d9831 GM |
41 | (defvar vc-ignore-vc-files nil "Obsolete -- use `vc-handled-backends'.") |
42 | (defvar vc-master-templates () "Obsolete -- use vc-BACKEND-master-templates.") | |
43 | (defvar vc-header-alist () "Obsolete -- use vc-BACKEND-header.") | |
44 | ||
45 | (defcustom vc-handled-backends '(RCS CVS SCCS) | |
46 | "*List of version control backends for which VC will be used. | |
47 | Entries in this list will be tried in order to determine whether a | |
48 | file is under that sort of version control. | |
49 | Removing an entry from the list prevents VC from being activated | |
50 | when visiting a file managed by that backend. | |
51 | An empty list disables VC altogether." | |
52 | :type '(repeat symbol) | |
099bd78a | 53 | :version "21.1" |
50bec091 | 54 | :group 'vc) |
31888047 | 55 | |
50bec091 | 56 | (defcustom vc-path |
e1c0c2d1 KH |
57 | (if (file-directory-p "/usr/sccs") |
58 | '("/usr/sccs") | |
59 | nil) | |
50bec091 KH |
60 | "*List of extra directories to search for version control commands." |
61 | :type '(repeat directory) | |
62 | :group 'vc) | |
e1c0c2d1 | 63 | |
50bec091 | 64 | (defcustom vc-make-backup-files nil |
5032bd23 | 65 | "*If non-nil, backups of registered files are made as with other files. |
50bec091 KH |
66 | If nil (the default), files covered by version control don't get backups." |
67 | :type 'boolean | |
68 | :group 'vc) | |
594722a8 | 69 | |
50bec091 | 70 | (defcustom vc-follow-symlinks 'ask |
0e0d9831 GM |
71 | "*What to do if visiting a symbolic link to a file under version control. |
72 | Editing such a file through the link bypasses the version control system, | |
73 | which is dangerous and probably not what you want. | |
74 | ||
75 | If this variable is t, VC follows the link and visits the real file, | |
b8063212 AS |
76 | telling you about it in the echo area. If it is `ask', VC asks for |
77 | confirmation whether it should follow the link. If nil, the link is | |
50bec091 | 78 | visited and a warning displayed." |
0e0d9831 GM |
79 | :type '(choice (const :tag "Ask for confirmation" ask) |
80 | (const :tag "Visit link and warn" nil) | |
81 | (const :tag "Follow link" t)) | |
50bec091 | 82 | :group 'vc) |
b8063212 | 83 | |
50bec091 | 84 | (defcustom vc-display-status t |
624c0e9d | 85 | "*If non-nil, display revision number and lock status in modeline. |
50bec091 KH |
86 | Otherwise, not displayed." |
87 | :type 'boolean | |
88 | :group 'vc) | |
89 | ||
198d5c00 | 90 | |
50bec091 KH |
91 | (defcustom vc-consult-headers t |
92 | "*If non-nil, identify work files by searching for version headers." | |
93 | :type 'boolean | |
94 | :group 'vc) | |
e1c0c2d1 | 95 | |
50bec091 | 96 | (defcustom vc-keep-workfiles t |
e1c0c2d1 KH |
97 | "*If non-nil, don't delete working files after registering changes. |
98 | If the back-end is CVS, workfiles are always kept, regardless of the | |
50bec091 KH |
99 | value of this flag." |
100 | :type 'boolean | |
101 | :group 'vc) | |
e1c0c2d1 | 102 | |
50bec091 | 103 | (defcustom vc-mistrust-permissions nil |
0e0d9831 GM |
104 | "*If non-nil, don't assume permissions/ownership track version-control status. |
105 | If nil, do rely on the permissions. | |
50bec091 KH |
106 | See also variable `vc-consult-headers'." |
107 | :type 'boolean | |
108 | :group 'vc) | |
e66eac08 AS |
109 | |
110 | (defun vc-mistrust-permissions (file) | |
0e0d9831 | 111 | "Internal access function to variable `vc-mistrust-permissions' for FILE." |
e66eac08 AS |
112 | (or (eq vc-mistrust-permissions 't) |
113 | (and vc-mistrust-permissions | |
0e0d9831 | 114 | (funcall vc-mistrust-permissions |
e66eac08 AS |
115 | (vc-backend-subdirectory-name file))))) |
116 | ||
594722a8 | 117 | ;; Tell Emacs about this new kind of minor mode |
0e0d9831 | 118 | (add-to-list 'minor-mode-alist '(vc-mode vc-mode)) |
594722a8 | 119 | |
7bc2b98b | 120 | (make-variable-buffer-local 'vc-mode) |
c43e436c | 121 | (put 'vc-mode 'permanent-local t) |
594722a8 | 122 | |
14fef9de SM |
123 | (defun vc-mode (&optional arg) |
124 | ;; Dummy function for C-h m | |
125 | "Version Control minor mode. | |
126 | This minor mode is automatically activated whenever you visit a file under | |
127 | control of one of the revision control systems in `vc-handled-backends'. | |
128 | VC commands are globally reachable under the prefix `\\[vc-prefix-map]': | |
129 | \\{vc-prefix-map}") | |
130 | ||
099bd78a SM |
131 | (defmacro vc-error-occurred (&rest body) |
132 | (list 'condition-case nil (cons 'progn (append body '(nil))) '(error t))) | |
133 | ||
594722a8 | 134 | ;; We need a notion of per-file properties because the version |
f2ee4191 | 135 | ;; control state of a file is expensive to derive --- we compute |
0e0d9831 | 136 | ;; them when the file is initially found, keep them up to date |
f2ee4191 RS |
137 | ;; during any subsequent VC operations, and forget them when |
138 | ;; the buffer is killed. | |
594722a8 | 139 | |
0e0d9831 | 140 | (defvar vc-file-prop-obarray (make-vector 16 0) |
594722a8 ER |
141 | "Obarray for per-file properties.") |
142 | ||
099bd78a SM |
143 | (defvar vc-touched-properties nil) |
144 | ||
594722a8 | 145 | (defun vc-file-setprop (file property value) |
0e0d9831 | 146 | "Set per-file VC PROPERTY for FILE to VALUE." |
099bd78a SM |
147 | (if (and vc-touched-properties |
148 | (not (memq property vc-touched-properties))) | |
149 | (setq vc-touched-properties (append (list property) | |
150 | vc-touched-properties))) | |
594722a8 ER |
151 | (put (intern file vc-file-prop-obarray) property value)) |
152 | ||
153 | (defun vc-file-getprop (file property) | |
099bd78a | 154 | "Get per-file VC PROPERTY for FILE." |
594722a8 ER |
155 | (get (intern file vc-file-prop-obarray) property)) |
156 | ||
e1c0c2d1 | 157 | (defun vc-file-clearprops (file) |
0e0d9831 | 158 | "Clear all VC properties of FILE." |
e1c0c2d1 | 159 | (setplist (intern file vc-file-prop-obarray) nil)) |
f2ee4191 | 160 | |
0e0d9831 GM |
161 | \f |
162 | ;; We keep properties on each symbol naming a backend as follows: | |
163 | ;; * `vc-functions': an alist mapping vc-FUNCTION to vc-BACKEND-FUNCTION. | |
164 | ||
165 | (defun vc-make-backend-sym (backend sym) | |
166 | "Return BACKEND-specific version of VC symbol SYM." | |
167 | (intern (concat "vc-" (downcase (symbol-name backend)) | |
168 | "-" (symbol-name sym)))) | |
169 | ||
170 | (defun vc-find-backend-function (backend fun) | |
171 | "Return BACKEND-specific implementation of FUN. | |
172 | If there is no such implementation, return the default implementation; | |
173 | if that doesn't exist either, return nil." | |
174 | (let ((f (vc-make-backend-sym backend fun))) | |
175 | (if (fboundp f) f | |
176 | ;; Load vc-BACKEND.el if needed. | |
177 | (require (intern (concat "vc-" (downcase (symbol-name backend))))) | |
178 | (if (fboundp f) f | |
179 | (let ((def (vc-make-backend-sym 'default fun))) | |
180 | (if (fboundp def) (cons def backend) nil)))))) | |
181 | ||
182 | (defun vc-call-backend (backend function-name &rest args) | |
183 | "Call for BACKEND the implementation of FUNCTION-NAME with the given ARGS. | |
184 | Calls | |
185 | ||
186 | (apply 'vc-BACKEND-FUN ARGS) | |
187 | ||
188 | if vc-BACKEND-FUN exists (after trying to find it in vc-BACKEND.el) | |
189 | and else calls | |
190 | ||
191 | (apply 'vc-default-FUN BACKEND ARGS) | |
192 | ||
193 | It is usually called via the `vc-call' macro." | |
194 | (let ((f (cdr (assoc function-name (get backend 'vc-functions))))) | |
195 | (unless f | |
196 | (setq f (vc-find-backend-function backend function-name)) | |
197 | (put backend 'vc-functions (cons (cons function-name f) | |
198 | (get backend 'vc-functions)))) | |
199 | (if (consp f) | |
200 | (apply (car f) (cdr f) args) | |
201 | (apply f args)))) | |
202 | ||
203 | (defmacro vc-call (fun file &rest args) | |
204 | ;; BEWARE!! `file' is evaluated twice!! | |
205 | `(vc-call-backend (vc-backend ,file) ',fun ,file ,@args)) | |
206 | ||
207 | \f | |
208 | (defsubst vc-parse-buffer (pattern i) | |
209 | "Find PATTERN in the current buffer and return its Ith submatch." | |
210 | (goto-char (point-min)) | |
211 | (if (re-search-forward pattern nil t) | |
212 | (match-string i))) | |
e1c0c2d1 | 213 | |
02d383eb | 214 | (defun vc-insert-file (file &optional limit blocksize) |
0e0d9831 GM |
215 | "Insert the contents of FILE into the current buffer. |
216 | ||
217 | Optional argument LIMIT is a regexp. If present, the file is inserted | |
218 | in chunks of size BLOCKSIZE (default 8 kByte), until the first | |
ff40374a AS |
219 | occurrence of LIMIT is found. The function returns non-nil if FILE |
220 | exists and its contents were successfully inserted." | |
07de4c3d | 221 | (erase-buffer) |
ff40374a AS |
222 | (when (file-exists-p file) |
223 | (if (not limit) | |
224 | (insert-file-contents file) | |
225 | (if (not blocksize) (setq blocksize 8192)) | |
4d2806e2 SM |
226 | (let ((filepos 0)) |
227 | (while | |
228 | (and (< 0 (cadr (insert-file-contents | |
229 | file nil filepos (incf filepos blocksize)))) | |
230 | (progn (beginning-of-line) | |
231 | (not (re-search-forward limit nil 'move))))))) | |
ff40374a AS |
232 | (set-buffer-modified-p nil) |
233 | t)) | |
02d383eb | 234 | |
14fef9de SM |
235 | ;; Access functions to file properties |
236 | ;; (Properties should be _set_ using vc-file-setprop, but | |
237 | ;; _retrieved_ only through these functions, which decide | |
238 | ;; if the property is already known or not. A property should | |
239 | ;; only be retrieved by vc-file-getprop if there is no | |
240 | ;; access function.) | |
02d383eb | 241 | |
14fef9de | 242 | ;; properties indicating the backend being used for FILE |
e1c0c2d1 | 243 | |
0e0d9831 GM |
244 | (defun vc-registered (file) |
245 | "Return non-nil if FILE is registered in a version control system. | |
246 | ||
1f22ad24 AS |
247 | This function performs the check each time it is called. To rely |
248 | on the result of a previous call, use `vc-backend' instead. If the | |
249 | file was previously registered under a certain backend, then that | |
250 | backend is tried first." | |
0e0d9831 GM |
251 | (let (handler) |
252 | (if (boundp 'file-name-handler-alist) | |
253 | (setq handler (find-file-name-handler file 'vc-registered))) | |
254 | (if handler | |
255 | ;; handler should set vc-backend and return t if registered | |
256 | (funcall handler 'vc-registered file) | |
257 | ;; There is no file name handler. | |
258 | ;; Try vc-BACKEND-registered for each handled BACKEND. | |
259 | (catch 'found | |
1f22ad24 AS |
260 | (let ((backend (vc-file-getprop file 'vc-backend))) |
261 | (mapcar | |
262 | (lambda (b) | |
263 | (and (vc-call-backend b 'registered file) | |
264 | (vc-file-setprop file 'vc-backend b) | |
265 | (throw 'found t))) | |
266 | (if (or (not backend) (eq backend 'none)) | |
267 | vc-handled-backends | |
268 | (cons backend vc-handled-backends)))) | |
0e0d9831 GM |
269 | ;; File is not registered. |
270 | (vc-file-setprop file 'vc-backend 'none) | |
271 | nil)))) | |
272 | ||
273 | (defun vc-backend (file) | |
274 | "Return the version control type of FILE, nil if it is not registered." | |
275 | ;; `file' can be nil in several places (typically due to the use of | |
276 | ;; code like (vc-backend (buffer-file-name))). | |
277 | (when (stringp file) | |
278 | (let ((property (vc-file-getprop file 'vc-backend))) | |
279 | ;; Note that internally, Emacs remembers unregistered | |
280 | ;; files by setting the property to `none'. | |
281 | (cond ((eq property 'none) nil) | |
282 | (property) | |
283 | ;; vc-registered sets the vc-backend property | |
284 | (t (if (vc-registered file) | |
285 | (vc-file-getprop file 'vc-backend) | |
286 | nil)))))) | |
287 | ||
288 | (defun vc-backend-subdirectory-name (file) | |
289 | "Return where the master and lock FILEs for the current directory are kept." | |
290 | (symbol-name (vc-backend file))) | |
e1c0c2d1 | 291 | |
02d383eb | 292 | (defun vc-name (file) |
5eb2b516 DL |
293 | "Return the master name of FILE. |
294 | If the file is not registered, or the master name is not known, return nil." | |
295 | ;; TODO: This should ultimately become obsolete, at least up here | |
0e0d9831 | 296 | ;; in vc-hooks. |
02d383eb | 297 | (or (vc-file-getprop file 'vc-name) |
64341022 AS |
298 | ;; force computation of the property by calling |
299 | ;; vc-BACKEND-registered explicitly | |
300 | (if (and (vc-backend file) | |
301 | (vc-call-backend (vc-backend file) 'registered file)) | |
8aa81ea8 | 302 | (vc-file-getprop file 'vc-name)))) |
e1c0c2d1 | 303 | |
04446ed0 | 304 | (defun vc-checkout-model (file) |
0e0d9831 GM |
305 | "Indicate how FILE is checked out. |
306 | ||
307 | Possible values: | |
308 | ||
309 | 'implicit File is always writeable, and checked out `implicitly' | |
310 | when the user saves the first changes to the file. | |
311 | ||
312 | 'locking File is read-only if up-to-date; user must type | |
313 | \\[vc-toggle-read-only] before editing. Strict locking | |
314 | is assumed. | |
315 | ||
316 | 'announce File is read-only if up-to-date; user must type | |
317 | \\[vc-toggle-read-only] before editing. But other users | |
318 | may be editing at the same time." | |
319 | (or (vc-file-getprop file 'vc-checkout-model) | |
320 | (vc-file-setprop file 'vc-checkout-model | |
321 | (vc-call checkout-model file)))) | |
7064821c | 322 | |
2a11c6f3 | 323 | (defun vc-user-login-name (&optional uid) |
0e0d9831 GM |
324 | "Return the name under which the user is logged in, as a string. |
325 | \(With optional argument UID, return the name of that user.) | |
326 | This function does the same as function `user-login-name', but unlike | |
327 | that, it never returns nil. If a UID cannot be resolved, that | |
328 | UID is returned as a string." | |
2a11c6f3 | 329 | (or (user-login-name uid) |
0e0d9831 GM |
330 | (number-to-string (or uid (user-uid))))) |
331 | ||
332 | (defun vc-state (file) | |
333 | "Return the version control state of FILE. | |
334 | ||
5eb2b516 | 335 | The value returned is one of: |
0e0d9831 GM |
336 | |
337 | 'up-to-date The working file is unmodified with respect to the | |
338 | latest version on the current branch, and not locked. | |
339 | ||
340 | 'edited The working file has been edited by the user. If | |
341 | locking is used for the file, this state means that | |
342 | the current version is locked by the calling user. | |
343 | ||
344 | USER The current version of the working file is locked by | |
345 | some other USER (a string). | |
346 | ||
347 | 'needs-patch The file has not been edited by the user, but there is | |
348 | a more recent version on the current branch stored | |
349 | in the master file. | |
350 | ||
351 | 'needs-merge The file has been edited by the user, and there is also | |
352 | a more recent version on the current branch stored in | |
353 | the master file. This state can only occur if locking | |
354 | is not used for the file. | |
355 | ||
356 | 'unlocked-changes The current version of the working file is not locked, | |
357 | but the working file has been changed with respect | |
358 | to that version. This state can only occur for files | |
359 | with locking; it represents an erroneous condition that | |
360 | should be resolved by the user (vc-next-action will | |
361 | prompt the user to do it)." | |
362 | (or (vc-file-getprop file 'vc-state) | |
363 | (vc-file-setprop file 'vc-state | |
364 | (vc-call state-heuristic file)))) | |
365 | ||
366 | (defsubst vc-up-to-date-p (file) | |
367 | "Convenience function that checks whether `vc-state' of FILE is `up-to-date'." | |
368 | (eq (vc-state file) 'up-to-date)) | |
369 | ||
370 | (defun vc-default-state-heuristic (backend file) | |
5eb2b516 DL |
371 | "Default implementation of vc-state-heuristic. |
372 | It simply calls the real state computation function `vc-BACKEND-state' | |
373 | and does not employ any heuristic at all." | |
0e0d9831 | 374 | (vc-call-backend backend 'state file)) |
1efcbf46 | 375 | |
e1c0c2d1 | 376 | (defun vc-workfile-version (file) |
0e0d9831 GM |
377 | "Return version level of the current workfile FILE." |
378 | (or (vc-file-getprop file 'vc-workfile-version) | |
379 | (vc-file-setprop file 'vc-workfile-version | |
380 | (vc-call workfile-version file)))) | |
f2ee4191 | 381 | |
2ce63cb7 AS |
382 | ;;; actual version-control code starts here |
383 | ||
0e0d9831 GM |
384 | (defun vc-default-registered (backend file) |
385 | "Check if FILE is registered in BACKEND using vc-BACKEND-master-templates." | |
386 | (let ((sym (vc-make-backend-sym backend 'master-templates))) | |
387 | (unless (get backend 'vc-templates-grabbed) | |
388 | (put backend 'vc-templates-grabbed t) | |
389 | (set sym (append (delq nil | |
390 | (mapcar | |
391 | (lambda (template) | |
392 | (and (consp template) | |
393 | (eq (cdr template) backend) | |
394 | (car template))) | |
395 | vc-master-templates)) | |
396 | (symbol-value sym)))) | |
397 | (let ((result (vc-check-master-templates file (symbol-value sym)))) | |
398 | (if (stringp result) | |
399 | (vc-file-setprop file 'vc-name result) | |
400 | nil)))) ; Not registered | |
401 | ||
402 | (defun vc-possible-master (s dirname basename) | |
403 | (cond | |
404 | ((stringp s) (format s dirname basename)) | |
405 | ((functionp s) | |
406 | ;; The template is a function to invoke. If the | |
407 | ;; function returns non-nil, that means it has found a | |
408 | ;; master. For backward compatibility, we also handle | |
409 | ;; the case that the function throws a 'found atom | |
410 | ;; and a pair (cons MASTER-FILE BACKEND). | |
411 | (let ((result (catch 'found (funcall s dirname basename)))) | |
412 | (if (consp result) (car result) result))))) | |
413 | ||
414 | (defun vc-check-master-templates (file templates) | |
415 | "Return non-nil if there is a master corresponding to FILE, | |
416 | according to any of the elements in TEMPLATES. | |
417 | ||
418 | TEMPLATES is a list of strings or functions. If an element is a | |
419 | string, it must be a control string as required by `format', with two | |
420 | string placeholders, such as \"%sRCS/%s,v\". The directory part of | |
421 | FILE is substituted for the first placeholder, the basename of FILE | |
422 | for the second. If a file with the resulting name exists, it is taken | |
423 | as the master of FILE, and returned. | |
424 | ||
425 | If an element of TEMPLATES is a function, it is called with the | |
426 | directory part and the basename of FILE as arguments. It should | |
427 | return non-nil if it finds a master; that value is then returned by | |
428 | this function." | |
429 | (let ((dirname (or (file-name-directory file) "")) | |
430 | (basename (file-name-nondirectory file))) | |
431 | (catch 'found | |
5eb2b516 | 432 | (mapcar |
0e0d9831 GM |
433 | (lambda (s) |
434 | (let ((trial (vc-possible-master s dirname basename))) | |
435 | (if (and trial (file-exists-p trial) | |
436 | ;; Make sure the file we found with name | |
437 | ;; TRIAL is not the source file itself. | |
438 | ;; That can happen with RCS-style names if | |
439 | ;; the file name is truncated (e.g. to 14 | |
440 | ;; chars). See if either directory or | |
441 | ;; attributes differ. | |
442 | (or (not (string= dirname | |
443 | (file-name-directory trial))) | |
444 | (not (equal (file-attributes file) | |
445 | (file-attributes trial))))) | |
446 | (throw 'found trial)))) | |
447 | templates)))) | |
f2ee4191 | 448 | |
c844616c | 449 | (defun vc-toggle-read-only (&optional verbose) |
c43e436c RS |
450 | "Change read-only status of current buffer, perhaps via version control. |
451 | If the buffer is visiting a file registered with version control, | |
452 | then check the file in or out. Otherwise, just change the read-only flag | |
ec44193a KH |
453 | of the buffer. |
454 | With prefix argument, ask for version number to check in or check out. | |
455 | Check-out of a specified version number does not lock the file; | |
456 | to do that, use this command a second time with no argument." | |
c844616c | 457 | (interactive "P") |
702220f3 | 458 | (if (or (and (boundp 'vc-dired-mode) vc-dired-mode) |
0e0d9831 GM |
459 | ;; use boundp because vc.el might not be loaded |
460 | (vc-backend (buffer-file-name))) | |
c844616c | 461 | (vc-next-action verbose) |
594722a8 | 462 | (toggle-read-only))) |
c43e436c | 463 | (define-key global-map "\C-x\C-q" 'vc-toggle-read-only) |
594722a8 | 464 | |
e896a9e1 | 465 | (defun vc-default-make-version-backups-p (backend file) |
d445a975 AS |
466 | "Return non-nil if unmodified repository versions should |
467 | be backed up locally. The default is to switch off this feature." | |
468 | nil) | |
469 | ||
e896a9e1 AS |
470 | (defun vc-version-backup-file-name (file &optional rev manual regexp) |
471 | "Return a backup file name for REV or the current version of FILE. | |
472 | If MANUAL is non-nil it means that a name for backups created by | |
473 | the user should be returned; if REGEXP is non-nil that means to return | |
474 | a regexp for matching all such backup files, regardless of the version." | |
e3f955b6 AS |
475 | (if regexp |
476 | (concat (regexp-quote (file-name-nondirectory file)) | |
477 | "\\.~[0-9.]+" (unless manual "\\.") "~") | |
478 | (expand-file-name (concat (file-name-nondirectory file) | |
479 | ".~" (or rev (vc-workfile-version file)) | |
480 | (unless manual ".") "~") | |
481 | (file-name-directory file)))) | |
e896a9e1 AS |
482 | |
483 | (defun vc-delete-automatic-version-backups (file) | |
484 | "Delete all existing automatic version backups for FILE." | |
d455f4f7 SM |
485 | (condition-case nil |
486 | (mapcar | |
487 | 'delete-file | |
488 | (directory-files (file-name-directory file) t | |
489 | (vc-version-backup-file-name file nil nil t))) | |
490 | ;; Don't fail when the directory doesn't exist. | |
491 | (file-error nil))) | |
e896a9e1 AS |
492 | |
493 | (defun vc-make-version-backup (file) | |
494 | "Make a backup copy of FILE, which is assumed in sync with the repository. | |
495 | Before doing that, check if there are any old backups and get rid of them." | |
48b15d3f AS |
496 | (unless (and (fboundp 'msdos-long-file-names) |
497 | (not (msdos-long-file-names))) | |
498 | (vc-delete-automatic-version-backups file) | |
499 | (copy-file file (vc-version-backup-file-name file) | |
500 | nil 'keep-date))) | |
d445a975 AS |
501 | |
502 | (defun vc-before-save () | |
503 | "Function to be called by `basic-save-buffer' (in files.el)." | |
504 | ;; If the file on disk is still in sync with the repository, | |
505 | ;; and version backups should be made, copy the file to | |
506 | ;; another name. This enables local diffs and local reverting. | |
507 | (let ((file (buffer-file-name))) | |
508 | (and (vc-backend file) | |
509 | (vc-up-to-date-p file) | |
510 | (eq (vc-checkout-model file) 'implicit) | |
e896a9e1 AS |
511 | (vc-call make-version-backups-p file) |
512 | (vc-make-version-backup file)))) | |
d445a975 | 513 | |
e66eac08 | 514 | (defun vc-after-save () |
0e0d9831 | 515 | "Function to be called by `basic-save-buffer' (in files.el)." |
5eb2b516 | 516 | ;; If the file in the current buffer is under version control, |
0e0d9831 GM |
517 | ;; up-to-date, and locking is not used for the file, set |
518 | ;; the state to 'edited and redisplay the mode line. | |
e66eac08 | 519 | (let ((file (buffer-file-name))) |
8aa81ea8 | 520 | (and (vc-backend file) |
b23a2306 AS |
521 | (or (and (equal (vc-file-getprop file 'vc-checkout-time) |
522 | (nth 5 (file-attributes file))) | |
523 | ;; File has been saved in the same second in which | |
524 | ;; it was checked out. Clear the checkout-time | |
525 | ;; to avoid confusion. | |
526 | (vc-file-setprop file 'vc-checkout-time nil)) | |
527 | t) | |
0e0d9831 GM |
528 | (vc-up-to-date-p file) |
529 | (eq (vc-checkout-model file) 'implicit) | |
530 | (vc-file-setprop file 'vc-state 'edited) | |
531 | (vc-mode-line file) | |
099bd78a SM |
532 | (if (featurep 'vc) |
533 | ;; If VC is not loaded, then there can't be | |
534 | ;; any VC Dired buffer to synchronize. | |
535 | (vc-dired-resynch-file file))))) | |
04446ed0 | 536 | |
0e0d9831 | 537 | (defun vc-mode-line (file) |
7bc2b98b | 538 | "Set `vc-mode' to display type of version control for FILE. |
594722a8 | 539 | The value is set in the current buffer, which should be the buffer |
0e0d9831 | 540 | visiting FILE." |
67c6f446 | 541 | (interactive (list buffer-file-name)) |
0e0d9831 | 542 | (unless (not (vc-backend file)) |
099bd78a SM |
543 | (setq vc-mode (concat " " (if vc-display-status |
544 | (vc-call mode-line-string file) | |
545 | (symbol-name (vc-backend file))))) | |
9becbeca RS |
546 | ;; If the file is locked by some other user, make |
547 | ;; the buffer read-only. Like this, even root | |
defccde3 | 548 | ;; cannot modify a file that someone else has locked. |
0e0d9831 GM |
549 | (and (equal file (buffer-file-name)) |
550 | (stringp (vc-state file)) | |
e66eac08 | 551 | (setq buffer-read-only t)) |
defccde3 RS |
552 | ;; If the user is root, and the file is not owner-writable, |
553 | ;; then pretend that we can't write it | |
554 | ;; even though we can (because root can write anything). | |
555 | ;; This way, even root cannot modify a file that isn't locked. | |
0e0d9831 | 556 | (and (equal file (buffer-file-name)) |
9becbeca RS |
557 | (not buffer-read-only) |
558 | (zerop (user-real-uid)) | |
559 | (zerop (logand (file-modes (buffer-file-name)) 128)) | |
0e0d9831 GM |
560 | (setq buffer-read-only t))) |
561 | (force-mode-line-update) | |
562 | (vc-backend file)) | |
563 | ||
564 | (defun vc-default-mode-line-string (backend file) | |
565 | "Return string for placement in modeline by `vc-mode-line' for FILE. | |
566 | Format: | |
567 | ||
568 | \"BACKEND-REV\" if the file is up-to-date | |
569 | \"BACKEND:REV\" if the file is edited (or locked by the calling user) | |
570 | \"BACKEND:LOCKER:REV\" if the file is locked by somebody else | |
0e0d9831 GM |
571 | |
572 | This function assumes that the file is registered." | |
573 | (setq backend (symbol-name backend)) | |
574 | (let ((state (vc-state file)) | |
575 | (rev (vc-workfile-version file))) | |
099bd78a | 576 | (cond ((or (eq state 'up-to-date) |
0e0d9831 GM |
577 | (eq state 'needs-patch)) |
578 | (concat backend "-" rev)) | |
579 | ((stringp state) | |
580 | (concat backend ":" state ":" rev)) | |
581 | (t | |
582 | ;; Not just for the 'edited state, but also a fallback | |
583 | ;; for all other states. Think about different symbols | |
584 | ;; for 'needs-patch and 'needs-merge. | |
585 | (concat backend ":" rev))))) | |
f2ee4191 | 586 | |
a3a39848 | 587 | (defun vc-follow-link () |
0e0d9831 GM |
588 | "If current buffer visits a symbolic link, visit the real file. |
589 | If the real file is already visited in another buffer, make that buffer | |
590 | current, and kill the buffer that visits the link." | |
566f2169 | 591 | (let* ((truename (abbreviate-file-name (file-chase-links buffer-file-name))) |
e7f5ddc2 RS |
592 | (true-buffer (find-buffer-visiting truename)) |
593 | (this-buffer (current-buffer))) | |
594 | (if (eq true-buffer this-buffer) | |
595 | (progn | |
d8221951 | 596 | (kill-buffer this-buffer) |
e7f5ddc2 RS |
597 | ;; In principle, we could do something like set-visited-file-name. |
598 | ;; However, it can't be exactly the same as set-visited-file-name. | |
599 | ;; I'm not going to work out the details right now. -- rms. | |
d8221951 | 600 | (set-buffer (find-file-noselect truename))) |
e7f5ddc2 RS |
601 | (set-buffer true-buffer) |
602 | (kill-buffer this-buffer)))) | |
a3a39848 | 603 | |
594722a8 | 604 | (defun vc-find-file-hook () |
0e0d9831 | 605 | "Function for `find-file-hooks' activating VC mode if appropriate." |
18c8a18e PE |
606 | ;; Recompute whether file is version controlled, |
607 | ;; if user has killed the buffer and revisited. | |
0e0d9831 | 608 | (when buffer-file-name |
f2ee4191 RS |
609 | (vc-file-clearprops buffer-file-name) |
610 | (cond | |
e1c0c2d1 | 611 | ((vc-backend buffer-file-name) |
f2ee4191 RS |
612 | (vc-mode-line buffer-file-name) |
613 | (cond ((not vc-make-backup-files) | |
614 | ;; Use this variable, not make-backup-files, | |
615 | ;; because this is for things that depend on the file name. | |
616 | (make-local-variable 'backup-inhibited) | |
45fc7cc3 RS |
617 | (setq backup-inhibited t)))) |
618 | ((let* ((link (file-symlink-p buffer-file-name)) | |
227d2bed | 619 | (link-type (and link (vc-backend (file-chase-links link))))) |
45fc7cc3 | 620 | (if link-type |
b8063212 AS |
621 | (cond ((eq vc-follow-symlinks nil) |
622 | (message | |
623 | "Warning: symbolic link to %s-controlled source file" link-type)) | |
566f2169 RS |
624 | ((or (not (eq vc-follow-symlinks 'ask)) |
625 | ;; If we already visited this file by following | |
626 | ;; the link, don't ask again if we try to visit | |
627 | ;; it again. GUD does that, and repeated questions | |
628 | ;; are painful. | |
629 | (get-file-buffer | |
5eb2b516 | 630 | (abbreviate-file-name |
0e0d9831 | 631 | (file-chase-links buffer-file-name)))) |
566f2169 RS |
632 | |
633 | (vc-follow-link) | |
634 | (message "Followed link to %s" buffer-file-name) | |
635 | (vc-find-file-hook)) | |
636 | (t | |
b8063212 AS |
637 | (if (yes-or-no-p (format |
638 | "Symbolic link to %s-controlled source file; follow link? " link-type)) | |
a3a39848 | 639 | (progn (vc-follow-link) |
b8063212 AS |
640 | (message "Followed link to %s" buffer-file-name) |
641 | (vc-find-file-hook)) | |
0e0d9831 | 642 | (message |
b8063212 | 643 | "Warning: editing through the link bypasses version control") |
0e0d9831 | 644 | ))))))))) |
594722a8 | 645 | |
6379911c | 646 | (add-hook 'find-file-hooks 'vc-find-file-hook) |
594722a8 | 647 | |
14fef9de | 648 | ;; more hooks, this time for file-not-found |
594722a8 | 649 | (defun vc-file-not-found-hook () |
0e0d9831 GM |
650 | "When file is not found, try to check it out from version control. |
651 | Returns t if checkout was successful, nil otherwise. | |
652 | Used in `find-file-not-found-hooks'." | |
5f42a1d4 RS |
653 | ;; When a file does not exist, ignore cached info about it |
654 | ;; from a previous visit. | |
655 | (vc-file-clearprops buffer-file-name) | |
0e0d9831 | 656 | (if (and (vc-backend buffer-file-name) |
5eb2b516 | 657 | (yes-or-no-p |
0e0d9831 GM |
658 | (format "File %s was lost; check out from version control? " |
659 | (file-name-nondirectory buffer-file-name)))) | |
660 | (save-excursion | |
661 | (require 'vc) | |
662 | (setq default-directory (file-name-directory buffer-file-name)) | |
663 | (not (vc-error-occurred (vc-checkout buffer-file-name)))))) | |
594722a8 | 664 | |
6379911c | 665 | (add-hook 'find-file-not-found-hooks 'vc-file-not-found-hook) |
594722a8 | 666 | |
f2ee4191 | 667 | (defun vc-kill-buffer-hook () |
0e0d9831 GM |
668 | "Discard VC info about a file when we kill its buffer." |
669 | (if (buffer-file-name) | |
670 | (vc-file-clearprops (buffer-file-name)))) | |
f2ee4191 | 671 | |
0e0d9831 | 672 | ;; ??? DL: why is this not done? |
f2ee4191 RS |
673 | ;;;(add-hook 'kill-buffer-hook 'vc-kill-buffer-hook) |
674 | ||
14fef9de SM |
675 | ;; Now arrange for (autoloaded) bindings of the main package. |
676 | ;; Bindings for this have to go in the global map, as we'll often | |
677 | ;; want to call them from random buffers. | |
678 | ||
679 | ;; Autoloading works fine, but it prevents shortcuts from appearing | |
680 | ;; in the menu because they don't exist yet when the menu is built. | |
681 | ;; (autoload 'vc-prefix-map "vc" nil nil 'keymap) | |
682 | (defvar vc-prefix-map | |
683 | (let ((map (make-sparse-keymap))) | |
684 | (define-key map "a" 'vc-update-change-log) | |
685 | (define-key map "b" 'vc-switch-backend) | |
686 | (define-key map "c" 'vc-cancel-version) | |
687 | (define-key map "d" 'vc-directory) | |
688 | (define-key map "g" 'vc-annotate) | |
689 | (define-key map "h" 'vc-insert-headers) | |
690 | (define-key map "i" 'vc-register) | |
691 | (define-key map "l" 'vc-print-log) | |
692 | (define-key map "m" 'vc-merge) | |
693 | (define-key map "r" 'vc-retrieve-snapshot) | |
694 | (define-key map "s" 'vc-create-snapshot) | |
695 | (define-key map "u" 'vc-revert-buffer) | |
696 | (define-key map "v" 'vc-next-action) | |
697 | (define-key map "=" 'vc-diff) | |
698 | (define-key map "~" 'vc-version-other-window) | |
699 | map)) | |
700 | (fset 'vc-prefix-map vc-prefix-map) | |
0e0d9831 | 701 | (define-key global-map "\C-xv" 'vc-prefix-map) |
624c0e9d | 702 | |
b662fbb8 RM |
703 | (if (not (boundp 'vc-menu-map)) |
704 | ;; Don't do the menu bindings if menu-bar.el wasn't loaded to defvar | |
705 | ;; vc-menu-map. | |
706 | () | |
707 | ;;(define-key vc-menu-map [show-files] | |
708 | ;; '("Show Files under VC" . (vc-directory t))) | |
9bef02e1 RS |
709 | (define-key vc-menu-map [vc-retrieve-snapshot] |
710 | '("Retrieve Snapshot" . vc-retrieve-snapshot)) | |
711 | (define-key vc-menu-map [vc-create-snapshot] | |
712 | '("Create Snapshot" . vc-create-snapshot)) | |
b64ea387 | 713 | (define-key vc-menu-map [vc-directory] '("VC Directory Listing" . vc-directory)) |
b662fbb8 | 714 | (define-key vc-menu-map [separator1] '("----")) |
14aa11f4 | 715 | (define-key vc-menu-map [vc-annotate] '("Annotate" . vc-annotate)) |
b662fbb8 RM |
716 | (define-key vc-menu-map [vc-rename-file] '("Rename File" . vc-rename-file)) |
717 | (define-key vc-menu-map [vc-version-other-window] | |
718 | '("Show Other Version" . vc-version-other-window)) | |
719 | (define-key vc-menu-map [vc-diff] '("Compare with Last Version" . vc-diff)) | |
720 | (define-key vc-menu-map [vc-update-change-log] | |
721 | '("Update ChangeLog" . vc-update-change-log)) | |
722 | (define-key vc-menu-map [vc-print-log] '("Show History" . vc-print-log)) | |
723 | (define-key vc-menu-map [separator2] '("----")) | |
724 | (define-key vc-menu-map [undo] '("Undo Last Check-In" . vc-cancel-version)) | |
725 | (define-key vc-menu-map [vc-revert-buffer] | |
726 | '("Revert to Last Version" . vc-revert-buffer)) | |
727 | (define-key vc-menu-map [vc-insert-header] | |
728 | '("Insert Header" . vc-insert-headers)) | |
9314395d | 729 | (define-key vc-menu-map [vc-next-action] '("Check In/Out" . vc-next-action)) |
738efc8e KH |
730 | (define-key vc-menu-map [vc-register] '("Register" . vc-register))) |
731 | ||
14fef9de SM |
732 | ;; These are not correct and it's not currently clear how doing it |
733 | ;; better (with more complicated expressions) might slow things down | |
734 | ;; on older systems. | |
735 | ||
736 | ;;(put 'vc-rename-file 'menu-enable 'vc-mode) | |
737 | ;;(put 'vc-annotate 'menu-enable '(eq (vc-buffer-backend) 'CVS)) | |
738 | ;;(put 'vc-version-other-window 'menu-enable 'vc-mode) | |
739 | ;;(put 'vc-diff 'menu-enable 'vc-mode) | |
740 | ;;(put 'vc-update-change-log 'menu-enable | |
741 | ;; '(member (vc-buffer-backend) '(RCS CVS))) | |
742 | ;;(put 'vc-print-log 'menu-enable 'vc-mode) | |
743 | ;;(put 'vc-cancel-version 'menu-enable 'vc-mode) | |
744 | ;;(put 'vc-revert-buffer 'menu-enable 'vc-mode) | |
745 | ;;(put 'vc-insert-headers 'menu-enable 'vc-mode) | |
746 | ;;(put 'vc-next-action 'menu-enable 'vc-mode) | |
747 | ;;(put 'vc-register 'menu-enable '(and buffer-file-name (not vc-mode))) | |
594722a8 | 748 | |
594722a8 ER |
749 | (provide 'vc-hooks) |
750 | ||
751 | ;;; vc-hooks.el ends here |