(Vload_read_function): New variable.
[bpt/emacs.git] / lisp / vc.el
CommitLineData
594722a8
ER
1;;; vc.el --- drive a version-control system from within Emacs
2
632e9525 3;; Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
594722a8
ER
4
5;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
eceb584f 6;; Maintainer: ttn@netcom.com
632e9525 7;; Version: 5.6
594722a8
ER
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to
23;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24
25;;; Commentary:
26
1a2f456b
ER
27;; This mode is fully documented in the Emacs user's manual.
28;;
594722a8
ER
29;; This was designed and implemented by Eric Raymond <esr@snark.thyrsus.com>.
30;; Paul Eggert <eggert@twinsun.com>, Sebastian Kremer <sk@thp.uni-koeln.de>,
31;; and Richard Stallman contributed valuable criticism, support, and testing.
632e9525
RS
32;; CVS support was added by Per Cederqvist <ceder@lysator.liu.se>
33;; in Jan-Feb 1994.
594722a8 34;;
632e9525
RS
35;; Supported version-control systems presently include SCCS, RCS, and CVS.
36;; The RCS lock-stealing code doesn't work right unless you use RCS 5.6.2
d0b81713 37;; or newer. Currently (January 1994) that is only a beta test release.
632e9525
RS
38;; Even initial checkins will fail if your RCS version is so old that ci
39;; doesn't understand -t-; this has been known to happen to people running
40;; NExTSTEP 3.0.
594722a8
ER
41;;
42;; The RCS code assumes strict locking. You can support the RCS -x option
43;; by adding pairs to the vc-master-templates list.
44;;
45;; Proper function of the SCCS diff commands requires the shellscript vcdiff
46;; to be installed somewhere on Emacs's path for executables.
47;;
1a2f456b 48;; If your site uses the ChangeLog convention supported by Emacs, the
594be62e 49;; function vc-comment-to-change-log should prove a useful checkin hook.
1a2f456b 50;;
594722a8 51;; This code depends on call-process passing back the subprocess exit
e1f297e6 52;; status. Thus, you need Emacs 18.58 or later to run it. For the
6ed5075c 53;; vc-directory command to work properly as documented, you need 19.
7ef84cf9 54;; You also need Emacs 19's ring.el.
594722a8
ER
55;;
56;; The vc code maintains some internal state in order to reduce expensive
57;; version-control operations to a minimum. Some names are only computed
34291cd2 58;; once. If you perform version control operations with RCS/SCCS/CVS while
594722a8
ER
59;; vc's back is turned, or move/rename master files while vc is running,
60;; vc may get seriously confused. Don't do these things!
61;;
62;; Developer's notes on some concurrency issues are included at the end of
63;; the file.
64
65;;; Code:
66
67(require 'vc-hooks)
8c0aaf40 68(require 'ring)
5c6f8be0 69(eval-when-compile (require 'dired)) ; for dired-map-over-marks macro
8c0aaf40
ER
70
71(if (not (assoc 'vc-parent-buffer minor-mode-alist))
72 (setq minor-mode-alist
73 (cons '(vc-parent-buffer vc-parent-buffer-name)
74 minor-mode-alist)))
594722a8
ER
75
76;; General customization
77
78(defvar vc-default-back-end nil
79 "*Back-end actually used by this interface; may be SCCS or RCS.
80The value is only computed when needed to avoid an expensive search.")
594722a8 81(defvar vc-suppress-confirm nil
34291cd2 82 "*If non-nil, treat user as expert; suppress yes-no prompts on some things.")
594722a8 83(defvar vc-keep-workfiles t
80688f5c
RS
84 "*If non-nil, don't delete working files after registering changes.
85If the back-end is CVS, workfiles are always kept, regardless of the
86value of this flag.")
594722a8
ER
87(defvar vc-initial-comment nil
88 "*Prompt for initial comment when a file is registered.")
89(defvar vc-command-messages nil
90 "*Display run messages from back-end commands.")
91(defvar vc-mistrust-permissions 'file-symlink-p
92 "*Don't assume that permissions and ownership track version-control status.")
666a0ebb
RM
93(defvar vc-checkin-switches nil
94 "*Extra switches passed to the checkin program by \\[vc-checkin].")
0fb16286
RM
95(defvar vc-checkout-switches nil
96 "*Extra switches passed to the checkout program by \\[vc-checkout].")
282aa6a2 97(defvar vc-path
b75f9a76
RS
98 (if (file-directory-p "/usr/sccs")
99 '("/usr/sccs")
100 nil)
282aa6a2 101 "*List of extra directories to search for version control commands.")
632e9525
RS
102(defvar vc-directory-exclusion-list '("SCCS" "RCS")
103 "*Directory names ignored by functions that recursively walk file trees.")
666a0ebb 104
8c0aaf40
ER
105(defconst vc-maximum-comment-ring-size 32
106 "Maximum number of saved comments in the comment ring.")
107
2e810285
RS
108;;; This is duplicated in diff.el.
109(defvar diff-switches "-c"
110 "*A string or list of strings specifying switches to be be passed to diff.")
111
67242a23
RM
112;;;###autoload
113(defvar vc-checkin-hook nil
34291cd2 114 "*List of functions called after a checkin is done. See `run-hooks'.")
67242a23 115
80688f5c
RS
116(defvar vc-make-buffer-writable-hook nil
117 "*List of functions called when a buffer is made writable. See `run-hooks.'
118This hook is only used when the version control system is CVS. It
119might be useful for sites who uses locking with CVS, or who uses link
120farms to gold trees.")
121
594722a8
ER
122;; Header-insertion hair
123
124(defvar vc-header-alist
80688f5c 125 '((SCCS "\%W\%") (RCS "\$Id\$") (CVS "\$Id\$"))
820bde8d 126 "*Header keywords to be inserted when `vc-insert-headers' is executed.")
ba5c3004 127(defvar vc-static-header-alist
594722a8
ER
128 '(("\\.c$" .
129 "\n#ifndef lint\nstatic char vcid[] = \"\%s\";\n#endif /* lint */\n"))
130 "*Associate static header string templates with file types. A \%s in the
131template is replaced with the first string associated with the file's
34291cd2 132version-control type in `vc-header-alist'.")
7b4f934d 133
594722a8
ER
134(defvar vc-comment-alist
135 '((nroff-mode ".\\\"" ""))
136 "*Special comment delimiters to be used in generating vc headers only.
137Add an entry in this list if you need to override the normal comment-start
138and comment-end variables. This will only be necessary if the mode language
139is sensitive to blank lines.")
140
bbf97570
RS
141;; Default is to be extra careful for super-user.
142(defvar vc-checkout-carefully (= (user-uid) 0)
143 "*Non-nil means be extra-careful in checkout.
144Verify that the file really is not locked
145and that its contents match what the master file says.")
146
594722a8
ER
147;; Variables the user doesn't need to know about.
148(defvar vc-log-entry-mode nil)
149(defvar vc-log-operation nil)
67242a23 150(defvar vc-log-after-operation-hook nil)
34291cd2 151(defvar vc-checkout-writable-buffer-hook 'vc-checkout-writable-buffer)
dbf87856
RS
152;; In a log entry buffer, this is a local variable
153;; that points to the buffer for which it was made
154;; (either a file, or a VC dired buffer).
1a2f456b 155(defvar vc-parent-buffer nil)
8c0aaf40 156(defvar vc-parent-buffer-name nil)
594722a8 157
db59472c
RS
158(defvar vc-log-file)
159(defvar vc-log-version)
160
594722a8
ER
161(defconst vc-name-assoc-file "VC-names")
162
8c0aaf40 163(defvar vc-dired-mode nil)
e1f297e6
ER
164(make-variable-buffer-local 'vc-dired-mode)
165
8c0aaf40
ER
166(defvar vc-comment-ring nil)
167(defvar vc-comment-ring-index nil)
168(defvar vc-last-comment-match nil)
169
632e9525
RS
170;; Back-portability to Emacs 18
171
172(defun file-executable-p-18 (f)
173 (let ((modes (file-modes f)))
174 (and modes (not (zerop (logand 292))))))
175
176(defun file-regular-p-18 (f)
177 (let ((attributes (file-attributes f)))
178 (and attributes (not (car attributes)))))
179
180; Conditionally rebind some things for Emacs 18 compatibility
181(if (not (boundp 'minor-mode-map-alist))
182 (progn
183 (setq compilation-old-error-list nil)
184 (fset 'file-executable-p 'file-executable-p-18)
185 (fset 'shrink-window-if-larger-than-buffer 'beginning-of-buffer)
186 ))
187
188(if (not (boundp 'file-regular-p))
189 (fset 'file-regular-p 'file-regular-p-18))
190
594722a8
ER
191;; File property caching
192
193(defun vc-file-clearprops (file)
194 ;; clear all properties of a given file
195 (setplist (intern file vc-file-prop-obarray) nil))
196
8c0aaf40
ER
197(defun vc-clear-context ()
198 "Clear all cached file properties and the comment ring."
199 (interactive)
200 (fillarray vc-file-prop-obarray nil)
201 ;; Note: there is potential for minor lossage here if there is an open
202 ;; log buffer with a nonzero local value of vc-comment-ring-index.
203 (setq vc-comment-ring nil))
204
594722a8
ER
205;; Random helper functions
206
7ef84cf9
RS
207(defun vc-registration-error (file)
208 (if file
590cc449
RS
209 (error "File %s is not under version control" file)
210 (error "Buffer %s is not associated with a file" (buffer-name))))
7ef84cf9 211
594722a8
ER
212(defvar vc-binary-assoc nil)
213
214(defun vc-find-binary (name)
215 "Look for a command anywhere on the subprocess-command search path."
216 (or (cdr (assoc name vc-binary-assoc))
deb9ebc6
PE
217 (catch 'found
218 (mapcar
219 (function
220 (lambda (s)
221 (if s
222 (let ((full (concat s "/" name)))
223 (if (file-executable-p full)
224 (progn
225 (setq vc-binary-assoc
226 (cons (cons name full) vc-binary-assoc))
227 (throw 'found full)))))))
228 exec-path)
229 nil)))
594722a8 230
80688f5c 231(defun vc-do-command (okstatus command file last &rest flags)
594722a8
ER
232 "Execute a version-control command, notifying user and checking for errors.
233The command is successful if its exit status does not exceed OKSTATUS.
234Output from COMMAND goes to buffer *vc*. The last argument of the command is
632e9525
RS
235the master name of FILE if LAST is 'MASTER, or the workfile of FILE if LAST is
236'WORKFILE; this is appended to an optional list of FLAGS."
594722a8
ER
237 (setq file (expand-file-name file))
238 (if vc-command-messages
02da6253 239 (message "Running %s on %s..." command file))
1a2f456b 240 (let ((obuf (current-buffer)) (camefrom (current-buffer))
594722a8
ER
241 (squeezed nil)
242 (vc-file (and file (vc-name file)))
632e9525 243 (olddir default-directory)
594722a8
ER
244 status)
245 (set-buffer (get-buffer-create "*vc*"))
8c0aaf40
ER
246 (set (make-local-variable 'vc-parent-buffer) camefrom)
247 (set (make-local-variable 'vc-parent-buffer-name)
248 (concat " from " (buffer-name camefrom)))
632e9525 249 (setq default-directory olddir)
8c0aaf40 250
594722a8 251 (erase-buffer)
315e49ed 252
594722a8
ER
253 (mapcar
254 (function (lambda (s) (and s (setq squeezed (append squeezed (list s))))))
255 flags)
80688f5c 256 (if (and vc-file (eq last 'MASTER))
594722a8 257 (setq squeezed (append squeezed (list vc-file))))
632e9525
RS
258 (if (eq last 'WORKFILE)
259 (progn
260 (let* ((pwd (expand-file-name default-directory))
261 (preflen (length pwd)))
262 (if (string= (substring file 0 preflen) pwd)
263 (setq file (substring file preflen))))
264 (setq squeezed (append squeezed (list file)))))
46cd263f 265 (let ((exec-path (append vc-path exec-path))
eadcb02c
RS
266 ;; Add vc-path to PATH for the execution of this command.
267 (process-environment
268 (cons (concat "PATH=" (getenv "PATH")
269 ":" (mapconcat 'identity vc-path ":"))
270 process-environment)))
1a2f456b 271 (setq status (apply 'call-process command nil t nil squeezed)))
594722a8 272 (goto-char (point-max))
632e9525 273 (set-buffer-modified-p nil)
4805f679 274 (forward-line -1)
594722a8
ER
275 (if (or (not (integerp status)) (< okstatus status))
276 (progn
594722a8 277 (pop-to-buffer "*vc*")
594722a8 278 (goto-char (point-min))
4c2d8cf9 279 (shrink-window-if-larger-than-buffer)
02da6253
PE
280 (error "Running %s...FAILED (%s)" command
281 (if (integerp status)
282 (format "status %d" status)
283 status))
594722a8
ER
284 )
285 (if vc-command-messages
02da6253 286 (message "Running %s...OK" command))
594722a8
ER
287 )
288 (set-buffer obuf)
289 status)
290 )
291
c4ae7096
JB
292;;; Save a bit of the text around POSN in the current buffer, to help
293;;; us find the corresponding position again later. This works even
294;;; if all markers are destroyed or corrupted.
295(defun vc-position-context (posn)
296 (list posn
297 (buffer-size)
298 (buffer-substring posn
299 (min (point-max) (+ posn 100)))))
300
301;;; Return the position of CONTEXT in the current buffer, or nil if we
302;;; couldn't find it.
303(defun vc-find-position-by-context (context)
304 (let ((context-string (nth 2 context)))
305 (if (equal "" context-string)
306 (point-max)
307 (save-excursion
308 (let ((diff (- (nth 1 context) (buffer-size))))
309 (if (< diff 0) (setq diff (- diff)))
310 (goto-char (nth 0 context))
311 (if (or (search-forward context-string nil t)
312 ;; Can't use search-backward since the match may continue
313 ;; after point.
314 (progn (goto-char (- (point) diff (length context-string)))
315 ;; goto-char doesn't signal an error at
316 ;; beginning of buffer like backward-char would
317 (search-forward context-string nil t)))
318 ;; to beginning of OSTRING
319 (- (point) (length context-string))))))))
320
594722a8 321(defun vc-revert-buffer1 (&optional arg no-confirm)
7b4f934d 322 ;; Most of this was shamelessly lifted from Sebastian Kremer's rcs.el mode.
c4ae7096 323 ;; Revert buffer, try to keep point and mark where user expects them in spite
594722a8
ER
324 ;; of changes because of expanded version-control key words.
325 ;; This is quite important since otherwise typeahead won't work as expected.
326 (interactive "P")
327 (widen)
c4ae7096 328 (let ((point-context (vc-position-context (point)))
cfadef63
RS
329 ;; Use mark-marker to avoid confusion in transient-mark-mode.
330 (mark-context (if (eq (marker-buffer (mark-marker)) (current-buffer))
331 (vc-position-context (mark-marker))))
332 ;; Make the right thing happen in transient-mark-mode.
ab877583
RM
333 (mark-active nil)
334 ;; We may want to reparse the compilation buffer after revert
335 (reparse (and (boundp 'compilation-error-list) ;compile loaded
336 (let ((curbuf (current-buffer)))
337 ;; Construct a list; each elt is nil or a buffer
338 ;; iff that buffer is a compilation output buffer
339 ;; that contains markers into the current buffer.
340 (save-excursion
7ef84cf9
RS
341 (mapcar (function
342 (lambda (buffer)
ab877583
RM
343 (set-buffer buffer)
344 (let ((errors (or
345 compilation-old-error-list
346 compilation-error-list))
347 (buffer-error-marked-p nil))
6fb6ab11 348 (while (and (consp errors)
ab877583 349 (not buffer-error-marked-p))
a1bda481 350 (and (markerp (cdr (car errors)))
e9c8e248
RM
351 (eq buffer
352 (marker-buffer
a1bda481 353 (cdr (car errors))))
e9c8e248 354 (setq buffer-error-marked-p t))
ab877583 355 (setq errors (cdr errors)))
7ef84cf9 356 (if buffer-error-marked-p buffer))))
ab877583 357 (buffer-list)))))))
7b4f934d 358
632e9525
RS
359 (let ((in-font-lock-mode (and (boundp 'font-lock-fontified)
360 font-lock-fontified)))
361 (if in-font-lock-mode
362 (font-lock-mode 0))
363
364 ;; the actual revisit
365 (revert-buffer arg no-confirm)
366
367 (if in-font-lock-mode
368 (font-lock-mode 1)))
7b4f934d 369
ab877583
RM
370 ;; Reparse affected compilation buffers.
371 (while reparse
372 (if (car reparse)
373 (save-excursion
374 (set-buffer (car reparse))
375 (let ((compilation-last-buffer (current-buffer)) ;select buffer
376 ;; Record the position in the compilation buffer of
377 ;; the last error next-error went to.
378 (error-pos (marker-position
379 (car (car-safe compilation-error-list)))))
380 ;; Reparse the error messages as far as they were parsed before.
381 (compile-reinitialize-errors '(4) compilation-parsing-end)
382 ;; Move the pointer up to find the error we were at before
383 ;; reparsing. Now next-error should properly go to the next one.
384 (while (and compilation-error-list
27f2f10b 385 (/= error-pos (car (car compilation-error-list))))
ab877583
RM
386 (setq compilation-error-list (cdr compilation-error-list))))))
387 (setq reparse (cdr reparse)))
e1f297e6 388
7b4f934d 389 ;; Restore point and mark
c4ae7096
JB
390 (let ((new-point (vc-find-position-by-context point-context)))
391 (if new-point (goto-char new-point)))
392 (if mark-context
393 (let ((new-mark (vc-find-position-by-context mark-context)))
394 (if new-mark (set-mark new-mark))))))
395
594722a8 396
97d3f950 397(defun vc-buffer-sync (&optional not-urgent)
594722a8 398 ;; Make sure the current buffer and its working file are in sync
97d3f950 399 ;; NOT-URGENT means it is ok to continue if the user says not to save.
bbf97570 400 (if (buffer-modified-p)
97d3f950
RS
401 (if (or vc-suppress-confirm
402 (y-or-n-p (format "Buffer %s modified; save it? " (buffer-name))))
403 (save-buffer)
404 (if not-urgent
405 nil
406 (error "Aborted")))))
407
594722a8 408
9a51ed3a 409(defun vc-workfile-unchanged-p (file &optional want-differences-if-changed)
594722a8
ER
410 ;; Has the given workfile changed since last checkout?
411 (let ((checkout-time (vc-file-getprop file 'vc-checkout-time))
412 (lastmod (nth 5 (file-attributes file))))
9a51ed3a
PE
413 (or (equal checkout-time lastmod)
414 (and (or (not checkout-time) want-differences-if-changed)
415 (let ((unchanged (zerop (vc-backend-diff file nil nil
416 (not want-differences-if-changed)))))
417 ;; 0 stands for an unknown time; it can't match any mod time.
418 (vc-file-setprop file 'vc-checkout-time (if unchanged lastmod 0))
419 unchanged)))))
594722a8 420
e1f297e6
ER
421(defun vc-next-action-on-file (file verbose &optional comment)
422 ;;; If comment is specified, it will be used as an admin or checkin comment.
80688f5c
RS
423 (let ((vc-file (vc-name file))
424 (vc-type (vc-backend-deduce file))
425 owner version)
e1f297e6
ER
426 (cond
427
428 ;; if there is no master file corresponding, create one
429 ((not vc-file)
632e9525
RS
430 (vc-register verbose comment)
431 (if vc-initial-comment
432 (setq vc-log-after-operation-hook
433 'vc-checkout-writable-buffer-hook)
434 (vc-checkout-writable-buffer file)))
e1f297e6
ER
435
436 ;; if there is no lock on the file, assert one and get it
80688f5c
RS
437 ((and (not (eq vc-type 'CVS)) ;There are no locks in CVS.
438 (not (setq owner (vc-locking-user file))))
bbf97570 439 (if (and vc-checkout-carefully
9a51ed3a 440 (not (vc-workfile-unchanged-p file t)))
bbf97570
RS
441 (if (save-window-excursion
442 (pop-to-buffer "*vc*")
443 (goto-char (point-min))
444 (insert-string (format "Changes to %s since last lock:\n\n"
445 file))
446 (not (beep))
447 (yes-or-no-p
448 (concat "File has unlocked changes, "
449 "claim lock retaining changes? ")))
450 (progn (vc-backend-steal file)
451 (vc-mode-line file))
452 (if (not (yes-or-no-p "Revert to checked-in version, instead? "))
453 (error "Checkout aborted.")
454 (vc-revert-buffer1 t t)
455 (vc-checkout-writable-buffer file))
456 )
457 (vc-checkout-writable-buffer file)))
e1f297e6
ER
458
459 ;; a checked-out version exists, but the user may not own the lock
80688f5c
RS
460 ((and (not (eq vc-type 'CVS)) ;There are no locks in CVS.
461 (not (string-equal owner (user-login-name))))
e1f297e6 462 (if comment
590cc449 463 (error "Sorry, you can't steal the lock on %s this way" file))
e1f297e6
ER
464 (vc-steal-lock
465 file
466 (and verbose (read-string "Version to steal: "))
467 owner))
80688f5c
RS
468
469 ;; changes to the master file needs to be merged back into the
470 ;; working file
471 ((and (eq vc-type 'CVS)
472 ;; "0" means "added, but not yet committed"
473 (not (string= (vc-file-getprop file 'vc-your-latest-version) "0"))
474 (progn
475 (vc-fetch-properties file)
476 (not (string= (vc-file-getprop file 'vc-your-latest-version)
477 (vc-file-getprop file 'vc-latest-version)))))
478 (vc-buffer-sync)
479 (if (yes-or-no-p (format "%s is not up-to-date. Merge in changes now? "
480 (buffer-name)))
481 (progn
482 (if (and (buffer-modified-p)
483 (not (yes-or-no-p
484 "Buffer %s modified; merge file on disc anyhow? "
485 (buffer-name))))
486 (error "Merge aborted"))
487 (if (not (zerop (vc-backend-merge-news file)))
488 ;; Overlaps detected - what now? Should use some
489 ;; fancy RCS conflict resolving package, or maybe
490 ;; emerge, but for now, simply warn the user with a
491 ;; message.
492 (message "Conflicts detected!"))
493 (vc-resynch-window file t (not (buffer-modified-p))))
494
495 (error "%s needs update" (buffer-name))))
496
497 ((and buffer-read-only (eq vc-type 'CVS))
498 (toggle-read-only)
499 ;; Sites who make link farms to a read-only gold tree (or
500 ;; something similar) can use the hook below to break the
501 ;; sym-link.
502 (run-hooks 'vc-make-buffer-writable-hook))
503
504 ;; OK, user owns the lock on the file (or we are running CVS)
8c0aaf40 505 (t
e1f297e6
ER
506 (find-file file)
507
508 ;; give luser a chance to save before checking in.
509 (vc-buffer-sync)
510
511 ;; Revert if file is unchanged and buffer is too.
512 ;; If buffer is modified, that means the user just said no
513 ;; to saving it; in that case, don't revert,
514 ;; because the user might intend to save
515 ;; after finishing the log entry.
80688f5c 516 (if (and (vc-workfile-unchanged-p file)
e1f297e6
ER
517 (not (buffer-modified-p)))
518 (progn
80688f5c
RS
519 (if (eq vc-type 'CVS)
520 (message "No changes to %s" file)
521
522 (vc-backend-revert file)
523 ;; DO NOT revert the file without asking the user!
524 (vc-resynch-window file t nil)))
e1f297e6
ER
525
526 ;; user may want to set nonstandard parameters
527 (if verbose
528 (setq version (read-string "New version level: ")))
529
530 ;; OK, let's do the checkin
531 (vc-checkin file version comment)
8c0aaf40 532 )))))
e1f297e6
ER
533
534(defun vc-next-action-dired (file rev comment)
535 ;; We've accepted a log comment, now do a vc-next-action using it on all
536 ;; marked files.
537 (set-buffer vc-parent-buffer)
632e9525
RS
538 (let ((configuration (current-window-configuration)))
539 (dired-map-over-marks
540 (save-window-excursion
541 (let ((file (dired-get-filename)))
542 (message "Processing %s..." file)
543 (vc-next-action-on-file file nil comment)
544 (message "Processing %s...done" file)))
545 nil t)
546 (set-window-configuration configuration))
e1f297e6
ER
547 )
548
637a8ae9 549;; Here's the major entry point.
594722a8 550
637a8ae9 551;;;###autoload
594722a8
ER
552(defun vc-next-action (verbose)
553 "Do the next logical checkin or checkout operation on the current file.
80688f5c
RS
554
555For RCS and SCCS files:
594722a8 556 If the file is not already registered, this registers it for version
34291cd2 557control and then retrieves a writable, locked copy for editing.
594722a8 558 If the file is registered and not locked by anyone, this checks out
34291cd2 559a writable and locked file ready for editing.
594722a8
ER
560 If the file is checked out and locked by the calling user, this
561first checks to see if the file has changed since checkout. If not,
562it performs a revert.
e1f297e6
ER
563 If the file has been changed, this pops up a buffer for entry
564of a log message; when the message has been entered, it checks in the
594722a8 565resulting changes along with the log message as change commentary. If
34291cd2 566the variable `vc-keep-workfiles' is non-nil (which is its default), a
594722a8
ER
567read-only copy of the changed file is left in place afterwards.
568 If the file is registered and locked by someone else, you are given
e1f297e6 569the option to steal the lock.
80688f5c
RS
570
571For CVS files:
572 If the file is not already registered, this registers it for version
573control. This does a \"cvs add\", but no \"cvs commit\".
574 If the file is added but not committed, it is committed.
575 If the file has not been changed, neither in your working area or
576in the repository, a message is printed and nothing is done.
577 If your working file is changed, but the repository file is
578unchanged, this pops up a buffer for entry of a log message; when the
579message has been entered, it checks in the resulting changes along
580with the logmessage as change commentary. A writable file is retained.
581 If the repository file is changed, you are asked if you want to
582merge in the changes into your working copy.
583
584The following is true regardless of which version control system you
585are using:
586
e1f297e6
ER
587 If you call this from within a VC dired buffer with no files marked,
588it will operate on the file in the current line.
589 If you call this from within a VC dired buffer, and one or more
590files are marked, it will accept a log message and then operate on
591each one. The log message will be used as a comment for any register
592or checkin operations, but ignored when doing checkouts. Attempted
bbf97570
RS
593lock steals will raise an error.
594
595 For checkin, a prefix argument lets you specify the version number to use."
594722a8 596 (interactive "P")
8c0aaf40
ER
597 (catch 'nogo
598 (if vc-dired-mode
599 (let ((files (dired-get-marked-files)))
600 (if (= (length files) 1)
cdaf7a1a 601 (find-file-other-window (car files))
8c0aaf40
ER
602 (vc-start-entry nil nil nil
603 "Enter a change comment for the marked files."
604 'vc-next-action-dired)
9a47d40b 605 (throw 'nogo nil))))
e1f297e6 606 (while vc-parent-buffer
1a2f456b 607 (pop-to-buffer vc-parent-buffer))
e1f297e6
ER
608 (if buffer-file-name
609 (vc-next-action-on-file buffer-file-name verbose)
7ef84cf9 610 (vc-registration-error nil))))
594722a8
ER
611
612;;; These functions help the vc-next-action entry point
613
34291cd2
RS
614(defun vc-checkout-writable-buffer (&optional file)
615 "Retrieve a writable copy of the latest version of the current buffer's file."
8c0aaf40 616 (vc-checkout (or file (buffer-file-name)) t)
02da6253
PE
617 )
618
637a8ae9 619;;;###autoload
e1f297e6 620(defun vc-register (&optional override comment)
594722a8
ER
621 "Register the current file into your version-control system."
622 (interactive "P")
e837a82f
RS
623 (let ((master (vc-name buffer-file-name)))
624 (and master (file-exists-p master)
625 (error "This file is already registered"))
626 (and master
627 (not (y-or-n-p "Previous master file has vanished. Make a new one? "))
628 (error "This file is already registered")))
02da6253
PE
629 ;; Watch out for new buffers of size 0: the corresponding file
630 ;; does not exist yet, even though buffer-modified-p is nil.
631 (if (and (not (buffer-modified-p))
632 (zerop (buffer-size))
633 (not (file-exists-p buffer-file-name)))
634 (set-buffer-modified-p t))
594722a8
ER
635 (vc-buffer-sync)
636 (vc-admin
637 buffer-file-name
e1f297e6
ER
638 (and override
639 (read-string
640 (format "Initial version level for %s: " buffer-file-name))))
594722a8
ER
641 )
642
624b4662 643(defun vc-resynch-window (file &optional keep noquery)
594722a8
ER
644 ;; If the given file is in the current buffer,
645 ;; either revert on it so we see expanded keyworks,
646 ;; or unvisit it (depending on vc-keep-workfiles)
624b4662
RS
647 ;; NOQUERY if non-nil inhibits confirmation for reverting.
648 ;; NOQUERY should be t *only* if it is known the only difference
649 ;; between the buffer and the file is due to RCS rather than user editing!
594722a8
ER
650 (and (string= buffer-file-name file)
651 (if keep
652 (progn
1ab31687 653 (vc-revert-buffer1 t noquery)
594722a8
ER
654 (vc-mode-line buffer-file-name))
655 (progn
656 (delete-window)
657 (kill-buffer (current-buffer))))))
658
b965445f 659(defun vc-start-entry (file rev comment msg action &optional after-hook)
e1f297e6
ER
660 ;; Accept a comment for an operation on FILE revision REV. If COMMENT
661 ;; is nil, pop up a VC-log buffer, emit MSG, and set the
662 ;; action on close to ACTION; otherwise, do action immediately.
cdaf7a1a 663 ;; Remember the file's buffer in vc-parent-buffer (current one if no file).
b965445f 664 ;; AFTER-HOOK specifies the local value for vc-log-operation-hook.
e1f297e6
ER
665 (let ((parent (if file (find-file-noselect file) (current-buffer))))
666 (if comment
667 (set-buffer (get-buffer-create "*VC-log*"))
668 (pop-to-buffer (get-buffer-create "*VC-log*")))
8c0aaf40
ER
669 (set (make-local-variable 'vc-parent-buffer) parent)
670 (set (make-local-variable 'vc-parent-buffer-name)
671 (concat " from " (buffer-name vc-parent-buffer)))
cf50f717 672 (vc-mode-line (or file " (no file)"))
e1f297e6 673 (vc-log-mode)
b965445f
RS
674 (make-local-variable 'vc-log-after-operation-hook)
675 (if after-hook
676 (setq vc-log-after-operation-hook after-hook))
e1f297e6
ER
677 (setq vc-log-operation action)
678 (setq vc-log-file file)
679 (setq vc-log-version rev)
680 (if comment
681 (progn
682 (erase-buffer)
8c0aaf40
ER
683 (if (eq comment t)
684 (vc-finish-logentry t)
685 (insert comment)
686 (vc-finish-logentry nil)))
e1f297e6 687 (message "%s Type C-c C-c when done." msg))))
594722a8 688
e1f297e6 689(defun vc-admin (file rev &optional comment)
624b4662 690 "Check a file into your version-control system.
594722a8 691FILE is the unmodified name of the file. REV should be the base version
e1f297e6 692level to check it in under. COMMENT, if specified, is the checkin comment."
b965445f
RS
693 (vc-start-entry file rev
694 (or comment (not vc-initial-comment))
695 "Enter initial comment." 'vc-backend-admin
6bcb1d4e 696 nil))
e1f297e6 697
34291cd2 698(defun vc-checkout (file &optional writable)
e1f297e6
ER
699 "Retrieve a copy of the latest version of the given file."
700 ;; If ftp is on this system and the name matches the ange-ftp format
701 ;; for a remote file, the user is trying something that won't work.
702 (if (and (string-match "^/[^/:]+:" file) (vc-find-binary "ftp"))
703 (error "Sorry, you can't check out files over FTP"))
34291cd2 704 (vc-backend-checkout file writable)
e1f297e6
ER
705 (if (string-equal file buffer-file-name)
706 (vc-resynch-window file t t))
707 )
594722a8
ER
708
709(defun vc-steal-lock (file rev &optional owner)
710 "Steal the lock on the current workfile."
29fc1ce9
RS
711 (let (file-description)
712 (if (not owner)
713 (setq owner (vc-locking-user file)))
714 (if rev
715 (setq file-description (format "%s:%s" file rev))
716 (setq file-description file))
717 (if (not (y-or-n-p (format "Take the lock on %s from %s? "
718 file-description owner)))
719 (error "Steal cancelled"))
720 (pop-to-buffer (get-buffer-create "*VC-mail*"))
721 (setq default-directory (expand-file-name "~/"))
722 (auto-save-mode auto-save-default)
723 (mail-mode)
724 (erase-buffer)
725 (mail-setup owner (format "Stolen lock on %s" file-description) nil nil nil
3e3da61f 726 (list (list 'vc-finish-steal file rev)))
29fc1ce9
RS
727 (goto-char (point-max))
728 (insert
729 (format "I stole the lock on %s, " file-description)
730 (current-time-string)
731 ".\n")
732 (message "Please explain why you stole the lock. Type C-c C-c when done.")))
594722a8 733
ad014629 734;; This is called when the notification has been sent.
594722a8 735(defun vc-finish-steal (file version)
594722a8 736 (vc-backend-steal file version)
3e3da61f
RS
737 (if (get-file-buffer file)
738 (save-excursion
6242bee4 739 (set-buffer (get-file-buffer file))
3e3da61f 740 (vc-resynch-window file t t))))
594722a8 741
594722a8
ER
742(defun vc-checkin (file &optional rev comment)
743 "Check in the file specified by FILE.
744The optional argument REV may be a string specifying the new version level
67242a23 745\(if nil increment the current level). The file is either retained with write
34291cd2 746permissions zeroed, or deleted (according to the value of `vc-keep-workfiles').
80688f5c 747If the back-end is CVS, a writable workfile is always kept.
594722a8
ER
748COMMENT is a comment string; if omitted, a buffer is
749popped up to accept a comment."
61446fe7 750 (vc-start-entry file rev comment
b965445f
RS
751 "Enter a change comment." 'vc-backend-checkin
752 'vc-checkin-hook))
594722a8 753
1a2f456b
ER
754;;; Here is a checkin hook that may prove useful to sites using the
755;;; ChangeLog facility supported by Emacs.
3b4dd9a9
RM
756(defun vc-comment-to-change-log (&optional whoami file-name)
757 "Enter last VC comment into change log file for current buffer's file.
758Optional arg (interactive prefix) non-nil means prompt for user name and site.
759Second arg is file name of change log. \
760If nil, uses `change-log-default-name'."
43cea1ab
RM
761 (interactive (if current-prefix-arg
762 (list current-prefix-arg
763 (prompt-for-change-log-name))))
41208291
KH
764 ;; Make sure the defvar for add-log-current-defun-function has been executed
765 ;; before binding it.
766 (require 'add-log)
3b4dd9a9
RM
767 (let (;; Extract the comment first so we get any error before doing anything.
768 (comment (ring-ref vc-comment-ring 0))
43cea1ab 769 ;; Don't let add-change-log-entry insert a defun name.
3b4dd9a9
RM
770 (add-log-current-defun-function 'ignore)
771 end)
772 ;; Call add-log to do half the work.
43cea1ab 773 (add-change-log-entry whoami file-name t t)
3b4dd9a9
RM
774 ;; Insert the VC comment, leaving point before it.
775 (setq end (save-excursion (insert comment) (point-marker)))
776 (if (looking-at "\\s *\\s(")
777 ;; It starts with an open-paren, as in "(foo): Frobbed."
43cea1ab 778 ;; So remove the ": " add-log inserted.
3b4dd9a9
RM
779 (delete-char -2))
780 ;; Canonicalize the white space between the file name and comment.
781 (just-one-space)
782 ;; Indent rest of the text the same way add-log indented the first line.
783 (let ((indentation (current-indentation)))
784 (save-excursion
785 (while (< (point) end)
786 (forward-line 1)
787 (indent-to indentation))
c124b1b4 788 (setq end (point))))
3b4dd9a9 789 ;; Fill the inserted text, preserving open-parens at bol.
6b60c5d1
BG
790 (let ((paragraph-separate (concat paragraph-separate "\\|\\s *\\s("))
791 (paragraph-start (concat paragraph-start "\\|\\s *\\s(")))
43cea1ab 792 (beginning-of-line)
c124b1b4
RM
793 (fill-region (point) end))
794 ;; Canonicalize the white space at the end of the entry so it is
795 ;; separated from the next entry by a single blank line.
796 (skip-syntax-forward " " end)
797 (delete-char (- (skip-syntax-backward " ")))
798 (or (eobp) (looking-at "\n\n")
799 (insert "\n"))))
3b4dd9a9 800
1a2f456b 801
8c0aaf40 802(defun vc-finish-logentry (&optional nocomment)
594722a8
ER
803 "Complete the operation implied by the current log entry."
804 (interactive)
8c0aaf40
ER
805 ;; Check and record the comment, if any.
806 (if (not nocomment)
807 (progn
808 (goto-char (point-max))
809 (if (not (bolp))
810 (newline))
811 ;; Comment too long?
812 (vc-backend-logentry-check vc-log-file)
813 ;; Record the comment in the comment ring
814 (if (null vc-comment-ring)
815 (setq vc-comment-ring (make-ring vc-maximum-comment-ring-size)))
816 (ring-insert vc-comment-ring (buffer-string))
817 ))
b2396d1f 818 ;; Sync parent buffer in case the user modified it while editing the comment.
cdaf7a1a 819 ;; But not if it is a vc-dired buffer.
b2396d1f 820 (save-excursion
cdaf7a1a
RS
821 (set-buffer vc-parent-buffer)
822 (or vc-dired-mode
823 (vc-buffer-sync)))
594722a8
ER
824 ;; OK, do it to it
825 (if vc-log-operation
e1f297e6
ER
826 (save-excursion
827 (funcall vc-log-operation
828 vc-log-file
829 vc-log-version
830 (buffer-string)))
590cc449 831 (error "No log operation is pending"))
e2bef5c3
RS
832 ;; save the vc-log-after-operation-hook of log buffer
833 (let ((after-hook vc-log-after-operation-hook))
834 ;; Return to "parent" buffer of this checkin and remove checkin window
835 (pop-to-buffer vc-parent-buffer)
836 (let ((logbuf (get-buffer "*VC-log*")))
837 (delete-windows-on logbuf)
838 (kill-buffer logbuf))
839 ;; Now make sure we see the expanded headers
840 (if buffer-file-name
e1f297e6 841 (vc-resynch-window buffer-file-name vc-keep-workfiles t))
e2bef5c3 842 (run-hooks after-hook)))
594722a8
ER
843
844;; Code for access to the comment ring
845
8c0aaf40
ER
846(defun vc-previous-comment (arg)
847 "Cycle backwards through comment history."
848 (interactive "*p")
849 (let ((len (ring-length vc-comment-ring)))
850 (cond ((<= len 0)
851 (message "Empty comment ring")
852 (ding))
853 (t
854 (erase-buffer)
855 ;; Initialize the index on the first use of this command
856 ;; so that the first M-p gets index 0, and the first M-n gets
857 ;; index -1.
858 (if (null vc-comment-ring-index)
859 (setq vc-comment-ring-index
860 (if (> arg 0) -1
861 (if (< arg 0) 1 0))))
862 (setq vc-comment-ring-index
c6bfcd12 863 (mod (+ vc-comment-ring-index arg) len))
8c0aaf40
ER
864 (message "%d" (1+ vc-comment-ring-index))
865 (insert (ring-ref vc-comment-ring vc-comment-ring-index))))))
866
867(defun vc-next-comment (arg)
868 "Cycle forwards through comment history."
869 (interactive "*p")
870 (vc-previous-comment (- arg)))
871
872(defun vc-comment-search-reverse (str)
873 "Searches backwards through comment history for substring match."
874 (interactive "sComment substring: ")
875 (if (string= str "")
876 (setq str vc-last-comment-match)
877 (setq vc-last-comment-match str))
878 (if (null vc-comment-ring-index)
879 (setq vc-comment-ring-index -1))
880 (let ((str (regexp-quote str))
881 (len (ring-length vc-comment-ring))
882 (n (1+ vc-comment-ring-index)))
883 (while (and (< n len) (not (string-match str (ring-ref vc-comment-ring n))))
884 (setq n (+ n 1)))
885 (cond ((< n len)
886 (vc-previous-comment (- n vc-comment-ring-index)))
887 (t (error "Not found")))))
888
889(defun vc-comment-search-forward (str)
890 "Searches forwards through comment history for substring match."
891 (interactive "sComment substring: ")
892 (if (string= str "")
893 (setq str vc-last-comment-match)
894 (setq vc-last-comment-match str))
895 (if (null vc-comment-ring-index)
896 (setq vc-comment-ring-index 0))
897 (let ((str (regexp-quote str))
898 (len (ring-length vc-comment-ring))
899 (n vc-comment-ring-index))
900 (while (and (>= n 0) (not (string-match str (ring-ref vc-comment-ring n))))
901 (setq n (- n 1)))
902 (cond ((>= n 0)
903 (vc-next-comment (- n vc-comment-ring-index)))
904 (t (error "Not found")))))
594722a8
ER
905
906;; Additional entry points for examining version histories
907
637a8ae9 908;;;###autoload
97d3f950 909(defun vc-diff (historic &optional not-urgent)
e8ee1ccf
RS
910 "Display diffs between file versions.
911Normally this compares the current file and buffer with the most recent
912checked in version of that file. This uses no arguments.
913With a prefix argument, it reads the file name to use
914and two version designators specifying which versions to compare."
594722a8 915 (interactive "P")
e1f297e6
ER
916 (if vc-dired-mode
917 (set-buffer (find-file-noselect (dired-get-filename))))
8ac8c82f 918 (while vc-parent-buffer
1a2f456b 919 (pop-to-buffer vc-parent-buffer))
594722a8
ER
920 (if historic
921 (call-interactively 'vc-version-diff)
8c0aaf40 922 (if (or (null buffer-file-name) (null (vc-name buffer-file-name)))
064726ac
JB
923 (error
924 "There is no version-control master associated with this buffer"))
02da6253 925 (let ((file buffer-file-name)
594722a8 926 unchanged)
7ef84cf9
RS
927 (or (and file (vc-name file))
928 (vc-registration-error file))
97d3f950 929 (vc-buffer-sync not-urgent)
594722a8
ER
930 (setq unchanged (vc-workfile-unchanged-p buffer-file-name))
931 (if unchanged
02da6253 932 (message "No changes to %s since latest version." file)
9a51ed3a 933 (vc-backend-diff file)
8c0aaf40
ER
934 ;; Ideally, we'd like at this point to parse the diff so that
935 ;; the buffer effectively goes into compilation mode and we
936 ;; can visit the old and new change locations via next-error.
937 ;; Unfortunately, this is just too painful to do. The basic
938 ;; problem is that the `old' file doesn't exist to be
939 ;; visited. This plays hell with numerous assumptions in
940 ;; the diff.el and compile.el machinery.
941 (pop-to-buffer "*vc*")
632e9525 942 (setq default-directory (file-name-directory file))
b246f571
RS
943 (if (= 0 (buffer-size))
944 (progn
945 (setq unchanged t)
946 (message "No changes to %s since latest version." file))
4c2d8cf9 947 (goto-char (point-min))
064726ac
JB
948 (shrink-window-if-larger-than-buffer)))
949 (not unchanged))))
594722a8
ER
950
951(defun vc-version-diff (file rel1 rel2)
952 "For FILE, report diffs between two stored versions REL1 and REL2 of it.
953If FILE is a directory, generate diffs between versions for all registered
954files in or below it."
3234e2a3 955 (interactive "FFile or directory to diff: \nsOlder version: \nsNewer version: ")
594722a8
ER
956 (if (string-equal rel1 "") (setq rel1 nil))
957 (if (string-equal rel2 "") (setq rel2 nil))
958 (if (file-directory-p file)
1a2f456b 959 (let ((camefrom (current-buffer)))
594722a8 960 (set-buffer (get-buffer-create "*vc-status*"))
8c0aaf40
ER
961 (set (make-local-variable 'vc-parent-buffer) camefrom)
962 (set (make-local-variable 'vc-parent-buffer-name)
963 (concat " from " (buffer-name camefrom)))
594722a8 964 (erase-buffer)
3234e2a3
ER
965 (insert "Diffs between "
966 (or rel1 "last version checked in")
967 " and "
968 (or rel2 "current workfile(s)")
969 ":\n\n")
594722a8 970 (set-buffer (get-buffer-create "*vc*"))
e1f297e6 971 (cd file)
594722a8
ER
972 (vc-file-tree-walk
973 (function (lambda (f)
a9a59766 974 (message "Looking at %s" f)
594722a8 975 (and
3234e2a3
ER
976 (not (file-directory-p f))
977 (vc-registered f)
02da6253
PE
978 (vc-backend-diff f rel1 rel2)
979 (append-to-buffer "*vc-status*" (point-min) (point-max)))
980 )))
594722a8
ER
981 (pop-to-buffer "*vc-status*")
982 (insert "\nEnd of diffs.\n")
983 (goto-char (point-min))
984 (set-buffer-modified-p nil)
985 )
36bed8bc
RS
986 (if (zerop (vc-backend-diff file rel1 rel2))
987 (message "No changes to %s between %s and %s." file rel1 rel2)
24f1977f 988 (pop-to-buffer "*vc*"))))
594722a8 989
f1818994
PE
990;;;###autoload
991(defun vc-version-other-window (rev)
992 "Visit version REV of the current buffer in another window.
993If the current buffer is named `F', the version is named `F.~REV~'.
994If `F.~REV~' already exists, it is used instead of being re-created."
995 (interactive "sVersion to visit (default is latest version): ")
996 (if vc-dired-mode
997 (set-buffer (find-file-noselect (dired-get-filename))))
998 (while vc-parent-buffer
999 (pop-to-buffer vc-parent-buffer))
1000 (if (and buffer-file-name (vc-name buffer-file-name))
1001 (let* ((version (if (string-equal rev "")
1002 (vc-latest-version buffer-file-name)
1003 rev))
1004 (filename (concat buffer-file-name ".~" version "~")))
1005 (or (file-exists-p filename)
1006 (vc-backend-checkout buffer-file-name nil version filename))
1007 (find-file-other-window filename))
1008 (vc-registration-error buffer-file-name)))
1009
594722a8
ER
1010;; Header-insertion code
1011
637a8ae9 1012;;;###autoload
594722a8
ER
1013(defun vc-insert-headers ()
1014 "Insert headers in a file for use with your version-control system.
1015Headers desired are inserted at the start of the buffer, and are pulled from
34291cd2 1016the variable `vc-header-alist'."
594722a8 1017 (interactive)
e1f297e6
ER
1018 (if vc-dired-mode
1019 (find-file-other-window (dired-get-filename)))
8ac8c82f 1020 (while vc-parent-buffer
1a2f456b 1021 (pop-to-buffer vc-parent-buffer))
594722a8
ER
1022 (save-excursion
1023 (save-restriction
1024 (widen)
1025 (if (or (not (vc-check-headers))
820bde8d 1026 (y-or-n-p "Version headers already exist. Insert another set? "))
594722a8
ER
1027 (progn
1028 (let* ((delims (cdr (assq major-mode vc-comment-alist)))
1029 (comment-start-vc (or (car delims) comment-start "#"))
1030 (comment-end-vc (or (car (cdr delims)) comment-end ""))
1031 (hdstrings (cdr (assoc (vc-backend-deduce (buffer-file-name)) vc-header-alist))))
1032 (mapcar (function (lambda (s)
1033 (insert comment-start-vc "\t" s "\t"
1034 comment-end-vc "\n")))
1035 hdstrings)
1036 (if vc-static-header-alist
1037 (mapcar (function (lambda (f)
1038 (if (string-match (car f) buffer-file-name)
1039 (insert (format (cdr f) (car hdstrings))))))
1040 vc-static-header-alist))
1041 )
1042 )))))
1043
e1f297e6
ER
1044;; The VC directory submode. Coopt Dired for this.
1045;; All VC commands get mapped into logical equivalents.
1046
ee7e9c88
RS
1047(defvar vc-dired-prefix-map (make-sparse-keymap))
1048(define-key vc-dired-prefix-map "\C-xv" vc-prefix-map)
1049
1434b7aa
RS
1050(or (not (boundp 'minor-mode-map-alist))
1051 (assq 'vc-dired-mode minor-mode-map-alist)
e1f297e6 1052 (setq minor-mode-map-alist
4f6e4ad6 1053 (cons (cons 'vc-dired-mode vc-dired-prefix-map)
ee7e9c88 1054 minor-mode-map-alist)))
e1f297e6
ER
1055
1056(defun vc-dired-mode ()
1057 "The augmented Dired minor mode used in VC directory buffers.
1058All Dired commands operate normally. Users currently locking listed files
ee7e9c88 1059are listed in place of the file's owner and group.
e1f297e6
ER
1060Keystrokes bound to VC commands will execute as though they had been called
1061on a buffer attached to the file named in the current Dired buffer line."
1062 (setq vc-dired-mode t)
1063 (setq vc-mode " under VC"))
594722a8 1064
8c0aaf40
ER
1065(defun vc-dired-reformat-line (x)
1066 ;; Hack a directory-listing line, plugging in locking-user info in
34291cd2
RS
1067 ;; place of the user and group info. Should have the beneficial
1068 ;; side-effect of shortening the listing line. Each call starts with
8c0aaf40
ER
1069 ;; point immediately following the dired mark area on the line to be
1070 ;; hacked.
1071 ;;
1072 ;; Simplest possible one:
1073 ;; (insert (concat x "\t")))
1074 ;;
1075 ;; This code, like dired, assumes UNIX -l format.
1076 (forward-word 1) ;; skip over any extra field due to -ibs options
80688f5c
RS
1077 (cond
1078 ;; This hack is used by the CVS code. See vc-locking-user.
1079 ((numberp x)
1080 (cond
1081 ((re-search-forward "\\([0-9]+ \\)\\([^ ]+\\)\\( .*\\)" nil 0)
1082 (save-excursion
632e9525
RS
1083 (goto-char (match-beginning 2))
1084 (insert "(")
1085 (goto-char (1+ (match-end 2)))
1086 (insert ")")
1087 (delete-char (- 17 (- (match-end 2) (match-beginning 2))))
1088 (insert (substring " " 0
1089 (- 7 (- (match-end 2) (match-beginning 2)))))))))
80688f5c
RS
1090 (t
1091 (if x (setq x (concat "(" x ")")))
1092 (if (re-search-forward "\\([0-9]+ \\).................\\( .*\\)" nil 0)
632e9525
RS
1093 (let ((rep (substring (concat x " ") 0 9)))
1094 (replace-match (concat "\\1" rep "\\2") t)))
80688f5c 1095 )))
8c0aaf40 1096
632e9525
RS
1097;;; Note in Emacs 18 the following defun gets overridden
1098;;; with the symbol 'vc-directory-18. See below.
637a8ae9 1099;;;###autoload
632e9525
RS
1100(defun vc-directory (verbose)
1101 "Show version-control status of the current directory and subdirectories.
1102Normally it creates a Dired buffer that lists only the locked files
1103in all these directories. With a prefix argument, it lists all files."
1104 (interactive "P")
1105 (let (nonempty
1106 (dl (length default-directory))
1107 (filelist nil) (userlist nil)
1108 dired-buf
1109 dired-buf-mod-count)
1110 (vc-file-tree-walk
1111 (function (lambda (f)
1112 (if (vc-registered f)
1113 (let ((user (vc-locking-user f)))
1114 (and (or verbose user)
1115 (setq filelist (cons (substring f dl) filelist))
1116 (setq userlist (cons user userlist))))))))
594722a8 1117 (save-excursion
34291cd2 1118 ;; This uses a semi-documented feature of dired; giving a switch
8c0aaf40
ER
1119 ;; argument forces the buffer to refresh each time.
1120 (dired
632e9525 1121 (cons default-directory (nreverse filelist))
8c0aaf40
ER
1122 dired-listing-switches)
1123 (setq dired-buf (current-buffer))
1124 (setq nonempty (not (zerop (buffer-size)))))
594722a8
ER
1125 (if nonempty
1126 (progn
e1f297e6
ER
1127 (pop-to-buffer dired-buf)
1128 (vc-dired-mode)
1129 (goto-char (point-min))
1130 (setq buffer-read-only nil)
8c0aaf40 1131 (forward-line 1) ;; Skip header line
e1f297e6 1132 (mapcar
7ef84cf9
RS
1133 (function
1134 (lambda (x)
8c0aaf40
ER
1135 (forward-char 2) ;; skip dired's mark area
1136 (vc-dired-reformat-line x)
7ef84cf9 1137 (forward-line 1))) ;; go to next line
8c0aaf40 1138 (nreverse userlist))
e1f297e6
ER
1139 (setq buffer-read-only t)
1140 (goto-char (point-min))
1141 )
02da6253
PE
1142 (message "No files are currently %s under %s"
1143 (if verbose "registered" "locked") default-directory))
594722a8
ER
1144 ))
1145
632e9525
RS
1146;; Emacs 18 version
1147(defun vc-directory-18 (verbose)
1148 "Show version-control status of all files under the current directory."
1149 (interactive "P")
1150 (let (nonempty (dir default-directory))
1151 (save-excursion
1152 (set-buffer (get-buffer-create "*vc-status*"))
1153 (erase-buffer)
1154 (cd dir)
1155 (vc-file-tree-walk
1156 (function (lambda (f)
1157 (if (vc-registered f)
1158 (let ((user (vc-locking-user f)))
1159 (if (or user verbose)
1160 (insert (format
1161 "%s %s\n"
1162 (concat user) f))))))))
1163 (setq nonempty (not (zerop (buffer-size)))))
1164 (if nonempty
1165 (progn
1166 (pop-to-buffer "*vc-status*" t)
1167 (goto-char (point-min))
1168 (shrink-window-if-larger-than-buffer)))
1169 (message "No files are currently %s under %s"
1170 (if verbose "registered" "locked") default-directory))
1171 )
1172
1173(or (boundp 'minor-mode-map-alist)
1174 (fset 'vc-directory 'vc-directory-18))
e70bdc98 1175
594722a8
ER
1176;; Named-configuration support for SCCS
1177
1178(defun vc-add-triple (name file rev)
1179 (save-excursion
1180 (find-file (concat (vc-backend-subdirectory-name file) "/" vc-name-assoc-file))
1181 (goto-char (point-max))
1182 (insert name "\t:\t" file "\t" rev "\n")
1183 (basic-save-buffer)
1184 (kill-buffer (current-buffer))
1185 ))
1186
1187(defun vc-record-rename (file newname)
1188 (save-excursion
1189 (find-file (concat (vc-backend-subdirectory-name file) "/" vc-name-assoc-file))
1190 (goto-char (point-min))
7d847440
RS
1191 ;; (replace-regexp (concat ":" (regexp-quote file) "$") (concat ":" newname))
1192 (while (re-search-forward (concat ":" (regexp-quote file) "$") nil t)
1193 (replace-match (concat ":" newname) nil nil))
594722a8
ER
1194 (basic-save-buffer)
1195 (kill-buffer (current-buffer))
1196 ))
1197
1198(defun vc-lookup-triple (file name)
7b4f934d
ER
1199 ;; Return the numeric version corresponding to a named snapshot of file
1200 ;; If name is nil or a version number string it's just passed through
47ca02a6 1201 (cond ((null name) name)
7b4f934d
ER
1202 ((let ((firstchar (aref name 0)))
1203 (and (>= firstchar ?0) (<= firstchar ?9)))
1204 name)
1205 (t
1206 (car (vc-master-info
1207 (concat (vc-backend-subdirectory-name file) "/" vc-name-assoc-file)
1208 (list (concat name "\t:\t" file "\t\\(.+\\)"))))
1209 )))
594722a8
ER
1210
1211;; Named-configuration entry points
1212
1dabb4e6
PE
1213(defun vc-locked-example ()
1214 ;; Return an example of why the current directory is not ready to be snapshot
1215 ;; or nil if no such example exists.
1216 (catch 'vc-locked-example
02da6253
PE
1217 (vc-file-tree-walk
1218 (function (lambda (f)
1219 (if (and (vc-registered f) (vc-locking-user f))
1dabb4e6
PE
1220 (throw 'vc-locked-example f)))))
1221 nil))
594722a8 1222
637a8ae9 1223;;;###autoload
594722a8
ER
1224(defun vc-create-snapshot (name)
1225 "Make a snapshot called NAME.
1226The snapshot is made from all registered files at or below the current
1227directory. For each file, the version level of its latest
1228version becomes part of the named configuration."
1229 (interactive "sNew snapshot name: ")
1dabb4e6
PE
1230 (let ((locked (vc-locked-example)))
1231 (if locked
1232 (error "File %s is locked" locked)
1233 (vc-file-tree-walk
1234 (function (lambda (f) (and
1235 (vc-name f)
1236 (vc-backend-assign-name f name)))))
1237 )))
594722a8 1238
637a8ae9 1239;;;###autoload
594722a8
ER
1240(defun vc-retrieve-snapshot (name)
1241 "Retrieve the snapshot called NAME.
1242This function fails if any files are locked at or below the current directory
1243Otherwise, all registered files are checked out (unlocked) at their version
1244levels in the snapshot."
1245 (interactive "sSnapshot name to retrieve: ")
1dabb4e6
PE
1246 (let ((locked (vc-locked-example)))
1247 (if locked
1248 (error "File %s is locked" locked)
1249 (vc-file-tree-walk
1250 (function (lambda (f) (and
1251 (vc-name f)
1252 (vc-error-occurred
1253 (vc-backend-checkout f nil name))))))
1254 )))
594722a8
ER
1255
1256;; Miscellaneous other entry points
1257
637a8ae9 1258;;;###autoload
594722a8
ER
1259(defun vc-print-log ()
1260 "List the change log of the current buffer in a window."
1261 (interactive)
e1f297e6
ER
1262 (if vc-dired-mode
1263 (set-buffer (find-file-noselect (dired-get-filename))))
8ac8c82f 1264 (while vc-parent-buffer
1a2f456b 1265 (pop-to-buffer vc-parent-buffer))
594722a8 1266 (if (and buffer-file-name (vc-name buffer-file-name))
632e9525
RS
1267 (let ((file buffer-file-name))
1268 (vc-backend-print-log file)
594722a8 1269 (pop-to-buffer (get-buffer-create "*vc*"))
632e9525 1270 (setq default-directory (file-name-directory file))
4805f679
BF
1271 (while (looking-at "=*\n")
1272 (delete-char (- (match-end 0) (match-beginning 0)))
1273 (forward-line -1))
594722a8 1274 (goto-char (point-min))
4805f679
BF
1275 (if (looking-at "[\b\t\n\v\f\r ]+")
1276 (delete-char (- (match-end 0) (match-beginning 0))))
4c2d8cf9 1277 (shrink-window-if-larger-than-buffer)
594722a8 1278 )
7ef84cf9 1279 (vc-registration-error buffer-file-name)
594722a8
ER
1280 )
1281 )
1282
637a8ae9 1283;;;###autoload
594722a8 1284(defun vc-revert-buffer ()
9c95ac44
RS
1285 "Revert the current buffer's file back to the latest checked-in version.
1286This asks for confirmation if the buffer contents are not identical
80688f5c
RS
1287to that version.
1288If the back-end is CVS, this will give you the most recent revision of
1289the file on the branch you are editing."
594722a8 1290 (interactive)
e1f297e6
ER
1291 (if vc-dired-mode
1292 (find-file-other-window (dired-get-filename)))
8ac8c82f 1293 (while vc-parent-buffer
1a2f456b 1294 (pop-to-buffer vc-parent-buffer))
594722a8 1295 (let ((file buffer-file-name)
97d3f950 1296 (obuf (current-buffer)) (changed (vc-diff nil t)))
9c95ac44
RS
1297 (if (and changed (or vc-suppress-confirm
1298 (not (yes-or-no-p "Discard changes? "))))
594722a8
ER
1299 (progn
1300 (delete-window)
590cc449 1301 (error "Revert cancelled"))
594722a8
ER
1302 (set-buffer obuf))
1303 (if changed
1304 (delete-window))
1305 (vc-backend-revert file)
624b4662 1306 (vc-resynch-window file t t)
594722a8
ER
1307 )
1308 )
1309
637a8ae9 1310;;;###autoload
594722a8 1311(defun vc-cancel-version (norevert)
34291cd2
RS
1312 "Get rid of most recently checked in version of this file.
1313A prefix argument means do not revert the buffer afterwards."
594722a8 1314 (interactive "P")
e1f297e6
ER
1315 (if vc-dired-mode
1316 (find-file-other-window (dired-get-filename)))
8ac8c82f
ER
1317 (while vc-parent-buffer
1318 (pop-to-buffer vc-parent-buffer))
1319 (let* ((target (concat (vc-latest-version (buffer-file-name))))
1320 (yours (concat (vc-your-latest-version (buffer-file-name))))
7b4f934d 1321 (prompt (if (string-equal yours target)
590cc449
RS
1322 "Remove your version %s from master? "
1323 "Version %s was not your change. Remove it anyway? ")))
7b4f934d
ER
1324 (if (null (yes-or-no-p (format prompt target)))
1325 nil
1326 (vc-backend-uncheck (buffer-file-name) target)
34291cd2
RS
1327 (if (or norevert
1328 (not (yes-or-no-p "Revert buffer to most recent remaining version? ")))
7b4f934d
ER
1329 (vc-mode-line (buffer-file-name))
1330 (vc-checkout (buffer-file-name) nil)))
8ac8c82f 1331 ))
594722a8 1332
29fc1ce9 1333;;;###autoload
594722a8 1334(defun vc-rename-file (old new)
34291cd2
RS
1335 "Rename file OLD to NEW, and rename its master file likewise."
1336 (interactive "fVC rename file: \nFRename to: ")
80688f5c
RS
1337 ;; There are several ways of renaming files under CVS 1.3, but they all
1338 ;; have serious disadvantages. See the FAQ (available from think.com in
1339 ;; pub/cvs/). I'd rather send the user an error, than do something he might
1340 ;; consider to be wrong. When the famous, long-awaited rename database is
1341 ;; implemented things might change for the better. This is unlikely to occur
1342 ;; until CVS 2.0 is released. --ceder 1994-01-23 21:27:51
1343 (if (eq (vc-backend-deduce old) 'CVS)
1344 (error "Renaming files under CVS is dangerous and not supported in VC."))
594722a8 1345 (let ((oldbuf (get-file-buffer old)))
d52f0de9 1346 (if (and oldbuf (buffer-modified-p oldbuf))
590cc449 1347 (error "Please save files before moving them"))
594722a8 1348 (if (get-file-buffer new)
590cc449 1349 (error "Already editing new file name"))
d52f0de9
RS
1350 (if (file-exists-p new)
1351 (error "New file already exists"))
594722a8
ER
1352 (let ((oldmaster (vc-name old)))
1353 (if oldmaster
d52f0de9
RS
1354 (progn
1355 (if (vc-locking-user old)
1356 (error "Please check in files before moving them"))
1357 (if (or (file-symlink-p oldmaster)
1358 ;; This had FILE, I changed it to OLD. -- rms.
1359 (file-symlink-p (vc-backend-subdirectory-name old)))
1360 (error "This is not a safe thing to do in the presence of symbolic links"))
1361 (rename-file
1362 oldmaster
1363 (let ((backend (vc-backend-deduce old))
1364 (newdir (or (file-name-directory new) ""))
1365 (newbase (file-name-nondirectory new)))
1366 (catch 'found
1367 (mapcar
1368 (function
1369 (lambda (s)
1370 (if (eq backend (cdr s))
1371 (let* ((newmaster (format (car s) newdir newbase))
1372 (newmasterdir (file-name-directory newmaster)))
1373 (if (or (not newmasterdir)
1374 (file-directory-p newmasterdir))
1375 (throw 'found newmaster))))))
1376 vc-master-templates)
1377 (error "New file lacks a version control directory"))))))
594722a8
ER
1378 (if (or (not oldmaster) (file-exists-p old))
1379 (rename-file old new)))
1380; ?? Renaming a file might change its contents due to keyword expansion.
1381; We should really check out a new copy if the old copy was precisely equal
1382; to some checked in version. However, testing for this is tricky....
1383 (if oldbuf
1384 (save-excursion
1385 (set-buffer oldbuf)
1386 (set-visited-file-name new)
1387 (set-buffer-modified-p nil))))
60213ed0
RS
1388 ;; This had FILE, I changed it to OLD. -- rms.
1389 (vc-backend-dispatch old
80688f5c
RS
1390 (vc-record-rename old new) ;SCCS
1391 nil ;RCS
1392 nil ;CVS
1393 )
594722a8
ER
1394 )
1395
637a8ae9 1396;;;###autoload
f35ecf88
RM
1397(defun vc-update-change-log (&rest args)
1398 "Find change log file and add entries from recent RCS logs.
1399The mark is left at the end of the text prepended to the change log.
1400With prefix arg of C-u, only find log entries for the current buffer's file.
1401With any numeric prefix arg, find log entries for all files currently visited.
4b40fdea 1402Otherwise, find log entries for all registered files in the default directory.
f35ecf88 1403From a program, any arguments are passed to the `rcs2log' script."
67242a23
RM
1404 (interactive
1405 (cond ((consp current-prefix-arg) ;C-u
1406 (list buffer-file-name))
1407 (current-prefix-arg ;Numeric argument.
1408 (let ((files nil)
1409 (buffers (buffer-list))
1410 file)
1411 (while buffers
1412 (setq file (buffer-file-name (car buffers)))
1413 (and file (vc-backend-deduce file)
4b40fdea 1414 (setq files (cons file files)))
67242a23 1415 (setq buffers (cdr buffers)))
4b40fdea
PE
1416 files))
1417 (t
1418 (let ((RCS (concat default-directory "RCS")))
1419 (and (file-directory-p RCS)
1420 (mapcar (function
1421 (lambda (f)
1422 (if (string-match "\\(.*\\),v$" f)
1423 (substring f 0 (match-end 1))
1424 f)))
1425 (directory-files RCS nil "...\\|^[^.]\\|^.[^.]")))))))
41dfb835
RS
1426 (let ((odefault default-directory))
1427 (find-file-other-window (find-change-log))
1428 (barf-if-buffer-read-only)
1429 (vc-buffer-sync)
1430 (undo-boundary)
1431 (goto-char (point-min))
1432 (push-mark)
1433 (message "Computing change log entries...")
4b40fdea
PE
1434 (message "Computing change log entries... %s"
1435 (if (or (null args)
1436 (eq 0 (apply 'call-process "rcs2log" nil t nil
ce37bf20
RS
1437 "-n"
1438 (user-login-name)
1439 (user-full-name)
1440 user-mail-address
4b40fdea
PE
1441 (mapcar (function
1442 (lambda (f)
1443 (file-relative-name
1444 (if (file-name-absolute-p f)
1445 f
1446 (concat odefault f)))))
1447 args))))
1448 "done" "failed"))))
594722a8
ER
1449
1450;; Functions for querying the master and lock files.
1451
caf15d4f 1452(defun vc-match-substring (bn)
594722a8
ER
1453 (buffer-substring (match-beginning bn) (match-end bn)))
1454
1455(defun vc-parse-buffer (patterns &optional file properties)
1456 ;; Use PATTERNS to parse information out of the current buffer
1457 ;; by matching each regular expression in the list and returning \\1.
1458 ;; If a regexp has two tag brackets, assume the second is a date
1459 ;; field and we want the most recent entry matching the template.
1460 ;; If FILE and PROPERTIES are given, the latter must be a list of
1461 ;; properties of the same length as PATTERNS; each property is assigned
1462 ;; the corresponding value.
1463 (mapcar (function (lambda (p)
1464 (goto-char (point-min))
1465 (if (string-match "\\\\(.*\\\\(" p)
1466 (let ((latest-date "") (latest-val))
1467 (while (re-search-forward p nil t)
caf15d4f 1468 (let ((date (vc-match-substring 2)))
594722a8
ER
1469 (if (string< latest-date date)
1470 (progn
1471 (setq latest-date date)
1472 (setq latest-val
caf15d4f 1473 (vc-match-substring 1))))))
594722a8
ER
1474 latest-val))
1475 (prog1
1892807a
ER
1476 (let ((value nil))
1477 (if (re-search-forward p nil t)
1478 (setq value (vc-match-substring 1)))
1479 (if file
1480 (vc-file-setprop file (car properties) value))
1481 value)
594722a8
ER
1482 (setq properties (cdr properties)))))
1483 patterns)
1484 )
1485
1486(defun vc-master-info (file fields &optional rfile properties)
1487 ;; Search for information in a master file.
1488 (if (and file (file-exists-p file))
1489 (save-excursion
1490 (let ((buf))
1491 (setq buf (create-file-buffer file))
1492 (set-buffer buf))
1493 (erase-buffer)
632e9525 1494 (insert-file-contents file)
594722a8
ER
1495 (set-buffer-modified-p nil)
1496 (auto-save-mode nil)
1497 (prog1
1498 (vc-parse-buffer fields rfile properties)
1499 (kill-buffer (current-buffer)))
1500 )
1501 (if rfile
1502 (mapcar
1503 (function (lambda (p) (vc-file-setprop rfile p nil)))
1504 properties))
1505 )
1506 )
1507
80688f5c 1508(defun vc-log-info (command file last flags patterns &optional properties)
594722a8
ER
1509 ;; Search for information in log program output
1510 (if (and file (file-exists-p file))
1511 (save-excursion
52110560
RM
1512 ;; Don't switch to the *vc* buffer before running vc-do-command,
1513 ;; because that would change its default-directory.
80688f5c 1514 (apply 'vc-do-command 0 command file last flags)
52110560 1515 (set-buffer (get-buffer "*vc*"))
594722a8
ER
1516 (set-buffer-modified-p nil)
1517 (prog1
1518 (vc-parse-buffer patterns file properties)
1519 (kill-buffer (current-buffer))
1520 )
1521 )
1522 (if file
1523 (mapcar
1524 (function (lambda (p) (vc-file-setprop file p nil)))
1525 properties))
1526 )
1527 )
1528
1529(defun vc-locking-user (file)
1530 "Return the name of the person currently holding a lock on FILE.
80688f5c
RS
1531Return nil if there is no such person.
1532Under CVS, a file is considered locked if it has been modified since it
1533was checked out. Under CVS, this will sometimes return the uid of
1534the owner of the file (as a number) instead of a string."
1535 (setq file (expand-file-name file));; ??? Work around bug in 19.0.4
1536 (cond
1537 ((eq (vc-backend-deduce file) 'CVS)
1538 (if (vc-workfile-unchanged-p file t)
1539 nil
1540 ;; The expression below should return the username of the owner
1541 ;; of the file. It doesn't. It returns the username if it is
1542 ;; you, or otherwise the UID of the owner of the file. The
1543 ;; return value from this function is only used by
1544 ;; vc-dired-reformat-line, and it does the proper thing if a UID
1545 ;; is returned.
1546 ;;
1547 ;; The *proper* way to fix this would be to implement a built-in
1548 ;; function in Emacs, say, (username UID), that returns the
1549 ;; username of a given UID.
1550 ;;
1551 ;; The result of this hack is that vc-directory will print the
1552 ;; name of the owner of the file for any files that are
1553 ;; modified.
1554 (let ((uid (nth 2 (file-attributes file))))
1555 (if (= uid (user-uid))
1556 (user-login-name)
1557 uid))))
1558 (t
1559 (if (or (not vc-keep-workfiles)
1560 (eq vc-mistrust-permissions 't)
1561 (and vc-mistrust-permissions
1562 (funcall vc-mistrust-permissions (vc-backend-subdirectory-name
1563 file))))
1564 (vc-true-locking-user file)
1565 ;; This implementation assumes that any file which is under version
1566 ;; control and has -rw-r--r-- is locked by its owner. This is true
1567 ;; for both RCS and SCCS, which keep unlocked files at -r--r--r--.
1568 ;; We have to be careful not to exclude files with execute bits on;
1569 ;; scripts can be under version control too. Also, we must ignore
1570 ;; the group-read and other-read bits, since paranoid users turn them off.
1571 ;; This hack wins because calls to the very expensive vc-fetch-properties
1572 ;; function only have to be made if (a) the file is locked by someone
1573 ;; other than the current user, or (b) some untoward manipulation
1574 ;; behind vc's back has changed the owner or the `group' or `other'
1575 ;; write bits.
1576 (let ((attributes (file-attributes file)))
1577 (cond ((string-match ".r-..-..-." (nth 8 attributes))
1578 nil)
1579 ((and (= (nth 2 attributes) (user-uid))
1580 (string-match ".rw..-..-." (nth 8 attributes)))
1581 (user-login-name))
1582 (t
1583 (vc-true-locking-user file))))))))
594722a8
ER
1584
1585(defun vc-true-locking-user (file)
1586 ;; The slow but reliable version
1587 (vc-fetch-properties file)
1588 (vc-file-getprop file 'vc-locking-user))
1589
1590(defun vc-latest-version (file)
1591 ;; Return version level of the latest version of FILE
1592 (vc-fetch-properties file)
1593 (vc-file-getprop file 'vc-latest-version))
1594
1595(defun vc-your-latest-version (file)
1596 ;; Return version level of the latest version of FILE checked in by you
1597 (vc-fetch-properties file)
1598 (vc-file-getprop file 'vc-your-latest-version))
1599
1600;; Collect back-end-dependent stuff here
1601;;
1602;; Everything eventually funnels through these functions. To implement
1603;; support for a new version-control system, add another branch to the
7b4f934d
ER
1604;; vc-backend-dispatch macro and fill it in in each call. The variable
1605;; vc-master-templates in vc-hooks.el will also have to change.
594722a8 1606
80688f5c
RS
1607(defmacro vc-backend-dispatch (f s r c)
1608 "Execute FORM1, FORM2 or FORM3 depending whether we're using SCCS, RCS or CVS.
1609If FORM3 is RCS, use FORM2 even if we are using CVS. (CVS shares some code
1610with RCS)."
594722a8
ER
1611 (list 'let (list (list 'type (list 'vc-backend-deduce f)))
1612 (list 'cond
1613 (list (list 'eq 'type (quote 'SCCS)) s) ;; SCCS
1614 (list (list 'eq 'type (quote 'RCS)) r) ;; RCS
80688f5c
RS
1615 (list (list 'eq 'type (quote 'CVS)) ;; CVS
1616 (if (eq c 'RCS) r c))
594722a8
ER
1617 )))
1618
1619(defun vc-lock-file (file)
1620 ;; Generate lock file name corresponding to FILE
1621 (let ((master (vc-name file)))
1622 (and
1623 master
1624 (string-match "\\(.*/\\)s\\.\\(.*\\)" master)
1625 (concat
1626 (substring master (match-beginning 1) (match-end 1))
1627 "p."
1628 (substring master (match-beginning 2) (match-end 2))))))
1629
1630
1631(defun vc-fetch-properties (file)
1632 ;; Re-fetch all properties associated with the given file.
1633 ;; Currently these properties are:
1634 ;; vc-locking-user
1635 ;; vc-locked-version
1636 ;; vc-latest-version
1637 ;; vc-your-latest-version
1638 (vc-backend-dispatch
1639 file
1640 ;; SCCS
1641 (progn
1642 (vc-master-info (vc-lock-file file)
1643 (list
1644 "^[^ ]+ [^ ]+ \\([^ ]+\\)"
1645 "^\\([^ ]+\\)")
1646 file
1647 '(vc-locking-user vc-locked-version))
1648 (vc-master-info (vc-name file)
1649 (list
1650 "^\001d D \\([^ ]+\\)"
1651 (concat "^\001d D \\([^ ]+\\) .* "
1652 (regexp-quote (user-login-name)) " ")
1653 )
1654 file
1655 '(vc-latest-version vc-your-latest-version))
1656 )
1657 ;; RCS
80688f5c 1658 (vc-log-info "rlog" file 'MASTER nil
594722a8
ER
1659 (list
1660 "^locks: strict\n\t\\([^:]+\\)"
1661 "^locks: strict\n\t[^:]+: \\(.+\\)"
1662 "^revision[\t ]+\\([0-9.]+\\).*\ndate: \\([ /0-9:]+\\);"
1663 (concat
3234e2a3 1664 "^revision[\t ]+\\([0-9.]+\\)\n.*author: "
594722a8 1665 (regexp-quote (user-login-name))
3234e2a3 1666 ";"))
594722a8
ER
1667 '(vc-locking-user vc-locked-version
1668 vc-latest-version vc-your-latest-version))
80688f5c
RS
1669 ;; CVS
1670 ;; Don't fetch vc-locking-user and vc-locked-version here, since they
1671 ;; should always be nil anyhow. Don't fetch vc-your-latest-version, since
1672 ;; that is done in vc-find-cvs-master.
1673 (vc-log-info
632e9525 1674 "cvs" file 'WORKFILE '("status")
80688f5c
RS
1675 ;; CVS 1.3 says "RCS Version:", other releases "RCS Revision:",
1676 ;; and CVS 1.4a1 says "Repository revision:". The regexp below
1677 ;; matches much more, but because of the way vc-log-info is
1678 ;; implemented it is impossible to use additional groups.
1679 '("R[eC][pS][ositry]* [VRr]e[rv][si][is]i?on:[\t ]+\\([0-9.]+\\)")
1680 '(vc-latest-version))
594722a8
ER
1681 ))
1682
1683(defun vc-backend-subdirectory-name (&optional file)
1684 ;; Where the master and lock files for the current directory are kept
1685 (symbol-name
1686 (or
1687 (and file (vc-backend-deduce file))
1688 vc-default-back-end
1689 (setq vc-default-back-end (if (vc-find-binary "rcs") 'RCS 'SCCS)))))
1690
1691(defun vc-backend-admin (file &optional rev comment)
1692 ;; Register a file into the version-control system
1693 ;; Automatically retrieves a read-only version of the file with
1694 ;; keywords expanded if vc-keep-workfiles is non-nil, otherwise
1695 ;; it deletes the workfile.
1696 (vc-file-clearprops file)
1697 (or vc-default-back-end
1698 (setq vc-default-back-end (if (vc-find-binary "rcs") 'RCS 'SCCS)))
1699 (message "Registering %s..." file)
1700 (let ((backend
1701 (cond
1702 ((file-exists-p (vc-backend-subdirectory-name)) vc-default-back-end)
1703 ((file-exists-p "RCS") 'RCS)
1704 ((file-exists-p "SCCS") 'SCCS)
80688f5c 1705 ((file-exists-p "CVS") 'CVS)
594722a8
ER
1706 (t vc-default-back-end))))
1707 (cond ((eq backend 'SCCS)
80688f5c 1708 (vc-do-command 0 "admin" file 'MASTER ;; SCCS
594722a8
ER
1709 (and rev (concat "-r" rev))
1710 "-fb"
1711 (concat "-i" file)
1712 (and comment (concat "-y" comment))
1713 (format
1714 (car (rassq 'SCCS vc-master-templates))
1715 (or (file-name-directory file) "")
1716 (file-name-nondirectory file)))
1717 (delete-file file)
1718 (if vc-keep-workfiles
80688f5c 1719 (vc-do-command 0 "get" file 'MASTER)))
594722a8 1720 ((eq backend 'RCS)
80688f5c 1721 (vc-do-command 0 "ci" file 'MASTER ;; RCS
594722a8
ER
1722 (concat (if vc-keep-workfiles "-u" "-r") rev)
1723 (and comment (concat "-t-" comment))
80688f5c
RS
1724 file))
1725 ((eq backend 'CVS)
632e9525 1726 (vc-do-command 0 "cvs" file 'WORKFILE ;; CVS
80688f5c
RS
1727 "add"
1728 (and comment (not (string= comment ""))
1729 (concat "-m" comment)))
594722a8
ER
1730 )))
1731 (message "Registering %s...done" file)
1732 )
1733
f1818994 1734(defun vc-backend-checkout (file &optional writable rev workfile)
594722a8 1735 ;; Retrieve a copy of a saved version into a workfile
f1818994
PE
1736 (let ((filename (or workfile file)))
1737 (message "Checking out %s..." filename)
f797cb30
RM
1738 (save-excursion
1739 ;; Change buffers to get local value of vc-checkin-switches.
1740 (set-buffer (or (get-file-buffer file) (current-buffer)))
1741 (vc-backend-dispatch file
1742 (if workfile;; SCCS
1743 ;; Some SCCS implementations allow checking out directly to a
1744 ;; file using the -G option, but then some don't so use the
1745 ;; least common denominator approach and use the -p option
1746 ;; ala RCS.
1747 (let ((vc-modes (logior (file-modes (vc-name file))
1748 (if writable 128 0)))
1749 (failed t))
1750 (unwind-protect
1751 (progn
1752 (apply 'vc-do-command
1753 0 "/bin/sh" file 'MASTER "-c"
1754 ;; Some shells make the "" dummy argument into $0
1755 ;; while others use the shell's name as $0 and
1756 ;; use the "" as $1. The if-statement
1757 ;; converts the latter case to the former.
1758 (format "if [ x\"$1\" = x ]; then shift; fi; \
29fc1ce9
RS
1759 umask %o; exec >\"$1\" || exit; \
1760 shift; umask %o; exec get \"$@\""
f797cb30
RM
1761 (logand 511 (lognot vc-modes))
1762 (logand 511 (lognot (default-file-modes))))
1763 "" ; dummy argument for shell's $0
1764 filename
1765 (if writable "-e")
1766 "-p" (and rev
1767 (concat "-r" (vc-lookup-triple file rev)))
1768 vc-checkout-switches)
1769 (setq failed nil))
1770 (and failed (file-exists-p filename) (delete-file filename))))
1771 (apply 'vc-do-command 0 "get" file 'MASTER;; SCCS
1772 (if writable "-e")
1773 (and rev (concat "-r" (vc-lookup-triple file rev))))
1774 vc-checkout-switches)
1775 (if workfile;; RCS
1776 ;; RCS doesn't let us check out into arbitrary file names directly.
1777 ;; Use `co -p' and make stdout point to the correct file.
1778 (let ((vc-modes (logior (file-modes (vc-name file))
1779 (if writable 128 0)))
1780 (failed t))
1781 (unwind-protect
1782 (progn
1783 (apply 'vc-do-command
1784 0 "/bin/sh" file 'MASTER "-c"
1785 ;; See the SCCS case, above, regarding the
1786 ;; if-statement.
1787 (format "if [ x\"$1\" = x ]; then shift; fi; \
29fc1ce9
RS
1788 umask %o; exec >\"$1\" || exit; \
1789 shift; umask %o; exec co \"$@\""
f797cb30
RM
1790 (logand 511 (lognot vc-modes))
1791 (logand 511 (lognot (default-file-modes))))
1792 "" ; dummy argument for shell's $0
1793 filename
1794 (if writable "-l")
1795 (concat "-p" rev)
1796 vc-checkout-switches)
1797 (setq failed nil))
1798 (and failed (file-exists-p filename) (delete-file filename))))
1799 (apply 'vc-do-command 0 "co" file 'MASTER
1800 (if writable "-l")
1801 (and rev (concat "-r" rev)))
1802 vc-checkout-switches)
1803 (if workfile;; CVS
1804 ;; CVS is much like RCS
1805 (let ((failed t))
1806 (unwind-protect
1807 (progn
1808 (apply 'vc-do-command
632e9525 1809 0 "/bin/sh" file 'WORKFILE "-c"
f797cb30
RM
1810 "exec >\"$1\" || exit; shift; exec cvs update \"$@\""
1811 "" ; dummy argument for shell's $0
1812 workfile
1813 (concat "-r" rev)
1814 "-p"
1815 vc-checkout-switches)
1816 (setq failed nil))
1817 (and failed (file-exists-p filename) (delete-file filename))))
632e9525 1818 (apply 'vc-do-command 0 "cvs" file 'WORKFILE
f797cb30
RM
1819 (and rev (concat "-r" rev))
1820 file
1821 vc-checkout-switches))
1822 ))
f1818994 1823 (or workfile
f797cb30
RM
1824 (vc-file-setprop file
1825 'vc-checkout-time (nth 5 (file-attributes file))))
f1818994 1826 (message "Checking out %s...done" filename))
594722a8
ER
1827 )
1828
1829(defun vc-backend-logentry-check (file)
1830 (vc-backend-dispatch file
8c0aaf40 1831 (if (>= (buffer-size) 512) ;; SCCS
594722a8
ER
1832 (progn
1833 (goto-char 512)
1834 (error
590cc449 1835 "Log must be less than 512 characters; point is now at pos 512")))
80688f5c
RS
1836 nil ;; RCS
1837 nil) ;; CVS
594722a8
ER
1838 )
1839
80688f5c 1840(defun vc-backend-checkin (file rev comment)
594722a8
ER
1841 ;; Register changes to FILE as level REV with explanatory COMMENT.
1842 ;; Automatically retrieves a read-only version of the file with
1843 ;; keywords expanded if vc-keep-workfiles is non-nil, otherwise
1844 ;; it deletes the workfile.
1845 (message "Checking in %s..." file)
2ea8ce47
RM
1846 (save-excursion
1847 ;; Change buffers to get local value of vc-checkin-switches.
1848 (set-buffer (or (get-file-buffer file) (current-buffer)))
1849 (vc-backend-dispatch file
1850 (progn
80688f5c 1851 (apply 'vc-do-command 0 "delta" file 'MASTER
2ea8ce47
RM
1852 (if rev (concat "-r" rev))
1853 (concat "-y" comment)
1854 vc-checkin-switches)
1855 (if vc-keep-workfiles
80688f5c 1856 (vc-do-command 0 "get" file 'MASTER))
2ea8ce47 1857 )
80688f5c 1858 (apply 'vc-do-command 0 "ci" file 'MASTER
2ea8ce47
RM
1859 (concat (if vc-keep-workfiles "-u" "-r") rev)
1860 (concat "-m" comment)
1861 vc-checkin-switches)
80688f5c 1862 (progn
632e9525 1863 (apply 'vc-do-command 0 "cvs" file 'WORKFILE
80688f5c
RS
1864 "ci" "-m" comment
1865 vc-checkin-switches)
1866 (vc-file-setprop file 'vc-checkout-time
1867 (nth 5 (file-attributes file))))
2ea8ce47 1868 ))
594722a8
ER
1869 (vc-file-setprop file 'vc-locking-user nil)
1870 (message "Checking in %s...done" file)
1871 )
1872
1873(defun vc-backend-revert (file)
1874 ;; Revert file to latest checked-in version.
1875 (message "Reverting %s..." file)
1876 (vc-backend-dispatch
1877 file
1878 (progn ;; SCCS
80688f5c
RS
1879 (vc-do-command 0 "unget" file 'MASTER nil)
1880 (vc-do-command 0 "get" file 'MASTER nil))
1881 (vc-do-command 0 "co" file 'MASTER ;; RCS. This deletes the work file.
1882 "-f" "-u")
1883 (progn ;; CVS
1884 (delete-file file)
632e9525 1885 (vc-do-command 0 "cvs" file 'WORKFILE "update"))
80688f5c 1886 )
594722a8
ER
1887 (vc-file-setprop file 'vc-locking-user nil)
1888 (message "Reverting %s...done" file)
1889 )
1890
1891(defun vc-backend-steal (file &optional rev)
1892 ;; Steal the lock on the current workfile. Needs RCS 5.6.2 or later for -M.
1893 (message "Stealing lock on %s..." file)
af1b8606 1894 (vc-backend-dispatch file
80688f5c
RS
1895 (progn ;SCCS
1896 (vc-do-command 0 "unget" file 'MASTER "-n" (if rev (concat "-r" rev)))
1897 (vc-do-command 0 "get" file 'MASTER "-g" (if rev (concat "-r" rev)))
d76b8d38 1898 )
80688f5c
RS
1899 (vc-do-command 0 "rcs" file 'MASTER ;RCS
1900 "-M" (concat "-u" rev) (concat "-l" rev))
1901 (error "You cannot steal a CVS lock; there are no CVS locks to steal.") ;CVS
1902 )
594722a8
ER
1903 (vc-file-setprop file 'vc-locking-user (user-login-name))
1904 (message "Stealing lock on %s...done" file)
1905 )
1906
1907(defun vc-backend-uncheck (file target)
1908 ;; Undo the latest checkin. Note: this code will have to get a lot
1909 ;; smarter when we support multiple branches.
1910 (message "Removing last change from %s..." file)
1911 (vc-backend-dispatch file
80688f5c
RS
1912 (vc-do-command 0 "rmdel" file 'MASTER (concat "-r" target))
1913 (vc-do-command 0 "rcs" file 'MASTER (concat "-o" target))
1914 (error "Unchecking files under CVS is dangerous and not supported in VC.")
594722a8
ER
1915 )
1916 (message "Removing last change from %s...done" file)
1917 )
1918
1919(defun vc-backend-print-log (file)
1920 ;; Print change log associated with FILE to buffer *vc*.
80688f5c
RS
1921 (vc-backend-dispatch
1922 file
1923 (vc-do-command 0 "prs" file 'MASTER)
1924 (vc-do-command 0 "rlog" file 'MASTER)
632e9525 1925 (vc-do-command 0 "cvs" file 'WORKFILE "rlog")))
594722a8
ER
1926
1927(defun vc-backend-assign-name (file name)
1928 ;; Assign to a FILE's latest version a given NAME.
1929 (vc-backend-dispatch file
80688f5c
RS
1930 (vc-add-triple name file (vc-latest-version file)) ;; SCCS
1931 (vc-do-command 0 "rcs" file 'MASTER (concat "-n" name ":")) ;; RCS
632e9525 1932 (vc-do-command 0 "cvs" file 'WORKFILE "tag" name) ;; CVS
3234e2a3 1933 )
3234e2a3 1934 )
594722a8 1935
9a51ed3a
PE
1936(defun vc-backend-diff (file &optional oldvers newvers cmp)
1937 ;; Get a difference report between two versions of FILE.
1938 ;; Get only a brief comparison report if CMP, a difference report otherwise.
80688f5c
RS
1939 (let ((backend (vc-backend-deduce file)))
1940 (cond
1941 ((eq backend 'SCCS)
7b4f934d 1942 (setq oldvers (vc-lookup-triple file oldvers))
80688f5c
RS
1943 (setq newvers (vc-lookup-triple file newvers))))
1944 (cond
1945 ;; SCCS and RCS shares a lot of code.
1946 ((or (eq backend 'SCCS) (eq backend 'RCS))
1947 (let* ((command (if (eq backend 'SCCS)
1948 "vcdiff"
1949 "rcsdiff"))
632e9525 1950 (mode (if (eq backend 'RCS) 'WORKFILE 'MASTER))
80688f5c
RS
1951 (options (append (list (and cmp "--brief")
1952 "-q"
1953 (and oldvers (concat "-r" oldvers))
1954 (and newvers (concat "-r" newvers)))
1955 (and (not cmp)
1956 (if (listp diff-switches)
1957 diff-switches
1958 (list diff-switches)))))
632e9525 1959 (status (apply 'vc-do-command 2 command file mode options)))
80688f5c
RS
1960 ;; Some RCS versions don't understand "--brief"; work around this.
1961 (if (eq status 2)
632e9525 1962 (apply 'vc-do-command 1 command file 'WORKFILE
80688f5c
RS
1963 (if cmp (cdr options) options))
1964 status)))
1965 ;; CVS is different.
1966 ;; cmp is not yet implemented -- we always do a full diff.
1967 ((eq backend 'CVS)
1968 (if (string= (vc-file-getprop file 'vc-your-latest-version) "0") ;CVS
1969 ;; This file is added but not yet committed; there is no master file.
1970 ;; diff it against /dev/null.
1971 (if (or oldvers newvers)
1972 (error "No revisions of %s exists" file)
1973 (apply 'vc-do-command
632e9525 1974 1 "diff" file 'WORKFILE "/dev/null"
80688f5c
RS
1975 (if (listp diff-switches)
1976 diff-switches
1977 (list diff-switches))))
1978 (apply 'vc-do-command
632e9525 1979 1 "cvs" file 'WORKFILE "diff"
80688f5c
RS
1980 (and oldvers (concat "-r" oldvers))
1981 (and newvers (concat "-r" newvers))
1982 (if (listp diff-switches)
1983 diff-switches
1984 (list diff-switches)))))
1985 (t
1986 (vc-registration-error file)))))
1987
1988(defun vc-backend-merge-news (file)
1989 ;; Merge in any new changes made to FILE.
1990 (vc-backend-dispatch
1991 file
1992 (error "vc-backend-merge-news not meaningful for SCCS files") ;SCCS
1993 (error "vc-backend-merge-news not meaningful for RCS files") ;RCS
632e9525 1994 (vc-do-command 1 "cvs" file 'WORKFILE "update") ;CVS
80688f5c 1995 ))
594722a8
ER
1996
1997(defun vc-check-headers ()
1998 "Check if the current file has any headers in it."
1999 (interactive)
2000 (save-excursion
2001 (goto-char (point-min))
2002 (vc-backend-dispatch buffer-file-name
2003 (re-search-forward "%[MIRLBSDHTEGUYFPQCZWA]%" nil t) ;; SCCS
2004 (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t) ;; RCS
80688f5c 2005 'RCS ;; CVS works like RCS in this regard.
594722a8
ER
2006 )
2007 ))
2008
2009;; Back-end-dependent stuff ends here.
2010
2011;; Set up key bindings for use while editing log messages
2012
2013(defun vc-log-mode ()
2014 "Minor mode for driving version-control tools.
2015These bindings are added to the global keymap when you enter this mode:
2016\\[vc-next-action] perform next logical version-control operation on current file
2017\\[vc-register] register current file
2018\\[vc-toggle-read-only] like next-action, but won't register files
2019\\[vc-insert-headers] insert version-control headers in current file
2020\\[vc-print-log] display change history of current file
2021\\[vc-revert-buffer] revert buffer to latest version
2022\\[vc-cancel-version] undo latest checkin
2023\\[vc-diff] show diffs between file versions
f1818994 2024\\[vc-version-other-window] visit old version in another window
594722a8
ER
2025\\[vc-directory] show all files locked by any user in or below .
2026\\[vc-update-change-log] add change log entry from recent checkins
2027
2028While you are entering a change log message for a version, the following
2029additional bindings will be in effect.
2030
2031\\[vc-finish-logentry] proceed with check in, ending log message entry
2032
2033Whenever you do a checkin, your log comment is added to a ring of
2034saved comments. These can be recalled as follows:
2035
2036\\[vc-next-comment] replace region with next message in comment ring
2037\\[vc-previous-comment] replace region with previous message in comment ring
8c0aaf40
ER
2038\\[vc-comment-search-reverse] search backward for regexp in the comment ring
2039\\[vc-comment-search-forward] search backward for regexp in the comment ring
594722a8
ER
2040
2041Entry to the change-log submode calls the value of text-mode-hook, then
2042the value of vc-log-mode-hook.
2043
2044Global user options:
2045 vc-initial-comment If non-nil, require user to enter a change
2046 comment upon first checkin of the file.
2047
2048 vc-keep-workfiles Non-nil value prevents workfiles from being
2049 deleted when changes are checked in
2050
2051 vc-suppress-confirm Suppresses some confirmation prompts,
2052 notably for reversions.
2053
7b4f934d 2054 vc-header-alist Which keywords to insert when adding headers
594722a8 2055 with \\[vc-insert-headers]. Defaults to
80688f5c
RS
2056 '(\"\%\W\%\") under SCCS, '(\"\$Id\$\") under
2057 RCS and CVS.
594722a8
ER
2058
2059 vc-static-header-alist By default, version headers inserted in C files
2060 get stuffed in a static string area so that
80688f5c
RS
2061 ident(RCS/CVS) or what(SCCS) can see them in
2062 the compiled object code. You can override
2063 this by setting this variable to nil, or change
594722a8
ER
2064 the header template by changing it.
2065
2066 vc-command-messages if non-nil, display run messages from the
2067 actual version-control utilities (this is
2068 intended primarily for people hacking vc
2069 itself).
2070"
2071 (interactive)
2072 (set-syntax-table text-mode-syntax-table)
2073 (use-local-map vc-log-entry-mode)
2074 (setq local-abbrev-table text-mode-abbrev-table)
2075 (setq major-mode 'vc-log-mode)
2076 (setq mode-name "VC-Log")
2077 (make-local-variable 'vc-log-file)
2078 (make-local-variable 'vc-log-version)
8c0aaf40 2079 (make-local-variable 'vc-comment-ring-index)
594722a8
ER
2080 (set-buffer-modified-p nil)
2081 (setq buffer-file-name nil)
2082 (run-hooks 'text-mode-hook 'vc-log-mode-hook)
2083)
2084
2085;; Initialization code, to be done just once at load-time
2086(if vc-log-entry-mode
2087 nil
2088 (setq vc-log-entry-mode (make-sparse-keymap))
2089 (define-key vc-log-entry-mode "\M-n" 'vc-next-comment)
2090 (define-key vc-log-entry-mode "\M-p" 'vc-previous-comment)
8c0aaf40 2091 (define-key vc-log-entry-mode "\M-r" 'vc-comment-search-reverse)
594722a8
ER
2092 (define-key vc-log-entry-mode "\M-s" 'vc-comment-search-forward)
2093 (define-key vc-log-entry-mode "\C-c\C-c" 'vc-finish-logentry)
2094 )
2095
2096;;; These things should probably be generally available
2097
02da6253 2098(defun vc-file-tree-walk (func &rest args)
34291cd2
RS
2099 "Walk recursively through default directory.
2100Invoke FUNC f ARGS on each non-directory file f underneath it."
02da6253
PE
2101 (vc-file-tree-walk-internal default-directory func args)
2102 (message "Traversing directory %s...done" default-directory))
2103
2104(defun vc-file-tree-walk-internal (file func args)
2105 (if (not (file-directory-p file))
2106 (apply func file args)
2107 (message "Traversing directory %s..." file)
2108 (let ((dir (file-name-as-directory file)))
2109 (mapcar
2110 (function
2111 (lambda (f) (or
2112 (string-equal f ".")
2113 (string-equal f "..")
632e9525 2114 (member f vc-directory-exclusion-list)
02da6253
PE
2115 (let ((dirf (concat dir f)))
2116 (or
2117 (file-symlink-p dirf) ;; Avoid possible loops
2118 (vc-file-tree-walk-internal dirf func args))))))
2119 (directory-files dir)))))
594722a8
ER
2120
2121(provide 'vc)
2122
2123;;; DEVELOPER'S NOTES ON CONCURRENCY PROBLEMS IN THIS CODE
2124;;;
2125;;; These may be useful to anyone who has to debug or extend the package.
2126;;;
2127;;; A fundamental problem in VC is that there are time windows between
2128;;; vc-next-action's computations of the file's version-control state and
2129;;; the actions that change it. This is a window open to lossage in a
2130;;; multi-user environment; someone else could nip in and change the state
2131;;; of the master during it.
2132;;;
2133;;; The performance problem is that rlog/prs calls are very expensive; we want
2134;;; to avoid them as much as possible.
2135;;;
2136;;; ANALYSIS:
2137;;;
2138;;; The performance problem, it turns out, simplifies in practice to the
2139;;; problem of making vc-locking-user fast. The two other functions that call
2140;;; prs/rlog will not be so commonly used that the slowdown is a problem; one
2141;;; makes snapshots, the other deletes the calling user's last change in the
2142;;; master.
2143;;;
2144;;; The race condition implies that we have to either (a) lock the master
2145;;; during the entire execution of vc-next-action, or (b) detect and
2146;;; recover from errors resulting from dispatch on an out-of-date state.
2147;;;
2148;;; Alternative (a) appears to be unfeasible. The problem is that we can't
2149;;; guarantee that the lock will ever be removed. Suppose a user starts a
2150;;; checkin, the change message buffer pops up, and the user, having wandered
2151;;; off to do something else, simply forgets about it?
2152;;;
2153;;; Alternative (b), on the other hand, works well with a cheap way to speed up
2154;;; vc-locking-user. Usually, if a file is registered, we can read its locked/
2155;;; unlocked state and its current owner from its permissions.
2156;;;
2157;;; This shortcut will fail if someone has manually changed the workfile's
2158;;; permissions; also if developers are munging the workfile in several
2159;;; directories, with symlinks to a master (in this latter case, the
2160;;; permissions shortcut will fail to detect a lock asserted from another
2161;;; directory).
2162;;;
2163;;; Note that these cases correspond exactly to the errors which could happen
2164;;; because of a competing checkin/checkout race in between two instances of
2165;;; vc-next-action.
2166;;;
2167;;; For VC's purposes, a workfile/master pair may have the following states:
2168;;;
2169;;; A. Unregistered. There is a workfile, there is no master.
2170;;;
2171;;; B. Registered and not locked by anyone.
2172;;;
2173;;; C. Locked by calling user and unchanged.
2174;;;
2175;;; D. Locked by the calling user and changed.
2176;;;
2177;;; E. Locked by someone other than the calling user.
2178;;;
2179;;; This makes for 25 states and 20 error conditions. Here's the matrix:
2180;;;
2181;;; VC's idea of state
2182;;; |
2183;;; V Actual state RCS action SCCS action Effect
2184;;; A B C D E
2185;;; A . 1 2 3 4 ci -u -t- admin -fb -i<file> initial admin
2186;;; B 5 . 6 7 8 co -l get -e checkout
2187;;; C 9 10 . 11 12 co -u unget; get revert
2188;;; D 13 14 15 . 16 ci -u -m<comment> delta -y<comment>; get checkin
2189;;; E 17 18 19 20 . rcs -u -M ; rcs -l unget -n ; get -g steal lock
2190;;;
2191;;; All commands take the master file name as a last argument (not shown).
2192;;;
2193;;; In the discussion below, a "self-race" is a pathological situation in
2194;;; which VC operations are being attempted simultaneously by two or more
2195;;; Emacsen running under the same username.
2196;;;
2197;;; The vc-next-action code has the following windows:
2198;;;
2199;;; Window P:
2200;;; Between the check for existence of a master file and the call to
2201;;; admin/checkin in vc-buffer-admin (apparent state A). This window may
2202;;; never close if the initial-comment feature is on.
2203;;;
2204;;; Window Q:
2205;;; Between the call to vc-workfile-unchanged-p in and the immediately
2206;;; following revert (apparent state C).
2207;;;
2208;;; Window R:
2209;;; Between the call to vc-workfile-unchanged-p in and the following
2210;;; checkin (apparent state D). This window may never close.
2211;;;
2212;;; Window S:
2213;;; Between the unlock and the immediately following checkout during a
2214;;; revert operation (apparent state C). Included in window Q.
2215;;;
2216;;; Window T:
2217;;; Between vc-locking-user and the following checkout (apparent state B).
2218;;;
2219;;; Window U:
2220;;; Between vc-locking-user and the following revert (apparent state C).
2221;;; Includes windows Q and S.
2222;;;
2223;;; Window V:
2224;;; Between vc-locking-user and the following checkin (apparent state
2225;;; D). This window may never be closed if the user fails to complete the
2226;;; checkin message. Includes window R.
2227;;;
2228;;; Window W:
2229;;; Between vc-locking-user and the following steal-lock (apparent
34291cd2 2230;;; state E). This window may never close if the user fails to complete
594722a8
ER
2231;;; the steal-lock message. Includes window X.
2232;;;
2233;;; Window X:
2234;;; Between the unlock and the immediately following re-lock during a
2235;;; steal-lock operation (apparent state E). This window may never cloce
2236;;; if the user fails to complete the steal-lock message.
2237;;;
2238;;; Errors:
2239;;;
2240;;; Apparent state A ---
2241;;;
2242;;; 1. File looked unregistered but is actually registered and not locked.
2243;;;
2244;;; Potential cause: someone else's admin during window P, with
2245;;; caller's admin happening before their checkout.
2246;;;
2247;;; RCS: ci will fail with a "no lock set by <user>" message.
2248;;; SCCS: admin will fail with error (ad19).
2249;;;
2250;;; We can let these errors be passed up to the user.
2251;;;
2252;;; 2. File looked unregistered but is actually locked by caller, unchanged.
2253;;;
2254;;; Potential cause: self-race during window P.
2255;;;
2256;;; RCS: will revert the file to the last saved version and unlock it.
2257;;; SCCS: will fail with error (ad19).
2258;;;
2259;;; Either of these consequences is acceptable.
2260;;;
2261;;; 3. File looked unregistered but is actually locked by caller, changed.
2262;;;
2263;;; Potential cause: self-race during window P.
2264;;;
2265;;; RCS: will register the caller's workfile as a delta with a
2266;;; null change comment (the -t- switch will be ignored).
2267;;; SCCS: will fail with error (ad19).
2268;;;
2269;;; 4. File looked unregistered but is locked by someone else.
2270;;;
2271;;; Potential cause: someone else's admin during window P, with
2272;;; caller's admin happening *after* their checkout.
2273;;;
2274;;; RCS: will fail with a "no lock set by <user>" message.
2275;;; SCCS: will fail with error (ad19).
2276;;;
2277;;; We can let these errors be passed up to the user.
2278;;;
2279;;; Apparent state B ---
2280;;;
2281;;; 5. File looked registered and not locked, but is actually unregistered.
2282;;;
2283;;; Potential cause: master file got nuked during window P.
2284;;;
2285;;; RCS: will fail with "RCS/<file>: No such file or directory"
2286;;; SCCS: will fail with error ut4.
2287;;;
2288;;; We can let these errors be passed up to the user.
2289;;;
2290;;; 6. File looked registered and not locked, but is actually locked by the
2291;;; calling user and unchanged.
2292;;;
2293;;; Potential cause: self-race during window T.
2294;;;
2295;;; RCS: in the same directory as the previous workfile, co -l will fail
2296;;; with "co error: writable foo exists; checkout aborted". In any other
2297;;; directory, checkout will succeed.
2298;;; SCCS: will fail with ge17.
2299;;;
2300;;; Either of these consequences is acceptable.
2301;;;
2302;;; 7. File looked registered and not locked, but is actually locked by the
2303;;; calling user and changed.
2304;;;
2305;;; As case 6.
2306;;;
2307;;; 8. File looked registered and not locked, but is actually locked by another
2308;;; user.
2309;;;
2310;;; Potential cause: someone else checks it out during window T.
2311;;;
2312;;; RCS: co error: revision 1.3 already locked by <user>
2313;;; SCCS: fails with ge4 (in directory) or ut7 (outside it).
2314;;;
2315;;; We can let these errors be passed up to the user.
2316;;;
2317;;; Apparent state C ---
2318;;;
2319;;; 9. File looks locked by calling user and unchanged, but is unregistered.
2320;;;
2321;;; As case 5.
2322;;;
2323;;; 10. File looks locked by calling user and unchanged, but is actually not
2324;;; locked.
2325;;;
2326;;; Potential cause: a self-race in window U, or by the revert's
2327;;; landing during window X of some other user's steal-lock or window S
2328;;; of another user's revert.
2329;;;
2330;;; RCS: succeeds, refreshing the file from the identical version in
2331;;; the master.
2332;;; SCCS: fails with error ut4 (p file nonexistent).
2333;;;
2334;;; Either of these consequences is acceptable.
2335;;;
2336;;; 11. File is locked by calling user. It looks unchanged, but is actually
2337;;; changed.
2338;;;
2339;;; Potential cause: the file would have to be touched by a self-race
2340;;; during window Q.
2341;;;
2342;;; The revert will succeed, removing whatever changes came with
2343;;; the touch. It is theoretically possible that work could be lost.
2344;;;
2345;;; 12. File looks like it's locked by the calling user and unchanged, but
2346;;; it's actually locked by someone else.
2347;;;
2348;;; Potential cause: a steal-lock in window V.
2349;;;
2350;;; RCS: co error: revision <rev> locked by <user>; use co -r or rcs -u
2351;;; SCCS: fails with error un2
2352;;;
2353;;; We can pass these errors up to the user.
2354;;;
2355;;; Apparent state D ---
2356;;;
2357;;; 13. File looks like it's locked by the calling user and changed, but it's
2358;;; actually unregistered.
2359;;;
2360;;; Potential cause: master file got nuked during window P.
2361;;;
2362;;; RCS: Checks in the user's version as an initial delta.
2363;;; SCCS: will fail with error ut4.
2364;;;
2365;;; This case is kind of nasty. It means VC may fail to detect the
2366;;; loss of previous version information.
2367;;;
2368;;; 14. File looks like it's locked by the calling user and changed, but it's
2369;;; actually unlocked.
2370;;;
2371;;; Potential cause: self-race in window V, or the checkin happening
2372;;; during the window X of someone else's steal-lock or window S of
2373;;; someone else's revert.
2374;;;
2375;;; RCS: ci will fail with "no lock set by <user>".
2376;;; SCCS: delta will fail with error ut4.
2377;;;
2378;;; 15. File looks like it's locked by the calling user and changed, but it's
2379;;; actually locked by the calling user and unchanged.
2380;;;
2381;;; Potential cause: another self-race --- a whole checkin/checkout
2382;;; sequence by the calling user would have to land in window R.
2383;;;
2384;;; SCCS: checks in a redundant delta and leaves the file unlocked as usual.
2385;;; RCS: reverts to the file state as of the second user's checkin, leaving
2386;;; the file unlocked.
2387;;;
2388;;; It is theoretically possible that work could be lost under RCS.
2389;;;
2390;;; 16. File looks like it's locked by the calling user and changed, but it's
2391;;; actually locked by a different user.
2392;;;
2393;;; RCS: ci error: no lock set by <user>
2394;;; SCCS: unget will fail with error un2
2395;;;
2396;;; We can pass these errors up to the user.
2397;;;
2398;;; Apparent state E ---
2399;;;
2400;;; 17. File looks like it's locked by some other user, but it's actually
2401;;; unregistered.
2402;;;
2403;;; As case 13.
2404;;;
2405;;; 18. File looks like it's locked by some other user, but it's actually
2406;;; unlocked.
2407;;;
2408;;; Potential cause: someone released a lock during window W.
2409;;;
2410;;; RCS: The calling user will get the lock on the file.
2411;;; SCCS: unget -n will fail with cm4.
2412;;;
2413;;; Either of these consequences will be OK.
2414;;;
2415;;; 19. File looks like it's locked by some other user, but it's actually
2416;;; locked by the calling user and unchanged.
2417;;;
2418;;; Potential cause: the other user relinquishing a lock followed by
2419;;; a self-race, both in window W.
2420;;;
2421;;; Under both RCS and SCCS, both unlock and lock will succeed, making
2422;;; the sequence a no-op.
2423;;;
2424;;; 20. File looks like it's locked by some other user, but it's actually
2425;;; locked by the calling user and changed.
2426;;;
2427;;; As case 19.
2428;;;
2429;;; PROBLEM CASES:
2430;;;
2431;;; In order of decreasing severity:
2432;;;
2433;;; Cases 11 and 15 under RCS are the only one that potentially lose work.
2434;;; They would require a self-race for this to happen.
2435;;;
2436;;; Case 13 in RCS loses information about previous deltas, retaining
2437;;; only the information in the current workfile. This can only happen
2438;;; if the master file gets nuked in window P.
2439;;;
2440;;; Case 3 in RCS and case 15 under SCCS insert a redundant delta with
2441;;; no change comment in the master. This would require a self-race in
2442;;; window P or R respectively.
2443;;;
2444;;; Cases 2, 10, 19 and 20 do extra work, but make no changes.
2445;;;
2446;;; Unfortunately, it appears to me that no recovery is possible in these
2447;;; cases. They don't yield error messages, so there's no way to tell that
2448;;; a race condition has occurred.
2449;;;
2450;;; All other cases don't change either the workfile or the master, and
2451;;; trigger command errors which the user will see.
2452;;;
2453;;; Thus, there is no explicit recovery code.
2454
2455;;; vc.el ends here