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